diff --git a/3rdparty/Archive/Tar.php b/3rdparty/Archive/Tar.php
new file mode 100644
index 0000000000000000000000000000000000000000..d8eae851bdcde846279a7832bbfca0ac9181c7db
--- /dev/null
+++ b/3rdparty/Archive/Tar.php
@@ -0,0 +1,1954 @@
+<?php
+/* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4: */
+
+/**
+ * File::CSV
+ *
+ * PHP versions 4 and 5
+ *
+ * Copyright (c) 1997-2008,
+ * Vincent Blavet <vincent@phpconcept.net>
+ * All rights reserved.
+ *
+ * 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.
+ *
+ * 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 COPYRIGHT OWNER 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.
+ *
+ * @category  File_Formats
+ * @package   Archive_Tar
+ * @author    Vincent Blavet <vincent@phpconcept.net>
+ * @copyright 1997-2010 The Authors
+ * @license   http://www.opensource.org/licenses/bsd-license.php New BSD License
+ * @version   CVS: $Id: Tar.php 323476 2012-02-24 15:27:26Z mrook $
+ * @link      http://pear.php.net/package/Archive_Tar
+ */
+
+require_once 'PEAR.php';
+
+define('ARCHIVE_TAR_ATT_SEPARATOR', 90001);
+define('ARCHIVE_TAR_END_BLOCK', pack("a512", ''));
+
+/**
+* Creates a (compressed) Tar archive
+*
+* @package Archive_Tar
+* @author  Vincent Blavet <vincent@phpconcept.net>
+* @license http://www.opensource.org/licenses/bsd-license.php New BSD License
+* @version $Revision: 323476 $
+*/
+class Archive_Tar extends PEAR
+{
+    /**
+    * @var string Name of the Tar
+    */
+    var $_tarname='';
+
+    /**
+    * @var boolean if true, the Tar file will be gzipped
+    */
+    var $_compress=false;
+
+    /**
+    * @var string Type of compression : 'none', 'gz' or 'bz2'
+    */
+    var $_compress_type='none';
+
+    /**
+    * @var string Explode separator
+    */
+    var $_separator=' ';
+
+    /**
+    * @var file descriptor
+    */
+    var $_file=0;
+
+    /**
+    * @var string Local Tar name of a remote Tar (http:// or ftp://)
+    */
+    var $_temp_tarname='';
+
+    /**
+    * @var string regular expression for ignoring files or directories
+    */
+    var $_ignore_regexp='';
+
+    /**
+     * @var object PEAR_Error object
+     */
+    var $error_object=null; 
+
+    // {{{ constructor
+    /**
+    * Archive_Tar Class constructor. This flavour of the constructor only
+    * declare a new Archive_Tar object, identifying it by the name of the
+    * tar file.
+    * If the compress argument is set the tar will be read or created as a
+    * gzip or bz2 compressed TAR file.
+    *
+    * @param string $p_tarname  The name of the tar archive to create
+    * @param string $p_compress can be null, 'gz' or 'bz2'. This
+    *               parameter indicates if gzip or bz2 compression
+    *               is required.  For compatibility reason the
+    *               boolean value 'true' means 'gz'.
+    *
+    * @access public
+    */
+    function Archive_Tar($p_tarname, $p_compress = null)
+    {
+        $this->PEAR();
+        $this->_compress = false;
+        $this->_compress_type = 'none';
+        if (($p_compress === null) || ($p_compress == '')) {
+            if (@file_exists($p_tarname)) {
+                if ($fp = @fopen($p_tarname, "rb")) {
+                    // look for gzip magic cookie
+                    $data = fread($fp, 2);
+                    fclose($fp);
+                    if ($data == "\37\213") {
+                        $this->_compress = true;
+                        $this->_compress_type = 'gz';
+                        // No sure it's enought for a magic code ....
+                    } elseif ($data == "BZ") {
+                        $this->_compress = true;
+                        $this->_compress_type = 'bz2';
+                    }
+                }
+            } else {
+                // probably a remote file or some file accessible
+                // through a stream interface
+                if (substr($p_tarname, -2) == 'gz') {
+                    $this->_compress = true;
+                    $this->_compress_type = 'gz';
+                } elseif ((substr($p_tarname, -3) == 'bz2') ||
+                          (substr($p_tarname, -2) == 'bz')) {
+                    $this->_compress = true;
+                    $this->_compress_type = 'bz2';
+                }
+            }
+        } else {
+            if (($p_compress === true) || ($p_compress == 'gz')) {
+                $this->_compress = true;
+                $this->_compress_type = 'gz';
+            } else if ($p_compress == 'bz2') {
+                $this->_compress = true;
+                $this->_compress_type = 'bz2';
+            } else {
+                $this->_error("Unsupported compression type '$p_compress'\n".
+                    "Supported types are 'gz' and 'bz2'.\n");
+                return false;
+            }
+        }
+        $this->_tarname = $p_tarname;
+        if ($this->_compress) { // assert zlib or bz2 extension support
+            if ($this->_compress_type == 'gz')
+                $extname = 'zlib';
+            else if ($this->_compress_type == 'bz2')
+                $extname = 'bz2';
+
+            if (!extension_loaded($extname)) {
+                PEAR::loadExtension($extname);
+            }
+            if (!extension_loaded($extname)) {
+                $this->_error("The extension '$extname' couldn't be found.\n".
+                    "Please make sure your version of PHP was built ".
+                    "with '$extname' support.\n");
+                return false;
+            }
+        }
+    }
+    // }}}
+
+    // {{{ destructor
+    function _Archive_Tar()
+    {
+        $this->_close();
+        // ----- Look for a local copy to delete
+        if ($this->_temp_tarname != '')
+            @unlink($this->_temp_tarname);
+        $this->_PEAR();
+    }
+    // }}}
+
+    // {{{ create()
+    /**
+    * This method creates the archive file and add the files / directories
+    * that are listed in $p_filelist.
+    * If a file with the same name exist and is writable, it is replaced
+    * by the new tar.
+    * The method return false and a PEAR error text.
+    * The $p_filelist parameter can be an array of string, each string
+    * representing a filename or a directory name with their path if
+    * needed. It can also be a single string with names separated by a
+    * single blank.
+    * For each directory added in the archive, the files and
+    * sub-directories are also added.
+    * See also createModify() method for more details.
+    *
+    * @param array $p_filelist An array of filenames and directory names, or a
+    *              single string with names separated by a single
+    *              blank space.
+    *
+    * @return true on success, false on error.
+    * @see    createModify()
+    * @access public
+    */
+    function create($p_filelist)
+    {
+        return $this->createModify($p_filelist, '', '');
+    }
+    // }}}
+
+    // {{{ add()
+    /**
+    * This method add the files / directories that are listed in $p_filelist in
+    * the archive. If the archive does not exist it is created.
+    * The method return false and a PEAR error text.
+    * The files and directories listed are only added at the end of the archive,
+    * even if a file with the same name is already archived.
+    * See also createModify() method for more details.
+    *
+    * @param array $p_filelist An array of filenames and directory names, or a
+    *              single string with names separated by a single
+    *              blank space.
+    *
+    * @return true on success, false on error.
+    * @see    createModify()
+    * @access public
+    */
+    function add($p_filelist)
+    {
+        return $this->addModify($p_filelist, '', '');
+    }
+    // }}}
+
+    // {{{ extract()
+    function extract($p_path='', $p_preserve=false)
+    {
+        return $this->extractModify($p_path, '', $p_preserve);
+    }
+    // }}}
+
+    // {{{ listContent()
+    function listContent()
+    {
+        $v_list_detail = array();
+
+        if ($this->_openRead()) {
+            if (!$this->_extractList('', $v_list_detail, "list", '', '')) {
+                unset($v_list_detail);
+                $v_list_detail = 0;
+            }
+            $this->_close();
+        }
+
+        return $v_list_detail;
+    }
+    // }}}
+
+    // {{{ createModify()
+    /**
+    * This method creates the archive file and add the files / directories
+    * that are listed in $p_filelist.
+    * If the file already exists and is writable, it is replaced by the
+    * new tar. It is a create and not an add. If the file exists and is
+    * read-only or is a directory it is not replaced. The method return
+    * false and a PEAR error text.
+    * The $p_filelist parameter can be an array of string, each string
+    * representing a filename or a directory name with their path if
+    * needed. It can also be a single string with names separated by a
+    * single blank.
+    * The path indicated in $p_remove_dir will be removed from the
+    * memorized path of each file / directory listed when this path
+    * exists. By default nothing is removed (empty path '')
+    * The path indicated in $p_add_dir will be added at the beginning of
+    * the memorized path of each file / directory listed. However it can
+    * be set to empty ''. The adding of a path is done after the removing
+    * of path.
+    * The path add/remove ability enables the user to prepare an archive
+    * for extraction in a different path than the origin files are.
+    * See also addModify() method for file adding properties.
+    *
+    * @param array  $p_filelist   An array of filenames and directory names,
+    *                             or a single string with names separated by
+    *                             a single blank space.
+    * @param string $p_add_dir    A string which contains a path to be added
+    *                             to the memorized path of each element in
+    *                             the list.
+    * @param string $p_remove_dir A string which contains a path to be
+    *                             removed from the memorized path of each
+    *                             element in the list, when relevant.
+    *
+    * @return boolean true on success, false on error.
+    * @access public
+    * @see addModify()
+    */
+    function createModify($p_filelist, $p_add_dir, $p_remove_dir='')
+    {
+        $v_result = true;
+
+        if (!$this->_openWrite())
+            return false;
+
+        if ($p_filelist != '') {
+            if (is_array($p_filelist))
+                $v_list = $p_filelist;
+            elseif (is_string($p_filelist))
+                $v_list = explode($this->_separator, $p_filelist);
+            else {
+                $this->_cleanFile();
+                $this->_error('Invalid file list');
+                return false;
+            }
+
+            $v_result = $this->_addList($v_list, $p_add_dir, $p_remove_dir);
+        }
+
+        if ($v_result) {
+            $this->_writeFooter();
+            $this->_close();
+        } else
+            $this->_cleanFile();
+
+        return $v_result;
+    }
+    // }}}
+
+    // {{{ addModify()
+    /**
+    * This method add the files / directories listed in $p_filelist at the
+    * end of the existing archive. If the archive does not yet exists it
+    * is created.
+    * The $p_filelist parameter can be an array of string, each string
+    * representing a filename or a directory name with their path if
+    * needed. It can also be a single string with names separated by a
+    * single blank.
+    * The path indicated in $p_remove_dir will be removed from the
+    * memorized path of each file / directory listed when this path
+    * exists. By default nothing is removed (empty path '')
+    * The path indicated in $p_add_dir will be added at the beginning of
+    * the memorized path of each file / directory listed. However it can
+    * be set to empty ''. The adding of a path is done after the removing
+    * of path.
+    * The path add/remove ability enables the user to prepare an archive
+    * for extraction in a different path than the origin files are.
+    * If a file/dir is already in the archive it will only be added at the
+    * end of the archive. There is no update of the existing archived
+    * file/dir. However while extracting the archive, the last file will
+    * replace the first one. This results in a none optimization of the
+    * archive size.
+    * If a file/dir does not exist the file/dir is ignored. However an
+    * error text is send to PEAR error.
+    * If a file/dir is not readable the file/dir is ignored. However an
+    * error text is send to PEAR error.
+    *
+    * @param array  $p_filelist   An array of filenames and directory
+    *                             names, or a single string with names
+    *                             separated by a single blank space.
+    * @param string $p_add_dir    A string which contains a path to be
+    *                             added to the memorized path of each
+    *                             element in the list.
+    * @param string $p_remove_dir A string which contains a path to be
+    *                             removed from the memorized path of
+    *                             each element in the list, when
+    *                             relevant.
+    *
+    * @return true on success, false on error.
+    * @access public
+    */
+    function addModify($p_filelist, $p_add_dir, $p_remove_dir='')
+    {
+        $v_result = true;
+
+        if (!$this->_isArchive())
+            $v_result = $this->createModify($p_filelist, $p_add_dir,
+                                            $p_remove_dir);
+        else {
+            if (is_array($p_filelist))
+                $v_list = $p_filelist;
+            elseif (is_string($p_filelist))
+                $v_list = explode($this->_separator, $p_filelist);
+            else {
+                $this->_error('Invalid file list');
+                return false;
+            }
+
+            $v_result = $this->_append($v_list, $p_add_dir, $p_remove_dir);
+        }
+
+        return $v_result;
+    }
+    // }}}
+
+    // {{{ addString()
+    /**
+    * This method add a single string as a file at the
+    * end of the existing archive. If the archive does not yet exists it
+    * is created.
+    *
+    * @param string $p_filename A string which contains the full
+    *                           filename path that will be associated
+    *                           with the string.
+    * @param string $p_string   The content of the file added in
+    *                           the archive.
+    *
+    * @return true on success, false on error.
+    * @access public
+    */
+    function addString($p_filename, $p_string)
+    {
+        $v_result = true;
+
+        if (!$this->_isArchive()) {
+            if (!$this->_openWrite()) {
+                return false;
+            }
+            $this->_close();
+        }
+
+        if (!$this->_openAppend())
+            return false;
+
+        // Need to check the get back to the temporary file ? ....
+        $v_result = $this->_addString($p_filename, $p_string);
+
+        $this->_writeFooter();
+
+        $this->_close();
+
+        return $v_result;
+    }
+    // }}}
+
+    // {{{ extractModify()
+    /**
+    * This method extract all the content of the archive in the directory
+    * indicated by $p_path. When relevant the memorized path of the
+    * files/dir can be modified by removing the $p_remove_path path at the
+    * beginning of the file/dir path.
+    * While extracting a file, if the directory path does not exists it is
+    * created.
+    * While extracting a file, if the file already exists it is replaced
+    * without looking for last modification date.
+    * While extracting a file, if the file already exists and is write
+    * protected, the extraction is aborted.
+    * While extracting a file, if a directory with the same name already
+    * exists, the extraction is aborted.
+    * While extracting a directory, if a file with the same name already
+    * exists, the extraction is aborted.
+    * While extracting a file/directory if the destination directory exist
+    * and is write protected, or does not exist but can not be created,
+    * the extraction is aborted.
+    * If after extraction an extracted file does not show the correct
+    * stored file size, the extraction is aborted.
+    * When the extraction is aborted, a PEAR error text is set and false
+    * is returned. However the result can be a partial extraction that may
+    * need to be manually cleaned.
+    *
+    * @param string  $p_path        The path of the directory where the
+    *                               files/dir need to by extracted.
+    * @param string  $p_remove_path Part of the memorized path that can be
+    *                               removed if present at the beginning of
+    *                               the file/dir path.
+    * @param boolean $p_preserve    Preserve user/group ownership of files
+    *
+    * @return boolean true on success, false on error.
+    * @access public
+    * @see    extractList()
+    */
+    function extractModify($p_path, $p_remove_path, $p_preserve=false)
+    {
+        $v_result = true;
+        $v_list_detail = array();
+
+        if ($v_result = $this->_openRead()) {
+            $v_result = $this->_extractList($p_path, $v_list_detail,
+                "complete", 0, $p_remove_path, $p_preserve);
+            $this->_close();
+        }
+
+        return $v_result;
+    }
+    // }}}
+
+    // {{{ extractInString()
+    /**
+    * This method extract from the archive one file identified by $p_filename.
+    * The return value is a string with the file content, or NULL on error.
+    *
+    * @param string $p_filename The path of the file to extract in a string.
+    *
+    * @return a string with the file content or NULL.
+    * @access public
+    */
+    function extractInString($p_filename)
+    {
+        if ($this->_openRead()) {
+            $v_result = $this->_extractInString($p_filename);
+            $this->_close();
+        } else {
+            $v_result = null;
+        }
+
+        return $v_result;
+    }
+    // }}}
+
+    // {{{ extractList()
+    /**
+    * This method extract from the archive only the files indicated in the
+    * $p_filelist. These files are extracted in the current directory or
+    * in the directory indicated by the optional $p_path parameter.
+    * If indicated the $p_remove_path can be used in the same way as it is
+    * used in extractModify() method.
+    *
+    * @param array   $p_filelist    An array of filenames and directory names,
+    *                               or a single string with names separated
+    *                               by a single blank space.
+    * @param string  $p_path        The path of the directory where the
+    *                               files/dir need to by extracted.
+    * @param string  $p_remove_path Part of the memorized path that can be
+    *                               removed if present at the beginning of
+    *                               the file/dir path.
+    * @param boolean $p_preserve    Preserve user/group ownership of files
+    *
+    * @return true on success, false on error.
+    * @access public
+    * @see    extractModify()
+    */
+    function extractList($p_filelist, $p_path='', $p_remove_path='', $p_preserve=false)
+    {
+        $v_result = true;
+        $v_list_detail = array();
+
+        if (is_array($p_filelist))
+            $v_list = $p_filelist;
+        elseif (is_string($p_filelist))
+            $v_list = explode($this->_separator, $p_filelist);
+        else {
+            $this->_error('Invalid string list');
+            return false;
+        }
+
+        if ($v_result = $this->_openRead()) {
+            $v_result = $this->_extractList($p_path, $v_list_detail, "partial",
+                $v_list, $p_remove_path, $p_preserve);
+            $this->_close();
+        }
+
+        return $v_result;
+    }
+    // }}}
+
+    // {{{ setAttribute()
+    /**
+    * This method set specific attributes of the archive. It uses a variable
+    * list of parameters, in the format attribute code + attribute values :
+    * $arch->setAttribute(ARCHIVE_TAR_ATT_SEPARATOR, ',');
+    *
+    * @param mixed $argv variable list of attributes and values
+    *
+    * @return true on success, false on error.
+    * @access public
+    */
+    function setAttribute()
+    {
+        $v_result = true;
+
+        // ----- Get the number of variable list of arguments
+        if (($v_size = func_num_args()) == 0) {
+            return true;
+        }
+
+        // ----- Get the arguments
+        $v_att_list = &func_get_args();
+
+        // ----- Read the attributes
+        $i=0;
+        while ($i<$v_size) {
+
+            // ----- Look for next option
+            switch ($v_att_list[$i]) {
+                // ----- Look for options that request a string value
+                case ARCHIVE_TAR_ATT_SEPARATOR :
+                    // ----- Check the number of parameters
+                    if (($i+1) >= $v_size) {
+                        $this->_error('Invalid number of parameters for '
+						              .'attribute ARCHIVE_TAR_ATT_SEPARATOR');
+                        return false;
+                    }
+
+                    // ----- Get the value
+                    $this->_separator = $v_att_list[$i+1];
+                    $i++;
+                break;
+
+                default :
+                    $this->_error('Unknow attribute code '.$v_att_list[$i].'');
+                    return false;
+            }
+
+            // ----- Next attribute
+            $i++;
+        }
+
+        return $v_result;
+    }
+    // }}}
+
+    // {{{ setIgnoreRegexp()
+    /**
+    * This method sets the regular expression for ignoring files and directories
+    * at import, for example:
+    * $arch->setIgnoreRegexp("#CVS|\.svn#");
+    *
+    * @param string $regexp regular expression defining which files or directories to ignore
+    *
+    * @access public
+    */
+    function setIgnoreRegexp($regexp)
+    {
+    	$this->_ignore_regexp = $regexp;
+    }
+    // }}}
+
+    // {{{ setIgnoreList()
+    /**
+    * This method sets the regular expression for ignoring all files and directories
+    * matching the filenames in the array list at import, for example:
+    * $arch->setIgnoreList(array('CVS', '.svn', 'bin/tool'));
+    *
+    * @param array $list a list of file or directory names to ignore
+    *
+    * @access public
+    */
+    function setIgnoreList($list)
+    {
+    	$regexp = str_replace(array('#', '.', '^', '$'), array('\#', '\.', '\^', '\$'), $list);
+    	$regexp = '#/'.join('$|/', $list).'#';
+    	$this->setIgnoreRegexp($regexp);
+    }
+    // }}}
+
+    // {{{ _error()
+    function _error($p_message)
+    {
+        $this->error_object = $this->raiseError($p_message);
+    }
+    // }}}
+
+    // {{{ _warning()
+    function _warning($p_message)
+    {
+        $this->error_object = $this->raiseError($p_message);
+    }
+    // }}}
+
+    // {{{ _isArchive()
+    function _isArchive($p_filename=null)
+    {
+        if ($p_filename == null) {
+            $p_filename = $this->_tarname;
+        }
+        clearstatcache();
+        return @is_file($p_filename) && !@is_link($p_filename);
+    }
+    // }}}
+
+    // {{{ _openWrite()
+    function _openWrite()
+    {
+        if ($this->_compress_type == 'gz' && function_exists('gzopen'))
+            $this->_file = @gzopen($this->_tarname, "wb9");
+        else if ($this->_compress_type == 'bz2' && function_exists('bzopen'))
+            $this->_file = @bzopen($this->_tarname, "w");
+        else if ($this->_compress_type == 'none')
+            $this->_file = @fopen($this->_tarname, "wb");
+        else
+            $this->_error('Unknown or missing compression type ('
+			              .$this->_compress_type.')');
+
+        if ($this->_file == 0) {
+            $this->_error('Unable to open in write mode \''
+			              .$this->_tarname.'\'');
+            return false;
+        }
+
+        return true;
+    }
+    // }}}
+
+    // {{{ _openRead()
+    function _openRead()
+    {
+        if (strtolower(substr($this->_tarname, 0, 7)) == 'http://') {
+
+          // ----- Look if a local copy need to be done
+          if ($this->_temp_tarname == '') {
+              $this->_temp_tarname = uniqid('tar').'.tmp';
+              if (!$v_file_from = @fopen($this->_tarname, 'rb')) {
+                $this->_error('Unable to open in read mode \''
+				              .$this->_tarname.'\'');
+                $this->_temp_tarname = '';
+                return false;
+              }
+              if (!$v_file_to = @fopen($this->_temp_tarname, 'wb')) {
+                $this->_error('Unable to open in write mode \''
+				              .$this->_temp_tarname.'\'');
+                $this->_temp_tarname = '';
+                return false;
+              }
+              while ($v_data = @fread($v_file_from, 1024))
+                  @fwrite($v_file_to, $v_data);
+              @fclose($v_file_from);
+              @fclose($v_file_to);
+          }
+
+          // ----- File to open if the local copy
+          $v_filename = $this->_temp_tarname;
+
+        } else
+          // ----- File to open if the normal Tar file
+          $v_filename = $this->_tarname;
+
+        if ($this->_compress_type == 'gz')
+            $this->_file = @gzopen($v_filename, "rb");
+        else if ($this->_compress_type == 'bz2')
+            $this->_file = @bzopen($v_filename, "r");
+        else if ($this->_compress_type == 'none')
+            $this->_file = @fopen($v_filename, "rb");
+        else
+            $this->_error('Unknown or missing compression type ('
+			              .$this->_compress_type.')');
+
+        if ($this->_file == 0) {
+            $this->_error('Unable to open in read mode \''.$v_filename.'\'');
+            return false;
+        }
+
+        return true;
+    }
+    // }}}
+
+    // {{{ _openReadWrite()
+    function _openReadWrite()
+    {
+        if ($this->_compress_type == 'gz')
+            $this->_file = @gzopen($this->_tarname, "r+b");
+        else if ($this->_compress_type == 'bz2') {
+            $this->_error('Unable to open bz2 in read/write mode \''
+			              .$this->_tarname.'\' (limitation of bz2 extension)');
+            return false;
+        } else if ($this->_compress_type == 'none')
+            $this->_file = @fopen($this->_tarname, "r+b");
+        else
+            $this->_error('Unknown or missing compression type ('
+			              .$this->_compress_type.')');
+
+        if ($this->_file == 0) {
+            $this->_error('Unable to open in read/write mode \''
+			              .$this->_tarname.'\'');
+            return false;
+        }
+
+        return true;
+    }
+    // }}}
+
+    // {{{ _close()
+    function _close()
+    {
+        //if (isset($this->_file)) {
+        if (is_resource($this->_file)) {
+            if ($this->_compress_type == 'gz')
+                @gzclose($this->_file);
+            else if ($this->_compress_type == 'bz2')
+                @bzclose($this->_file);
+            else if ($this->_compress_type == 'none')
+                @fclose($this->_file);
+            else
+                $this->_error('Unknown or missing compression type ('
+				              .$this->_compress_type.')');
+
+            $this->_file = 0;
+        }
+
+        // ----- Look if a local copy need to be erase
+        // Note that it might be interesting to keep the url for a time : ToDo
+        if ($this->_temp_tarname != '') {
+            @unlink($this->_temp_tarname);
+            $this->_temp_tarname = '';
+        }
+
+        return true;
+    }
+    // }}}
+
+    // {{{ _cleanFile()
+    function _cleanFile()
+    {
+        $this->_close();
+
+        // ----- Look for a local copy
+        if ($this->_temp_tarname != '') {
+            // ----- Remove the local copy but not the remote tarname
+            @unlink($this->_temp_tarname);
+            $this->_temp_tarname = '';
+        } else {
+            // ----- Remove the local tarname file
+            @unlink($this->_tarname);
+        }
+        $this->_tarname = '';
+
+        return true;
+    }
+    // }}}
+
+    // {{{ _writeBlock()
+    function _writeBlock($p_binary_data, $p_len=null)
+    {
+      if (is_resource($this->_file)) {
+          if ($p_len === null) {
+              if ($this->_compress_type == 'gz')
+                  @gzputs($this->_file, $p_binary_data);
+              else if ($this->_compress_type == 'bz2')
+                  @bzwrite($this->_file, $p_binary_data);
+              else if ($this->_compress_type == 'none')
+                  @fputs($this->_file, $p_binary_data);
+              else
+                  $this->_error('Unknown or missing compression type ('
+				                .$this->_compress_type.')');
+          } else {
+              if ($this->_compress_type == 'gz')
+                  @gzputs($this->_file, $p_binary_data, $p_len);
+              else if ($this->_compress_type == 'bz2')
+                  @bzwrite($this->_file, $p_binary_data, $p_len);
+              else if ($this->_compress_type == 'none')
+                  @fputs($this->_file, $p_binary_data, $p_len);
+              else
+                  $this->_error('Unknown or missing compression type ('
+				                .$this->_compress_type.')');
+
+          }
+      }
+      return true;
+    }
+    // }}}
+
+    // {{{ _readBlock()
+    function _readBlock()
+    {
+      $v_block = null;
+      if (is_resource($this->_file)) {
+          if ($this->_compress_type == 'gz')
+              $v_block = @gzread($this->_file, 512);
+          else if ($this->_compress_type == 'bz2')
+              $v_block = @bzread($this->_file, 512);
+          else if ($this->_compress_type == 'none')
+              $v_block = @fread($this->_file, 512);
+          else
+              $this->_error('Unknown or missing compression type ('
+			                .$this->_compress_type.')');
+      }
+      return $v_block;
+    }
+    // }}}
+
+    // {{{ _jumpBlock()
+    function _jumpBlock($p_len=null)
+    {
+      if (is_resource($this->_file)) {
+          if ($p_len === null)
+              $p_len = 1;
+
+          if ($this->_compress_type == 'gz') {
+              @gzseek($this->_file, gztell($this->_file)+($p_len*512));
+          }
+          else if ($this->_compress_type == 'bz2') {
+              // ----- Replace missing bztell() and bzseek()
+              for ($i=0; $i<$p_len; $i++)
+                  $this->_readBlock();
+          } else if ($this->_compress_type == 'none')
+              @fseek($this->_file, $p_len*512, SEEK_CUR);
+          else
+              $this->_error('Unknown or missing compression type ('
+			                .$this->_compress_type.')');
+
+      }
+      return true;
+    }
+    // }}}
+
+    // {{{ _writeFooter()
+    function _writeFooter()
+    {
+      if (is_resource($this->_file)) {
+          // ----- Write the last 0 filled block for end of archive
+          $v_binary_data = pack('a1024', '');
+          $this->_writeBlock($v_binary_data);
+      }
+      return true;
+    }
+    // }}}
+
+    // {{{ _addList()
+    function _addList($p_list, $p_add_dir, $p_remove_dir)
+    {
+      $v_result=true;
+      $v_header = array();
+
+      // ----- Remove potential windows directory separator
+      $p_add_dir = $this->_translateWinPath($p_add_dir);
+      $p_remove_dir = $this->_translateWinPath($p_remove_dir, false);
+
+      if (!$this->_file) {
+          $this->_error('Invalid file descriptor');
+          return false;
+      }
+
+      if (sizeof($p_list) == 0)
+          return true;
+
+      foreach ($p_list as $v_filename) {
+          if (!$v_result) {
+              break;
+          }
+
+        // ----- Skip the current tar name
+        if ($v_filename == $this->_tarname)
+            continue;
+
+        if ($v_filename == '')
+            continue;
+
+       	// ----- ignore files and directories matching the ignore regular expression
+       	if ($this->_ignore_regexp && preg_match($this->_ignore_regexp, '/'.$v_filename)) {
+            $this->_warning("File '$v_filename' ignored");
+       	    continue;
+       	}
+
+        if (!file_exists($v_filename) && !is_link($v_filename)) {
+            $this->_warning("File '$v_filename' does not exist");
+            continue;
+        }
+
+        // ----- Add the file or directory header
+        if (!$this->_addFile($v_filename, $v_header, $p_add_dir, $p_remove_dir))
+            return false;
+
+        if (@is_dir($v_filename) && !@is_link($v_filename)) {
+            if (!($p_hdir = opendir($v_filename))) {
+                $this->_warning("Directory '$v_filename' can not be read");
+                continue;
+            }
+            while (false !== ($p_hitem = readdir($p_hdir))) {
+                if (($p_hitem != '.') && ($p_hitem != '..')) {
+                    if ($v_filename != ".")
+                        $p_temp_list[0] = $v_filename.'/'.$p_hitem;
+                    else
+                        $p_temp_list[0] = $p_hitem;
+
+                    $v_result = $this->_addList($p_temp_list,
+					                            $p_add_dir,
+												$p_remove_dir);
+                }
+            }
+
+            unset($p_temp_list);
+            unset($p_hdir);
+            unset($p_hitem);
+        }
+      }
+
+      return $v_result;
+    }
+    // }}}
+
+    // {{{ _addFile()
+    function _addFile($p_filename, &$p_header, $p_add_dir, $p_remove_dir,$v_stored_filename=null)
+    {
+      if (!$this->_file) {
+          $this->_error('Invalid file descriptor');
+          return false;
+      }
+
+      if ($p_filename == '') {
+          $this->_error('Invalid file name');
+          return false;
+      }
+      if(is_null($v_stored_filename)){
+
+		// ----- Calculate the stored filename
+		$p_filename = $this->_translateWinPath($p_filename, false);
+		$v_stored_filename = $p_filename;
+		if (strcmp($p_filename, $p_remove_dir) == 0) {
+			return true;
+		}
+		if ($p_remove_dir != '') {
+			if (substr($p_remove_dir, -1) != '/')
+				$p_remove_dir .= '/';
+
+			if (substr($p_filename, 0, strlen($p_remove_dir)) == $p_remove_dir)
+				$v_stored_filename = substr($p_filename, strlen($p_remove_dir));
+		}
+		$v_stored_filename = $this->_translateWinPath($v_stored_filename);
+		if ($p_add_dir != '') {
+			if (substr($p_add_dir, -1) == '/')
+				$v_stored_filename = $p_add_dir.$v_stored_filename;
+			else
+				$v_stored_filename = $p_add_dir.'/'.$v_stored_filename;
+		}
+
+		$v_stored_filename = $this->_pathReduction($v_stored_filename);
+	}
+
+      if ($this->_isArchive($p_filename)) {
+          if (($v_file = @fopen($p_filename, "rb")) == 0) {
+              $this->_warning("Unable to open file '".$p_filename
+			                  ."' in binary read mode");
+              return true;
+          }
+
+          if (!$this->_writeHeader($p_filename, $v_stored_filename))
+              return false;
+
+          while (($v_buffer = fread($v_file, 512)) != '') {
+              $v_binary_data = pack("a512", "$v_buffer");
+              $this->_writeBlock($v_binary_data);
+          }
+
+          fclose($v_file);
+
+      } else {
+          // ----- Only header for dir
+          if (!$this->_writeHeader($p_filename, $v_stored_filename))
+              return false;
+      }
+
+      return true;
+    }
+    // }}}
+
+    // {{{ _addString()
+    function _addString($p_filename, $p_string)
+    {
+      if (!$this->_file) {
+          $this->_error('Invalid file descriptor');
+          return false;
+      }
+
+      if ($p_filename == '') {
+          $this->_error('Invalid file name');
+          return false;
+      }
+
+      // ----- Calculate the stored filename
+      $p_filename = $this->_translateWinPath($p_filename, false);;
+
+      if (!$this->_writeHeaderBlock($p_filename, strlen($p_string),
+	                                  time(), 384, "", 0, 0))
+          return false;
+
+      $i=0;
+      while (($v_buffer = substr($p_string, (($i++)*512), 512)) != '') {
+          $v_binary_data = pack("a512", $v_buffer);
+          $this->_writeBlock($v_binary_data);
+      }
+
+      return true;
+    }
+    // }}}
+
+    // {{{ _writeHeader()
+    function _writeHeader($p_filename, $p_stored_filename)
+    {
+        if ($p_stored_filename == '')
+            $p_stored_filename = $p_filename;
+        $v_reduce_filename = $this->_pathReduction($p_stored_filename);
+
+        if (strlen($v_reduce_filename) > 99) {
+          if (!$this->_writeLongHeader($v_reduce_filename))
+            return false;
+        }
+
+        $v_info = lstat($p_filename);
+        $v_uid = sprintf("%07s", DecOct($v_info[4]));
+        $v_gid = sprintf("%07s", DecOct($v_info[5]));
+        $v_perms = sprintf("%07s", DecOct($v_info['mode'] & 000777));
+
+        $v_mtime = sprintf("%011s", DecOct($v_info['mtime']));
+
+        $v_linkname = '';
+
+        if (@is_link($p_filename)) {
+          $v_typeflag = '2';
+          $v_linkname = readlink($p_filename);
+          $v_size = sprintf("%011s", DecOct(0));
+        } elseif (@is_dir($p_filename)) {
+          $v_typeflag = "5";
+          $v_size = sprintf("%011s", DecOct(0));
+        } else {
+          $v_typeflag = '0';
+          clearstatcache();
+          $v_size = sprintf("%011s", DecOct($v_info['size']));
+        }
+
+        $v_magic = 'ustar ';
+
+        $v_version = ' ';
+        
+        if (function_exists('posix_getpwuid'))
+        {
+          $userinfo = posix_getpwuid($v_info[4]);
+          $groupinfo = posix_getgrgid($v_info[5]);
+          
+          $v_uname = $userinfo['name'];
+          $v_gname = $groupinfo['name'];
+        }
+        else
+        {
+          $v_uname = '';
+          $v_gname = '';
+        }
+
+        $v_devmajor = '';
+
+        $v_devminor = '';
+
+        $v_prefix = '';
+
+        $v_binary_data_first = pack("a100a8a8a8a12a12",
+		                            $v_reduce_filename, $v_perms, $v_uid,
+									$v_gid, $v_size, $v_mtime);
+        $v_binary_data_last = pack("a1a100a6a2a32a32a8a8a155a12",
+		                           $v_typeflag, $v_linkname, $v_magic,
+								   $v_version, $v_uname, $v_gname,
+								   $v_devmajor, $v_devminor, $v_prefix, '');
+
+        // ----- Calculate the checksum
+        $v_checksum = 0;
+        // ..... First part of the header
+        for ($i=0; $i<148; $i++)
+            $v_checksum += ord(substr($v_binary_data_first,$i,1));
+        // ..... Ignore the checksum value and replace it by ' ' (space)
+        for ($i=148; $i<156; $i++)
+            $v_checksum += ord(' ');
+        // ..... Last part of the header
+        for ($i=156, $j=0; $i<512; $i++, $j++)
+            $v_checksum += ord(substr($v_binary_data_last,$j,1));
+
+        // ----- Write the first 148 bytes of the header in the archive
+        $this->_writeBlock($v_binary_data_first, 148);
+
+        // ----- Write the calculated checksum
+        $v_checksum = sprintf("%06s ", DecOct($v_checksum));
+        $v_binary_data = pack("a8", $v_checksum);
+        $this->_writeBlock($v_binary_data, 8);
+
+        // ----- Write the last 356 bytes of the header in the archive
+        $this->_writeBlock($v_binary_data_last, 356);
+
+        return true;
+    }
+    // }}}
+
+    // {{{ _writeHeaderBlock()
+    function _writeHeaderBlock($p_filename, $p_size, $p_mtime=0, $p_perms=0,
+	                           $p_type='', $p_uid=0, $p_gid=0)
+    {
+        $p_filename = $this->_pathReduction($p_filename);
+
+        if (strlen($p_filename) > 99) {
+          if (!$this->_writeLongHeader($p_filename))
+            return false;
+        }
+
+        if ($p_type == "5") {
+          $v_size = sprintf("%011s", DecOct(0));
+        } else {
+          $v_size = sprintf("%011s", DecOct($p_size));
+        }
+
+        $v_uid = sprintf("%07s", DecOct($p_uid));
+        $v_gid = sprintf("%07s", DecOct($p_gid));
+        $v_perms = sprintf("%07s", DecOct($p_perms & 000777));
+
+        $v_mtime = sprintf("%11s", DecOct($p_mtime));
+
+        $v_linkname = '';
+
+        $v_magic = 'ustar ';
+
+        $v_version = ' ';
+
+        if (function_exists('posix_getpwuid'))
+        {
+          $userinfo = posix_getpwuid($p_uid);
+          $groupinfo = posix_getgrgid($p_gid);
+          
+          $v_uname = $userinfo['name'];
+          $v_gname = $groupinfo['name'];
+        }
+        else
+        {
+          $v_uname = '';
+          $v_gname = '';
+        }
+        
+        $v_devmajor = '';
+
+        $v_devminor = '';
+
+        $v_prefix = '';
+
+        $v_binary_data_first = pack("a100a8a8a8a12A12",
+		                            $p_filename, $v_perms, $v_uid, $v_gid,
+									$v_size, $v_mtime);
+        $v_binary_data_last = pack("a1a100a6a2a32a32a8a8a155a12",
+		                           $p_type, $v_linkname, $v_magic,
+								   $v_version, $v_uname, $v_gname,
+								   $v_devmajor, $v_devminor, $v_prefix, '');
+
+        // ----- Calculate the checksum
+        $v_checksum = 0;
+        // ..... First part of the header
+        for ($i=0; $i<148; $i++)
+            $v_checksum += ord(substr($v_binary_data_first,$i,1));
+        // ..... Ignore the checksum value and replace it by ' ' (space)
+        for ($i=148; $i<156; $i++)
+            $v_checksum += ord(' ');
+        // ..... Last part of the header
+        for ($i=156, $j=0; $i<512; $i++, $j++)
+            $v_checksum += ord(substr($v_binary_data_last,$j,1));
+
+        // ----- Write the first 148 bytes of the header in the archive
+        $this->_writeBlock($v_binary_data_first, 148);
+
+        // ----- Write the calculated checksum
+        $v_checksum = sprintf("%06s ", DecOct($v_checksum));
+        $v_binary_data = pack("a8", $v_checksum);
+        $this->_writeBlock($v_binary_data, 8);
+
+        // ----- Write the last 356 bytes of the header in the archive
+        $this->_writeBlock($v_binary_data_last, 356);
+
+        return true;
+    }
+    // }}}
+
+    // {{{ _writeLongHeader()
+    function _writeLongHeader($p_filename)
+    {
+        $v_size = sprintf("%11s ", DecOct(strlen($p_filename)));
+
+        $v_typeflag = 'L';
+
+        $v_linkname = '';
+
+        $v_magic = '';
+
+        $v_version = '';
+
+        $v_uname = '';
+
+        $v_gname = '';
+
+        $v_devmajor = '';
+
+        $v_devminor = '';
+
+        $v_prefix = '';
+
+        $v_binary_data_first = pack("a100a8a8a8a12a12",
+		                            '././@LongLink', 0, 0, 0, $v_size, 0);
+        $v_binary_data_last = pack("a1a100a6a2a32a32a8a8a155a12",
+		                           $v_typeflag, $v_linkname, $v_magic,
+								   $v_version, $v_uname, $v_gname,
+								   $v_devmajor, $v_devminor, $v_prefix, '');
+
+        // ----- Calculate the checksum
+        $v_checksum = 0;
+        // ..... First part of the header
+        for ($i=0; $i<148; $i++)
+            $v_checksum += ord(substr($v_binary_data_first,$i,1));
+        // ..... Ignore the checksum value and replace it by ' ' (space)
+        for ($i=148; $i<156; $i++)
+            $v_checksum += ord(' ');
+        // ..... Last part of the header
+        for ($i=156, $j=0; $i<512; $i++, $j++)
+            $v_checksum += ord(substr($v_binary_data_last,$j,1));
+
+        // ----- Write the first 148 bytes of the header in the archive
+        $this->_writeBlock($v_binary_data_first, 148);
+
+        // ----- Write the calculated checksum
+        $v_checksum = sprintf("%06s ", DecOct($v_checksum));
+        $v_binary_data = pack("a8", $v_checksum);
+        $this->_writeBlock($v_binary_data, 8);
+
+        // ----- Write the last 356 bytes of the header in the archive
+        $this->_writeBlock($v_binary_data_last, 356);
+
+        // ----- Write the filename as content of the block
+        $i=0;
+        while (($v_buffer = substr($p_filename, (($i++)*512), 512)) != '') {
+            $v_binary_data = pack("a512", "$v_buffer");
+            $this->_writeBlock($v_binary_data);
+        }
+
+        return true;
+    }
+    // }}}
+
+    // {{{ _readHeader()
+    function _readHeader($v_binary_data, &$v_header)
+    {
+        if (strlen($v_binary_data)==0) {
+            $v_header['filename'] = '';
+            return true;
+        }
+
+        if (strlen($v_binary_data) != 512) {
+            $v_header['filename'] = '';
+            $this->_error('Invalid block size : '.strlen($v_binary_data));
+            return false;
+        }
+
+        if (!is_array($v_header)) {
+            $v_header = array();
+        }
+        // ----- Calculate the checksum
+        $v_checksum = 0;
+        // ..... First part of the header
+        for ($i=0; $i<148; $i++)
+            $v_checksum+=ord(substr($v_binary_data,$i,1));
+        // ..... Ignore the checksum value and replace it by ' ' (space)
+        for ($i=148; $i<156; $i++)
+            $v_checksum += ord(' ');
+        // ..... Last part of the header
+        for ($i=156; $i<512; $i++)
+           $v_checksum+=ord(substr($v_binary_data,$i,1));
+
+        $v_data = unpack("a100filename/a8mode/a8uid/a8gid/a12size/a12mtime/" .
+                         "a8checksum/a1typeflag/a100link/a6magic/a2version/" .
+                         "a32uname/a32gname/a8devmajor/a8devminor/a131prefix",
+                         $v_binary_data);
+                         
+        if (strlen($v_data["prefix"]) > 0) {
+            $v_data["filename"] = "$v_data[prefix]/$v_data[filename]";
+        }
+
+        // ----- Extract the checksum
+        $v_header['checksum'] = OctDec(trim($v_data['checksum']));
+        if ($v_header['checksum'] != $v_checksum) {
+            $v_header['filename'] = '';
+
+            // ----- Look for last block (empty block)
+            if (($v_checksum == 256) && ($v_header['checksum'] == 0))
+                return true;
+
+            $this->_error('Invalid checksum for file "'.$v_data['filename']
+			              .'" : '.$v_checksum.' calculated, '
+						  .$v_header['checksum'].' expected');
+            return false;
+        }
+
+        // ----- Extract the properties
+        $v_header['filename'] = $v_data['filename'];
+        if ($this->_maliciousFilename($v_header['filename'])) {
+            $this->_error('Malicious .tar detected, file "' . $v_header['filename'] .
+                '" will not install in desired directory tree');
+            return false;
+        }
+        $v_header['mode'] = OctDec(trim($v_data['mode']));
+        $v_header['uid'] = OctDec(trim($v_data['uid']));
+        $v_header['gid'] = OctDec(trim($v_data['gid']));
+        $v_header['size'] = OctDec(trim($v_data['size']));
+        $v_header['mtime'] = OctDec(trim($v_data['mtime']));
+        if (($v_header['typeflag'] = $v_data['typeflag']) == "5") {
+          $v_header['size'] = 0;
+        }
+        $v_header['link'] = trim($v_data['link']);
+        /* ----- All these fields are removed form the header because
+		they do not carry interesting info
+        $v_header[magic] = trim($v_data[magic]);
+        $v_header[version] = trim($v_data[version]);
+        $v_header[uname] = trim($v_data[uname]);
+        $v_header[gname] = trim($v_data[gname]);
+        $v_header[devmajor] = trim($v_data[devmajor]);
+        $v_header[devminor] = trim($v_data[devminor]);
+        */
+
+        return true;
+    }
+    // }}}
+
+    // {{{ _maliciousFilename()
+    /**
+     * Detect and report a malicious file name
+     *
+     * @param string $file
+     *
+     * @return bool
+     * @access private
+     */
+    function _maliciousFilename($file)
+    {
+        if (strpos($file, '/../') !== false) {
+            return true;
+        }
+        if (strpos($file, '../') === 0) {
+            return true;
+        }
+        return false;
+    }
+    // }}}
+
+    // {{{ _readLongHeader()
+    function _readLongHeader(&$v_header)
+    {
+      $v_filename = '';
+      $n = floor($v_header['size']/512);
+      for ($i=0; $i<$n; $i++) {
+        $v_content = $this->_readBlock();
+        $v_filename .= $v_content;
+      }
+      if (($v_header['size'] % 512) != 0) {
+        $v_content = $this->_readBlock();
+        $v_filename .= trim($v_content);
+      }
+
+      // ----- Read the next header
+      $v_binary_data = $this->_readBlock();
+
+      if (!$this->_readHeader($v_binary_data, $v_header))
+        return false;
+
+      $v_filename = trim($v_filename);
+      $v_header['filename'] = $v_filename;
+        if ($this->_maliciousFilename($v_filename)) {
+            $this->_error('Malicious .tar detected, file "' . $v_filename .
+                '" will not install in desired directory tree');
+            return false;
+      }
+
+      return true;
+    }
+    // }}}
+
+    // {{{ _extractInString()
+    /**
+    * This method extract from the archive one file identified by $p_filename.
+    * The return value is a string with the file content, or null on error.
+    *
+    * @param string $p_filename The path of the file to extract in a string.
+    *
+    * @return a string with the file content or null.
+    * @access private
+    */
+    function _extractInString($p_filename)
+    {
+        $v_result_str = "";
+
+        While (strlen($v_binary_data = $this->_readBlock()) != 0)
+        {
+          if (!$this->_readHeader($v_binary_data, $v_header))
+            return null;
+
+          if ($v_header['filename'] == '')
+            continue;
+
+          // ----- Look for long filename
+          if ($v_header['typeflag'] == 'L') {
+            if (!$this->_readLongHeader($v_header))
+              return null;
+          }
+
+          if ($v_header['filename'] == $p_filename) {
+              if ($v_header['typeflag'] == "5") {
+                  $this->_error('Unable to extract in string a directory '
+				                .'entry {'.$v_header['filename'].'}');
+                  return null;
+              } else {
+                  $n = floor($v_header['size']/512);
+                  for ($i=0; $i<$n; $i++) {
+                      $v_result_str .= $this->_readBlock();
+                  }
+                  if (($v_header['size'] % 512) != 0) {
+                      $v_content = $this->_readBlock();
+                      $v_result_str .= substr($v_content, 0,
+					                          ($v_header['size'] % 512));
+                  }
+                  return $v_result_str;
+              }
+          } else {
+              $this->_jumpBlock(ceil(($v_header['size']/512)));
+          }
+        }
+
+        return null;
+    }
+    // }}}
+
+    // {{{ _extractList()
+    function _extractList($p_path, &$p_list_detail, $p_mode,
+                          $p_file_list, $p_remove_path, $p_preserve=false)
+    {
+    $v_result=true;
+    $v_nb = 0;
+    $v_extract_all = true;
+    $v_listing = false;
+
+    $p_path = $this->_translateWinPath($p_path, false);
+    if ($p_path == '' || (substr($p_path, 0, 1) != '/'
+	    && substr($p_path, 0, 3) != "../" && !strpos($p_path, ':'))) {
+      $p_path = "./".$p_path;
+    }
+    $p_remove_path = $this->_translateWinPath($p_remove_path);
+
+    // ----- Look for path to remove format (should end by /)
+    if (($p_remove_path != '') && (substr($p_remove_path, -1) != '/'))
+      $p_remove_path .= '/';
+    $p_remove_path_size = strlen($p_remove_path);
+
+    switch ($p_mode) {
+      case "complete" :
+        $v_extract_all = true;
+        $v_listing = false;
+      break;
+      case "partial" :
+          $v_extract_all = false;
+          $v_listing = false;
+      break;
+      case "list" :
+          $v_extract_all = false;
+          $v_listing = true;
+      break;
+      default :
+        $this->_error('Invalid extract mode ('.$p_mode.')');
+        return false;
+    }
+
+    clearstatcache();
+
+    while (strlen($v_binary_data = $this->_readBlock()) != 0)
+    {
+      $v_extract_file = FALSE;
+      $v_extraction_stopped = 0;
+
+      if (!$this->_readHeader($v_binary_data, $v_header))
+        return false;
+
+      if ($v_header['filename'] == '') {
+        continue;
+      }
+
+      // ----- Look for long filename
+      if ($v_header['typeflag'] == 'L') {
+        if (!$this->_readLongHeader($v_header))
+          return false;
+      }
+
+      if ((!$v_extract_all) && (is_array($p_file_list))) {
+        // ----- By default no unzip if the file is not found
+        $v_extract_file = false;
+
+        for ($i=0; $i<sizeof($p_file_list); $i++) {
+          // ----- Look if it is a directory
+          if (substr($p_file_list[$i], -1) == '/') {
+            // ----- Look if the directory is in the filename path
+            if ((strlen($v_header['filename']) > strlen($p_file_list[$i]))
+			    && (substr($v_header['filename'], 0, strlen($p_file_list[$i]))
+				    == $p_file_list[$i])) {
+              $v_extract_file = true;
+              break;
+            }
+          }
+
+          // ----- It is a file, so compare the file names
+          elseif ($p_file_list[$i] == $v_header['filename']) {
+            $v_extract_file = true;
+            break;
+          }
+        }
+      } else {
+        $v_extract_file = true;
+      }
+
+      // ----- Look if this file need to be extracted
+      if (($v_extract_file) && (!$v_listing))
+      {
+        if (($p_remove_path != '')
+            && (substr($v_header['filename'], 0, $p_remove_path_size)
+			    == $p_remove_path))
+          $v_header['filename'] = substr($v_header['filename'],
+		                                 $p_remove_path_size);
+        if (($p_path != './') && ($p_path != '/')) {
+          while (substr($p_path, -1) == '/')
+            $p_path = substr($p_path, 0, strlen($p_path)-1);
+
+          if (substr($v_header['filename'], 0, 1) == '/')
+              $v_header['filename'] = $p_path.$v_header['filename'];
+          else
+            $v_header['filename'] = $p_path.'/'.$v_header['filename'];
+        }
+        if (file_exists($v_header['filename'])) {
+          if (   (@is_dir($v_header['filename']))
+		      && ($v_header['typeflag'] == '')) {
+            $this->_error('File '.$v_header['filename']
+			              .' already exists as a directory');
+            return false;
+          }
+          if (   ($this->_isArchive($v_header['filename']))
+		      && ($v_header['typeflag'] == "5")) {
+            $this->_error('Directory '.$v_header['filename']
+			              .' already exists as a file');
+            return false;
+          }
+          if (!is_writeable($v_header['filename'])) {
+            $this->_error('File '.$v_header['filename']
+			              .' already exists and is write protected');
+            return false;
+          }
+          if (filemtime($v_header['filename']) > $v_header['mtime']) {
+            // To be completed : An error or silent no replace ?
+          }
+        }
+
+        // ----- Check the directory availability and create it if necessary
+        elseif (($v_result
+		         = $this->_dirCheck(($v_header['typeflag'] == "5"
+				                    ?$v_header['filename']
+									:dirname($v_header['filename'])))) != 1) {
+            $this->_error('Unable to create path for '.$v_header['filename']);
+            return false;
+        }
+
+        if ($v_extract_file) {
+          if ($v_header['typeflag'] == "5") {
+            if (!@file_exists($v_header['filename'])) {
+                if (!@mkdir($v_header['filename'], 0777)) {
+                    $this->_error('Unable to create directory {'
+					              .$v_header['filename'].'}');
+                    return false;
+                }
+            }
+          } elseif ($v_header['typeflag'] == "2") {
+              if (@file_exists($v_header['filename'])) {
+                  @unlink($v_header['filename']);
+              }
+              if (!@symlink($v_header['link'], $v_header['filename'])) {
+                  $this->_error('Unable to extract symbolic link {'
+                                .$v_header['filename'].'}');
+                  return false;
+              }
+          } else {
+              if (($v_dest_file = @fopen($v_header['filename'], "wb")) == 0) {
+                  $this->_error('Error while opening {'.$v_header['filename']
+				                .'} in write binary mode');
+                  return false;
+              } else {
+                  $n = floor($v_header['size']/512);
+                  for ($i=0; $i<$n; $i++) {
+                      $v_content = $this->_readBlock();
+                      fwrite($v_dest_file, $v_content, 512);
+                  }
+            if (($v_header['size'] % 512) != 0) {
+              $v_content = $this->_readBlock();
+              fwrite($v_dest_file, $v_content, ($v_header['size'] % 512));
+            }
+
+            @fclose($v_dest_file);
+            
+            if ($p_preserve) {
+                @chown($v_header['filename'], $v_header['uid']);
+                @chgrp($v_header['filename'], $v_header['gid']);
+            }
+
+            // ----- Change the file mode, mtime
+            @touch($v_header['filename'], $v_header['mtime']);
+            if ($v_header['mode'] & 0111) {
+                // make file executable, obey umask
+                $mode = fileperms($v_header['filename']) | (~umask() & 0111);
+                @chmod($v_header['filename'], $mode);
+            }
+          }
+
+          // ----- Check the file size
+          clearstatcache();
+          if (!is_file($v_header['filename'])) {
+              $this->_error('Extracted file '.$v_header['filename']
+                            .'does not exist. Archive may be corrupted.');
+              return false;
+          }
+          
+          $filesize = filesize($v_header['filename']);
+          if ($filesize != $v_header['size']) {
+              $this->_error('Extracted file '.$v_header['filename']
+                            .' does not have the correct file size \''
+                            .$filesize
+                            .'\' ('.$v_header['size']
+                            .' expected). Archive may be corrupted.');
+              return false;
+          }
+          }
+        } else {
+          $this->_jumpBlock(ceil(($v_header['size']/512)));
+        }
+      } else {
+          $this->_jumpBlock(ceil(($v_header['size']/512)));
+      }
+
+      /* TBC : Seems to be unused ...
+      if ($this->_compress)
+        $v_end_of_file = @gzeof($this->_file);
+      else
+        $v_end_of_file = @feof($this->_file);
+        */
+
+      if ($v_listing || $v_extract_file || $v_extraction_stopped) {
+        // ----- Log extracted files
+        if (($v_file_dir = dirname($v_header['filename']))
+		    == $v_header['filename'])
+          $v_file_dir = '';
+        if ((substr($v_header['filename'], 0, 1) == '/') && ($v_file_dir == ''))
+          $v_file_dir = '/';
+
+        $p_list_detail[$v_nb++] = $v_header;
+        if (is_array($p_file_list) && (count($p_list_detail) == count($p_file_list))) {
+            return true;
+        }
+      }
+    }
+
+        return true;
+    }
+    // }}}
+
+    // {{{ _openAppend()
+    function _openAppend()
+    {
+        if (filesize($this->_tarname) == 0)
+          return $this->_openWrite();
+
+        if ($this->_compress) {
+            $this->_close();
+
+            if (!@rename($this->_tarname, $this->_tarname.".tmp")) {
+                $this->_error('Error while renaming \''.$this->_tarname
+				              .'\' to temporary file \''.$this->_tarname
+							  .'.tmp\'');
+                return false;
+            }
+
+            if ($this->_compress_type == 'gz')
+                $v_temp_tar = @gzopen($this->_tarname.".tmp", "rb");
+            elseif ($this->_compress_type == 'bz2')
+                $v_temp_tar = @bzopen($this->_tarname.".tmp", "r");
+
+            if ($v_temp_tar == 0) {
+                $this->_error('Unable to open file \''.$this->_tarname
+				              .'.tmp\' in binary read mode');
+                @rename($this->_tarname.".tmp", $this->_tarname);
+                return false;
+            }
+
+            if (!$this->_openWrite()) {
+                @rename($this->_tarname.".tmp", $this->_tarname);
+                return false;
+            }
+
+            if ($this->_compress_type == 'gz') {
+                while (!@gzeof($v_temp_tar)) {
+                    $v_buffer = @gzread($v_temp_tar, 512);
+                    if ($v_buffer == ARCHIVE_TAR_END_BLOCK || strlen($v_buffer) == 0) {
+                        // do not copy end blocks, we will re-make them
+                        // after appending
+                        continue;
+                    }
+                    $v_binary_data = pack("a512", $v_buffer);
+                    $this->_writeBlock($v_binary_data);
+                }
+
+                @gzclose($v_temp_tar);
+            }
+            elseif ($this->_compress_type == 'bz2') {
+                while (strlen($v_buffer = @bzread($v_temp_tar, 512)) > 0) {
+                    if ($v_buffer == ARCHIVE_TAR_END_BLOCK) {
+                        continue;
+                    }
+                    $v_binary_data = pack("a512", $v_buffer);
+                    $this->_writeBlock($v_binary_data);
+                }
+
+                @bzclose($v_temp_tar);
+            }
+
+            if (!@unlink($this->_tarname.".tmp")) {
+                $this->_error('Error while deleting temporary file \''
+				              .$this->_tarname.'.tmp\'');
+            }
+
+        } else {
+            // ----- For not compressed tar, just add files before the last
+			//       one or two 512 bytes block
+            if (!$this->_openReadWrite())
+               return false;
+
+            clearstatcache();
+            $v_size = filesize($this->_tarname);
+
+            // We might have zero, one or two end blocks.
+            // The standard is two, but we should try to handle
+            // other cases.
+            fseek($this->_file, $v_size - 1024);
+            if (fread($this->_file, 512) == ARCHIVE_TAR_END_BLOCK) {
+                fseek($this->_file, $v_size - 1024);
+            }
+            elseif (fread($this->_file, 512) == ARCHIVE_TAR_END_BLOCK) {
+                fseek($this->_file, $v_size - 512);
+            }
+        }
+
+        return true;
+    }
+    // }}}
+
+    // {{{ _append()
+    function _append($p_filelist, $p_add_dir='', $p_remove_dir='')
+    {
+        if (!$this->_openAppend())
+            return false;
+
+        if ($this->_addList($p_filelist, $p_add_dir, $p_remove_dir))
+           $this->_writeFooter();
+
+        $this->_close();
+
+        return true;
+    }
+    // }}}
+
+    // {{{ _dirCheck()
+
+    /**
+     * Check if a directory exists and create it (including parent
+     * dirs) if not.
+     *
+     * @param string $p_dir directory to check
+     *
+     * @return bool true if the directory exists or was created
+     */
+    function _dirCheck($p_dir)
+    {
+        clearstatcache();
+        if ((@is_dir($p_dir)) || ($p_dir == ''))
+            return true;
+
+        $p_parent_dir = dirname($p_dir);
+
+        if (($p_parent_dir != $p_dir) &&
+            ($p_parent_dir != '') &&
+            (!$this->_dirCheck($p_parent_dir)))
+             return false;
+
+        if (!@mkdir($p_dir, 0777)) {
+            $this->_error("Unable to create directory '$p_dir'");
+            return false;
+        }
+
+        return true;
+    }
+
+    // }}}
+
+    // {{{ _pathReduction()
+
+    /**
+     * Compress path by changing for example "/dir/foo/../bar" to "/dir/bar",
+     * rand emove double slashes.
+     *
+     * @param string $p_dir path to reduce
+     *
+     * @return string reduced path
+     *
+     * @access private
+     *
+     */
+    function _pathReduction($p_dir)
+    {
+        $v_result = '';
+
+        // ----- Look for not empty path
+        if ($p_dir != '') {
+            // ----- Explode path by directory names
+            $v_list = explode('/', $p_dir);
+
+            // ----- Study directories from last to first
+            for ($i=sizeof($v_list)-1; $i>=0; $i--) {
+                // ----- Look for current path
+                if ($v_list[$i] == ".") {
+                    // ----- Ignore this directory
+                    // Should be the first $i=0, but no check is done
+                }
+                else if ($v_list[$i] == "..") {
+                    // ----- Ignore it and ignore the $i-1
+                    $i--;
+                }
+                else if (   ($v_list[$i] == '')
+				         && ($i!=(sizeof($v_list)-1))
+						 && ($i!=0)) {
+                    // ----- Ignore only the double '//' in path,
+                    // but not the first and last /
+                } else {
+                    $v_result = $v_list[$i].($i!=(sizeof($v_list)-1)?'/'
+					            .$v_result:'');
+                }
+            }
+        }
+        
+        if (defined('OS_WINDOWS') && OS_WINDOWS) {
+            $v_result = strtr($v_result, '\\', '/');
+        }
+        
+        return $v_result;
+    }
+
+    // }}}
+
+    // {{{ _translateWinPath()
+    function _translateWinPath($p_path, $p_remove_disk_letter=true)
+    {
+      if (defined('OS_WINDOWS') && OS_WINDOWS) {
+          // ----- Look for potential disk letter
+          if (   ($p_remove_disk_letter)
+		      && (($v_position = strpos($p_path, ':')) != false)) {
+              $p_path = substr($p_path, $v_position+1);
+          }
+          // ----- Change potential windows directory separator
+          if ((strpos($p_path, '\\') > 0) || (substr($p_path, 0,1) == '\\')) {
+              $p_path = strtr($p_path, '\\', '/');
+          }
+      }
+      return $p_path;
+    }
+    // }}}
+
+}
+?>
diff --git a/README b/README
index 4d4be2728e1088526a41fe7c5d2812974e06e4fb..77379a46456e3aad1702ff1dc57e4e65cec50821 100644
--- a/README
+++ b/README
@@ -3,10 +3,11 @@ A personal cloud which runs on your own server.
 
 http://ownCloud.org
 
-Installation instructions: http://owncloud.org/support/setup-and-installation/
-Source code: http://gitorious.org/owncloud
+Installation instructions: http://owncloud.org/support
 
+Source code: http://gitorious.org/owncloud
 Mailing list: http://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/calendar/ajax/import/import.php b/apps/calendar/ajax/import/import.php
index c0797f6e425524124ff91a96748c630e856dbb65..d0bdab4f0d508a13cb219c4cacd9e50032bdb8ee 100644
--- a/apps/calendar/ajax/import/import.php
+++ b/apps/calendar/ajax/import/import.php
@@ -10,7 +10,7 @@ ob_start();
 require_once('../../../../lib/base.php');
 OC_JSON::checkLoggedIn();
 OC_Util::checkAppEnabled('calendar');
-$nl = "\n";
+$nl = "\n\r";
 $progressfile = OC::$APPSROOT . '/apps/calendar/import_tmp/' . md5(session_id()) . '.txt';
 if(is_writable('import_tmp/')){
 	$progressfopen = fopen($progressfile, 'w');
diff --git a/apps/calendar/export.php b/apps/calendar/export.php
index 9886ad8e8cca318ac0d2d08ef9f1bddc83ede755..2736eec96c2124e0d9363b30f0aaad6b6bd10fe3 100644
--- a/apps/calendar/export.php
+++ b/apps/calendar/export.php
@@ -11,7 +11,7 @@ OC_Util::checkLoggedIn();
 OC_Util::checkAppEnabled('calendar');
 $cal = isset($_GET['calid']) ? $_GET['calid'] : NULL;
 $event = isset($_GET['eventid']) ? $_GET['eventid'] : NULL;
-$nl = "\n";
+$nl = "\n\r";
 if(isset($cal)){
 	$calendar = OC_Calendar_App::getCalendar($cal);
 	$calobjects = OC_Calendar_Object::all($cal);
diff --git a/apps/contacts/ajax/deletecard.php b/apps/contacts/ajax/deletecard.php
index e26dfd6ebfebc20fc2f498b029cb37803cc59284..5675aef5f15143d1ca5ade32a2a6b161a644d426 100644
--- a/apps/contacts/ajax/deletecard.php
+++ b/apps/contacts/ajax/deletecard.php
@@ -19,6 +19,11 @@
  * License along with this library.  If not, see <http://www.gnu.org/licenses/>.
  *
  */
+function bailOut($msg) {
+	OC_JSON::error(array('data' => array('message' => $msg)));
+	OC_Log::write('contacts','ajax/saveproperty.php: '.$msg, OC_Log::DEBUG);
+	exit();
+}
 
 // Init owncloud
 require_once('../../../lib/base.php');
@@ -27,7 +32,10 @@ require_once('../../../lib/base.php');
 OC_JSON::checkLoggedIn();
 OC_JSON::checkAppEnabled('contacts');
 
-$id = $_GET['id'];
+$id = isset($_GET['id'])?$_GET['id']:null;
+if(!$id) {
+	bailOut(OC_Contacts_App::$l10n->t('id is not set.'));
+}
 $card = OC_Contacts_App::getContactObject( $id );
 
 OC_Contacts_VCard::delete($id);
diff --git a/apps/contacts/appinfo/migrate.php b/apps/contacts/appinfo/migrate.php
new file mode 100644
index 0000000000000000000000000000000000000000..a6c6bc20fa4dd5f4098c5fc3e681a319bf2325cd
--- /dev/null
+++ b/apps/contacts/appinfo/migrate.php
@@ -0,0 +1,68 @@
+<?php
+class OC_Migration_Provider_Contacts extends OC_Migration_Provider{
+	
+	// Create the xml for the user supplied
+	function export( ){
+		$options = array(
+			'table'=>'contacts_addressbooks',
+			'matchcol'=>'userid',
+			'matchval'=>$this->uid,
+			'idcol'=>'id'
+		);
+		$ids = $this->content->copyRows( $options );
+
+		$options = array(
+			'table'=>'contacts_cards',
+			'matchcol'=>'addressbookid',
+			'matchval'=>$ids
+		);
+		
+		// Export tags
+		$ids2 = $this->content->copyRows( $options );
+		
+		// If both returned some ids then they worked
+		if( is_array( $ids ) && is_array( $ids2 ) )
+		{
+			return true;	
+		} else {
+			return false;
+		}
+		
+	}
+	
+	// Import function for bookmarks
+	function import( ){
+		switch( $this->appinfo->version ){
+			default:
+				// All versions of the app have had the same db structure, so all can use the same import function
+				$query = $this->content->prepare( "SELECT * FROM contacts_addressbooks WHERE userid LIKE ?" );
+				$results = $query->execute( array( $this->olduid ) );
+				$idmap = array();
+				while( $row = $results->fetchRow() ){
+					// Import each bookmark, saving its id into the map	
+					$query = OC_DB::prepare( "INSERT INTO *PREFIX*contacts_addressbooks (`userid`, `displayname`, `uri`, `description`, `ctag`) VALUES (?, ?, ?, ?, ?)" );
+					$query->execute( array( $this->uid, $row['displayname'], $row['uri'], $row['description'], $row['ctag'] ) );
+					// Map the id
+					$idmap[$row['id']] = OC_DB::insertid();
+				}
+				// Now tags
+				foreach($idmap as $oldid => $newid){
+					$query = $this->content->prepare( "SELECT * FROM contacts_cards WHERE addressbookid LIKE ?" );
+					$results = $query->execute( array( $oldid ) );
+					while( $row = $results->fetchRow() ){
+						// Import the tags for this bookmark, using the new bookmark id
+						$query = OC_DB::prepare( "INSERT INTO *PREFIX*contacts_cards (`addressbookid`, `fullname`, `carddata`, `uri`, `lastmodified`) VALUES (?, ?, ?, ?, ?)" );
+						$query->execute( array( $newid, $row['fullname'], $row['carddata'], $row['uri'], $row['lastmodified'] ) );	
+					}		
+				}
+				// All done!
+			break;
+		}
+		
+		return true;
+	}
+	
+}
+
+// Load the provider
+new OC_Migration_Provider_Contacts( 'contacts' );
\ No newline at end of file
diff --git a/apps/contacts/css/contacts.css b/apps/contacts/css/contacts.css
index 7c36a511d6e6e783893c2b9e9accb8b85d59e8d5..76b5972ba3c497ce88e9f9734ad383953d032f95 100644
--- a/apps/contacts/css/contacts.css
+++ b/apps/contacts/css/contacts.css
@@ -10,8 +10,6 @@
 #contacts_propertymenu_button { position:absolute;top:15px;right:150px; background:url('../../../core/img/actions/add.svg') no-repeat center;	 }
 #contacts_propertymenu { background-color: #fff; position:absolute;top:40px;right:150px; overflow:hidden; text-overflow:ellipsis; /*border: thin solid #1d2d44;*/  -moz-box-shadow:0 0 10px #000; -webkit-box-shadow:0 0 10px #000; box-shadow:0 0 10px #000; -moz-border-radius:0.5em; -webkit-border-radius:0.5em; border-radius:0.5em; -moz-border-radius:0.5em; -webkit-border-radius:0.5em; border-radius:0.5em; }
 #contacts_propertymenu li { display: block; font-weight: bold; height: 20px; width: 100px; }
-/*#contacts_propertymenu li:first-child { border-top: thin solid #1d2d44; -moz-border-radius-topleft:0.5em; -webkit-border-top-left-radius:0.5em; border-top-left-radius:0.5em; -moz-border-radius-topright:0.5em; -webkit-border-top-right-radius:0.5em; border-top-right-radius:0.5em; }
-#contacts_propertymenu li:last-child { border-bottom: thin solid #1d2d44; -moz-border-radius-bottomleft:0.5em; -webkit-border-bottom-left-radius:0.5em; border-bottom-left-radius:0.5em; -moz-border-radius-bottomright:0.5em; -webkit-border-bottom-right-radius:0.5em; border-bottom-right-radius:0.5em; }*/
 #contacts_propertymenu li a { padding: 3px; display: block }
 #contacts_propertymenu li:hover { background-color: #1d2d44; }
 #contacts_propertymenu li a:hover { color: #fff }
@@ -25,54 +23,12 @@
 #card input[type="text"],input[type="email"],input[type="tel"],input[type="date"], select { background-color: #f8f8f8; border: 0 !important; -webkit-appearance:none !important; -moz-appearance:none  !important; -webkit-box-sizing:none !important; -moz-box-sizing:none !important; box-sizing:none !important; -moz-box-shadow: none; -webkit-box-shadow: none; box-shadow: none; -moz-border-radius: 0px; -webkit-border-radius: 0px; border-radius: 0px; float: left; }
 #card input[type="text"]:hover, input[type="text"]:focus, input[type="text"]:active,input[type="email"]:hover,input[type="tel"]:hover,input[type="date"]:hover,input[type="date"],input[type="date"]:hover,input[type="date"]:active,input[type="date"]:active,input[type="date"]:active,input[type="email"]:active,input[type="tel"]:active, select:hover, select:focus, select:active { border: 0 !important; -webkit-appearance:textfield; -moz-appearance:textfield; -webkit-box-sizing:content-box; -moz-box-sizing:content-box; box-sizing:content-box; 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; float: left; }
 input[type="text"]:invalid,input[type="email"]:invalid,input[type="tel"]:invalid,input[type="date"]:invalid { background-color: #ffc0c0 !important; }
-/*input[type="text"]:valid,input[type="email"]:valid,input[type="tel"]:valid,input[type="date"]:valid { background-color: #b1d28f !important; }*/
-dl.form
-{
-	width: 100%;
-	float: left;
-	clear: right;
-	margin: 0;
-	padding: 0;
-}
-
-.form dt
-{
-	display: table-cell;
-	clear: left;
-	float: left;
-	width: 7em;
-	/*overflow: hidden;*/
-	margin: 0;
-	padding: 0.8em 0.5em 0 0;
-	font-weight: bold;
-	text-align:right;
-	text-overflow:ellipsis;
-	o-text-overflow: ellipsis;
-	vertical-align: text-bottom;
-	/*
-	white-space: pre-wrap;
-	white-space: -moz-pre-wrap !important;
-	white-space: -pre-wrap;
-	white-space: -o-pre-wrap;*/
-}
-
-.form dd
-{
-	display: table-cell;
-	clear: right;
-	float: left;
-	margin: 0;
-	padding: 0px;
-	white-space: nowrap;
-	vertical-align: text-bottom;
-	/*min-width: 20em;*/
-	/*background-color: yellow;*/
-}
+dl.form { width: 100%; float: left; clear: right; margin: 0; padding: 0; }
+.form dt { display: table-cell; clear: left; float: left; width: 7em; margin: 0; padding: 0.8em 0.5em 0 0; font-weight: bold; text-align:right; text-overflow:ellipsis; o-text-overflow: ellipsis; vertical-align: text-bottom;/* white-space: pre-wrap; white-space: -moz-pre-wrap !important; white-space: -pre-wrap; white-space: -o-pre-wrap;*/ }
+.form dd { display: table-cell; clear: right; float: left; margin: 0; padding: 0px; white-space: nowrap; vertical-align: text-bottom; }
 
 .loading { background: url('../../../core/img/loading.gif') no-repeat center !important; /*cursor: progress; */ cursor: wait; }
 
-/*.add { cursor: pointer;  width: 25px; height: 25px; margin: 0px; float: right; position:relative; content: "\+"; font-weight: bold; color: #666; font-size: large; bottom: 0px; right: 0px; clear: both; text-align: center; vertical-align: bottom; display: none; }*/
-
 .listactions { height: 1em; width:60px; float: left; clear: right; }
 .add,.edit,.delete,.mail, .globe { cursor: pointer; width: 20px; height: 20px; margin: 0; float: left; position:relative; display: none; }
 .add { background:url('../../../core/img/actions/add.svg') no-repeat center; clear: both; }
@@ -82,75 +38,21 @@ dl.form
 /*.globe { background:url('../img/globe.svg') no-repeat center; }*/
 .globe { background:url('../../../core/img/actions/public.svg') no-repeat center; }
 
-#messagebox_msg { font-weight: bold; font-size: 1.2em; }
-
-/* Name editor */
-#edit_name_dialog {
-	/*width: 25em;*/
-	padding:0;
-}
-#edit_name_dialog > input {
-	width: 15em;
-}
-/* Address editor */
-#edit_address_dialog {
-	/*width: 30em;*/
-}
-#edit_address_dialog > input {
-	width: 15em;
-}
+#edit_name_dialog { padding:0; }
+#edit_name_dialog > input { width: 15em; }
+#edit_address_dialog { /*width: 30em;*/ }
+#edit_address_dialog > input { width: 15em; }
 #edit_photo_dialog_img { display: block; width: 150; height: 200; border: thin solid black; }
 #fn { float: left; }
 /**
  * Create classes form, floateven and floatodd which flows left and right respectively.
  */
-.contactsection { 
-	float: left; 
-	min-width: 30em;
-	max-width: 40em;
-	margin: 0.5em;
-	border: thin solid lightgray;
-	-webkit-border-radius: 0.5em;
-	-moz-border-radius: 0.5em;
-	border-radius: 0.5em;
-	background-color: #f8f8f8;
-}
+.contactsection { float: left; min-width: 30em; max-width: 40em; margin: 0.5em; border: thin solid lightgray; -webkit-border-radius: 0.5em; -moz-border-radius: 0.5em; border-radius: 0.5em; background-color: #f8f8f8; }
 
-.contactpart legend {
-	/*background: #fff;
-	font-weight: bold;
-	left: 1em;
-	border: thin solid gray;
-	-webkit-border-radius: 0.5em;
-	-moz-border-radius: 0.5em;
-	border-radius: 0.5em;
-	padding: 3px;*/
-width:auto; padding:.3em; 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;
-}
-/*#contacts_details_photo { 
-	cursor: pointer; 
-	z-index:1; 
-	margin: auto;
-}
-*/
-#cropbox { 
-	margin: auto;
-}
+.contactpart legend { width:auto; padding:.3em; 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; }
+#cropbox { margin: auto; }
 
-/* Photo editor */
-/*#contacts_details_photo_wrapper {
-	z-index: 1000;
-}*/
-#contacts_details_photo {
-	border-radius: 0.5em;
-	border: thin solid #bbb;
-	padding: 0.5em;
-	margin: 1em 1em 1em 7em;
-	cursor: pointer;
-	/*background: #f8f8f8;*/
-	background: url(../../../core/img/loading.gif) no-repeat center center;
-	clear: right;
-}
+#contacts_details_photo { border-radius: 0.5em; border: thin solid #bbb; padding: 0.5em; margin: 1em 1em 1em 7em; cursor: pointer; background: url(../../../core/img/loading.gif) no-repeat center center; clear: right; }
 #contacts_details_photo:hover { background: #fff; }
 #contacts_details_photo_progress { margin: 0.3em 0.3em 0.3em 7em; clear: left; }
 /* Address editor */
@@ -168,13 +70,6 @@ dl.addresscard dd > ul { margin: 0.3em; padding: 0.3em; }
 #adr_zipcode {}
 #adr_country {}
 
-.delimiter {
-	height: 10px;
-	clear: both;
-}
-
-/*input[type="text"] { float: left; max-width: 15em; }
-input[type="radio"] { float: left; -khtml-appearance: none; width: 20px; height: 20px; vertical-align: middle; }*/
 #file_upload_target, #crop_target { display:none; }
 
 #file_upload_start { opacity:0; filter:alpha(opacity=0); z-index:1; position:absolute; left:0; top:0; cursor:pointer; width:0; height:0;}
diff --git a/apps/contacts/js/contacts.js b/apps/contacts/js/contacts.js
index e1827027453c0436e1f16216e4ca36fba6ef265c..d314878cc0a0d89ebccfd7ed451dd4b0142efe9a 100644
--- a/apps/contacts/js/contacts.js
+++ b/apps/contacts/js/contacts.js
@@ -213,19 +213,27 @@ Contacts={
 			honpre:'',
 			honsuf:'',
 			data:undefined,
-			update:function() {
+			update:function(id) {
 				// Make sure proper DOM is loaded.
-				console.log('Card.update(), #n: ' + $('#n').length);
+				var newid;
+				console.log('Card.update(), id: ' + id);
 				console.log('Card.update(), #contacts: ' + $('#contacts li').length);
-				if($('#n').length == 0 && $('#contacts li').length > 0) {
+				if(id == undefined) {
+					newid = $('#contacts li:first-child').data('id');
+				} else {
+					newid = id;
+				}
+				if($('#contacts li').length > 0) {
 					$.getJSON(OC.filePath('contacts', 'ajax', 'loadcard.php'),{},function(jsondata){
 						if(jsondata.status == 'success'){
 							$('#rightcontent').html(jsondata.data.page);
 							Contacts.UI.loadHandlers();
 							if($('#contacts li').length > 0) {
-								var firstid = $('#contacts li:first-child').data('id');
-								console.log('trying to load: ' + firstid);
-								$.getJSON(OC.filePath('contacts', 'ajax', 'contactdetails.php'),{'id':firstid},function(jsondata){
+								//var newid = $('#contacts li:first-child').data('id');
+								//$('#contacts li:first-child').addClass('active');
+								$('#leftcontent li[data-id="'+newid+'"]').addClass('active');
+								console.log('trying to load: ' + newid);
+								$.getJSON(OC.filePath('contacts', 'ajax', 'contactdetails.php'),{'id':newid},function(jsondata){
 									if(jsondata.status == 'success'){
 										Contacts.UI.Card.loadContact(jsondata.data);
 									} else{
@@ -300,35 +308,49 @@ Contacts={
 					}
 				});
 			},
-			delete: function() {
+			delete:function() {
 				$('#contacts_deletecard').tipsy('hide');
-				$.getJSON('ajax/deletecard.php',{'id':this.id},function(jsondata){
-					if(jsondata.status == 'success'){
-						$('#leftcontent [data-id="'+jsondata.data.id+'"]').remove();
-						$('#rightcontent').data('id','');
-						//$('#rightcontent').empty();
-						this.id = this.fn = this.fullname = this.shortname = this.famname = this.givname = this.addname = this.honpre = this.honsuf = '';
-						this.data = undefined;
-						// Load first in list.
-						if($('#contacts li').length > 0) {
-							Contacts.UI.Card.update();
-						} else {
-							// load intro page
-							$.getJSON('ajax/loadintro.php',{},function(jsondata){
-								if(jsondata.status == 'success'){
-									id = '';
-									$('#rightcontent').data('id','');
-									$('#rightcontent').html(jsondata.data.page);
+				OC.dialogs.confirm(t('contacts', 'Are you sure you want to delete this contact?'), t('contacts', 'Warning'), function(answer) {
+					if(answer == true) {
+						$.getJSON('ajax/deletecard.php',{'id':Contacts.UI.Card.id},function(jsondata){
+							if(jsondata.status == 'success'){
+								var newid = '';
+								var curlistitem = $('#leftcontent [data-id="'+jsondata.data.id+'"]');
+								var newlistitem = curlistitem.prev();
+								console.log('Previous: ' + newlistitem);
+								if(newlistitem == undefined) {
+									newlistitem = curlistitem.next();
 								}
-								else{
-									OC.dialogs.alert(jsondata.data.message, t('contacts', 'Error'));
+								curlistitem.remove();
+								if(newlistitem != undefined) {
+									newid = newlistitem.data('id');
 								}
-							});
-						}
-					}
-					else{
-						OC.dialogs.alert(jsondata.data.message, t('contacts', 'Error'));
-						//alert(jsondata.data.message);
+								$('#rightcontent').data('id',newid);
+								//$('#rightcontent').empty();
+								this.id = this.fn = this.fullname = this.shortname = this.famname = this.givname = this.addname = this.honpre = this.honsuf = '';
+								this.data = undefined;
+								// Load first in list.
+								if($('#contacts li').length > 0) {
+									Contacts.UI.Card.update(newid);
+								} else {
+									// load intro page
+									$.getJSON('ajax/loadintro.php',{},function(jsondata){
+										if(jsondata.status == 'success'){
+											id = '';
+											$('#rightcontent').data('id','');
+											$('#rightcontent').html(jsondata.data.page);
+										}
+										else{
+											OC.dialogs.alert(jsondata.data.message, t('contacts', 'Error'));
+										}
+									});
+								}
+							}
+							else{
+								OC.dialogs.alert(jsondata.data.message, t('contacts', 'Error'));
+								//alert(jsondata.data.message);
+							}
+						});
 					}
 				});
 				return false;
@@ -1232,6 +1254,7 @@ $(document).ready(function(){
 	 */
 	$('#leftcontent li').live('click',function(){
 		var id = $(this).data('id');
+		$(this).addClass('active');
 		var oldid = $('#rightcontent').data('id');
 		if(oldid != 0){
 			$('#leftcontent li[data-id="'+oldid+'"]').removeClass('active');
diff --git a/apps/contacts/templates/part.contact.php b/apps/contacts/templates/part.contact.php
index a93069fa7227dcb514168309db93ec2b95fa2028..d243c2b5e144c46aca6b49d6f6e2664b7e15e81c 100644
--- a/apps/contacts/templates/part.contact.php
+++ b/apps/contacts/templates/part.contact.php
@@ -56,13 +56,6 @@ $id = isset($_['id']) ? $_['id'] : '';
 		<dd style="display:none;" class="propertycontainer" id="nickname_value" data-element="NICKNAME"><input id="nickname" required="required" name="value[NICKNAME]" type="text" class="contacts_property" style="width:16em;" name="value" value="" placeholder="<?php echo $l->t('Enter nickname'); ?>" /><a class="action delete" onclick="$(this).tipsy('hide');Contacts.UI.Card.deleteProperty(this, 'single');" title="<?php echo $l->t('Delete'); ?>"></a></dd>
 		<dt style="display:none;" id="bday_label" data-element="BDAY"><label for="bday"><?php echo $l->t('Birthday'); ?></label></dt>
 		<dd style="display:none;" class="propertycontainer" id="bday_value" data-element="BDAY"><input id="bday"  required="required" name="value" type="text" class="contacts_property" value="" placeholder="<?php echo $l->t('dd-mm-yyyy'); ?>" /><a class="action delete" onclick="$(this).tipsy('hide');Contacts.UI.Card.deleteProperty(this, 'single');" title="<?php echo $l->t('Delete'); ?>"></a></dd>
-		<!-- dt id="categories_label" data-element="CATEGORIES"><label for="categories"><?php echo $l->t('Categories'); ?></label></dt>
-		<dd class="propertycontainer" id="categories_value" data-element="CATEGORIES">
-			<select class="contacts_property" multiple="multiple" id="categories" name="value[]">
-				<?php echo html_select_options($_['categories'], array(), array('combine'=>true)) ?>
-			</select>
-			<a class="action edit" onclick="$(this).tipsy('hide');OCCategories.edit();" title="<?php echo $l->t('Edit categories'); ?>"></a>
-		</dd -->
 		<dt style="display:none;" id="categories_label" data-element="CATEGORIES"><label for="categories"><?php echo $l->t('Categories'); ?></label></dt>
 		<dd style="display:none;" class="propertycontainer" id="categories_value" data-element="CATEGORIES"><input id="categories" required="required" name="value[CATEGORIES]" type="text" class="contacts_property" style="width:16em;" name="value" value="" placeholder="<?php echo $l->t('Categories'); ?>" /><a class="action delete" onclick="$(this).tipsy('hide');Contacts.UI.Card.deleteProperty(this, 'single');" title="<?php echo $l->t('Delete'); ?>"></a><a class="action edit" onclick="$(this).tipsy('hide');OCCategories.edit();" title="<?php echo $l->t('Edit categories'); ?>"></a></dd>
 	</dl>
@@ -131,6 +124,7 @@ $(document).ready(function(){
 	if('<?php echo $id; ?>'!='') {
 		$.getJSON(OC.filePath('contacts', 'ajax', 'contactdetails.php'),{'id':'<?php echo $id; ?>'},function(jsondata){
 			if(jsondata.status == 'success'){
+				$('#leftcontent li[data-id="<?php echo $id; ?>"]').addClass('active');
 				Contacts.UI.Card.loadContact(jsondata.data);
 			}
 			else{
diff --git a/apps/files_archive/appinfo/app.php b/apps/files_archive/appinfo/app.php
index 693c28d98a07a44d4b6e7fffc8a9d80c02fb895d..67809ec980aee9446f65a3a4efe2d9b4f45a9521 100644
--- a/apps/files_archive/appinfo/app.php
+++ b/apps/files_archive/appinfo/app.php
@@ -7,7 +7,8 @@
  */
 
 OC::$CLASSPATH['OC_Archive'] = 'apps/files_archive/lib/archive.php';
-foreach(array('ZIP') as $type){
+OC::$CLASSPATH['Archive_Tar'] = '3rdparty/Archive/Tar.php';
+foreach(array('ZIP','TAR') as $type){
 	OC::$CLASSPATH['OC_Archive_'.$type] = 'apps/files_archive/lib/'.strtolower($type).'.php';
 }
 
diff --git a/apps/files_archive/appinfo/info.xml b/apps/files_archive/appinfo/info.xml
index df767d39f6b602d297fb040c3017737b870dfa50..236b5a64b0586b8f27025dd7ac68a4700792f81e 100644
--- a/apps/files_archive/appinfo/info.xml
+++ b/apps/files_archive/appinfo/info.xml
@@ -7,4 +7,7 @@
 	<licence>AGPL</licence>
 	<author>Robin Appelman</author>
 	<require>3</require>
+	<types>
+		<filesystem/>
+	</types>
 </info>
diff --git a/apps/files_archive/js/archive.js b/apps/files_archive/js/archive.js
index ec316c7bf2c3dd0d2e45cd887a3554341dbe2042..531eb61c01af4fc60150699d110e21249e306c11 100644
--- a/apps/files_archive/js/archive.js
+++ b/apps/files_archive/js/archive.js
@@ -11,5 +11,9 @@ $(document).ready(function() {
 			window.location='index.php?dir='+encodeURIComponent($('#dir').val()).replace(/%2F/g, '/')+'/'+encodeURIComponent(filename);
 		});
 		FileActions.setDefault('application/zip','Open');
+		FileActions.register('application/x-gzip','Open','',function(filename){
+			window.location='index.php?dir='+encodeURIComponent($('#dir').val()).replace(/%2F/g, '/')+'/'+encodeURIComponent(filename);
+		});
+		FileActions.setDefault('application/x-gzip','Open');
 	}
 });
diff --git a/apps/files_archive/lib/archive.php b/apps/files_archive/lib/archive.php
index be89f894fb7ff8c3fcab13a5a81ce9bb53d31100..113f92e9604d1e2e934ea7b9e2bfbc3d2a695b32 100644
--- a/apps/files_archive/lib/archive.php
+++ b/apps/files_archive/lib/archive.php
@@ -17,6 +17,15 @@ abstract class OC_Archive{
 		switch($ext){
 			case '.zip':
 				return new OC_Archive_ZIP($path);
+			case '.gz':
+			case '.bz':
+			case '.bz2':
+				if(strpos($path,'.tar.')){
+					return new OC_Archive_TAR($path);
+				}
+				break;
+			case '.tgz':
+				return new OC_Archive_TAR($path);
 		}
 	}
 	
@@ -77,6 +86,13 @@ abstract class OC_Archive{
 	 * @return bool
 	 */
 	abstract function extractFile($path,$dest);
+	/**
+	 * extract the archive
+	 * @param string path
+	 * @param string dest
+	 * @return bool
+	 */
+	abstract function extract($dest);
 	/**
 	 * check if a file or folder exists in the archive
 	 * @param string path
diff --git a/apps/files_archive/lib/storage.php b/apps/files_archive/lib/storage.php
index 72a96ca5a5d7f4b8eead02d621f87818d18accdb..700d963304230d7834423ca20dd6a83cbc955370 100644
--- a/apps/files_archive/lib/storage.php
+++ b/apps/files_archive/lib/storage.php
@@ -125,7 +125,7 @@ class OC_Filestorage_Archive extends OC_Filestorage_Common{
 			self::$rootView=new OC_FilesystemView('');
 		}
 		self::$enableAutomount=false;//prevent recursion
-		$supported=array('zip');
+		$supported=array('zip','tar.gz','tar.bz2','tgz');
 		foreach($supported as $type){
 			$ext='.'.$type.'/';
 			if(($pos=strpos(strtolower($path),$ext))!==false){
@@ -139,4 +139,8 @@ class OC_Filestorage_Archive extends OC_Filestorage_Common{
 		}
 		self::$enableAutomount=true;
 	}
+
+	public function rename($path1,$path2){
+		return $this->archive->rename($path1,$path2);
+	}
 }
diff --git a/apps/files_archive/lib/tar.php b/apps/files_archive/lib/tar.php
new file mode 100644
index 0000000000000000000000000000000000000000..a5d54004788f7fe0b6f8a64213ca3795f53b4fc4
--- /dev/null
+++ b/apps/files_archive/lib/tar.php
@@ -0,0 +1,277 @@
+<?php
+/**
+ * Copyright (c) 2012 Robin Appelman <icewind@owncloud.com>
+ * This file is licensed under the Affero General Public License version 3 or
+ * later.
+ * See the COPYING-README file.
+ */
+
+class OC_Archive_TAR extends OC_Archive{
+	const PLAIN=0;
+	const GZIP=1;
+	const BZIP=2;
+	
+	/**
+	 * @var Archive_Tar tar
+	 */
+	private $tar=null;
+	private $path;
+
+	function __construct($source){
+		$types=array(null,'gz','bz');
+		$this->path=$source;
+		$this->tar=new Archive_Tar($source,$types[self::getTarType($source)]);
+	}
+
+	/**
+	 * try to detect the type of tar compression
+	 * @param string file
+	 * @return str
+	 */
+	static public function getTarType($file){
+		if(strpos($file,'.')){
+			$extention=substr($file,strrpos($file,'.'));
+			switch($extention){
+				case 'gz':
+				case 'tgz':
+					return self::GZIP;
+				case 'bz':
+				case 'bz2':
+					return self::BZIP;
+				default:
+					return self::PLAIN;
+			}
+		}else{
+			return self::PLAIN;
+		}
+	}
+
+	/**
+	 * add an empty folder to the archive
+	 * @param string path
+	 * @return bool
+	 */
+	function addFolder($path){
+		$tmpBase=get_temp_dir().'/';
+		if(substr($path,-1,1)!='/'){
+			$path.='/';
+		}
+		if($this->fileExists($path)){
+			return false;
+		}
+		mkdir($tmpBase.$path);
+		$result=$this->tar->addModify(array($tmpBase.$path),'',$tmpBase);
+		rmdir($tmpBase.$path);
+		return $result;
+	}
+	/**
+	 * add a file to the archive
+	 * @param string path
+	 * @param string source either a local file or string data
+	 * @return bool
+	 */
+	function addFile($path,$source=''){
+		if($this->fileExists($path)){
+			$this->remove($path);
+		}
+		if(file_exists($source)){
+			$header=array();
+			$dummy='';
+			$this->tar->_openAppend();
+			$result=$this->tar->_addfile($source,$header,$dummy,$dummy,$path);
+		}else{
+			$result=$this->tar->addString($path,$source);
+		}
+		return $result;
+	}
+
+	/**
+	 * rename a file or folder in the archive
+	 * @param string source
+	 * @param string dest
+	 * @return bool
+	 */
+	function rename($source,$dest){
+		//no proper way to delete, rename entire archive, rename file and remake archive
+		$tmp=OC_Helper::tmpFolder();
+		$this->tar->extract($tmp);
+		rename($tmp.$source,$tmp.$dest);
+		$this->tar=null;
+		unlink($this->path);
+		$types=array(null,'gz','bz');
+		$this->tar=new Archive_Tar($this->path,$types[self::getTarType($this->path)]);
+		$this->tar->createModify(array($tmp),'',$tmp.'/');
+	}
+
+	private function getHeader($file){
+		$headers=$this->tar->listContent();
+		foreach($headers as $header){
+			if($file==$header['filename'] or $file.'/'==$header['filename']){
+				return $header;
+			}
+		}
+		return null;
+	}
+	
+	/**
+	 * get the uncompressed size of a file in the archive
+	 * @param string path
+	 * @return int
+	 */
+	function filesize($path){
+		$stat=$this->getHeader($path);
+		return $stat['size'];
+	}
+	/**
+	 * get the last modified time of a file in the archive
+	 * @param string path
+	 * @return int
+	 */
+	function mtime($path){
+		$stat=$this->getHeader($path);
+		return $stat['mtime'];
+	}
+
+	/**
+	 * get the files in a folder
+	 * @param path
+	 * @return array
+	 */
+	function getFolder($path){
+		$files=$this->getFiles();
+		$folderContent=array();
+		$pathLength=strlen($path);
+		foreach($files as $file){
+			if(substr($file,0,$pathLength)==$path and $file!=$path){
+				if(strrpos(substr($file,0,-1),'/')<=$pathLength){
+					$folderContent[]=substr($file,$pathLength);
+				}
+			}
+		}
+		return $folderContent;
+	}
+	/**
+	 *get all files in the archive
+	 * @return array
+	 */
+	function getFiles(){
+		$headers=$this->tar->listContent();
+		$files=array();
+		foreach($headers as $header){
+			$files[]=$header['filename'];
+		}
+		return $files;
+	}
+	/**
+	 * get the content of a file
+	 * @param string path
+	 * @return string
+	 */
+	function getFile($path){
+		return $this->tar->extractInString($path);
+	}
+	/**
+	 * extract a single file from the archive
+	 * @param string path
+	 * @param string dest
+	 * @return bool
+	 */
+	function extractFile($path,$dest){
+		$tmp=OC_Helper::tmpFolder();
+		if(!$this->fileExists($path)){
+			return false;
+		}
+		$success=$this->tar->extractList(array($path),$tmp);
+		if($success){
+			rename($tmp.$path,$dest);
+		}
+		OC_Helper::rmdirr($tmp);
+		return $success;
+	}
+	/**
+	 * extract the archive
+	 * @param string path
+	 * @param string dest
+	 * @return bool
+	 */
+	function extract($dest){
+		return $this->tar->extract($dest);
+	}
+	/**
+	 * check if a file or folder exists in the archive
+	 * @param string path
+	 * @return bool
+	 */
+	function fileExists($path){
+		return $this->getHeader($path)!==null;
+	}
+	
+	/**
+	 * remove a file or folder from the archive
+	 * @param string path
+	 * @return bool
+	 */
+	function remove($path){
+		if(!$this->fileExists($path)){
+			return false;
+		}
+		//no proper way to delete, extract entire archive, delete file and remake archive
+		$tmp=OC_Helper::tmpFolder();
+		$this->tar->extract($tmp);
+		OC_Helper::rmdirr($tmp.$path);
+		$this->tar=null;
+		unlink($this->path);
+		$this->reopen();
+		$this->tar->createModify(array($tmp),'',$tmp);
+		return true;
+	}
+	/**
+	 * get a file handler
+	 * @param string path
+	 * @param string mode
+	 * @return resource
+	 */
+	function getStream($path,$mode){
+		if(strrpos($path,'.')!==false){
+			$ext=substr($path,strrpos($path,'.'));
+		}else{
+			$ext='';
+		}
+		$tmpFile=OC_Helper::tmpFile($ext);
+		if($this->fileExists($path)){
+			$this->extractFile($path,$tmpFile);
+		}elseif($mode=='r' or $mode=='rb'){
+			return false;
+		}
+		if($mode=='r' or $mode=='rb'){
+			return fopen($tmpFile,$mode);
+		}else{
+			OC_CloseStreamWrapper::$callBacks[$tmpFile]=array($this,'writeBack');
+			self::$tempFiles[$tmpFile]=$path;
+			return fopen('close://'.$tmpFile,$mode);
+		}
+	}
+
+	private static $tempFiles=array();
+	/**
+	 * write back temporary files
+	 */
+	function writeBack($tmpFile){
+		if(isset(self::$tempFiles[$tmpFile])){
+			$this->addFile(self::$tempFiles[$tmpFile],$tmpFile);
+			unlink($tmpFile);
+		}
+	}
+
+	/**
+	 * reopen the archive to ensure everything is written
+	 */
+	private function reopen(){
+		if($this->tar){
+			$this->tar->_close();
+			$this->tar=null;
+		}
+		$types=array(null,'gz','bz');
+		$this->tar=new Archive_Tar($this->path,$types[self::getTarType($this->path)]);
+	}
+}
diff --git a/apps/files_archive/lib/zip.php b/apps/files_archive/lib/zip.php
index eab101b3a5c0d7ae2bcff246d2527309201eff0e..5a5bc766875ddde02b3d02abac2f2fd825dc274b 100644
--- a/apps/files_archive/lib/zip.php
+++ b/apps/files_archive/lib/zip.php
@@ -11,7 +11,6 @@ class OC_Archive_ZIP extends OC_Archive{
 	 * @var ZipArchive zip
 	 */
 	private $zip=null;
-	private $contents=array();
 	private $success=false;
 	private $path;
 	
@@ -56,7 +55,9 @@ class OC_Archive_ZIP extends OC_Archive{
 	 * @return bool
 	 */
 	function rename($source,$dest){
-		return $this->zip->renameName($source,$dest);
+		$source=$this->stripPath($source);
+		$dest=$this->stripPath($dest);
+		$this->zip->renameName($source,$dest);
 	}
 	/**
 	 * get the uncompressed size of a file in the archive
@@ -99,15 +100,11 @@ class OC_Archive_ZIP extends OC_Archive{
 	 * @return array
 	 */
 	function getFiles(){
-		if(count($this->contents)){
-			return $this->contents;
-		}
 		$fileCount=$this->zip->numFiles;
 		$files=array();
 		for($i=0;$i<$fileCount;$i++){
 			$files[]=$this->zip->getNameIndex($i);
 		}
-		$this->contents=$files;
 		return $files;
 	}
 	/**
@@ -128,13 +125,22 @@ class OC_Archive_ZIP extends OC_Archive{
 		$fp = $this->zip->getStream($path);
 		file_put_contents($dest,$fp);
 	}
+	/**
+	 * extract the archive
+	 * @param string path
+	 * @param string dest
+	 * @return bool
+	 */
+	function extract($dest){
+		return $this->zip->extractTo($dest);
+	}
 	/**
 	 * check if a file or folder exists in the archive
 	 * @param string path
 	 * @return bool
 	 */
 	function fileExists($path){
-		return $this->zip->locateName($path)!==false;
+		return ($this->zip->locateName($path)!==false) or ($this->zip->locateName($path.'/')!==false);
 	}
 	/**
 	 * remove a file or folder from the archive
@@ -142,7 +148,11 @@ class OC_Archive_ZIP extends OC_Archive{
 	 * @return bool
 	 */
 	function remove($path){
-		return $this->zip->deleteName($path);
+		if($this->fileExists($path.'/')){
+			return $this->zip->deleteName($path.'/');
+		}else{
+			return $this->zip->deleteName($path);
+		}
 	}
 	/**
 	 * get a file handler
@@ -179,4 +189,12 @@ class OC_Archive_ZIP extends OC_Archive{
 			unlink($tmpFile);
 		}
 	}
+
+	private function stripPath($path){
+		if(substr($path,0,1)=='/'){
+			return substr($path,1);
+		}else{
+			return $path;
+		}
+	}
 }
diff --git a/apps/files_archive/tests/archive.php b/apps/files_archive/tests/archive.php
index 2e26b5e03b5c430456394f7e28f9a6030beb25d6..9e99466a521c334dfb5890bbb0739852a017a4fd 100644
--- a/apps/files_archive/tests/archive.php
+++ b/apps/files_archive/tests/archive.php
@@ -27,10 +27,10 @@ abstract class Test_Archive extends UnitTestCase {
 		$this->instance=$this->getExisting();
 		$allFiles=$this->instance->getFiles();
 		$expected=array('lorem.txt','logo-wide.png','dir/','dir/lorem.txt');
-		$this->assertEqual(4,count($allFiles));
+		$this->assertEqual(4,count($allFiles),'only found '.count($allFiles).' out of 4 expected files');
 		foreach($expected as $file){
 			$this->assertNotIdentical(false,array_search($file,$allFiles),'cant find '.$file.' in archive');
-			$this->assertTrue($this->instance->fileExists($file));
+			$this->assertTrue($this->instance->fileExists($file),'file '.$file.' does not exist in archive');
 		}
 		$this->assertFalse($this->instance->fileExists('non/existing/file'));
 		
@@ -68,6 +68,7 @@ abstract class Test_Archive extends UnitTestCase {
 		$this->instance->addFile('lorem.txt',$textFile);
 		$this->assertEqual(1,count($this->instance->getFiles()));
 		$this->assertTrue($this->instance->fileExists('lorem.txt'));
+		$this->assertFalse($this->instance->fileExists('lorem.txt/'));
 		
 		$this->assertEqual(file_get_contents($textFile),$this->instance->getFile('lorem.txt'));
 		$this->instance->addFile('lorem.txt','foobar');
@@ -94,4 +95,39 @@ abstract class Test_Archive extends UnitTestCase {
 		$this->assertTrue($this->instance->fileExists('lorem.txt'));
 		$this->assertEqual(file_get_contents($dir.'/lorem.txt'),$this->instance->getFile('lorem.txt'));
 	}
+	public function testFolder(){
+		$this->instance=$this->getNew();
+		$this->assertFalse($this->instance->fileExists('/test'));
+		$this->assertFalse($this->instance->fileExists('/test/'));
+		$this->instance->addFolder('/test');
+		$this->assertTrue($this->instance->fileExists('/test'));
+		$this->assertTrue($this->instance->fileExists('/test/'));
+		$this->instance->remove('/test');
+		$this->assertFalse($this->instance->fileExists('/test'));
+		$this->assertFalse($this->instance->fileExists('/test/'));
+	}
+	public function testExtract(){
+		$dir=OC::$SERVERROOT.'/apps/files_archive/tests/data';
+		$this->instance=$this->getExisting();
+		$tmpDir=OC_Helper::tmpFolder();
+		$this->instance->extract($tmpDir);
+		$this->assertEqual(true,file_exists($tmpDir.'lorem.txt'));
+		$this->assertEqual(true,file_exists($tmpDir.'dir/lorem.txt'));
+		$this->assertEqual(true,file_exists($tmpDir.'logo-wide.png'));
+		$this->assertEqual(file_get_contents($dir.'/lorem.txt'),file_get_contents($tmpDir.'lorem.txt'));
+		OC_Helper::rmdirr($tmpDir);
+	}
+	public function testMoveRemove(){
+		$dir=OC::$SERVERROOT.'/apps/files_archive/tests/data';
+		$textFile=$dir.'/lorem.txt';
+		$this->instance=$this->getNew();
+		$this->instance->addFile('lorem.txt',$textFile);
+		$this->assertFalse($this->instance->fileExists('target.txt'));
+		$this->instance->rename('lorem.txt','target.txt');
+		$this->assertTrue($this->instance->fileExists('target.txt'));
+		$this->assertFalse($this->instance->fileExists('lorem.txt'));
+		$this->assertEqual(file_get_contents($textFile),$this->instance->getFile('target.txt'));
+		$this->instance->remove('target.txt');
+		$this->assertFalse($this->instance->fileExists('target.txt'));
+	}
 }
diff --git a/apps/files_archive/tests/tar.php b/apps/files_archive/tests/tar.php
new file mode 100644
index 0000000000000000000000000000000000000000..193a65b550a1e32e5d20960dc1e4aef95e3bed1d
--- /dev/null
+++ b/apps/files_archive/tests/tar.php
@@ -0,0 +1,20 @@
+<?php
+/**
+ * Copyright (c) 2012 Robin Appelman <icewind@owncloud.com>
+ * This file is licensed under the Affero General Public License version 3 or
+ * later.
+ * See the COPYING-README file.
+ */
+
+require_once('archive.php');
+
+class Test_Archive_TAR extends Test_Archive{
+	protected function getExisting(){
+		$dir=OC::$SERVERROOT.'/apps/files_archive/tests/data';
+		return new OC_Archive_TAR($dir.'/data.tar.gz');
+	}
+
+	protected function getNew(){
+		return new OC_Archive_TAR(OC_Helper::tmpFile('.tar.gz'));
+	}
+}
diff --git a/apps/files_encryption/appinfo/info.xml b/apps/files_encryption/appinfo/info.xml
index 053044aaed25a74d2def50f69259fa33fc9696ff..691b265bf60721ca3d0914633d78edd48cd48ebb 100644
--- a/apps/files_encryption/appinfo/info.xml
+++ b/apps/files_encryption/appinfo/info.xml
@@ -7,4 +7,7 @@
 	<licence>AGPL</licence>
 	<author>Robin Appelman</author>
 	<require>3</require>
+	<types>
+		<filesystem/>
+	</types>
 </info>
diff --git a/apps/files_external/appinfo/app.php b/apps/files_external/appinfo/app.php
new file mode 100644
index 0000000000000000000000000000000000000000..95770b44b75446e1789aa45057a6fe9441507151
--- /dev/null
+++ b/apps/files_external/appinfo/app.php
@@ -0,0 +1,11 @@
+<?php
+/**
+ * Copyright (c) 2012 Robin Appelman <icewind@owncloud.com>
+ * This file is licensed under the Affero General Public License version 3 or
+ * later.
+ * See the COPYING-README file.
+ */
+
+OC::$CLASSPATH['OC_Filestorage_FTP']='apps/files_external/lib/ftp.php';
+OC::$CLASSPATH['OC_Filestorage_DAV']='apps/files_external/lib/webdav.php';
+OC::$CLASSPATH['OC_Filestorage_Google']='apps/files_external/lib/google.php';
diff --git a/apps/files_external/appinfo/info.xml b/apps/files_external/appinfo/info.xml
new file mode 100644
index 0000000000000000000000000000000000000000..fb58297ff172c9ccb5ebf55085a4002d13cea1f9
--- /dev/null
+++ b/apps/files_external/appinfo/info.xml
@@ -0,0 +1,13 @@
+<?xml version="1.0"?>
+<info>
+	<id>files_external</id>
+	<name>External storage support</name>
+	<description>Mount external storage sources</description>
+	<version>0.1</version>
+	<licence>AGPL</licence>
+	<author>Robin Appelman</author>
+	<require>3</require>
+	<types>
+		<filesystem/>
+	</types>
+</info>
diff --git a/apps/files_remote/lib/ftp.php b/apps/files_external/lib/ftp.php
similarity index 100%
rename from apps/files_remote/lib/ftp.php
rename to apps/files_external/lib/ftp.php
diff --git a/apps/files_remote/lib/google.php b/apps/files_external/lib/google.php
similarity index 100%
rename from apps/files_remote/lib/google.php
rename to apps/files_external/lib/google.php
diff --git a/apps/files_remote/lib/webdav.php b/apps/files_external/lib/webdav.php
similarity index 100%
rename from apps/files_remote/lib/webdav.php
rename to apps/files_external/lib/webdav.php
diff --git a/apps/files_remote/tests/config.php b/apps/files_external/tests/config.php
similarity index 100%
rename from apps/files_remote/tests/config.php
rename to apps/files_external/tests/config.php
diff --git a/apps/files_remote/tests/ftp.php b/apps/files_external/tests/ftp.php
similarity index 89%
rename from apps/files_remote/tests/ftp.php
rename to apps/files_external/tests/ftp.php
index 03633b7c0d1095bbb90b1f6188a22b2419a7e114..aa565751ba315fc07af2bd7797708223bbde823a 100644
--- a/apps/files_remote/tests/ftp.php
+++ b/apps/files_external/tests/ftp.php
@@ -12,7 +12,7 @@ class Test_Filestorage_FTP extends Test_FileStorage {
 
 	public function setUp(){
 		$id=uniqid();
-		$this->config=include('apps/files_remote/tests/config.php');
+		$this->config=include('apps/files_external/tests/config.php');
 		$this->config['ftp']['root'].='/'.$id;//make sure we have an new empty folder to work in
 		$this->instance=new OC_Filestorage_FTP($this->config['ftp']);
 	}
diff --git a/apps/files_remote/tests/google.php b/apps/files_external/tests/google.php
similarity index 94%
rename from apps/files_remote/tests/google.php
rename to apps/files_external/tests/google.php
index b49f9e4647c868883278acf3c7209d47ee67f38b..1c028945228bda6788b8018e5e896bb1b22249a8 100644
--- a/apps/files_remote/tests/google.php
+++ b/apps/files_external/tests/google.php
@@ -27,7 +27,7 @@ class Test_Filestorage_Google extends Test_FileStorage {
 
 	public function setUp(){
 		$id=uniqid();
-		$this->config=include('apps/files_remote/tests/config.php');
+		$this->config=include('apps/files_external/tests/config.php');
 		$this->config['google']['root'].='/'.$id;//make sure we have an new empty folder to work in
 		$this->instance=new OC_Filestorage_Google($this->config['google']);
 	}
@@ -35,4 +35,4 @@ class Test_Filestorage_Google extends Test_FileStorage {
 	public function tearDown(){
 		$this->instance->rmdir('/');
 	}
-}
\ No newline at end of file
+}
diff --git a/apps/files_remote/tests/webdav.php b/apps/files_external/tests/webdav.php
similarity index 89%
rename from apps/files_remote/tests/webdav.php
rename to apps/files_external/tests/webdav.php
index 219fff8852d0e7fb5468ce5932104b409e8bff37..517992905408bdbe7cef071345b762d8af44d750 100644
--- a/apps/files_remote/tests/webdav.php
+++ b/apps/files_external/tests/webdav.php
@@ -12,7 +12,7 @@ class Test_Filestorage_DAV extends Test_FileStorage {
 
 	public function setUp(){
 		$id=uniqid();
-		$this->config=include('apps/files_remote/tests/config.php');
+		$this->config=include('apps/files_external/tests/config.php');
 		$this->config['webdav']['root'].='/'.$id;//make sure we have an new empty folder to work in
 		$this->instance=new OC_Filestorage_DAV($this->config['webdav']);
 	}
diff --git a/apps/files_remote/appinfo/app.php b/apps/files_remote/appinfo/app.php
deleted file mode 100644
index 02c1c3ae31344b670f058024aac753e96efe94c7..0000000000000000000000000000000000000000
--- a/apps/files_remote/appinfo/app.php
+++ /dev/null
@@ -1,11 +0,0 @@
-<?php
-/**
- * Copyright (c) 2012 Robin Appelman <icewind@owncloud.com>
- * This file is licensed under the Affero General Public License version 3 or
- * later.
- * See the COPYING-README file.
- */
-
-OC::$CLASSPATH['OC_Filestorage_FTP']='apps/files_remote/lib/ftp.php';
-OC::$CLASSPATH['OC_Filestorage_DAV']='apps/files_remote/lib/webdav.php';
-OC::$CLASSPATH['OC_Filestorage_Google']='apps/files_remote/lib/google.php';
diff --git a/apps/files_remote/appinfo/info.xml b/apps/files_remote/appinfo/info.xml
deleted file mode 100644
index 0720b6095b918ff6ef5107f2b37e37989ac734db..0000000000000000000000000000000000000000
--- a/apps/files_remote/appinfo/info.xml
+++ /dev/null
@@ -1,10 +0,0 @@
-<?xml version="1.0"?>
-<info>
-	<id>files_remote</id>
-	<name>Remote storage support</name>
-	<description>Mount remote storage sources</description>
-	<version>0.1</version>
-	<licence>AGPL</licence>
-	<author>Robin Appelman</author>
-	<require>3</require>
-</info>
diff --git a/apps/files_sharing/ajax/userautocomplete.php b/apps/files_sharing/ajax/userautocomplete.php
index 9d971fb62af7546c685c9c3352dce0754d12d05c..38b673ee51b585ebccbf7f9145a3d26a530fef94 100644
--- a/apps/files_sharing/ajax/userautocomplete.php
+++ b/apps/files_sharing/ajax/userautocomplete.php
@@ -7,21 +7,23 @@ OC_JSON::checkLoggedIn();
 OC_JSON::checkAppEnabled('files_sharing');
 
 $users = array();
-$ocusers = OC_User::getUsers();
+$groups = array();
 $self = OC_User::getUser();
-$groups = OC_Group::getUserGroups($self);
+$userGroups = OC_Group::getUserGroups($self);
 $users[] = "<optgroup label='Users'>";
-foreach ($ocusers as $user) {
-	if ($user != $self) {
-		$users[] = "<option value='".$user."'>".$user."</option>";
+$groups[] = "<optgroup label='Groups'>";
+foreach ($userGroups as $group) {
+	$groupUsers = OC_Group::usersInGroup($group);
+	foreach ($groupUsers as $user) {
+		if ($user != $self) {
+			$users[] = "<option value='".$user."'>".$user."</option>";
+		}
 	}
+	$groups[] = "<option value='".$group."'>".$group."</option>";
 }
 $users[] = "</optgroup>";
-$users[] = "<optgroup label='Groups'>";
-foreach ($groups as $group) {
-	$users[] = "<option value='".$group."'>".$group."</option>";
-}
-$users[] = "</optgroup>";
+$groups[] = "</optgroup>";
+$users = array_merge($users, $groups);
 OC_JSON::encodedPrint($users);
 
 ?>
diff --git a/apps/files_sharing/appinfo/info.xml b/apps/files_sharing/appinfo/info.xml
index abf847b4483a2ccf6a8d747f2b5f9148608790fa..8fda775520b602ae4b217b975aeeb0fb221f811c 100644
--- a/apps/files_sharing/appinfo/info.xml
+++ b/apps/files_sharing/appinfo/info.xml
@@ -8,4 +8,7 @@
 	<author>Michael Gapczynski</author>
 	<require>2</require>
 	<default_enable/>
+	<types>
+		<filesystem/>
+	</types>
 </info>
diff --git a/apps/files_sharing/lib_share.php b/apps/files_sharing/lib_share.php
index 42739bdfba94f628d364173b306147611d4379dd..673984f393b6fe28f20f7983c6f6ec9f21c899fb 100644
--- a/apps/files_sharing/lib_share.php
+++ b/apps/files_sharing/lib_share.php
@@ -52,8 +52,18 @@ class OC_Share {
 				// 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);
+				$userGroups = OC_Group::getUserGroups($uid_owner);
+				// Check if the user is in one of the owner's groups
+				foreach ($userGroups as $group) {
+					if ($inGroup = OC_Group::inGroup($uid_shared_with, $group)) {
+						$gid = null;
+						$uid_shared_with = array($uid_shared_with);
+						break;
+					}
+				}
+				if (!$inGroup) {
+					throw new Exception("You can't share with ".$uid_shared_with);
+				}
 			} else {
 				throw new Exception($uid_shared_with." is not a user");
 			}
diff --git a/apps/files_texteditor/js/editor.js b/apps/files_texteditor/js/editor.js
index 02d39b984309732fb7508e8653ed22f42f271a0b..1e136fe68e53dba29e7d4d33189101b8d921b6b9 100644
--- a/apps/files_texteditor/js/editor.js
+++ b/apps/files_texteditor/js/editor.js
@@ -76,7 +76,7 @@ function showControls(filename,writeperms){
  
 function bindControlEvents(){
 	$("#editor_save").die('click',doFileSave).live('click',doFileSave);	
-	$('#editor_close').die('click',hideFileEditor).live('click',hideFileEditor);
+	$('#editor_close').die('click',closeBtnClick).live('click',closeBtnClick);
 	$('#gotolineval').die('keyup', goToLine).live('keyup', goToLine);
 	$('#editorsearchval').die('keyup', doSearch).live('keyup', doSearch);
 	$('#clearsearchbtn').die('click', resetSearch).live('click', resetSearch);
@@ -141,31 +141,38 @@ function doSearch(){
 // Tries to save the file.
 function doFileSave(){
 	if(editorIsShown()){
-		// Get file path
-		var path = $('#editor').attr('data-dir')+'/'+$('#editor').attr('data-filename');
-		// Get original mtime
-		var mtime = $('#editor').attr('data-mtime');
-		// Show saving spinner
-		$("#editor_save").die('click',doFileSave);
-		$('#save_result').remove();
-		$('#editor_save').text(t('files_texteditor','Saving...'));
-		// Get the data
-		var filecontents = window.aceEditor.getSession().getValue();
-		// Send the data
-		$.post(OC.filePath('files_texteditor','ajax','savefile.php'), { filecontents: filecontents, path: path, mtime: mtime },function(jsondata){
-			if(jsondata.status!='success'){
-				// Save failed
-				$('#editor_save').text(t('files_texteditor','Save'));
-				$('#editor_save').after('<p id="save_result" style="float: left">Failed to save file</p>');
-				$("#editor_save").live('click',doFileSave); 
-			} else {
-				// Save OK	
-				// Update mtime
-				$('#editor').attr('data-mtime',jsondata.data.mtime);
-				$('#editor_save').text(t('files_texteditor','Save'));     
-				$("#editor_save").live('click',doFileSave);
-			}
-		},'json');
+		// Changed contents?
+		if($('#editor').attr('data-edited')=='true'){
+			// Get file path
+			var path = $('#editor').attr('data-dir')+'/'+$('#editor').attr('data-filename');
+			// Get original mtime
+			var mtime = $('#editor').attr('data-mtime');
+			// Show saving spinner
+			$("#editor_save").die('click',doFileSave);
+			$('#save_result').remove();
+			$('#editor_save').text(t('files_texteditor','Saving...'));
+			// Get the data
+			var filecontents = window.aceEditor.getSession().getValue();
+			// Send the data
+			$.post(OC.filePath('files_texteditor','ajax','savefile.php'), { filecontents: filecontents, path: path, mtime: mtime },function(jsondata){
+				if(jsondata.status!='success'){
+					// Save failed
+					$('#editor_save').text(t('files_texteditor','Save'));
+					$('#editor_save').after('<p id="save_result" style="float: left">Failed to save file</p>');
+					$("#editor_save").live('click',doFileSave); 
+				} else {
+					// Save OK	
+					// Update mtime
+					$('#editor').attr('data-mtime',jsondata.data.mtime);
+					$('#editor_save').text(t('files_texteditor','Save'));     
+					$("#editor_save").live('click',doFileSave);
+					// Update titles
+					$('#editor').attr('data-edited', 'false');
+					$('#breadcrumb_file').text($('#editor').attr('data-filename'));	
+					document.title = $('#editor').attr('data-filename')+' - ownCloud';
+				}
+			},'json');
+		}
 	}
 };
 
@@ -192,10 +199,11 @@ function showFileEditor(dir,filename){
 						// Show the control bar
 						showControls(filename,result.data.write);
 						// Update document title
-						document.title = filename;
+						document.title = filename+' - ownCloud';
 						$('#editor').text(result.data.filecontents);
 						$('#editor').attr('data-dir', dir);
 						$('#editor').attr('data-filename', filename);
+						$('#editor').attr('data-edited', 'false');
 						window.aceEditor = ace.edit("editor");
 						aceEditor.setShowPrintMargin(false);
 						aceEditor.getSession().setUseWrapMode(true);
@@ -207,10 +215,17 @@ function showFileEditor(dir,filename){
 						OC.addScript('files_texteditor','aceeditor/theme-clouds', function(){
 							window.aceEditor.setTheme("ace/theme/clouds");
 						});
+						window.aceEditor.getSession().on('change', function(){
+							if($('#editor').attr('data-edited')!='true'){
+								$('#editor').attr('data-edited', 'true');
+								$('#breadcrumb_file').text($('#breadcrumb_file').text()+' *');	
+								document.title = $('#editor').attr('data-filename')+' * - ownCloud';
+							}
+						});
 					});
 				} else {
 					// Failed to get the file.
-					alert(result.data.message);
+					OC.dialogs.alert(result.data.message, t('files_texteditor','An error occurred!'));
 				}
 			// End success
 			}
@@ -220,6 +235,19 @@ function showFileEditor(dir,filename){
 	}
 }
 
+function closeBtnClick(){
+	if($('#editor').attr('data-edited')=='true'){
+		// Show confirm
+		OC.dialogs.confirm(t('files_texteditor','You have unsaved changes that will be lost! Do you still want to close?'),t('files_texteditor','Really close?'),function(close){
+			if(close){
+				hideFileEditor();
+			}	
+		});
+	} else {
+		hideFileEditor();
+	}	
+}
+
 // Fades out the editor.
 function hideFileEditor(){
 	// Fades out editor controls
@@ -287,4 +315,5 @@ $(document).ready(function(){
 	$('#editor').remove();
 	// Binds the save keyboard shortcut events
 	//$(document).unbind('keydown').bind('keydown',checkForSaveKeyPress);
+
 });
diff --git a/apps/gallery/ajax/galleryOp.php b/apps/gallery/ajax/galleryOp.php
index b0433898cda88aa8cf26309f773f4618e2aaefba..1b3ad48f561046574d6f3c7ffadcf5580196a8c5 100644
--- a/apps/gallery/ajax/galleryOp.php
+++ b/apps/gallery/ajax/galleryOp.php
@@ -127,6 +127,9 @@ function handleGetGallery($path) {
 function handleShare($path, $share, $recursive) {
   $recursive = $recursive == 'true' ? 1 : 0;
   $owner = OC_User::getUser();
+  $root = OC_Preferences::getValue(OC_User::getUser(),'gallery', 'root', '/');
+  $path = utf8_decode(rtrim($root.$path,'/'));
+  if($path == '') $path = '/';
   $r = OC_Gallery_Album::find($owner, null, $path);
   if ($row = $r->fetchRow()) {
     $albumId = $row['album_id'];
diff --git a/apps/gallery/appinfo/app.php b/apps/gallery/appinfo/app.php
index 3e7e38301cfa46153fc11ca8790c4121b7f8974e..f7e0651275e19f26d15ea612660309677bff0ded 100644
--- a/apps/gallery/appinfo/app.php
+++ b/apps/gallery/appinfo/app.php
@@ -32,14 +32,14 @@ $l = new OC_L10N('gallery');
 OC_App::register(array(
   'order' => 20,
   'id' => 'gallery',
-  'name' => 'Gallery'));
+  'name' => 'Pictures'));
 
 OC_App::addNavigationEntry( array(
  'id' => 'gallery_index',
  'order' => 20,
  'href' => OC_Helper::linkTo('gallery', 'index.php'),
  'icon' => OC_Helper::imagePath('core', 'places/picture.svg'),
- 'name' => $l->t('Gallery')));
+ 'name' => $l->t('Pictures')));
 
  class OC_GallerySearchProvider implements OC_Search_Provider{
 	static function search($query){
diff --git a/apps/gallery/appinfo/info.xml b/apps/gallery/appinfo/info.xml
index 19c5dc8b25eddd4e326b17e179faf7e21a7104a6..4c8c1cee24257d05c429d4de5267dbee0caae51f 100644
--- a/apps/gallery/appinfo/info.xml
+++ b/apps/gallery/appinfo/info.xml
@@ -1,11 +1,11 @@
 <?xml version="1.0"?> 
 <info>
 	<id>gallery</id>
-	<name>Gallery</name>
+	<name>Pictures</name>
 	<version>0.4</version>
 	<licence>AGPL</licence>
 	<author>Bartek Przybylski</author>
 	<require>2</require>
-	<description>Gallery application for ownCloud</description>
+	<description>Dedicated pictures application</description>
 	<default_enable/>
 </info>
diff --git a/apps/gallery/js/album_cover.js b/apps/gallery/js/album_cover.js
index 061bbcd0b47f74ca1e9db7065480cbd45ac2daee..d44e7f83d1f7a4ff74e5c0943e0d0742daf2e781 100644
--- a/apps/gallery/js/album_cover.js
+++ b/apps/gallery/js/album_cover.js
@@ -43,8 +43,9 @@ function shareGallery() {
                      {text: 'Shared gallery address', name: 'address', type: 'text', value: existing_token}];
     OC.dialogs.form(form_fields, t('gallery', 'Share gallery'), function(values){
     var p = '';
-    for (var i in paths) p += '/'+paths[i];
+    for (var i in paths) p += paths[i]+'/';
     if (p == '') p = '/';
+    alert(p);
     $.getJSON(OC.filePath('gallery', 'ajax', 'galleryOp.php'), {operation: 'share', path: p, share: values[0].value, recursive: values[1].value}, function(r) {
       if (r.status == 'success') {
         Albums.shared = r.sharing;
@@ -112,42 +113,28 @@ function scanForAlbums(cleanup) {
 }
 
 function settings() {
-	$( '#g-dialog-settings' ).dialog({
-		height: 180,
-		width: 350,
-		modal: false,
-		buttons: [
-			{
-				text: t('gallery', 'Apply'),
-				click: function() {
-					var scanning_root = $('#g-scanning-root').val();
-					var disp_order = $('#g-display-order option:selected').val();
+  OC.dialogs.form([{text: t('gallery', 'Scanning root'), name: 'root', type:'text', value:gallery_scanning_root},
+                  {text: t('gallery', 'Default order'), name: 'order', type:'select', value:gallery_default_order, options:[
+                      {text:t('gallery', 'Ascending'), value:'ASC'}, {text: t('gallery', 'Descending'), value:'DESC'} ]}],
+                  t('gallery', 'Settings'),
+                  function(values) {
+                    var scanning_root = values[0].value;
+                    var disp_order = values[1].value;
 					if (scanning_root == '') {
-						alert('Scanning root cannot be empty');
+            OC.dialogs.alert(t('gallery', 'Scanning root cannot be empty'), t('gallery', 'Error'));
 						return;
 					}
 					$.getJSON(OC.filePath('gallery','ajax','galleryOp.php'), {operation: 'store_settings', root: scanning_root, order: disp_order}, function(r) {
 						if (r.status == 'success') {
-						if (r.rescan == 'yes') {
-							$('#g-dialog-settings').dialog('close');
-							Albums.clear(document.getElementById('gallery_list'));
-							scanForAlbums(true);
-							return;
-						}
+              if (r.rescan == 'yes') {
+                Albums.clear(document.getElementById('gallery_list'));
+                scanForAlbums(true);
+              }
+              gallery_scanning_root = scanning_root;
 						} else {
-						alert('Error: ' + r.cause);
-						return;
+              OC.dialogs.alert(t('gallery', 'Error: ') + r.cause, t('gallery', 'Error'));
+              return;
 						}
-						$('#g-dialog-settings').dialog('close');
 					});
-				}
-			},
-			{
-				text: t('gallery', 'Cancel'),
-				click: function() {
-				$(this).dialog('close');
-				}
-			}
-		],
-	});
+				});
 }
diff --git a/apps/gallery/templates/index.php b/apps/gallery/templates/index.php
index c6373d3b0a2b1e49e5e69134a1e7d505fc717e48..9bec5db1b9131da268db2ba004e69f896d1f625a 100644
--- a/apps/gallery/templates/index.php
+++ b/apps/gallery/templates/index.php
@@ -9,7 +9,7 @@ OC_Util::addScript('files_imageviewer', 'jquery.fancybox-1.3.4.pack');
 OC_Util::addStyle( 'files_imageviewer', 'jquery.fancybox-1.3.4' );
 $l = new OC_L10N('gallery');
 ?>
-
+<script type="text/javascript">var gallery_scanning_root='<? echo OC_Preferences::getValue(OC_User::getUser(), 'gallery', 'root', '/'); ?>'; var gallery_default_order = '<? echo OC_Preferences::getValue(OC_User::getUser(), 'gallery', 'order', 'ASC'); ?>';</script>
 <div id="controls">
 	<div id="scan">
 		<div id="scanprogressbar"></div>
@@ -29,40 +29,3 @@ $l = new OC_L10N('gallery');
 </div>
 <div id="gallery_list">
 </div>
-
-<div id="dialog-confirm" title="<?php echo $l->t('Remove confirmation');?>" style="display: none">
-	<p><span class="ui-icon ui-icon-alert" style="float:left; margin:0 7px 20px 0;"></span><?php echo $l->t('Do you want to remove album');?> <span id="albumName"></span>?</p>
-</div>
-
-<div id="dialog-form" title="<?php echo $l->t('Change album name');?>" style="display:none">
-	<form>
-	<fieldset>
-		<label for="name"><?php echo $l->t('New album name');?></label>
-		<input type="text" name="name" id="name" class="text ui-widget-content ui-corner-all" />
-	</fieldset>
-	</form>
-</div>
-
-<div id="g-dialog-settings" title="<?php echo $l->t('Settings');?>" style="display:none">
-	<form>
-		<fieldset><?php $root = OC_Preferences::getValue(OC_User::getUser(), 'gallery', 'root', '/'); $order = OC_Preferences::getValue(OC_User::getUser(), 'gallery', 'order', 'ASC');?>
-		<label for="name"><?php echo $l->t('Scanning root');?></label>
-		<input type="text" name="g-scanning-root" id="g-scanning-root" class="text ui-widget-content ui-corner-all" value="<?php echo $root;?>" /><br/>
-
-		<label for="sort"><?php echo $l->t('Default sorting'); ?></label>
-		<select id="g-display-order">
-			<option value="ASC"<?php echo $order=='ASC'?'selected':'';?>><?php echo $l->t('Ascending'); ?></option>
-			<option value="DESC"<?php echo $order=='DESC'?'selected':'';?>><?php echo $l->t('Descending'); ?></option>
-		</select><br/>
-<!--
-		<label for="sort"><?php echo $l->t('Thumbnails size'); ?></label>
-		<select>
-			<option value="100">100px</option>
-			<option value="150">150px</option>
-			<option value="200">200px</option>
-			</select>
-			-->
-	</fieldset>
-	</form>
-</div>
-
diff --git a/apps/media/css/music.css b/apps/media/css/music.css
index 07173624018c05cf7828ed9a1b4e106dee508fb2..164a6c62ae6f0d0ee5e4840d1489772a1b3e99e7 100644
--- a/apps/media/css/music.css
+++ b/apps/media/css/music.css
@@ -22,6 +22,7 @@ div.jp-volume-bar-value { background:#ccc; width:0; height:0.4em; }
 #leftcontent img.remove { display:none; float:right; cursor:pointer; opacity: 0; }
 #leftcontent li:hover img.remove { display:inline; opacity: .3; }
 #leftcontent li div.label { float: left; width: 200px; overflow: hidden; text-overflow: ellipsis; }
+#rightcontent { overflow: auto; }
 #playlist li { list-style-type:none; }
 .template { display:none; }
 .collection_playing { background:#eee; font-weight: bold; }
diff --git a/apps/remoteStorage/appinfo/info.xml b/apps/remoteStorage/appinfo/info.xml
index 1ab55e8c094a8d0b51b0f9aa36193dac96b1f4af..1f9618a3334a8911d850ec3c29a239cd73124848 100644
--- a/apps/remoteStorage/appinfo/info.xml
+++ b/apps/remoteStorage/appinfo/info.xml
@@ -2,7 +2,7 @@
 <info>
 	<id>remoteStorage</id>
 	<name>remoteStorage compatibility</name>
-	<description>Enables your users to use ownCloud as their remote storage for unhosted applications.</description>
+	<description>Enables you to use ownCloud as their remote storage for unhosted applications. This app requires the Webfinger app to be installed and enabled correctly. More info on <a href="http://unhosted.org">the website of the unhosted movement</a>.</description>
 	<version>0.6</version>
 	<licence>AGPL or MIT</licence>
 	<author>Michiel de Jong</author>
diff --git a/core/css/styles.css b/core/css/styles.css
index f5a181c4529f42721acb0df5bf4ceaea0648b7e2..1c50df9e58b28d14be5d74c1f2b6ca1e045a0944 100644
--- a/core/css/styles.css
+++ b/core/css/styles.css
@@ -131,3 +131,9 @@ li.error { width:640px; margin:4em auto; padding:1em 1em 1em 4em; background:#ff
 .separator { display: inline; border-left: 1px solid #d3d3d3; border-right: 1px solid #fff; height: 10px; width:0px; margin: 4px; }
 
 a.bookmarklet { background-color: #ddd; border:1px solid #ccc; padding: 5px;padding-top: 0px;padding-bottom: 2px; text-decoration: none; margin-top: 5px }
+
+/* ---- DIALOGS ---- */
+
+#dirtree {width: 100%;}
+#filelist {height: 270px; overflow:scroll; background-color: white;}
+.filepicker_element_selected { background-color: lightblue;}
diff --git a/core/js/eventsource.js b/core/js/eventsource.js
index dece1a69d048bf5399ce259750695291dc425a4d..08259e02cae46adef9b943744b091e11d1e38917 100644
--- a/core/js/eventsource.js
+++ b/core/js/eventsource.js
@@ -33,8 +33,12 @@
  */
 OC.EventSource=function(src,data){
 	var dataStr='';
-	for(name in data){
-		dataStr+=name+'='+encodeURIComponent(data[name])+'&';
+	this.typelessListeners=[];
+	this.listeners={};
+	if(data){
+		for(name in data){
+			dataStr+=name+'='+encodeURIComponent(data[name])+'&';
+		}
 	}
 	if(!this.useFallBack && typeof EventSource !='undefined'){
 		this.source=new EventSource(src+'?'+dataStr);
@@ -42,7 +46,7 @@ OC.EventSource=function(src,data){
 			for(var i=0;i<this.typelessListeners.length;i++){
 				this.typelessListeners[i](JSON.parse(e.data));
 			}
-		}
+		}.bind(this);
 	}else{
 		iframeId='oc_eventsource_iframe_'+OC.EventSource.iframeCount;
 		OC.EventSource.fallBackSources[OC.EventSource.iframeCount]=this;
@@ -64,7 +68,7 @@ OC.EventSource=function(src,data){
 OC.EventSource.fallBackSources=[];
 OC.EventSource.iframeCount=0;//number of fallback iframes
 OC.EventSource.fallBackCallBack=function(id,type,data){
-	OC.EventSource.fallBackSources[id].fallBackCallBack(type,JSON.parse(data));
+	OC.EventSource.fallBackSources[id].fallBackCallBack(type,data);
 }
 OC.EventSource.prototype={
 	typelessListeners:[],
diff --git a/core/js/js.js b/core/js/js.js
index df1b5c6ce769f19c58a26035ba3d948176da2a34..44b4f503b8cf174a912c77440f5faedcb6888454 100644
--- a/core/js/js.js
+++ b/core/js/js.js
@@ -126,7 +126,13 @@ OC={
 			});
 		}
 	},
-	dialogs:OCdialogs
+	dialogs:OCdialogs,
+  mtime2date:function(mtime) {
+    mtime = parseInt(mtime);
+    var date = new Date(1000*mtime);
+    var ret = date.getDate()+'.'+(date.getMonth()+1)+'.'+date.getFullYear()+', '+date.getHours()+':'+date.getMinutes();
+    return ret;
+  }
 };
 OC.search.customResults={};
 OC.search.currentResult=-1;
diff --git a/core/js/oc-dialogs.js b/core/js/oc-dialogs.js
index c11ac13332b07ff624b5734e72b5190ecc3b9564..d40c433bda2ffc638df9e8575c2c433fa3b6ae3c 100644
--- a/core/js/oc-dialogs.js
+++ b/core/js/oc-dialogs.js
@@ -17,7 +17,6 @@
  * 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(bartek): add select option in form
  */
 
 /**
@@ -30,9 +29,9 @@ OCdialogs = {
    * @param title dialog title
    * @param callback which will be triggered when user press OK
    */
-  alert:function(text, title, callback) {
+  alert:function(text, title, callback, modal) {
     var content = '<p><span class="ui-icon ui-icon-alert"></span>'+text+'</p>';
-    OCdialogs.message(content, title, OCdialogs.ALERT_DIALOG, OCdialogs.OK_BUTTON, callback);
+    OCdialogs.message(content, title, OCdialogs.ALERT_DIALOG, OCdialogs.OK_BUTTON, callback, modal);
   },
   /**
    * displays info dialog
@@ -40,9 +39,9 @@ OCdialogs = {
    * @param title dialog title
    * @param callback which will be triggered when user press OK
    */
-  info:function(text, title, callback) {
+  info:function(text, title, callback, modal) {
     var content = '<p><span class="ui-icon ui-icon-info"></span>'+text+'</p>';
-    OCdialogs.message(content, title, OCdialogs.ALERT_DIALOG, OCdialogs.OK_BUTTON, callback);
+    OCdialogs.message(content, title, OCdialogs.ALERT_DIALOG, OCdialogs.OK_BUTTON, callback, modal);
   },
   /**
    * displays confirmation dialog
@@ -50,9 +49,9 @@ OCdialogs = {
    * @param title dialog title
    * @param callback which will be triggered when user press YES or NO (true or false would be passed to callback respectively)
    */
-  confirm:function(text, title, callback) {
+  confirm:function(text, title, callback, modal) {
     var content = '<p><span class="ui-icon ui-icon-notice"></span>'+text+'</p>';
-    OCdialogs.message(content, title, OCdialogs.ALERT_DIALOG, OCdialogs.YES_NO_BUTTONS, callback);
+    OCdialogs.message(content, title, OCdialogs.ALERT_DIALOG, OCdialogs.YES_NO_BUTTONS, callback, modal);
   },
   /**
    * prompt for user input
@@ -60,9 +59,9 @@ OCdialogs = {
    * @param title dialog title
    * @param callback which will be triggered when user press OK (input text will be passed to callback)
    */
-  prompt:function(text, title, default_value, callback) {
+  prompt:function(text, title, default_value, callback, modal) {
     var content = '<p><span class="ui-icon ui-icon-pencil"></span>'+text+':<br/><input type="text" id="oc-dialog-prompt-input" value="'+default_value+'" style="width:90%"></p>';
-    OCdialogs.message(content, title, OCdialogs.PROMPT_DIALOG, OCdialogs.OK_CANCEL_BUTTONS, callback);
+    OCdialogs.message(content, title, OCdialogs.PROMPT_DIALOG, OCdialogs.OK_CANCEL_BUTTONS, callback, modal);
   },
   /**
    * prompt user for input with custom form
@@ -71,7 +70,7 @@ OCdialogs = {
    * @param title dialog title
    * @param callback which will be triggered when user press OK (user answers will be passed to callback in following format: [{name:'return name', value: 'user value'},...])
    */
-  form:function(fields, title, callback) {
+  form:function(fields, title, callback, modal) {
     var content = '<table>';
     for (var a in fields) {
       content += '<tr><td>'+fields[a].text+'</td><td>';
@@ -84,16 +83,63 @@ OCdialogs = {
             } else content += '>';
           } else if (type == 'text' || type == 'password' && fields[a].value)
             content += ' value="'+fields[a].value+'">';
+      } else if (type == 'select') {
+        content += '<select name="'+fields[a].name+'"';
+        if (fields[a].value != undefined)
+          content += ' value="'+fields[a].value+'"';
+        content += '>';
+        for (var o in fields[a].options)
+          content += '<option value="'+fields[a].options[o].value+'">'+fields[a].options[o].text+'</option>';
+        content += '</select>';
       }
-      content += "</td></tr>"
+      content += '</td></tr>';
     }
-    content += "</table>";
-    OCdialogs.message(content, title, OCdialogs.FORM_DIALOG, OCdialogs.OK_CANCEL_BUTTONS, callback);
+    content += '</table>';
+    OCdialogs.message(content, title, OCdialogs.FORM_DIALOG, OCdialogs.OK_CANCEL_BUTTONS, callback, modal);
   },
-  message:function(content, title, dialog_type, buttons, callback) {
+  filepicker:function(title, callback, multiselect, mimetype_filter, modal) {
+    var c_name = 'oc-dialog-'+OCdialogs.dialogs_counter+'-content';
+    var c_id = '#'+c_name;
+    var d = '<div id="'+c_name+'" title="'+title+'"><select id="dirtree"><option value="0">'+OC.currentUser+'</option></select><div id="filelist"></div></div>';
+    if (!modal) modal = false;
+    if (!multiselect) multiselect = false;
+    $('body').append(d);
+    $(c_id + ' #dirtree').focus(function() { var t = $(this); t.data('oldval',  t.val())})
+                         .change({dcid: c_id}, OC.dialogs.handleTreeListSelect);
+    $(c_id).ready(function(){
+      $.getJSON(OC.webroot+'/files/ajax/rawlist.php', function(r){OC.dialogs.fillFilePicker(r, c_id, callback)});
+    }).data('multiselect', multiselect);
+    // build buttons
+    var b = [
+      {text: t('dialogs', 'Choose'), click: function(){
+          if (callback != undefined) {
+            var p;
+            if ($(c_id).data('multiselect') == true) {
+              p = [];
+              $(c_id+' .filepicker_element_selected #filename').each(function(i, elem) {
+                p.push(($(c_id).data('path')?$(c_id).data('path'):'')+'/'+$(elem).text());
+              });
+            } else {
+              var p = $(c_id).data('path');
+              if (p == undefined) p = '';
+              p = p+'/'+$(c_id+' .filepicker_element_selected #filename').text()
+            }
+            callback(p);
+            $(c_id).dialog('close');
+          }
+        }
+      },
+      {text: t('dialogs', 'Cancel'), click: function(){$(c_id).dialog('close'); }}
+      ];
+    $(c_id).dialog({width: 4*$(document).width()/9, height: 400, modal: modal, buttons: b});
+    OCdialogs.dialogs_counter++;
+  },
+  // guts, dont use, dont touch
+  message:function(content, title, dialog_type, buttons, callback, modal) {
     var c_name = 'oc-dialog-'+OCdialogs.dialogs_counter+'-content';
     var c_id = '#'+c_name;
     var d = '<div id="'+c_name+'" title="'+title+'">'+content+'</div>';
+    if (modal == undefined) modal = false;
     $('body').append(d);
     var b = [];
     switch (buttons) {
@@ -107,7 +153,7 @@ OCdialogs = {
         var f;
         switch(dialog_type) {
           case OCdialogs.ALERT_DIALOG:
-            f = function(){$(c_id).dialog('close'); };
+            f = function(){$(c_id).dialog('close'); callback();};
           break;
           case OCdialogs.PROMPT_DIALOG:
             f = function(){OCdialogs.prompt_ok_handler(callback, c_id)};
@@ -120,7 +166,7 @@ OCdialogs = {
       break;
     }
     var possible_height = ($('tr', d).size()+1)*30;
-    $(c_id).dialog({width: 4*$(document).width()/9, height: possible_height + 120, modal: false, buttons: b});
+    $(c_id).dialog({width: 4*$(document).width()/9, height: possible_height + 120, modal: modal, buttons: b});
     OCdialogs.dialogs_counter++;
   },
   // dialogs buttons types
@@ -144,7 +190,7 @@ OCdialogs = {
     if (callback != undefined) {
       var r = [];
       var c = 0;
-      $(c_id + ' input').each(function(i, elem) {
+      $(c_id + ' input, '+c_id+' select').each(function(i, elem) {
         r[c] = {name: $(elem).attr('name'), value: OCdialogs.determineValue(elem)};
         c++;
       });
@@ -153,5 +199,44 @@ OCdialogs = {
     } else {
       $(c_id).dialog('close');
     }
+  },
+  fillFilePicker:function(r, dialog_content_id) {
+    var entry_template = '<div onclick="javascript:OC.dialogs.handlePickerClick(this, \'*ENTRYNAME*\',\''+dialog_content_id+'\')" data="*ENTRYTYPE*"><img src="*MIMETYPEICON*" style="margin-right:1em;"><span id="filename">*NAME*</span><div style="float:right;margin-right:1em;">*LASTMODDATE*</div></div>';
+    var names = '';
+    for (var a in r.data) {
+      names += entry_template.replace('*LASTMODDATE*', OC.mtime2date(r.data[a].mtime)).replace('*NAME*', r.data[a].name).replace('*MIMETYPEICON*', OC.webroot+'/core/img/filetypes/'+(r.data[a].type=='dir'?'folder':r.data[a].mimetype.replace('/','-'))+'.png').replace('*ENTRYNAME*', r.data[a].name).replace('*ENTRYTYPE*', r.data[a].type);
+    }
+    $(dialog_content_id + ' #filelist').html(names);
+  },
+  handleTreeListSelect:function(event) {
+    var newval = parseInt($(this).val());
+    var oldval = parseInt($(this).data('oldval'));
+    while (newval != oldval && oldval > 0) {
+      $('option:last', this).remove();
+      $('option:last', this).attr('selected','selected');
+      oldval--;
+    }
+    var skip_first = true;
+    var path = '';
+    $(this).children().each(function(i, element) { if (skip_first) {skip_first = false; return; }path += '/'+$(element).text(); });
+    $(event.data.dcid).data('path', path);
+    $.getJSON(OC.webroot+'/files/ajax/rawlist.php', {dir: path}, function(r){OC.dialogs.fillFilePicker(r, event.data.dcid)});
+  },
+  // this function is in early development state, please dont use it unlsess you know what you are doing
+  handlePickerClick:function(element, name, dcid) {
+    var p = $(dcid).data('path');
+    if (p == undefined) p = '';
+    p = p+'/'+name;
+    if ($(element).attr('data') == 'file'){
+      if ($(dcid).data('multiselect') != true)
+        $(dcid+' .filepicker_element_selected').removeClass('filepicker_element_selected');
+      $(element).toggleClass('filepicker_element_selected');
+      return;
+    }
+    $(dcid).data('path', p);
+    $(dcid + ' #dirtree option:last').removeAttr('selected');
+    var newval = parseInt($(dcid + ' #dirtree option:last').val())+1;
+    $(dcid + ' #dirtree').append('<option selected="selected" value="'+newval+'">'+name+'</option>');
+    $.getJSON(OC.webroot+'/files/ajax/rawlist.php', {dir: p}, function(r){OC.dialogs.fillFilePicker(r, dcid)});
   }
 };
diff --git a/core/templates/login.php b/core/templates/login.php
index 6c0a7a12227a64102ddf4af3c14e893cd9d9b0d3..4ba92221a7d43a01a9fe48920d5aaa1a9b0e7189 100644
--- a/core/templates/login.php
+++ b/core/templates/login.php
@@ -1,3 +1,4 @@
+<!--[if IE 8]><style>input[type="checkbox"]{padding:0;}</style><![endif]-->
 <form action="index.php" method="post">
 	<fieldset>
 		<?php if(!empty($_['redirect'])) { echo '<input type="hidden" name="redirect_url" value="'.$_['redirect'].'" />'; } ?>
@@ -6,7 +7,7 @@
 		<?php endif; ?>
 		<p class="infield">
 			<label for="user" class="infield"><?php echo $l->t( 'Username' ); ?></label>
-			<input type="text" name="user" id="user" value="<?php echo !empty($_POST['user'])?$_POST['user'].'"':'" autofocus'; ?> autocomplete="off" required />
+			<input type="text" name="user" id="user" value="<?php echo !empty($_POST['user'])?htmlentities($_POST['user']).'"':'" autofocus'; ?> autocomplete="off" required />
 		</p>
 		<p class="infield">
 			<label for="password" class="infield"><?php echo $l->t( 'Password' ); ?></label>
diff --git a/db_structure.xml b/db_structure.xml
index 5eef44d8e845a3bf589d3fc4869b7924aba0045e..2df218d359c83277dd178f905d158ccde24f15ad 100644
--- a/db_structure.xml
+++ b/db_structure.xml
@@ -64,6 +64,14 @@
     <length>512</length>
    </field>
 
+   <field>
+	   <name>path_hash</name>
+	   <type>text</type>
+	   <default></default>
+	   <notnull>true</notnull>
+	   <length>32</length>
+   </field>
+
    <field>
 	   <name>parent</name>
 	   <type>integer</type>
@@ -79,7 +87,7 @@
 	   <default>
 	   </default>
 	   <notnull>true</notnull>
-	   <length>512</length>
+	   <length>300</length>
    </field>
 
    <field>
@@ -159,14 +167,13 @@
 	   <length>1</length>
    </field>
 
-   <!--<index>
-	   <name>fscache_path_index</name>
-	<unique>true</unique>
+   <index>
+	   <name>fscache_path_hash_index</name>
     <field>
-     <name>path</name>
+     <name>path_hash</name>
      <sorting>ascending</sorting>
     </field>
-   </index>-->
+   </index>
 
    <index>
 	   <name>parent_index</name>
@@ -176,6 +183,14 @@
 	   </field>
    </index>
 
+   <index>
+	   <name>name_index</name>
+	   <field>
+		   <name>name</name>
+		   <sorting>ascending</sorting>
+	   </field>
+   </index>
+
    <index>
 	   <name>parent_name_index</name>
 	   <field>
diff --git a/files/ajax/download.php b/files/ajax/download.php
index 198069f3fa1c212a83360a13443fdfe39f867c97..39852613ab95ae10dd86fd86b7e07e029dc1d7db 100644
--- a/files/ajax/download.php
+++ b/files/ajax/download.php
@@ -21,6 +21,9 @@
 *
 */
 
+// only need filesystem apps
+$RUNTIME_APPTYPES=array('filesystem');
+
 // Init owncloud
 require_once('../../lib/base.php');
 
diff --git a/files/ajax/list.php b/files/ajax/list.php
index 8a414827e1c8c43db3dee18a0005ea89e87c17c9..ec9ab7342dd94df17585afc359de882816c375c0 100644
--- a/files/ajax/list.php
+++ b/files/ajax/list.php
@@ -1,5 +1,8 @@
 <?php
 
+// only need filesystem apps
+$RUNTIME_APPTYPES=array('filesystem');
+
 // Init owncloud
 require_once('../../lib/base.php');
 
diff --git a/files/ajax/mimeicon.php b/files/ajax/mimeicon.php
index 8724016b3a190b25e2010dcd78fbebe05cfdad18..ff72ba0f5b76af7142000c5b981a7f60dc6f6d22 100644
--- a/files/ajax/mimeicon.php
+++ b/files/ajax/mimeicon.php
@@ -1,5 +1,8 @@
 <?php
 
+// no need for apps
+$RUNTIME_NOAPPS=false;
+
 // Init owncloud
 require_once('../../lib/base.php');
 
diff --git a/files/ajax/rawlist.php b/files/ajax/rawlist.php
new file mode 100644
index 0000000000000000000000000000000000000000..0abe81e672599e1f4f5b2a877977a3c5c6be9837
--- /dev/null
+++ b/files/ajax/rawlist.php
@@ -0,0 +1,23 @@
+<?php
+
+// only need filesystem apps
+$RUNTIME_APPTYPES=array('filesystem');
+
+// Init owncloud
+require_once('../../lib/base.php');
+
+OC_JSON::checkLoggedIn();
+
+// Load the files
+$dir = isset( $_GET['dir'] ) ? $_GET['dir'] : '';
+
+// make filelist
+$files = array();
+foreach( OC_Files::getdirectorycontent( $dir ) as $i ){
+	$i["date"] = OC_Util::formatDate($i["mtime"] );
+	$files[] = $i;
+}
+
+OC_JSON::success(array('data' => $files));
+
+?>
diff --git a/files/ajax/scan.php b/files/ajax/scan.php
index 565275911b4e1591cefc8ff6b4f2664479c935b5..db09b7d5c642a6a829d6c5752aa864ef86735270 100644
--- a/files/ajax/scan.php
+++ b/files/ajax/scan.php
@@ -17,6 +17,7 @@ if($force or !OC_FileCache::inCache('')){
 	if(!$checkOnly){
 		OC_DB::beginTransaction();
 		OC_FileCache::scan('',$eventSource);
+		OC_FileCache::clean();
 		OC_DB::commit();
 		$eventSource->send('success',true);
 	}else{
diff --git a/files/css/files.css b/files/css/files.css
index 512e462cb6f9f4395e6e752365231374e73af560..9e950517b82ff279ce6b7b7196431bebbca46725 100644
--- a/files/css/files.css
+++ b/files/css/files.css
@@ -64,8 +64,8 @@ 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 td.filename>input[type="checkbox"]:hover:first-child { opacity:.8; }
+#fileList tr td.filename>input[type=checkbox]:first-child { opacity:0; -ms-filter:"progid:DXImageTransform.Microsoft.Alpha(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 td.filename>input[type="checkbox"]:hover:first-child { opacity:.8; -ms-filter:"progid:DXImageTransform.Microsoft.Alpha(Opacity=80)"; }
 #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; }
diff --git a/files/js/files.js b/files/js/files.js
index 539d5598899aa9ddee31cbee3a3e97895cb15573..df9f45a7af41198db704f96007a5e570863e2358 100644
--- a/files/js/files.js
+++ b/files/js/files.js
@@ -98,7 +98,7 @@ $(document).ready(function() {
 		procesSelection();
 	});
 
-	$('td.filename input:checkbox').live('click',function(event) {
+	$('td.filename input:checkbox').live('change',function(event) {
 		if (event.shiftKey) {
 			var last = $(lastChecked).parent().parent().prevAll().length;
 			var first = $(this).parent().parent().prevAll().length;
diff --git a/files/templates/index.php b/files/templates/index.php
index da1e58ce137c9bf3d1487e0c6d0af0d46c157288..f591d066d8c65c56f93db4e10fb264d3f1ca4bc5 100644
--- a/files/templates/index.php
+++ b/files/templates/index.php
@@ -1,3 +1,4 @@
+<!--[if IE 8]><style>input[type="checkbox"]{padding:0;}table td{position:static !important;}</style><![endif]-->
 <div id="controls">
 	<?php echo($_['breadcrumb']); ?>
 	<?php if (!isset($_['readonly']) || !$_['readonly']):?>
@@ -62,12 +63,12 @@
 </div>
 <div id="scanning-message">
 	<h3>
-		<?php echo $l->t('Files are being scanned, please wait.');?> <span id='scan-count'></spann>
+		<?php echo $l->t('Files are being scanned, please wait.');?> <span id='scan-count'></span>
 	</h3>
 	<p>
-		<?php echo $l->t('Current scanning');?> <span id='scan-current'></spann>
+		<?php echo $l->t('Current scanning');?> <span id='scan-current'></span>
 	</p>
 </div>
 
 <!-- config hints for javascript -->
-<input type="hidden" name="allowZipDownload" id="allowZipDownload" value="<?php echo $_['allowZipDownload']; ?>" />
\ No newline at end of file
+<input type="hidden" name="allowZipDownload" id="allowZipDownload" value="<?php echo $_['allowZipDownload']; ?>" />
diff --git a/files/webdav.php b/files/webdav.php
index 6fae33a8f719bc50f65d9584616b936e442533d7..1120973787c8bce721c4ee39bcd2163b9f15e877 100644
--- a/files/webdav.php
+++ b/files/webdav.php
@@ -26,6 +26,9 @@
 // Do not load FS ...
 $RUNTIME_NOSETUPFS = true;
 
+// only need filesystem apps
+$RUNTIME_APPTYPES=array('filesystem');
+
 require_once('../lib/base.php');
 
 // Backends
diff --git a/lib/app.php b/lib/app.php
index 3daf539aa210cbce42669c93ba369bb4e69f8e82..7d5e8fa91c6e61a0ba037fad536d28065b7a7cc2 100755
--- a/lib/app.php
+++ b/lib/app.php
@@ -34,16 +34,20 @@ class OC_App{
 	static private $settingsForms = array();
 	static private $adminForms = array();
 	static private $personalForms = array();
+	static private $appInfo = array();
 
 	/**
 	 * @brief loads all apps
+	 * @param array $types
 	 * @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.
+	 *
+	 * if $types is set, only apps of those types will be loaded
 	 */
-	public static function loadApps(){
+	public static function loadApps($types=null){
 		// Did we allready load everything?
 		if( self::$init ){
 			return true;
@@ -51,13 +55,15 @@ class OC_App{
 
 		// Our very own core apps are hardcoded
 		foreach( array('files', 'settings') as $app ){
-			require( $app.'/appinfo/app.php' );
+			if(is_null($types)){
+				require( $app.'/appinfo/app.php' );
+			}
 		}
 
 		// The rest comes here
-		$apps = OC_Appconfig::getApps();
+		$apps = self::getEnabledApps();
 		foreach( $apps as $app ){
-			if( self::isEnabled( $app )){
+			if(is_null($types) or self::isType($app,$types)){
 				if(is_file(OC::$APPSROOT.'/apps/'.$app.'/appinfo/app.php')){
 					require( $app.'/appinfo/app.php' );
 				}
@@ -70,6 +76,41 @@ class OC_App{
 		return true;
 	}
 
+	/**
+	 * check if an app is of a sepcific type
+	 * @param string $app
+	 * @param string/array $types
+	 */
+	public static function isType($app,$types){
+		if(is_string($types)){
+			$types=array($types);
+		}
+		$appData=self::getAppInfo($app);
+		if(!isset($appData['types'])){
+			return false;
+		}
+		$appTypes=$appData['types'];
+		foreach($types as $type){
+			if(array_search($type,$appTypes)!==false){
+				return true;
+			}
+		}
+		return false;
+	}
+
+	/**
+	 * get all enabled apps
+	 */
+	public static function getEnabledApps(){
+		$apps=array();
+		$query = OC_DB::prepare( 'SELECT appid FROM *PREFIX*appconfig WHERE configkey = \'enabled\' AND configvalue=\'yes\'' );
+		$result=$query->execute();
+		while($row=$result->fetchRow()){
+			$apps[]=$row['appid'];
+		}
+		return $apps;
+	}
+
 	/**
 	 * @brief checks whether or not an app is enabled
 	 * @param $app app
@@ -265,24 +306,36 @@ class OC_App{
 	/**
 	 * @brief Read app metadata from the info.xml file
 	 * @param string $appid id of the app or the path of the info.xml file
+	 * @param boolean path (optional)
 	 * @returns array
 	*/
-	public static function getAppInfo($appid){
-		if(is_file($appid)){
+	public static function getAppInfo($appid,$path=false){
+		if($path){
 			$file=$appid;
 		}else{
-			$file=OC::$APPSROOT.'/apps/'.$appid.'/appinfo/info.xml';
-			if(!is_file($file)){
-				return array();
+			if(isset(self::$appInfo[$appid])){
+				return self::$appInfo[$appid];
 			}
+			$file=OC::$APPSROOT.'/apps/'.$appid.'/appinfo/info.xml';
 		}
 		$data=array();
 		$content=file_get_contents($file);
+		if(!$content){
+			return;
+		}
 		$xml = new SimpleXMLElement($content);
 		$data['info']=array();
 		foreach($xml->children() as $child){
-			$data[$child->getName()]=(string)$child;
+			if($child->getName()=='types'){
+				$data['types']=array();
+				foreach($child->children() as $type){
+					$data['types'][]=$type->getName();
+				}
+			}else{
+				$data[$child->getName()]=(string)$child;
+			}
 		}
+		self::$appInfo[$appid]=$data;
 		return $data;
 	}
 
@@ -381,9 +434,8 @@ class OC_App{
 	 */
 	public static function updateApps(){
 		// The rest comes here
-		$apps = OC_Appconfig::getApps();
-		foreach( $apps as $app ){
-			$installedVersion=OC_Appconfig::getValue($app,'installed_version');
+		$versions = self::getAppVersions();
+		foreach( $versions as $app=>$installedVersion ){
 			$appInfo=OC_App::getAppInfo($app);
 			if (isset($appInfo['version'])) {
 				$currentVersion=$appInfo['version'];
@@ -395,6 +447,19 @@ class OC_App{
 		}
 	}
 
+	/**
+	 * get the installed version of all papps
+	 */
+	public static function getAppVersions(){
+		$versions=array();
+		$query = OC_DB::prepare( 'SELECT appid, configvalue FROM *PREFIX*appconfig WHERE configkey = \'installed_version\'' );
+		$result = $query->execute();
+		while($row = $result->fetchRow()){
+			$versions[$row['appid']]=$row['configvalue'];
+		}
+		return $versions;
+	}
+
 	/**
 	 * update the database for the app and call the update script
 	 * @param string appid
diff --git a/lib/base.php b/lib/base.php
index b07ac5af416bb3901d616683b8ab28eeae9d7ff5..a4a94e869686a3a52656a39d0ea722fa56d95a66 100644
--- a/lib/base.php
+++ b/lib/base.php
@@ -229,6 +229,39 @@ class OC{
 		}
 	}
 
+	public static function initTemplateEngine() {
+		// if the formfactor is not yet autodetected do the autodetection now. For possible forfactors check the detectFormfactor documentation
+		if(!isset($_SESSION['formfactor'])){
+			$_SESSION['formfactor']=OC::detectFormfactor();
+		}
+		// allow manual override via GET parameter
+		if(isset($_GET['formfactor'])){
+			$_SESSION['formfactor']=$_GET['formfactor'];
+		}
+
+		// Add the stuff we need always
+		OC_Util::addScript( "jquery-1.6.4.min" );
+		OC_Util::addScript( "jquery-ui-1.8.16.custom.min" );
+		OC_Util::addScript( "jquery-showpassword" );
+		OC_Util::addScript( "jquery.infieldlabel.min" );
+		OC_Util::addScript( "jquery-tipsy" );
+		OC_Util::addScript( "oc-dialogs" );
+		OC_Util::addScript( "js" );
+		OC_Util::addScript( "eventsource" );
+		OC_Util::addScript( "config" );
+		//OC_Util::addScript( "multiselect" );
+		OC_Util::addScript('search','result');
+		OC_Util::addStyle( "styles" );
+		OC_Util::addStyle( "multiselect" );
+		OC_Util::addStyle( "jquery-ui-1.8.16.custom" );
+		OC_Util::addStyle( "jquery-tipsy" );
+	}
+
+	public static function initSession() {
+		ini_set('session.cookie_httponly','1;');
+		session_start();
+	}
+
 	public static function init(){
 		// register autoloader
 		spl_autoload_register(array('OC','autoload'));
@@ -244,6 +277,24 @@ class OC{
 		date_default_timezone_set('Europe/Berlin');
 		ini_set('arg_separator.output','&amp;');
 
+		//try to configure php to enable big file uploads.
+		//this doesn´t work always depending on the webserver and php configuration.
+		//Let´s try to overwrite some defaults anyways
+		
+		//try to set the maximum execution time to 60min
+		@set_time_limit(3600);
+		@ini_set('max_execution_time',3600);
+		@ini_set('max_input_time',3600);
+
+		//try to set the maximum filesize to 10G
+		@ini_set('upload_max_filesize','10G');
+		@ini_set('post_max_size','10G');
+		@ini_set('file_uploads','50');
+
+		//try to set the session lifetime to 60min
+		@ini_set('gc_maxlifetime','3600');
+
+
 		//set http auth headers for apache+php-cgi work around
 		if (isset($_SERVER['HTTP_AUTHORIZATION']) && preg_match('/Basic\s+(.*)$/i', $_SERVER['HTTP_AUTHORIZATION'], $matches))
 		{
@@ -270,37 +321,10 @@ class OC{
 
 		self::checkInstalled();
 		self::checkSSL();
-		self::checkUpgrade();
-
-		ini_set('session.cookie_httponly','1;');
-		session_start();
 
-		// if the formfactor is not yet autodetected do the autodetection now. For possible forfactors check the detectFormfactor documentation
-		if(!isset($_SESSION['formfactor'])){
-			$_SESSION['formfactor']=OC::detectFormfactor();
-		}
-		// allow manual override via GET parameter
-		if(isset($_GET['formfactor'])){
-			$_SESSION['formfactor']=$_GET['formfactor'];
-		}
-
-
-		// Add the stuff we need always
-		OC_Util::addScript( "jquery-1.6.4.min" );
-		OC_Util::addScript( "jquery-ui-1.8.16.custom.min" );
-		OC_Util::addScript( "jquery-showpassword" );
-		OC_Util::addScript( "jquery.infieldlabel.min" );
-		OC_Util::addScript( "jquery-tipsy" );
-		OC_Util::addScript( "oc-dialogs" );
-		OC_Util::addScript( "js" );
-		OC_Util::addScript( "eventsource" );
-		OC_Util::addScript( "config" );
-		//OC_Util::addScript( "multiselect" );
-		OC_Util::addScript('search','result');
-		OC_Util::addStyle( "styles" );
-		OC_Util::addStyle( "multiselect" );
-		OC_Util::addStyle( "jquery-ui-1.8.16.custom" );
-		OC_Util::addStyle( "jquery-tipsy" );
+		self::initSession();
+		self::initTemplateEngine();
+		self::checkUpgrade();
 
 		$errors=OC_Util::checkServer();
 		if(count($errors)>0) {
@@ -333,8 +357,13 @@ class OC{
 		// Load Apps
 		// This includes plugins for users and filesystems as well
 		global $RUNTIME_NOAPPS;
+		global $RUNTIME_APPTYPES;
 		if(!$RUNTIME_NOAPPS ){
-			OC_App::loadApps();
+			if($RUNTIME_APPTYPES){
+				OC_App::loadApps($RUNTIME_APPTYPES);
+			}else{
+				OC_App::loadApps();
+			}
 		}
 
 		//make sure temporary files are cleaned up
diff --git a/lib/db.php b/lib/db.php
index 9fab51edfcd5420c7f860ec4aeecb46e97c8dec0..a0fb6c385d8e3bf3969b3628596fe07f60fa7df8 100644
--- a/lib/db.php
+++ b/lib/db.php
@@ -318,9 +318,6 @@ class OC_DB {
 		
 		// Make changes and save them to an in-memory file
 		$file2 = 'static://db_scheme';
-		if($file2 == ''){
-			die('could not create tempfile in get_temp_dir() - aborting');
-		}
 		$content = str_replace( '*dbname*', $CONFIG_DBNAME, $content );
 		$content = str_replace( '*dbprefix*', $CONFIG_DBTABLEPREFIX, $content );
 		if( $CONFIG_DBTYPE == 'pgsql' ){ //mysql support it too but sqlite doesn't
diff --git a/lib/eventsource.php b/lib/eventsource.php
index 523f72403c35884486f3abca8a754e602ac4e17a..cf10660b94cef8eef84f1de02b8dca807dd97b69 100644
--- a/lib/eventsource.php
+++ b/lib/eventsource.php
@@ -32,6 +32,7 @@ class OC_EventSource{
 	private $fallBackId=0;
 	
 	public function __construct(){
+		@ob_end_clean();
 		header('Cache-Control: no-cache');
 		$this->fallback=isset($_GET['fallback']) and $_GET['fallback']=='true';
 		if($this->fallback){
@@ -58,7 +59,7 @@ class OC_EventSource{
 			$type=null;
 		}
 		if($this->fallback){
-			$response='<script type="text/javascript">window.parent.OC.EventSource.fallBackCallBack('.$this->fallBackId.',"'.$type.'","'.json_encode($data).'")</script>'.PHP_EOL;
+			$response='<script type="text/javascript">window.parent.OC.EventSource.fallBackCallBack('.$this->fallBackId.',"'.$type.'",'.json_encode($data).')</script>'.PHP_EOL;
 			echo $response;
 		}else{
 			if($type){
diff --git a/lib/filecache.php b/lib/filecache.php
index 280a9929db0407af12c4897f716a5ef5ba5ad420..4a4183cbdb5eef1f743c74218f57352d0502b2cf 100644
--- a/lib/filecache.php
+++ b/lib/filecache.php
@@ -59,8 +59,8 @@ class OC_FileCache{
 			$root='';
 		}
 		$path=$root.$path;
-		$query=OC_DB::prepare('SELECT ctime,mtime,mimetype,size,encrypted,versioned,writable FROM *PREFIX*fscache WHERE path=?');
-		$result=$query->execute(array($path))->fetchRow();
+		$query=OC_DB::prepare('SELECT ctime,mtime,mimetype,size,encrypted,versioned,writable FROM *PREFIX*fscache WHERE path_hash=?');
+		$result=$query->execute(array(md5($path)))->fetchRow();
 		if(is_array($result)){
 			return $result;
 		}else{
@@ -111,8 +111,8 @@ class OC_FileCache{
 		}
 		$mimePart=dirname($data['mimetype']);
 		$user=OC_User::getUser();
-		$query=OC_DB::prepare('INSERT INTO *PREFIX*fscache(parent, name, path, size, mtime, ctime, mimetype, mimepart,user,writable,encrypted,versioned) VALUES(?,?,?,?,?,?,?,?,?,?,?,?)');
-		$result=$query->execute(array($parent,basename($path),$path,$data['size'],$data['mtime'],$data['ctime'],$data['mimetype'],$mimePart,$user,$data['writable'],$data['encrypted'],$data['versioned']));
+		$query=OC_DB::prepare('INSERT INTO *PREFIX*fscache(parent, name, path, path_hash, size, mtime, ctime, mimetype, mimepart,user,writable,encrypted,versioned) VALUES(?,?,?,?,?,?,?,?,?,?,?,?,?)');
+		$result=$query->execute(array($parent,basename($path),$path,md5($path),$data['size'],$data['mtime'],$data['ctime'],$data['mimetype'],$mimePart,$user,$data['writable'],$data['encrypted'],$data['versioned']));
 		if(OC_DB::isError($result)){
 			OC_Log::write('files','error while writing file('.$path.') to cache',OC_Log::ERROR);
 		}
@@ -162,8 +162,8 @@ class OC_FileCache{
 		$oldPath=$root.$oldPath;
 		$newPath=$root.$newPath;
 		$newParent=self::getParentId($newPath);
-		$query=OC_DB::prepare('UPDATE *PREFIX*fscache SET parent=? ,name=?, path=? WHERE path=?');
-		$query->execute(array($newParent,basename($newPath),$newPath,$oldPath));
+		$query=OC_DB::prepare('UPDATE *PREFIX*fscache SET parent=? ,name=?, path=?, path_hash=? WHERE path_hash=?');
+		$query->execute(array($newParent,basename($newPath),$newPath,md5($newPath),md5($oldPath)));
 	}
 
 	/**
@@ -285,12 +285,12 @@ class OC_FileCache{
 	 * @return int
 	 */
 	private static function getFileId($path){
-		$query=OC_DB::prepare('SELECT id FROM *PREFIX*fscache WHERE path=?');
+		$query=OC_DB::prepare('SELECT id FROM *PREFIX*fscache WHERE path_hash=?');
 		if(OC_DB::isError($query)){
 			OC_Log::write('files','error while getting file id of '.$path,OC_Log::ERROR);
 			return -1;
 		}
-		$result=$query->execute(array($path));
+		$result=$query->execute(array(md5($path)));
 		if(OC_DB::isError($result)){
 			OC_Log::write('files','error while getting file id of '.$path,OC_Log::ERROR);
 			return -1;
@@ -367,8 +367,8 @@ class OC_FileCache{
 			}
 		}
 		$path=$root.$path;
-		$query=OC_DB::prepare('SELECT ctime,mtime,mimetype,size,encrypted,versioned,writable FROM *PREFIX*fscache WHERE path=?');
-		$result=$query->execute(array($path))->fetchRow();
+		$query=OC_DB::prepare('SELECT ctime,mtime,mimetype,size,encrypted,versioned,writable FROM *PREFIX*fscache WHERE path_hash=?');
+		$result=$query->execute(array(md5($path)))->fetchRow();
 		if(is_array($result)){
 			if(isset(self::$savedData[$path])){
 				$result=array_merge($result,self::$savedData[$path]);
@@ -389,8 +389,8 @@ class OC_FileCache{
 			}
 		}
 		$path=$root.$path;
-		$query=OC_DB::prepare('SELECT size FROM *PREFIX*fscache WHERE path=?');
-		$result=$query->execute(array($path));
+		$query=OC_DB::prepare('SELECT size FROM *PREFIX*fscache WHERE path_hash=?');
+		$result=$query->execute(array(md5($path)));
 		if($row=$result->fetchRow()){
 			return $row['size'];
 		}else{//file not in cache
@@ -469,6 +469,10 @@ class OC_FileCache{
 	 * @param string root (optionak)
 	 */
 	public static function scan($path,$eventSource=false,&$count=0,$root=''){
+		if($eventSource){
+			$eventSource->send('scanning',array('file'=>$path,'count'=>$count));
+		}
+		$lastSend=$count;
 		if(!$root){
 			$view=OC_Filesystem::getView();
 		}else{
@@ -482,13 +486,14 @@ class OC_FileCache{
 				if($filename != '.' and $filename != '..'){
 					$file=$path.'/'.$filename;
 					if($view->is_dir($file.'/')){
-						if($eventSource){
-							$eventSource->send('scanning',array('file'=>$file,'count'=>$count));
-						}
 						self::scan($file,$eventSource,$count,$root);
 					}else{
 						$totalSize+=self::scanFile($file,$root);
 						$count++;
+						if($count>$lastSend+25 and $eventSource){
+							$lastSend=$count;
+							$eventSource->send('scanning',array('file'=>$path,'count'=>$count));
+						}
 					}
 				}
 			}
@@ -579,8 +584,8 @@ class OC_FileCache{
 		$mtime=$view->filemtime($path);
 		$isDir=$view->is_dir($path);
 		$path=$root.$path;
-		$query=OC_DB::prepare('SELECT mtime FROM *PREFIX*fscache WHERE path=?');
-		$result=$query->execute(array($path));
+		$query=OC_DB::prepare('SELECT mtime FROM *PREFIX*fscache WHERE path_hash=?');
+		$result=$query->execute(array(md5($path)));
 		if($row=$result->fetchRow()){
 			$cachedMTime=$row['mtime'];
 			return ($mtime>$cachedMTime);
@@ -637,6 +642,14 @@ class OC_FileCache{
 			self::fileSystemWatcherWrite(array('path'=>$path),$root);
 		}
 	}
+
+	/**
+	 * clean old pre-path_hash entries
+	 */
+	public static function clean(){
+		$query=OC_DB::prepare('DELETE FROM *PREFIX*fscache WHERE LENGTH(path_hash)<30');
+		$query->execute();
+	}
 }
 
 //watch for changes and try to keep the cache up to date
diff --git a/lib/filestorage/local.php b/lib/filestorage/local.php
index 688501aee906ed23ade34222dd6b1b2778181586..bd757f52ce76725dbc9a99a5f188c72690836fd0 100644
--- a/lib/filestorage/local.php
+++ b/lib/filestorage/local.php
@@ -86,6 +86,10 @@ class OC_Filestorage_Local extends OC_Filestorage{
 		return $this->delTree($path);
 	}
 	public function rename($path1,$path2){
+		if (!$this->is_writable($path1)) {
+			OC_Log::write('core','unable to rename, file is not writable : '.$path1,OC_Log::ERROR);
+			return false;
+		}
 		if(! $this->file_exists($path1)){
 			OC_Log::write('core','unable to rename, file does not exists : '.$path1,OC_Log::ERROR);
 			return false;
diff --git a/lib/filesystemview.php b/lib/filesystemview.php
index 39e47975b284094c0f8bbbb4f68ca787dafd8eb8..9d530c7ad6343807fdf283d1d99556bd00655a57 100644
--- a/lib/filesystemview.php
+++ b/lib/filesystemview.php
@@ -137,13 +137,16 @@ class OC_FilesystemView {
 	}
 	public function readfile($path){
 		$handle=$this->fopen($path,'r');
-		$chunkSize = 1024*1024;// 1 MB chunks
-		while (!feof($handle)) {
-			echo fread($handle, $chunkSize);
-			@ob_flush();
-			flush();
+		if ($handle) {
+			$chunkSize = 1024*1024;// 1 MB chunks
+			while (!feof($handle)) {
+				echo fread($handle, $chunkSize);
+				@ob_flush();
+				flush();
+			}
+			return $this->filesize($path);
 		}
-		return $this->filesize($path);
+		return false;
 	}
 	public function is_readable($path){
 		return $this->basicOperation('is_readable',$path);
@@ -189,7 +192,7 @@ class OC_FilesystemView {
 		return $this->basicOperation('unlink',$path,array('delete'));
 	}
 	public function rename($path1,$path2){
-		if(OC_FileProxy::runPreProxies('rename',$path1,$path2) and $this->is_writable($path1) and OC_Filesystem::isValidPath($path2)){
+		if(OC_FileProxy::runPreProxies('rename',$path1,$path2) and OC_Filesystem::isValidPath($path2)){
 			$run=true;
 			OC_Hook::emit( OC_Filesystem::CLASSNAME, OC_Filesystem::signal_rename, array( OC_Filesystem::signal_param_oldpath => $path1 , OC_Filesystem::signal_param_newpath=>$path2, OC_Filesystem::signal_param_run => &$run));
 			if($run){
diff --git a/lib/helper.php b/lib/helper.php
index 66f31d929beca9fb4e275c569ec75f852b08acc6..efff00c2fe6eb23498a8d39946f089cb2f4abcac 100755
--- a/lib/helper.php
+++ b/lib/helper.php
@@ -432,6 +432,19 @@ class OC_Helper {
 		self::$tmpFiles[]=$file;
 		return $file;
 	}
+
+	/**
+	 * create a temporary folder with an unique filename
+	 * @return string
+	 *
+	 * temporary files are automatically cleaned up after the script is finished
+	 */
+	public static function tmpFolder(){
+		$path=get_temp_dir().'/'.md5(time().rand());
+		mkdir($path);
+		self::$tmpFiles[]=$path;
+		return $path.'/';
+	}
 	
 	/**
 	 * remove all files created by self::tmpFile
@@ -439,7 +452,7 @@ class OC_Helper {
 	public static function cleanTmp(){
 		foreach(self::$tmpFiles as $file){
 			if(file_exists($file)){
-				unlink($file);
+				self::rmdirr($file);
 			}
 		}
 	}
diff --git a/lib/installer.php b/lib/installer.php
index 2a9676998f6e537982fda061176aed2139d9c9ef..38e17130e3c5ff55b96e9b50cca33457e652b5ab 100644
--- a/lib/installer.php
+++ b/lib/installer.php
@@ -62,7 +62,7 @@ class OC_Installer{
 		
 		//download the file if necesary
 		if($data['source']=='http'){
-			$path=OC_Helper::tmpFile('.zip');
+			$path=OC_Helper::tmpFile();
 			if(!isset($data['href'])){
 				OC_Log::write('core','No href specified when installing app from http',OC_Log::ERROR);
 				return false;
@@ -76,14 +76,24 @@ class OC_Installer{
 			$path=$data['path'];
 		}
 		
+		//detect the archive type
+		$mime=OC_Helper::getMimeType($path);
+		if($mime=='application/zip'){
+			rename($path,$path.'.zip');
+			$path.='.zip';
+		}elseif($mime=='application/x-gzip'){
+			rename($path,$path.'.tgz');
+			$path.='.tgz';
+		}else{
+			OC_Log::write('core','Archives of type '.$mime.' are not supported',OC_Log::ERROR);
+			return false;
+		}
+		
 		//extract the archive in a temporary folder
-		$extractDir=tempnam(get_temp_dir(),'oc_installer_uncompressed_');
-		unlink($extractDir);
+		$extractDir=OC_Helper::tmpFolder();
 		mkdir($extractDir);
-		$zip = new ZipArchive;
-		if($zip->open($path)===true){
-			$zip->extractTo($extractDir);
-			$zip->close();
+		if($archive=OC_Archive::open($path)){
+			$archive->extract($extractDir);
 		} else {
 			OC_Log::write('core','Failed to open archive when installing app',OC_Log::ERROR);
 			OC_Helper::rmdirr($extractDir);
@@ -94,6 +104,17 @@ class OC_Installer{
 		}
 		
 		//load the info.xml file of the app
+		if(!is_file($extractDir.'/appinfo/info.xml')){
+			//try to find it in a subdir
+			$dh=opendir($extractDir);
+			while($folder=readdir($dh)){
+				if(substr($folder,0,1)!='.' and is_dir($extractDir.'/'.$folder)){
+					if(is_file($extractDir.'/'.$folder.'/appinfo/info.xml')){
+						$extractDir.='/'.$folder;
+					}
+				}
+			}
+		}
 		if(!is_file($extractDir.'/appinfo/info.xml')){
 			OC_Log::write('core','App does not provide an info.xml file',OC_Log::ERROR);
 			OC_Helper::rmdirr($extractDir);
@@ -102,7 +123,7 @@ class OC_Installer{
 			}
 			return false;
 		}
-		$info=OC_App::getAppInfo($extractDir.'/appinfo/info.xml');
+		$info=OC_App::getAppInfo($extractDir.'/appinfo/info.xml',true);
 		$basedir=OC::$APPSROOT.'/apps/'.$info['id'];
 		
 		//check if an app with the same id is already installed
@@ -275,7 +296,7 @@ class OC_Installer{
 		if(is_file(OC::$APPSROOT."/apps/$app/appinfo/install.php")){
 			include(OC::$APPSROOT."/apps/$app/appinfo/install.php");
 		}
-		$info=OC_App::getAppInfo(OC::$APPSROOT."/apps/$app/appinfo/info.xml");
+		$info=OC_App::getAppInfo($app);
 		OC_Appconfig::setValue($app,'installed_version',$info['version']);
 		return $info;
 	}
diff --git a/lib/log.php b/lib/log.php
index 4e450a027f5367289a2cc67697f3d9c7b00e4886..8bb2839be66a36686539348fbfb913bbd16c361e 100644
--- a/lib/log.php
+++ b/lib/log.php
@@ -1,78 +1,39 @@
 <?php
 /**
- * ownCloud
- *
- * @author Robin Appelman
- * @copyright 2012 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/>.
- *
+ * Copyright (c) 2012 Bart Visscher <bartv@thisnet.nl>
+ * This file is licensed under the Affero General Public License version 3 or
+ * later.
+ * See the COPYING-README file.
  */
 
 /**
- *logging utilities
+ * logging utilities
  *
- * Log is saved at data/owncloud.log (on default)
+ * Log is saved by default at data/owncloud.log using OC_Log_Owncloud.
+ * Selecting other backend is done with a config option 'log_type'.
  */
 
-class OC_Log{
+class OC_Log {
 	const DEBUG=0;
 	const INFO=1;
 	const WARN=2;
 	const ERROR=3;
 	const FATAL=4;
 
+	static protected $class = null;
+
 	/**
 	 * write a message in the log
 	 * @param string $app
 	 * @param string $message
 	 * @param int level
 	 */
-	public static function write($app,$message,$level){
-		$minLevel=OC_Config::getValue( "loglevel", 2 );
-		if($level>=$minLevel){
-			$datadir=OC_Config::getValue( "datadirectory", OC::$SERVERROOT.'/data' );
-			$logFile=OC_Config::getValue( "logfile", $datadir.'/owncloud.log' );
-			$entry=array('app'=>$app,'message'=>$message,'level'=>$level,'time'=>time());
-			$fh=fopen($logFile,'a');
-			fwrite($fh,json_encode($entry)."\n");
-			fclose($fh);
-		}
-	}
-
-	/**
-	 * get entries from the log in reverse chronological order
-	 * @param int limit
-	 * @param int offset
-	 * @return array
-	 */
-	public static function getEntries($limit=50,$offset=0){
-		$datadir=OC_Config::getValue( "datadirectory", OC::$SERVERROOT.'/data' );
-		$logFile=OC_Config::getValue( "logfile", $datadir.'/owncloud.log' );
-		$entries=array();
-		if(!file_exists($logFile)){
-			return array();
-		}
-		$contents=file($logFile);
-		if(!$contents){//error while reading log
-			return array();
-		}
-		$end=max(count($contents)-$offset-1,0);
-		$start=max($end-$limit,0);
-		for($i=$end;$i>$start;$i--){
-			$entries[]=json_decode($contents[$i]);
+	public static function write($app, $message, $level) {
+		if (!self::$class) {
+			self::$class = 'OC_Log_'.ucfirst(OC_Config::getValue('log_type', 'owncloud'));
+			call_user_func(array(self::$class, 'init'));
 		}
-		return $entries;
+		$log_class=self::$class;
+		$log_class::write($app, $message, $level);
 	}
 }
diff --git a/lib/log/owncloud.php b/lib/log/owncloud.php
new file mode 100644
index 0000000000000000000000000000000000000000..0ed30510134407756cd2a57ac21ec77802927583
--- /dev/null
+++ b/lib/log/owncloud.php
@@ -0,0 +1,79 @@
+<?php
+/**
+ * ownCloud
+ *
+ * @author Robin Appelman
+ * @copyright 2012 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/>.
+ *
+ */
+
+/**
+ * logging utilities
+ *
+ * Log is saved at data/owncloud.log (on default)
+ */
+
+class OC_Log_Owncloud {
+	static protected $logFile;
+
+	/**
+	 * Init class data
+	 */
+	public static function init() {
+		$datadir=OC_Config::getValue( "datadirectory", OC::$SERVERROOT.'/data' );
+		self::$logFile=OC_Config::getValue( "logfile", $datadir.'/owncloud.log' );
+	}
+
+	/**
+	 * write a message in the log
+	 * @param string $app
+	 * @param string $message
+	 * @param int level
+	 */
+	public static function write($app, $message, $level) {
+		$minLevel=OC_Config::getValue( "loglevel", 2 );
+		if($level>=$minLevel){
+			$entry=array('app'=>$app, 'message'=>$message, 'level'=>$level,'time'=>time());
+			$fh=fopen(self::$logFile, 'a');
+			fwrite($fh, json_encode($entry)."\n");
+			fclose($fh);
+		}
+	}
+
+	/**
+	 * get entries from the log in reverse chronological order
+	 * @param int limit
+	 * @param int offset
+	 * @return array
+	 */
+	public static function getEntries($limit=50, $offset=0){
+		self::init();
+		$entries=array();
+		if(!file_exists(self::$logFile)) {
+			return array();
+		}
+		$contents=file(self::$logFile);
+		if(!$contents) {//error while reading log
+			return array();
+		}
+		$end=max(count($contents)-$offset-1, 0);
+		$start=max($end-$limit,0);
+		for($i=$end;$i>$start;$i--) {
+			$entries[]=json_decode($contents[$i]);
+		}
+		return $entries;
+	}
+}
diff --git a/lib/log/syslog.php b/lib/log/syslog.php
new file mode 100644
index 0000000000000000000000000000000000000000..d1fb28d8b0a7f7e1a36c5ac2601f49eaf2845dbb
--- /dev/null
+++ b/lib/log/syslog.php
@@ -0,0 +1,37 @@
+<?php
+/**
+ * Copyright (c) 2012 Bart Visscher <bartv@thisnet.nl>
+ * This file is licensed under the Affero General Public License version 3 or
+ * later.
+ * See the COPYING-README file.
+ */
+
+class OC_Log_Syslog {
+	static protected $levels = array(
+		OC_Log::DEBUG => LOG_DEBUG,
+		OC_Log::INFO => LOG_INFO,
+		OC_Log::WARN => LOG_WARNING,
+		OC_Log::ERROR => LOG_ERR,
+		OC_Log::FATAL => LOG_CRIT,
+	);
+
+	/**
+	 * Init class data
+	 */
+	public static function init() {
+		openlog('ownCloud', LOG_PID | LOG_CONS, LOG_USER);
+		// Close at shutdown
+		register_shutdown_function('closelog');
+	}
+
+	/**
+	 * write a message in the log
+	 * @param string $app
+	 * @param string $message
+	 * @param int level
+	 */
+	public static function write($app, $message, $level) {
+		$syslog_level = self::$levels[$level];
+		syslog($syslog_level, '{'.$app.'} '.$message);
+	}
+}
diff --git a/lib/updater.php b/lib/updater.php
index 57623797ae5c7a74cf32c1a5b207bc2b91c719d8..196822ac35d42084979e8c5e08741fff7be7a0a8 100644
--- a/lib/updater.php
+++ b/lib/updater.php
@@ -36,6 +36,7 @@ class OC_Updater{
 		$version['installed']=OC_Config::getValue('installedat');
 		$version['updated']=OC_Appconfig::getValue('core', 'lastupdatedat', OC_Config::getValue( 'lastupdatedat'));
 		$version['updatechannel']='stable';
+		$version['edition']=OC_Util::getEditionString();
 		$versionstring=implode('x',$version);
 
 		//fetch xml data from updater
diff --git a/lib/util.php b/lib/util.php
index fa5b3daaab6ccaf788dffc593e64e28215a9d227..722b7404d0c4b8b3ed8d8e8e5eff3dee62ec9055 100644
--- a/lib/util.php
+++ b/lib/util.php
@@ -66,7 +66,7 @@ class OC_Util {
 	 * @return array
 	 */
 	public static function getVersion(){
-		return array(3,00,3);
+		return array(3,00,4);
 	}
 
 	/**
@@ -77,6 +77,14 @@ class OC_Util {
 		return '3';
 	}
 
+        /**
+         * get the current installed edition of ownCloud. There is the community edition that just returns an empty string and the enterprise edition that returns "Enterprise".
+         * @return string
+         */
+        public static function getEditionString(){
+                return '';
+        }
+
 	/**
 	 * add a javascript file
 	 *
diff --git a/settings/ajax/getlog.php b/settings/ajax/getlog.php
index 600ebefcecef77d5f5ae8c9184ec964d4b60f9b1..ed48b2cae1ad14af1f4cfdb8e6b02375ed590832 100644
--- a/settings/ajax/getlog.php
+++ b/settings/ajax/getlog.php
@@ -13,5 +13,5 @@ OC_JSON::checkAdminUser();
 $count=(isset($_GET['count']))?$_GET['count']:50;
 $offset=(isset($_GET['offset']))?$_GET['offset']:0;
 
-$entries=OC_Log::getEntries($count,$offset);
+$entries=OC_Log_Owncloud::getEntries($count,$offset);
 OC_JSON::success(array("data" => $entries));
diff --git a/settings/log.php b/settings/log.php
index 946f2b6f8e5678b428b326cf021dcac1842c4b1a..ddbf72c4433c2f08270fd2b127c12eef3ea38ba5 100644
--- a/settings/log.php
+++ b/settings/log.php
@@ -28,7 +28,7 @@ OC_Util::addStyle( "settings", "settings" );
 OC_Util::addScript( "settings", "apps" );
 OC_App::setActiveNavigationEntry( "core_log" );
 
-$entries=OC_Log::getEntries();
+$entries=OC_Log_Owncloud::getEntries();
 
 OC_Util::addScript('settings','log');
 OC_Util::addStyle('settings','settings');
diff --git a/settings/templates/personal.php b/settings/templates/personal.php
index 57731d979d9c29ab25665211f76e2b4cd1c6908c..d40da7eb773fca58d2bc870619f91a7856f73296 100644
--- a/settings/templates/personal.php
+++ b/settings/templates/personal.php
@@ -50,7 +50,7 @@
 };?>
 
 <p class="personalblock">
-	<strong>ownCloud</strong> <?php echo(OC_Util::getVersionString()); ?><br />
+	<strong>ownCloud</strong> <?php echo(OC_Util::getVersionString()); ?> <?php echo(OC_Util::getEditionString()); ?><br />
 	developed by the <a href="http://ownCloud.org/credits" target="_blank">ownCloud community</a><br />
 	<?php echo(OC_Updater::ShowUpdatingHint());     ?><br />
 	<a href="http://gitorious.org/owncloud" target="_blank">source code</a> licensed freely under <a href="http://www.gnu.org/licenses/agpl-3.0.html" target="_blank">AGPL</a>
diff --git a/status.php b/status.php
index 94c8cfce8423314f9ac437d82f223d3fca9f633e..81f339fa53f8f172a8196e057a77c8610d7c245c 100644
--- a/status.php
+++ b/status.php
@@ -26,7 +26,7 @@ $RUNTIME_NOAPPS = TRUE; //no apps, yet
 require_once('lib/base.php');
 
 if(OC_Config::getValue('installed')==1) $installed='true'; else $installed='false';
-$values=array('installed'=>$installed,'version'=>implode('.',OC_Util::getVersion()),'versionstring'=>OC_Util::getVersionString());
+$values=array('installed'=>$installed,'version'=>implode('.',OC_Util::getVersion()),'versionstring'=>OC_Util::getVersionString(),'edition'=>OC_Util::getEditionString());
 
 echo(json_encode($values));
 
diff --git a/tests/index.php b/tests/index.php
index 2e86366740bfcd99ba1fb87be9f466e7e2da0f08..a6f678b3bc2ca416d6efa8e19abc8c907221975f 100644
--- a/tests/index.php
+++ b/tests/index.php
@@ -38,6 +38,7 @@ foreach($apps as $app){
 }
 
 function loadTests($dir=''){
+	$test=isset($_GET['test'])?$_GET['test']:false;
 	if($dh=opendir($dir)){
 		while($name=readdir($dh)){
 			if(substr($name,0,1)!='.'){//no hidden files, '.' or '..'
@@ -45,10 +46,13 @@ function loadTests($dir=''){
 				if(is_dir($file)){
 					loadTests($file);
 				}elseif(substr($file,-4)=='.php' and $file!=__FILE__){
-					$testCase=new TestSuite(getTestName($file));
-					$testCase->addFile($file);
-					if($testCase->getSize()>0){
-						$testCase->run(new HtmlReporter());
+					$name=getTestName($file);
+					if($test===false or $test==$name or substr($name,0,strlen($test))==$test){
+						$testCase=new TestSuite($name);
+						$testCase->addFile($file);
+						if($testCase->getSize()>0){
+							$testCase->run(new HtmlReporter());
+						}
 					}
 				}
 			}