[Pkg-owncloud-commits] [owncloud] 62/83: Imported Upstream version 5.0.14~rc1+dfsg

David Prévot taffit at moszumanska.debian.org
Wed Dec 18 13:05:32 UTC 2013


This is an automated email from the git hooks/post-receive script.

taffit pushed a commit to branch 5.0
in repository owncloud.

commit 68fb82fc30feccb14c877027f76d5a0357d10613
Merge: 1a98027 e98e98f
Author: David Prévot <taffit at debian.org>
Date:   Fri Dec 13 21:09:39 2013 -0400

    Imported Upstream version 5.0.14~rc1+dfsg

 3rdparty/smb4php/smb.php                           |   21 +-
 apps/calendar/lib/sabre/backend.php                |    2 +-
 apps/calendar/lib/share/event.php                  |   11 +-
 apps/contacts/appinfo/bootstrap.php                |    3 +-
 apps/contacts/lib/sabre/validatorplugin.php        |    1 +
 apps/files/ajax/getstoragestats.php                |    8 +-
 apps/files/ajax/list.php                           |    1 +
 apps/files/appinfo/remote.php                      |    3 +-
 apps/files/css/files.css                           |    3 +
 apps/files/index.php                               |    3 +-
 apps/files/js/filelist.js                          |   31 +
 apps/files/js/files.js                             |    8 +-
 apps/files/templates/index.php                     |   19 +-
 apps/files_encryption/lib/util.php                 |   47 +-
 apps/files_external/lib/ftp.php                    |   19 +
 apps/files_external/lib/smb.php                    |   25 +-
 apps/files_external/lib/streamwrapper.php          |   22 +-
 apps/files_external/tests/ftp.php                  |   12 +
 apps/files_external/tests/smbfunctions.php         |   41 +
 apps/files_imageviewer/appinfo/app.php             |    6 -
 apps/files_imageviewer/appinfo/info.xml            |   11 -
 apps/files_imageviewer/appinfo/version             |    1 -
 .../css/jquery.fancybox-1.3.4.css                  |  359 ------
 apps/files_imageviewer/img/blank.gif               |  Bin 43 -> 0 bytes
 apps/files_imageviewer/img/fancy_close.png         |  Bin 1517 -> 0 bytes
 apps/files_imageviewer/img/fancy_loading.png       |  Bin 10195 -> 0 bytes
 apps/files_imageviewer/img/fancy_nav_left.png      |  Bin 1446 -> 0 bytes
 apps/files_imageviewer/img/fancy_nav_right.png     |  Bin 1454 -> 0 bytes
 apps/files_imageviewer/img/fancy_shadow_e.png      |  Bin 107 -> 0 bytes
 apps/files_imageviewer/img/fancy_shadow_n.png      |  Bin 106 -> 0 bytes
 apps/files_imageviewer/img/fancy_shadow_ne.png     |  Bin 347 -> 0 bytes
 apps/files_imageviewer/img/fancy_shadow_nw.png     |  Bin 324 -> 0 bytes
 apps/files_imageviewer/img/fancy_shadow_s.png      |  Bin 111 -> 0 bytes
 apps/files_imageviewer/img/fancy_shadow_se.png     |  Bin 352 -> 0 bytes
 apps/files_imageviewer/img/fancy_shadow_sw.png     |  Bin 340 -> 0 bytes
 apps/files_imageviewer/img/fancy_shadow_w.png      |  Bin 103 -> 0 bytes
 apps/files_imageviewer/img/fancy_title_left.png    |  Bin 503 -> 0 bytes
 apps/files_imageviewer/img/fancy_title_main.png    |  Bin 96 -> 0 bytes
 apps/files_imageviewer/img/fancy_title_over.png    |  Bin 70 -> 0 bytes
 apps/files_imageviewer/img/fancy_title_right.png   |  Bin 506 -> 0 bytes
 apps/files_imageviewer/img/fancybox-x.png          |  Bin 203 -> 0 bytes
 apps/files_imageviewer/img/fancybox-y.png          |  Bin 166 -> 0 bytes
 apps/files_imageviewer/img/fancybox.png            |  Bin 15287 -> 0 bytes
 apps/files_imageviewer/js/jquery.fancybox-1.3.4.js | 1156 --------------------
 .../js/jquery.fancybox-1.3.4.pack.js               |    1 -
 .../js/jquery.mousewheel-3.0.4.js                  |   78 --
 .../js/jquery.mousewheel-3.0.4.pack.js             |   14 -
 apps/files_imageviewer/js/lightbox.js              |   32 -
 apps/files_pdfviewer/js/loader.js                  |    3 +-
 apps/gallery/lib/thumbnail.php                     |    6 +-
 apps/media/lib/ampache.php                         |   43 +-
 apps/media/lib/track.php                           |   10 +-
 apps/media/templates/ampache/albums.php            |    2 +-
 apps/media/templates/ampache/handshake.php         |    2 +-
 apps/search_lucene/ajax/lucene.php                 |    4 +-
 apps/search_lucene/appinfo/database.xml            |    7 +
 apps/search_lucene/appinfo/version                 |    2 +-
 apps/search_lucene/lib/indexer.php                 |   52 +-
 apps/search_lucene/lib/lucene.php                  |   95 +-
 apps/updater/js/controllers.js                     |    2 +-
 .../doc/admin/_sources/configuration/auth_ldap.txt |    1 +
 core/doc/admin/configuration/auth_ldap.html        |    1 +
 core/doc/admin/searchindex.js                      |    2 +-
 core/js/js.js                                      |   22 +
 db_structure.xml                                   |    2 +-
 lib/db.php                                         |   26 +-
 lib/fileproxy/quota.php                            |    7 +-
 lib/files.php                                      |    4 +-
 lib/files/cache/cache.php                          |    4 +-
 lib/files/cache/scanner.php                        |   48 +-
 lib/files/filesystem.php                           |   26 +-
 lib/files/storage/home.php                         |   12 +-
 lib/files/storage/local.php                        |   22 +-
 lib/files/storage/mappedlocal.php                  |   26 +-
 lib/files/view.php                                 |   12 +-
 lib/helper.php                                     |    3 +-
 lib/request.php                                    |   36 +-
 lib/search/provider/file.php                       |    3 +-
 lib/search/result.php                              |    3 +-
 lib/user.php                                       |    2 +
 lib/util.php                                       |    4 +-
 search/js/result.js                                |   55 +-
 82 files changed, 632 insertions(+), 1855 deletions(-)

diff --cc 3rdparty/smb4php/smb.php
index 9279de2,0000000..500256e
mode 100644,000000..100644
--- a/3rdparty/smb4php/smb.php
+++ b/3rdparty/smb4php/smb.php
@@@ -1,488 -1,0 +1,503 @@@
 +<?php
 +###################################################################
 +# smb.php
 +# This class implements a SMB stream wrapper based on 'smbclient'
 +#
 +# Date: lun oct 22 10:35:35 CEST 2007
 +#
 +# Homepage: http://www.phpclasses.org/smb4php
 +#
 +# Copyright (c) 2007 Victor M. Varela <vmvarela at gmail.com>
 +#
 +# This program is free software; you can redistribute it and/or
 +# modify it under the terms of the GNU General Public License
 +# as published by the Free Software Foundation; either version 2
 +# of the License, or (at your option) any later version.
 +#
 +# This program is distributed in the hope that it will be useful,
 +# but WITHOUT ANY WARRANTY; without even the implied warranty of
 +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 +# GNU General Public License for more details.
 +#
 +###################################################################
 +
 +define ('SMB4PHP_VERSION', '0.8');
 +
 +###################################################################
 +# CONFIGURATION SECTION - Change for your needs
 +###################################################################
 +
 +define ('SMB4PHP_SMBCLIENT', 'smbclient');
 +define ('SMB4PHP_SMBOPTIONS', 'TCP_NODELAY IPTOS_LOWDELAY SO_KEEPALIVE SO_RCVBUF=8192 SO_SNDBUF=8192');
 +define ('SMB4PHP_AUTHMODE', 'arg'); # set to 'env' to use USER enviroment variable
 +
 +###################################################################
 +# SMB - commands that does not need an instance
 +###################################################################
 +
 +$GLOBALS['__smb_cache'] = array ('stat' => array (), 'dir' => array ());
 +
 +class smb {
 +
 +	function parse_url ($url) {
 +		$pu = parse_url (trim($url));
 +		foreach (array ('domain', 'user', 'pass', 'host', 'port', 'path') as $i) {
 +			if (! isset($pu[$i])) {
 +				$pu[$i] = '';
 +			}
 +		}
 +		if (count ($userdomain = explode (';', urldecode ($pu['user']))) > 1) {
 +			@list ($pu['domain'], $pu['user']) = $userdomain;
 +		}
 +		$path = preg_replace (array ('/^\//', '/\/$/'), '', urldecode ($pu['path']));
 +		list ($pu['share'], $pu['path']) = (preg_match ('/^([^\/]+)\/(.*)/', $path, $regs))
 +			? array ($regs[1], preg_replace ('/\//', '\\', $regs[2]))
 +			: array ($path, '');
 +		$pu['type'] = $pu['path'] ? 'path' : ($pu['share'] ? 'share' : ($pu['host'] ? 'host' : '**error**'));
 +		if (! ($pu['port'] = intval(@$pu['port']))) {
 +			$pu['port'] = 139;
 +		}
 +
 +		// decode user and password
 +		$pu['user'] = urldecode($pu['user']);
 +		$pu['pass'] = urldecode($pu['pass']);
 +		return $pu;
 +	}
 +
 +
 +	function look ($purl) {
 +		return smb::client ('-L ' . escapeshellarg ($purl['host']), $purl);
 +	}
 +
 +
 +	function execute ($command, $purl) {
 +		return smb::client ('-d 0 '
 +				. escapeshellarg ('//' . $purl['host'] . '/' . $purl['share'])
 +				. ' -c ' . escapeshellarg ($command), $purl
 +		);
 +	}
 +
 +	function client ($params, $purl) {
 +
 +		static $regexp = array (
 +			'^added interface ip=(.*) bcast=(.*) nmask=(.*)$' => 'skip',
 +			'Anonymous login successful' => 'skip',
 +			'^Domain=\[(.*)\] OS=\[(.*)\] Server=\[(.*)\]$' => 'skip',
 +			'^\tSharename[ ]+Type[ ]+Comment$' => 'shares',
 +			'^\t---------[ ]+----[ ]+-------$' => 'skip',
 +			'^\tServer   [ ]+Comment$' => 'servers',
 +			'^\t---------[ ]+-------$' => 'skip',
 +			'^\tWorkgroup[ ]+Master$' => 'workg',
 +			'^\t(.*)[ ]+(Disk|IPC)[ ]+IPC.*$' => 'skip',
 +			'^\tIPC\\\$(.*)[ ]+IPC' => 'skip',
 +			'^\t(.*)[ ]+(Disk)[ ]+(.*)$' => 'share',
 +			'^\t(.*)[ ]+(Printer)[ ]+(.*)$' => 'skip',
 +			'([0-9]+) blocks of size ([0-9]+)\. ([0-9]+) blocks available' => 'skip',
 +			'Got a positive name query response from ' => 'skip',
 +			'^(session setup failed): (.*)$' => 'error',
 +			'^(.*): ERRSRV - ERRbadpw' => 'error',
 +			'^Error returning browse list: (.*)$' => 'error',
 +			'^tree connect failed: (.*)$' => 'error',
 +			'^(Connection to .* failed)$' => 'error',
 +			'^NT_STATUS_(.*) ' => 'error',
 +			'^NT_STATUS_(.*)\$' => 'error',
 +			'ERRDOS - ERRbadpath \((.*).\)' => 'error',
 +			'cd (.*): (.*)$' => 'error',
 +			'^cd (.*): NT_STATUS_(.*)' => 'error',
 +			'^\t(.*)$' => 'srvorwg',
 +			'^([0-9]+)[ ]+([0-9]+)[ ]+(.*)$' => 'skip',
 +			'^Job ([0-9]+) cancelled' => 'skip',
 +			'^[ ]+(.*)[ ]+([0-9]+)[ ]+(Mon|Tue|Wed|Thu|Fri|Sat|Sun)[ ](Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec)[ ]+([0-9]+)[ ]+([0-9]{2}:[0-9]{2}:[0-9]{2})[ ]([0-9]{4})$' => 'files',
 +			'^message start: ERRSRV - (ERRmsgoff)' => 'error'
 +		);
 +
 +		if (SMB4PHP_AUTHMODE == 'env') {
 +			putenv("USER={$purl['user']}%{$purl['pass']}");
 +			$auth = '';
 +		} else {
 +			$auth = ($purl['user'] <> '' ? (' -U ' . escapeshellarg ($purl['user'] . '%' . $purl['pass'])) : '');
 +		}
 +		if ($purl['domain'] <> '') {
 +			$auth .= ' -W ' . escapeshellarg ($purl['domain']);
 +		}
 +		$port = ($purl['port'] <> 139 ? ' -p ' . escapeshellarg ($purl['port']) : '');
 +		$options = '-O ' . escapeshellarg(SMB4PHP_SMBOPTIONS);
 +
 +		// this put env is necessary to read the output of smbclient correctly
 +		$old_locale = getenv('LC_ALL');
 +		putenv('LC_ALL=en_US.UTF-8');
 +		$output = popen (SMB4PHP_SMBCLIENT." -N {$auth} {$options} {$port} {$options} {$params} 2>/dev/null", 'r');
 +		$info = array ();
 +		$info['info']= array ();
 +		$mode = '';
 +		while ($line = fgets ($output, 4096)) {
 +			list ($tag, $regs, $i) = array ('skip', array (), array ());
 +			reset ($regexp);
 +			foreach ($regexp as $r => $t) if (preg_match ('/'.$r.'/', $line, $regs)) {
 +				$tag = $t;
 +				break;
 +			}
 +			switch ($tag) {
 +				case 'skip':    continue;
 +				case 'shares':  $mode = 'shares';     break;
 +				case 'servers': $mode = 'servers';    break;
 +				case 'workg':   $mode = 'workgroups'; break;
 +				case 'share':
 +					list($name, $type) = array (
 +						trim(substr($line, 1, 15)),
 +						trim(strtolower(substr($line, 17, 10)))
 +					);
 +					$i = ($type <> 'disk' && preg_match('/^(.*) Disk/', $line, $regs))
 +						? array(trim($regs[1]), 'disk')
 +						: array($name, 'disk');
 +					break;
 +				case 'srvorwg':
 +					list ($name, $master) = array (
 +						strtolower(trim(substr($line,1,21))),
 +						strtolower(trim(substr($line, 22)))
 +					);
 +					$i = ($mode == 'servers') ? array ($name, "server") : array ($name, "workgroup", $master);
 +					break;
 +				case 'files':
 +					list ($attr, $name) = preg_match ("/^(.*)[ ]+([D|A|H|S|R]+)$/", trim ($regs[1]), $regs2)
 +						? array (trim ($regs2[2]), trim ($regs2[1]))
 +						: array ('', trim ($regs[1]));
 +					list ($his, $im) = array (
 +						explode(':', $regs[6]), 1 + strpos("JanFebMarAprMayJunJulAugSepOctNovDec", $regs[4]) / 3);
 +					$i = ($name <> '.' && $name <> '..')
 +						? array (
 +							$name,
 +							(strpos($attr,'D') === FALSE) ? 'file' : 'folder',
 +							'attr' => $attr,
 +							'size' => intval($regs[2]),
 +							'time' => mktime ($his[0], $his[1], $his[2], $im, $regs[5], $regs[7])
 +						)
 +						: array();
 +					break;
 +				case 'error':
 +					if(substr($regs[0],0,22)=='NT_STATUS_NO_SUCH_FILE'){
 +						return false;
 +					}elseif(substr($regs[0],0,31)=='NT_STATUS_OBJECT_NAME_COLLISION'){
 +						return false;
 +					}elseif(substr($regs[0],0,31)=='NT_STATUS_OBJECT_PATH_NOT_FOUND'){
 +						return false;
 +					}elseif(substr($regs[0],0,31)=='NT_STATUS_OBJECT_NAME_NOT_FOUND'){
 +						return false;
 +					}elseif(substr($regs[0],0,29)=='NT_STATUS_FILE_IS_A_DIRECTORY'){
 +						return false;
 +					}
 +					trigger_error($regs[0].' params('.$params.')', E_USER_ERROR);
 +			}
 +			if ($i) switch ($i[1]) {
 +				case 'file':
 +				case 'folder':    $info['info'][$i[0]] = $i;
 +				case 'disk':
 +				case 'server':
 +				case 'workgroup': $info[$i[1]][] = $i[0];
 +			}
 +		}
 +		pclose($output);
 +
 +
 +		// restore previous locale
 +		if ($old_locale===false) {
 +			putenv('LC_ALL');
 +		} else {
 +			putenv('LC_ALL='.$old_locale);
 +		}
 +
 +		return $info;
 +	}
 +
 +
 +	# stats
 +
 +	function url_stat ($url, $flags = STREAM_URL_STAT_LINK) {
 +		if ($s = smb::getstatcache($url)) {
 +			return $s;
 +		}
 +		list ($stat, $pu) = array (array (), smb::parse_url ($url));
 +		switch ($pu['type']) {
 +			case 'host':
 +				if ($o = smb::look ($pu))
 +					$stat = stat ("/tmp");
 +				else
 +					trigger_error ("url_stat(): list failed for host '{$pu['host']}'", E_USER_WARNING);
 +				break;
 +			case 'share':
 +				if ($o = smb::look ($pu)) {
 +					$found = FALSE;
 +					$lshare = strtolower ($pu['share']);  # fix by Eric Leung
 +					foreach ($o['disk'] as $s) if ($lshare == strtolower($s)) {
 +						$found = TRUE;
 +						$stat = stat ("/tmp");
 +						break;
 +					}
 +					if (! $found)
 +						trigger_error ("url_stat(): disk resource '{$lshare}' not found in '{$pu['host']}'", E_USER_WARNING);
 +				}
 +				break;
 +			case 'path':
 +				if ($o = smb::execute ('dir "'.$pu['path'].'"', $pu)) {
 +					$p = explode('\\', $pu['path']);
 +					$name = $p[count($p)-1];
 +					if (isset ($o['info'][$name])) {
 +						$stat = smb::addstatcache ($url, $o['info'][$name]);
 +					} else {
 +						trigger_error ("url_stat(): path '{$pu['path']}' not found", E_USER_WARNING);
 +					}
 +				} else {
 +					return false;
 +// 					trigger_error ("url_stat(): dir failed for path '{$pu['path']}'", E_USER_WARNING);
 +				}
 +				break;
 +			default: trigger_error ('error in URL', E_USER_ERROR);
 +		}
 +		return $stat;
 +	}
 +
 +	function addstatcache ($url, $info) {
 +		$url = str_replace('//', '/', $url);
 +		$url = rtrim($url, '/');
 +		global $__smb_cache;
 +		$is_file = (strpos ($info['attr'],'D') === FALSE);
 +		$s = ($is_file) ? stat ('/etc/passwd') : stat ('/tmp');
 +		$s[7] = $s['size'] = $info['size'];
 +		$s[8] = $s[9] = $s[10] = $s['atime'] = $s['mtime'] = $s['ctime'] = $info['time'];
 +		return $__smb_cache['stat'][$url] = $s;
 +	}
 +
 +	function getstatcache ($url) {
 +		$url = str_replace('//', '/', $url);
 +		$url = rtrim($url, '/');
 +		global $__smb_cache;
 +		return isset ($__smb_cache['stat'][$url]) ? $__smb_cache['stat'][$url] : FALSE;
 +	}
 +
 +	function clearstatcache ($url='') {
 +		$url = str_replace('//', '/', $url);
 +		$url = rtrim($url, '/');
 +		global $__smb_cache;
 +		if ($url == '') $__smb_cache['stat'] = array (); else unset ($__smb_cache['stat'][$url]);
 +	}
 +
 +
 +	# commands
 +
 +	function unlink ($url) {
 +		$pu = smb::parse_url($url);
 +		if ($pu['type'] <> 'path') trigger_error('unlink(): error in URL', E_USER_ERROR);
 +		smb::clearstatcache ($url);
 +		smb_stream_wrapper::cleardircache (dirname($url));
 +		return smb::execute ('del "'.$pu['path'].'"', $pu);
 +	}
 +
 +	function rename ($url_from, $url_to) {
++		$replace = false;
 +		list ($from, $to) = array (smb::parse_url($url_from), smb::parse_url($url_to));
 +		if ($from['host'] <> $to['host'] ||
 +			$from['share'] <> $to['share'] ||
 +			$from['user'] <> $to['user'] ||
 +			$from['pass'] <> $to['pass'] ||
 +			$from['domain'] <> $to['domain']) {
 +			trigger_error('rename(): FROM & TO must be in same server-share-user-pass-domain', E_USER_ERROR);
 +		}
 +		if ($from['type'] <> 'path' || $to['type'] <> 'path') {
 +			trigger_error('rename(): error in URL', E_USER_ERROR);
 +		}
 +		smb::clearstatcache ($url_from);
- 		$result = smb::execute ('rename "'.$from['path'].'" "'.$to['path'].'"', $to);
++		$cmd = '';
++		// check if target file exists
++		if (smb::url_stat($url_to)) {
++			// delete target file first
++			$cmd = 'del "' . $to['path'] . '"; ';
++			$replace = true;
++		}
++		$cmd .= 'rename "' . $from['path'] . '" "' . $to['path'] . '"';
++		$result = smb::execute($cmd, $to);
++		if ($replace) {
++			// clear again, else the cache will return the info
++			// from the old file
++			smb::clearstatcache ($url_to);
++		}
 +		return $result !== false;
 +	}
 +
 +	function mkdir ($url, $mode, $options) {
 +		$pu = smb::parse_url($url);
 +		if ($pu['type'] <> 'path') trigger_error('mkdir(): error in URL', E_USER_ERROR);
 +		return smb::execute ('mkdir "'.$pu['path'].'"', $pu)!==false;
 +	}
 +
 +	function rmdir ($url) {
 +		$pu = smb::parse_url($url);
 +		if ($pu['type'] <> 'path') trigger_error('rmdir(): error in URL', E_USER_ERROR);
 +		smb::clearstatcache ($url);
 +		smb_stream_wrapper::cleardircache (dirname($url));
 +		return smb::execute ('rmdir "'.$pu['path'].'"', $pu)!==false;
 +	}
 +
 +}
 +
 +###################################################################
 +# SMB_STREAM_WRAPPER - class to be registered for smb:// URLs
 +###################################################################
 +
 +class smb_stream_wrapper extends smb {
 +
 +	# variables
 +
 +	private $stream, $url, $parsed_url = array (), $mode, $tmpfile;
 +	private $need_flush = FALSE;
 +	private $dir = array (), $dir_index = -1;
 +
 +
 +	# directories
 +
 +	function dir_opendir ($url, $options) {
 +		if ($d = $this->getdircache ($url)) {
 +			$this->dir = $d;
 +			$this->dir_index = 0;
 +			return TRUE;
 +		}
 +		$pu = smb::parse_url ($url);
 +		switch ($pu['type']) {
 +			case 'host':
 +				if ($o = smb::look ($pu)) {
 +					$this->dir = $o['disk'];
 +					$this->dir_index = 0;
 +				} else {
 +					trigger_error ("dir_opendir(): list failed for host '{$pu['host']}'", E_USER_WARNING);
 +					return false;
 +				}
 +				break;
 +			case 'share':
 +			case 'path':
 +				if (is_array($o = smb::execute ('dir "'.$pu['path'].'\*"', $pu))) {
 +					$this->dir = array_keys($o['info']);
 +					$this->dir_index = 0;
 +					$this->adddircache ($url, $this->dir);
 +					if(substr($url,-1,1)=='/'){
 +						$url=substr($url,0,-1);
 +					}
 +					foreach ($o['info'] as $name => $info) {
 +						smb::addstatcache($url . '/' . $name, $info);
 +					}
 +				} else {
 +					trigger_error ("dir_opendir(): dir failed for path '".$pu['path']."'", E_USER_WARNING);
 +					return false;
 +				}
 +				break;
 +			default:
 +				trigger_error ('dir_opendir(): error in URL', E_USER_ERROR);
 +				return false;
 +		}
 +		return TRUE;
 +	}
 +
 +	function dir_readdir () {
 +		return ($this->dir_index < count($this->dir)) ? $this->dir[$this->dir_index++] : FALSE;
 +	}
 +
 +	function dir_rewinddir () { $this->dir_index = 0; }
 +
 +	function dir_closedir () { $this->dir = array(); $this->dir_index = -1; return TRUE; }
 +
 +
 +	# cache
 +
 +	function adddircache ($url, $content) {
 +		$url = str_replace('//', '/', $url);
 +		$url = rtrim($url, '/');
 +		global $__smb_cache;
 +		return $__smb_cache['dir'][$url] = $content;
 +	}
 +
 +	function getdircache ($url) {
 +		$url = str_replace('//', '/', $url);
 +		$url = rtrim($url, '/');
 +		global $__smb_cache;
 +		return isset ($__smb_cache['dir'][$url]) ? $__smb_cache['dir'][$url] : FALSE;
 +	}
 +
 +	function cleardircache ($url='') {
 +		$url = str_replace('//', '/', $url);
 +		$url = rtrim($url, '/');
 +		global $__smb_cache;
 +		if ($url == ''){
 +			$__smb_cache['dir'] = array ();
 +		}else{
 +			unset ($__smb_cache['dir'][$url]);
 +		}
 +	}
 +
 +
 +	# streams
 +
 +	function stream_open ($url, $mode, $options, $opened_path) {
 +		$this->url = $url;
 +		$this->mode = $mode;
 +		$this->parsed_url = $pu = smb::parse_url($url);
 +		if ($pu['type'] <> 'path') trigger_error('stream_open(): error in URL', E_USER_ERROR);
 +		switch ($mode) {
 +			case 'r':
 +			case 'r+':
 +			case 'rb':
 +			case 'a':
 +			case 'a+':  $this->tmpfile = tempnam('/tmp', 'smb.down.');
 +				$result = smb::execute ('get "'.$pu['path'].'" "'.$this->tmpfile.'"', $pu);
 +				if($result === false){
 +					return $result;
 +				}
 +				break;
 +			case 'w':
 +			case 'w+':
 +			case 'wb':
 +			case 'x':
 +			case 'x+':  $this->cleardircache();
 +				$this->tmpfile = tempnam('/tmp', 'smb.up.');
 +				$this->need_flush=true;
 +		}
 +		$this->stream = fopen ($this->tmpfile, $mode);
 +		return TRUE;
 +	}
 +
 +	function stream_close () { return fclose($this->stream); }
 +
 +	function stream_read ($count) { return fread($this->stream, $count); }
 +
 +	function stream_write ($data) { $this->need_flush = TRUE; return fwrite($this->stream, $data); }
 +
 +	function stream_eof () { return feof($this->stream); }
 +
 +	function stream_tell () { return ftell($this->stream); }
- 
- 	function stream_seek ($offset, $whence=null) { return fseek($this->stream, $offset, $whence); }
++	
++	// PATCH: the wrapper must return true when fseek succeeded by returning 0.
++	function stream_seek ($offset, $whence=null) { return fseek($this->stream, $offset, $whence) === 0; }
 +
 +	function stream_flush () {
 +		if ($this->mode <> 'r' && $this->need_flush) {
 +			smb::clearstatcache ($this->url);
 +			smb::execute ('put "'.$this->tmpfile.'" "'.$this->parsed_url['path'].'"', $this->parsed_url);
 +			$this->need_flush = FALSE;
 +		}
 +	}
 +
 +	function stream_stat () { return smb::url_stat ($this->url); }
 +
 +	function __destruct () {
 +		if ($this->tmpfile <> '') {
 +			if ($this->need_flush) $this->stream_flush ();
 +			unlink ($this->tmpfile);
 +
 +		}
 +	}
 +
 +}
 +
 +###################################################################
 +# Register 'smb' protocol !
 +###################################################################
 +
 +stream_wrapper_register('smb', 'smb_stream_wrapper')
 +	or die ('Failed to register protocol');
diff --cc apps/calendar/lib/sabre/backend.php
index 54b80e9,0000000..88c8697
mode 100644,000000..100644
--- a/apps/calendar/lib/sabre/backend.php
+++ b/apps/calendar/lib/sabre/backend.php
@@@ -1,327 -1,0 +1,327 @@@
 +<?php
 +
 +class OC_Connector_Sabre_CalDAV extends Sabre_CalDAV_Backend_Abstract {
 +	/**
 +	 * List of CalDAV properties, and how they map to database fieldnames
 +	 *
 +	 * Add your own properties by simply adding on to this array
 +	 *
 +	 * @var array
 +	 */
 +	public $propertyMap = array(
 +		'{DAV:}displayname'						  => 'displayname',
 +		'{urn:ietf:params:xml:ns:caldav}calendar-timezone'	=> 'timezone',
 +		'{http://apple.com/ns/ical/}calendar-order'  => 'calendarorder',
 +		'{http://apple.com/ns/ical/}calendar-color'  => 'calendarcolor',
 +	);
 +
 +	/**
 +	 * Returns a list of calendars for a principal.
 +	 *
 +	 * Every project is an array with the following keys:
 +	 *  * id, a unique id that will be used by other functions to modify the
 +	 *	calendar. This can be the same as the uri or a database key.
 +	 *  * uri, which the basename of the uri with which the calendar is
 +	 *	accessed.
 +	 *  * principalUri. The owner of the calendar. Almost always the same as
 +	 *	principalUri passed to this method.
 +	 *
 +	 * Furthermore it can contain webdav properties in clark notation. A very
 +	 * common one is '{DAV:}displayname'.
 +	 *
 +	 * @param string $principalUri
 +	 * @return array
 +	 */
 +	public function getCalendarsForUser($principalUri) {
 +		$raw = OC_Calendar_Calendar::allCalendarsWherePrincipalURIIs($principalUri);
 +
 +		$calendars = array();
 +		foreach( $raw as $row ) {
 +			$components = explode(',',$row['components']);
 +
 +			if($row['userid'] != OCP\USER::getUser()) {
 +				$row['uri'] = $row['uri'] . '_shared_by_' . $row['userid'];
 +			}
 +			$calendar = array(
 +				'id' => $row['id'],
 +				'uri' => $row['uri'],
 +				'principaluri' => 'principals/'.$row['userid'],
 +				'{' . Sabre_CalDAV_Plugin::NS_CALENDARSERVER . '}getctag' => $row['ctag']?$row['ctag']:'0',
 +				'{' . Sabre_CalDAV_Plugin::NS_CALDAV . '}supported-calendar-component-set' => new Sabre_CalDAV_Property_SupportedCalendarComponentSet($components),
 +			);
 +
 +			foreach($this->propertyMap as $xmlName=>$dbName) {
 +				$calendar[$xmlName] = $row[$dbName];
 +			}
 +
 +			$calendars[] = $calendar;
 +		}
 +		return $calendars;
 +	}
 +
 +	/**
 +	 * Creates a new calendar for a principal.
 +	 *
 +	 * If the creation was a success, an id must be returned that can be used to reference
 +	 * this calendar in other methods, such as updateCalendar
 +	 *
 +	 * @param string $principalUri
 +	 * @param string $calendarUri
 +	 * @param array $properties
 +	 * @return mixed
 +	 */
 +	public function createCalendar($principalUri,$calendarUri, array $properties) {
 +		$fieldNames = array(
 +			'principaluri',
 +			'uri',
 +			'ctag',
 +		);
 +		$values = array(
 +			':principaluri' => $principalUri,
 +			':uri'		  => $calendarUri,
 +			':ctag'		 => 1,
 +		);
 +
 +		// Default value
 +		$sccs = '{urn:ietf:params:xml:ns:caldav}supported-calendar-component-set';
 +		$fieldNames[] = 'components';
 +		if (!isset($properties[$sccs])) {
 +			$values[':components'] = 'VEVENT,VTODO';
 +		} else {
 +			if (!($properties[$sccs] instanceof Sabre_CalDAV_Property_SupportedCalendarComponentSet)) {
 +				throw new Sabre_DAV_Exception('The ' . $sccs . ' property must be of type: Sabre_CalDAV_Property_SupportedCalendarComponentSet');
 +			}
 +			$values[':components'] = implode(',',$properties[$sccs]->getValue());
 +		}
 +
 +		foreach($this->propertyMap as $xmlName=>$dbName) {
 +			if (isset($properties[$xmlName])) {
 +
 +				$myValue = $properties[$xmlName];
 +				$values[':' . $dbName] = $properties[$xmlName];
 +				$fieldNames[] = $dbName;
 +			}
 +		}
 +
 +		if(!isset($newValues['displayname'])) $newValues['displayname'] = 'unnamed';
 +		if(!isset($newValues['components'])) $newValues['components'] = 'VEVENT,VTODO';
 +		if(!isset($newValues['timezone'])) $newValues['timezone'] = null;
 +		if(!isset($newValues['calendarorder'])) $newValues['calendarorder'] = 0;
 +		if(!isset($newValues['calendarcolor'])) $newValues['calendarcolor'] = null;
 +		if(!is_null($newValues['calendarcolor']) && strlen($newValues['calendarcolor']) == 9) {
 +			$newValues['calendarcolor'] = substr($newValues['calendarcolor'], 0, 7);
 +		}
 +
 +		return OC_Calendar_Calendar::addCalendarFromDAVData($principalUri,$calendarUri,$newValues['displayname'],$newValues['components'],$newValues['timezone'],$newValues['calendarorder'],$newValues['calendarcolor']);
 +	}
 +
 +	/**
 +	 * Updates a calendars properties
 +	 *
 +	 * The properties array uses the propertyName in clark-notation as key,
 +	 * and the array value for the property value. In the case a property
 +	 * should be deleted, the property value will be null.
 +	 *
 +	 * This method must be atomic. If one property cannot be changed, the
 +	 * entire operation must fail.
 +	 *
 +	 * If the operation was successful, true can be returned.
 +	 * If the operation failed, false can be returned.
 +	 *
 +	 * Deletion of a non-existant property is always succesful.
 +	 *
 +	 * Lastly, it is optional to return detailed information about any
 +	 * failures. In this case an array should be returned with the following
 +	 * structure:
 +	 *
 +	 * array(
 +	 *   403 => array(
 +	 *	  '{DAV:}displayname' => null,
 +	 *   ),
 +	 *   424 => array(
 +	 *	  '{DAV:}owner' => null,
 +	 *   )
 +	 * )
 +	 *
 +	 * In this example it was forbidden to update {DAV:}displayname.
 +	 * (403 Forbidden), which in turn also caused {DAV:}owner to fail
 +	 * (424 Failed Dependency) because the request needs to be atomic.
 +	 *
 +	 * @param string $calendarId
 +	 * @param array $properties
 +	 * @return bool|array
 +	 */
 +	public function updateCalendar($calendarId, array $properties) {
 +
 +		$newValues = array();
 +		$result = array(
 +			200 => array(), // Ok
 +			403 => array(), // Forbidden
 +			424 => array(), // Failed Dependency
 +		);
 +
 +		$hasError = false;
 +
 +		foreach($properties as $propertyName=>$propertyValue) {
 +
 +			// We don't know about this property.
 +			if (!isset($this->propertyMap[$propertyName])) {
 +				$hasError = true;
 +				$result[403][$propertyName] = null;
 +				unset($properties[$propertyName]);
 +				continue;
 +			}
 +
 +			$fieldName = $this->propertyMap[$propertyName];
 +			$newValues[$fieldName] = $propertyValue;
 +
 +		}
 +
 +		// If there were any errors we need to fail the request
 +		if ($hasError) {
 +			// Properties has the remaining properties
 +			foreach($properties as $propertyName=>$propertyValue) {
 +				$result[424][$propertyName] = null;
 +			}
 +
 +			// Removing unused statuscodes for cleanliness
 +			foreach($result as $status=>$properties) {
 +				if (is_array($properties) && count($properties)===0) unset($result[$status]);
 +			}
 +
 +			return $result;
 +
 +		}
 +
 +		// Success
 +		if(!isset($newValues['displayname'])) $newValues['displayname'] = null;
 +		if(!isset($newValues['timezone'])) $newValues['timezone'] = null;
 +		if(!isset($newValues['calendarorder'])) $newValues['calendarorder'] = null;
 +		if(!isset($newValues['calendarcolor'])) $newValues['calendarcolor'] = null;
 +		if(!is_null($newValues['calendarcolor']) && strlen($newValues['calendarcolor']) == 9) {
 +			$newValues['calendarcolor'] = substr($newValues['calendarcolor'], 0, 7);
 +		}
 +
 +		OC_Calendar_Calendar::editCalendar($calendarId,$newValues['displayname'],null,$newValues['timezone'],$newValues['calendarorder'],$newValues['calendarcolor']);
 +
 +		return true;
 +
 +	}
 +
 +	/**
 +	 * Delete a calendar and all it's objects
 +	 *
 +	 * @param string $calendarId
 +	 * @return void
 +	 */
 +	public function deleteCalendar($calendarId) {
 +	    if(preg_match( '=iCal/[1-4]?.*Mac OS X/10.[1-6](.[0-9])?=', $_SERVER['HTTP_USER_AGENT'] )) {
 +	    	throw new Sabre_DAV_Exception_Forbidden("Action is not possible with OSX 10.6.x", 403);
 +		}
 +
 +		OC_Calendar_Calendar::deleteCalendar($calendarId);
 +	}
 +
 +	/**
 +	 * Returns all calendar objects within a calendar object.
 +	 *
 +	 * Every item contains an array with the following keys:
 +	 *   * id - unique identifier which will be used for subsequent updates
 +	 *   * calendardata - The iCalendar-compatible calnedar data
 +	 *   * uri - a unique key which will be used to construct the uri. This can be any arbitrary string.
 +	 *   * lastmodified - a timestamp of the last modification time
 +	 *   * etag - An arbitrary string, surrounded by double-quotes. (e.g.:
 +	 *   '  "abcdef"')
 +	 *   * calendarid - The calendarid as it was passed to this function.
 +	 *
 +	 * Note that the etag is optional, but it's highly encouraged to return for
 +	 * speed reasons.
 +	 *
 +	 * The calendardata is also optional. If it's not returned
 +	 * 'getCalendarObject' will be called later, which *is* expected to return
 +	 * calendardata.
 +	 *
 +	 * @param string $calendarId
 +	 * @return array
 +	 */
 +	public function getCalendarObjects($calendarId) {
 +		$data = array();
 +		foreach(OC_Calendar_Object::all($calendarId) as $row) {
 +			$data[] = $this->OCAddETag($row);
 +		}
 +		return $data;
 +	}
 +
 +	/**
 +	 * Returns information from a single calendar object, based on it's object
 +	 * uri.
 +	 *
 +	 * The returned array must have the same keys as getCalendarObjects. The
 +	 * 'calendardata' object is required here though, while it's not required
 +	 * for getCalendarObjects.
 +	 *
 +	 * @param string $calendarId
 +	 * @param string $objectUri
 +	 * @return array
 +	 */
 +	public function getCalendarObject($calendarId,$objectUri) {
 +		$data = OC_Calendar_Object::findWhereDAVDataIs($calendarId,$objectUri);
 +		if(is_array($data)) {
++			$data = $this->OCAddETag($data);
 +			$object = OC_VObject::parse($data['calendardata']);
 +			if(!$object) {
 +				return false;
 +			}
 +			$object = OC_Calendar_Object::cleanByAccessClass($data['id'], $object);
 +			$data['calendardata'] = $object->serialize();
- 			$data = $this->OCAddETag($data);
 +		}
 +		return $data;
 +	}
 +
 +	/**
 +	 * Creates a new calendar object.
 +	 *
 +	 * @param string $calendarId
 +	 * @param string $objectUri
 +	 * @param string $calendarData
 +	 * @return void
 +	 */
 +	public function createCalendarObject($calendarId,$objectUri,$calendarData) {
 +		OC_Calendar_Object::addFromDAVData($calendarId,$objectUri,$calendarData);
 +	}
 +
 +	/**
 +	 * Updates an existing calendarobject, based on it's uri.
 +	 *
 +	 * @param string $calendarId
 +	 * @param string $objectUri
 +	 * @param string $calendarData
 +	 * @return void
 +	 */
 +	public function updateCalendarObject($calendarId,$objectUri,$calendarData) {
 +		OC_Calendar_Object::editFromDAVData($calendarId,$objectUri,$calendarData);
 +	}
 +
 +	/**
 +	 * Deletes an existing calendar object.
 +	 *
 +	 * @param string $calendarId
 +	 * @param string $objectUri
 +	 * @return void
 +	 */
 +	public function deleteCalendarObject($calendarId,$objectUri) {
 +		OC_Calendar_Object::deleteFromDAVData($calendarId,$objectUri);
 +	}
 +
 +	/**
 +	 * @brief Creates a etag
 +	 * @param array $row Database result
 +	 * @returns associative array
 +	 *
 +	 * Adds a key "etag" to the row
 +	 */
 +	private function OCAddETag($row) {
 +		$row['etag'] = '"'.md5($row['calendarid'].$row['uri'].$row['calendardata'].$row['lastmodified']).'"';
 +		return $row;
 +	}
 +}
diff --cc apps/calendar/lib/share/event.php
index 9934282,0000000..0aa6eff
mode 100644,000000..100644
--- a/apps/calendar/lib/share/event.php
+++ b/apps/calendar/lib/share/event.php
@@@ -1,43 -1,0 +1,48 @@@
 +<?php
 +/**
 + * Copyright (c) 2012 Bart Visscher <bartv at thisnet.nl>
 + * This file is licensed under the Affero General Public License version 3 or
 + * later.
 + * See the COPYING-README file.
 + */
 +
 +class OC_Share_Backend_Event implements OCP\Share_Backend {
 +
 +	const FORMAT_EVENT = 0;
 +
 +	private static $event;
 +
 +	public function isValidSource($itemSource, $uidOwner) {
 +		self::$event = OC_Calendar_Object::find($itemSource);
 +		if (self::$event) {
 +			return true;
 +		}
 +		return false;
 +	}
 +
 +	public function generateTarget($itemSource, $shareWith, $exclude = null) {
 +		if(!self::$event) {
 +			self::$event = OC_Calendar_Object::find($itemSource);
 +		}
 +		return self::$event['summary'];
 +	}
 +
 +	public function formatItems($items, $format, $parameters = null) {
 +		$events = array();
 +		if ($format == self::FORMAT_EVENT) {
 +			foreach ($items as $item) {
 +				$event = OC_Calendar_Object::find($item['item_source']);
- 				$event['summary'] = $item['item_target'];
- 				$event['permissions'] = $item['permissions'];
- 				$events[] = $event;
++				if ($event == False) {
++					\OCP\Util::writeLog('calendar', __METHOD__.', Missing event: ' . $item['item_target'], \OCP\Util::DEBUG);
++				}
++				else {
++					$event['summary'] = $item['item_target'];
++					$event['permissions'] = $item['permissions'];
++					$events[] = $event;
++				}
 +			}
 +		}
 +		return $events;
 +	}
 +
 +}
diff --cc apps/contacts/appinfo/bootstrap.php
index f57b57b,0000000..0e6607a
mode 100644,000000..100644
--- a/apps/contacts/appinfo/bootstrap.php
+++ b/apps/contacts/appinfo/bootstrap.php
@@@ -1,42 -1,0 +1,43 @@@
 +<?php
 +OC::$CLASSPATH['OCA\Contacts\App'] = 'contacts/lib/app.php';
 +OC::$CLASSPATH['OCA\Contacts\Addressbook'] = 'contacts/lib/addressbook.php';
 +OC::$CLASSPATH['OCA\Contacts\VCard'] = 'contacts/lib/vcard.php';
 +OC::$CLASSPATH['OCA\Contacts\Hooks'] = 'contacts/lib/hooks.php';
 +OC::$CLASSPATH['OCA\Contacts\Request'] = 'contacts/lib/request.php';
 +OC::$CLASSPATH['OCA\Contacts\JSONResponse'] = 'contacts/lib/jsonresponse.php';
 +OC::$CLASSPATH['OCA\Contacts\Controller\BaseController'] = 'contacts/lib/controller/basecontroller.php';
 +OC::$CLASSPATH['OCA\Contacts\Controller\ImportController'] = 'contacts/lib/controller/importcontroller.php';
 +OC::$CLASSPATH['OCA\Contacts\Share_Backend_Contact'] = 'contacts/lib/share/contact.php';
 +OC::$CLASSPATH['OCA\Contacts\Share_Backend_Addressbook'] = 'contacts/lib/share/addressbook.php';
 +OC::$CLASSPATH['OCA\Contacts\AddressbookProvider'] = 'contacts/lib/addressbookprovider.php';
 +OC::$CLASSPATH['OC_Connector_Sabre_CardDAV'] = 'contacts/lib/sabre/backend.php';
 +OC::$CLASSPATH['OC_Connector_Sabre_CardDAV_AddressBookRoot'] = 'contacts/lib/sabre/addressbookroot.php';
 +OC::$CLASSPATH['OC_Connector_Sabre_CardDAV_UserAddressBooks'] = 'contacts/lib/sabre/useraddressbooks.php';
 +OC::$CLASSPATH['OC_Connector_Sabre_CardDAV_AddressBook'] = 'contacts/lib/sabre/addressbook.php';
 +OC::$CLASSPATH['OC_Connector_Sabre_CardDAV_Card'] = 'contacts/lib/sabre/card.php';
 +OC::$CLASSPATH['OC_Connector_Sabre_CardDAV_ValidatorPlugin'] = 'contacts/lib/sabre/validatorplugin.php';
 +OC::$CLASSPATH['OCA\Contacts\VObject\StringProperty'] = 'contacts/lib/vobject/stringproperty.php';
 +OC::$CLASSPATH['OCA\\Contacts\\SearchProvider'] = 'contacts/lib/search.php';
 +
 +Sabre\VObject\Property::$classMap['FN'] = 'OC\VObject\StringProperty';
 +Sabre\VObject\Property::$classMap['TITLE'] = 'OC\VObject\StringProperty';
 +Sabre\VObject\Property::$classMap['ROLE'] = 'OC\VObject\StringProperty';
 +Sabre\VObject\Property::$classMap['NOTE'] = 'OC\VObject\StringProperty';
 +Sabre\VObject\Property::$classMap['NICKNAME'] = 'OC\VObject\StringProperty';
 +Sabre\VObject\Property::$classMap['EMAIL'] = 'OC\VObject\StringProperty';
 +Sabre\VObject\Property::$classMap['TEL'] = 'OC\VObject\StringProperty';
 +Sabre\VObject\Property::$classMap['IMPP'] = 'OC\VObject\StringProperty';
 +Sabre\VObject\Property::$classMap['URL'] = 'OC\VObject\StringProperty';
- Sabre\VObject\Property::$classMap['FN'] = 'OC\VObject\CompoundProperty';
++Sabre\VObject\Property::$classMap['N'] = 'OC\VObject\CompoundProperty';
++Sabre\VObject\Property::$classMap['ORG'] = 'OC\VObject\CompoundProperty';
 +Sabre\VObject\Property::$classMap['ADR'] = 'OC\VObject\CompoundProperty';
 +Sabre\VObject\Property::$classMap['CATEGORIES'] = 'OC\VObject\CompoundProperty';
 +Sabre\VObject\Property::$classMap['GEO'] = 'OC\VObject\CompoundProperty';
 +
 +OCP\Util::connectHook('OC_User', 'post_createUser', 'OCA\Contacts\Hooks', 'createUser');
 +OCP\Util::connectHook('OC_User', 'post_deleteUser', 'OCA\Contacts\Hooks', 'deleteUser');
 +OCP\Util::connectHook('OC_Calendar', 'getEvents', 'OCA\Contacts\Hooks', 'getBirthdayEvents');
 +OCP\Util::connectHook('OC_Calendar', 'getSources', 'OCA\Contacts\Hooks', 'getCalenderSources');
 +
 +OCP\Share::registerBackend('contact', 'OCA\Contacts\Share_Backend_Contact');
 +OCP\Share::registerBackend('addressbook', 'OCA\Contacts\Share_Backend_Addressbook', 'contact');
diff --cc apps/contacts/lib/sabre/validatorplugin.php
index fbf9ea6,0000000..005ec9c
mode 100644,000000..100644
--- a/apps/contacts/lib/sabre/validatorplugin.php
+++ b/apps/contacts/lib/sabre/validatorplugin.php
@@@ -1,122 -1,0 +1,123 @@@
 +<?php
 +
 +use Sabre\VObject;
 +
 +/**
 + * vCard validator
 + *
 + * Validates and tries to fix broken vCards before they're being
 + * handed over to Sabre and written to storage.
 + *
 + * @copyright Copyright (C) 2013 Thomas Tanghus
 + * @author Thomas Tanghus (http://tanghus.net/)
 + */
 +class OC_Connector_Sabre_CardDAV_ValidatorPlugin extends Sabre_DAV_ServerPlugin {
 +
 +	/**
 +	* Reference to Server class
 +	*
 +	* @var Sabre_DAV_Server
 +	*/
 +	protected $server;
 +
 +	/**
 +	* Initializes the plugin and registers event handlers
 +	*
 +	* @param Sabre_DAV_Server $server
 +	* @return void
 +	*/
 +	public function initialize(Sabre_DAV_Server $server) {
 +
 +		$this->server = $server;
 +		$server->subscribeEvent('beforeWriteContent', array($this, 'beforeWriteContent'), 90);
 +		$server->subscribeEvent('beforeCreateFile', array($this, 'beforeCreateFile'), 90);
 +
 +	}
 +
 +	/**
 +	* This method is triggered before a file gets updated with new content.
 +	*
 +	* This plugin uses this method to ensure that Card nodes receive valid
 +	* vcard data.
 +	*
 +	* @param string $path
 +	* @param Sabre_DAV_IFile $node
 +	* @param resource $data
 +	* @return void
 +	*/
 +	public function beforeWriteContent($path, Sabre_DAV_IFile $node, &$data) {
 +
 +		if (!$node instanceof Sabre_CardDAV_ICard) {
 +			return;
 +		}
 +
 +		$this->validateVCard($data);
 +
 +	}
 +
 +	/**
 +	* This method is triggered before a new file is created.
 +	*
 +	* This plugin uses this method to ensure that Card nodes receive valid
 +	* vcard data.
 +	*
 +	* @param string $path
 +	* @param resource $data
 +	* @param Sabre_DAV_ICollection $parentNode
 +	* @return void
 +	*/
 +	public function beforeCreateFile($path, &$data, Sabre_DAV_ICollection $parentNode) {
 +
 +		if (!$parentNode instanceof Sabre_CardDAV_IAddressBook) {
 +			return;
 +		}
 +
 +		$this->validateVCard($data);
 +
 +	}
 +
 +	/**
 +	* Checks if the submitted vCard data is in fact, valid.
 +	*
 +	* An exception is thrown if it's not.
 +	*
 +	* @param resource|string $data
 +	* @return void
 +	*/
 +	protected function validateVCard(&$data) {
 +
 +		// If it's a stream, we convert it to a string first.
 +		if (is_resource($data)) {
 +			$data = stream_get_contents($data);
 +		}
 +
 +		// Converting the data to unicode, if needed.
 +		$data = Sabre_DAV_StringUtil::ensureUTF8($data);
 +
 +		try {
 +
 +			$vobj = VObject\Reader::read($data);
 +
 +		} catch (VObject\ParseException $e) {
 +
 +			throw new Sabre_DAV_Exception_UnsupportedMediaType(
 +				'This resource only supports valid vcard data. Parse error: ' . $e->getMessage()
 +			);
 +
 +		}
 +
 +		if ($vobj->name !== 'VCARD') {
 +			throw new Sabre_DAV_Exception_UnsupportedMediaType(
 +				'This collection can only support vcard objects.'
 +			);
 +		}
 +
 +		if (!isset($vobj->UID)) {
 +			$uid = substr(md5(rand().time()), 0, 10);
 +			\OCP\Util::writeLog('contacts', __METHOD__.', Adding UID: ' . $uid, \OCP\Util::DEBUG);
 +			$vobj->add('UID', $uid);
++			$data = $vobj->serialize();
 +		}
 +
 +	}
 +}
diff --cc apps/files_pdfviewer/js/loader.js
index f595c93,0000000..12bca3e
mode 100644,000000..100644
--- a/apps/files_pdfviewer/js/loader.js
+++ b/apps/files_pdfviewer/js/loader.js
@@@ -1,38 -1,0 +1,39 @@@
 +function hidePDFviewer() {
 +	$('#content table').show();
 +	$("#controls").show();
 +	$("#editor").show();
 +	$('#pdframe, #pdfbar').remove();
 +}
 +
 +function showPDFviewer(dir,filename){
 +	if(!showPDFviewer.shown){
 +		$("#editor").hide();
 +		$('#content table').hide();
 +		$("#controls").hide();
 +		var oldcontent = $("#content").html();
 +		var viewer = OC.linkTo('files_pdfviewer', 'viewer.php')+'?dir='+encodeURIComponent(dir).replace(/%2F/g, '/')+'&file='+encodeURIComponent(filename.replace('&', '%26'));
 +		$("#content").append('<iframe id="pdframe" style="width:100%;height:100%;display:block;" src="'+viewer+'" /><div id="pdfbar"><a id="close" title="Close">X</a></div>');
 +		$("#pageWidthOption").attr("selected","selected");	
 +		$('#pdfbar').css({position:'absolute',top:'5px',right:'5px'});
 +		$('#close').css({display:'block',padding:'0 5px',color:'#BBBBBB','font-weight':'900',height:'18px',background:'transparent'}).click(function(){
 +			hidePDFviewer();
 +		});
 +	}
 +
 +}
 +showPDFviewer.oldCode='';
 +showPDFviewer.lastTitle='';
 +
 +$(document).ready(function(){
- 	if(!$.browser.msie){//doesn't work on IE
++	// doesn't work in IE or public link mode
++	if(!$.browser.msie && !$('#isPublic').val()){
 +		if(location.href.indexOf("files")!=-1) {
 +			if(typeof FileActions!=='undefined'){
 +				FileActions.register('application/pdf','Edit', OC.PERMISSION_READ, '',function(filename){
 +					showPDFviewer($('#dir').val(),filename);
 +				});
 +				FileActions.setDefault('application/pdf','Edit');
 +			}
 +		}
 +	}
 +});
diff --cc apps/gallery/lib/thumbnail.php
index 86b10b3,0000000..11e4b07
mode 100644,000000..100644
--- a/apps/gallery/lib/thumbnail.php
+++ b/apps/gallery/lib/thumbnail.php
@@@ -1,196 -1,0 +1,196 @@@
 +<?php
 +/**
 + * Copyright (c) 2012 Robin Appelman <icewind at owncloud.com>
 + * This file is licensed under the Affero General Public License version 3 or
 + * later.
 + * See the COPYING-README file.
 + */
 +
 +namespace OCA\Gallery;
 +
 +use OC\Files\Filesystem;
 +
 +class Thumbnail {
 +	static private $writeHookCount;
 +
 +	protected $image;
 +	protected $path;
 +	protected $user;
 +	protected $useOriginal = false;
 +
 +	/**
 +	 * @var \OC\Files\View $view
 +	 */
 +	protected $view;
 +
 +	public function __construct($imagePath, $user = null, $square = false) {
 +		if (!\OC\Files\Filesystem::isValidPath($imagePath)) {
 +			return;
 +		}
 +		if (is_null($user)) {
 +			$this->view = \OC\Files\Filesystem::getView();
 +			$this->user = \OCP\USER::getUser();
 +		} else {
 +			$this->view = new \OC\Files\View('/' . $user . '/files');
 +			$this->user = $user;
 +		}
 +		$this->useOriginal = (substr($imagePath, -4) === '.svg' or substr($imagePath, -5) === '.svgz');
 +		if ($this->useOriginal) {
 +			$this->path = $imagePath;
 +		} else {
 +			$galleryDir = \OC_User::getHome($this->user) . '/gallery/' . $this->user . '/';
 +			if (strrpos($imagePath, '.')) {
 +				$extension = substr($imagePath, strrpos($imagePath, '.') + 1);
 +				$image = substr($imagePath, 0, strrpos($imagePath, '.'));
 +			} else {
 +				$extension = '';
 +				$image = $imagePath;
 +			}
 +			if ($square) {
 +				$extension = 'square.' . $extension;
 +			}
 +			$this->path = $galleryDir . $image . '.' . $extension;
 +			if (!file_exists($this->path)) {
- 				self::create($imagePath, $square);
++				$this->create($imagePath, $square);
 +			}
 +		}
 +	}
 +
- 	public function create($imagePath, $square) {
- 		$galleryDir = \OC_User::getHome($this->user) . '/gallery/';
++	private function create($imagePath, $square) {
++		$galleryDir = \OC_User::getHome($this->user) . '/gallery/' . $this->user . '/';
 +		$dir = dirname($imagePath);
 +		if (!is_dir($galleryDir . $dir)) {
 +			mkdir($galleryDir . $dir, 0755, true);
 +		}
 +		if (!$this->view->file_exists($imagePath)) {
 +			return;
 +		}
 +		$this->image = new \OC_Image($this->view->getLocalFile($imagePath));
 +		if ($this->image->valid()) {
 +			$this->image->fixOrientation();
 +			if ($square) {
 +				$this->image->centerCrop(200);
 +			} else {
 +				$this->image->fitIn(400, 200);
 +			}
 +			$this->image->save($this->path);
 +		}
 +	}
 +
 +	public function get() {
 +		if (is_null($this->image)) {
 +			$this->image = new \OC_Image($this->path);
 +		}
 +		return $this->image;
 +	}
 +
 +	public function show() {
 +		if ($this->useOriginal) {
 +			$fp = @$this->view->fopen($this->path, 'rb');
 +			$mtime = $this->view->filemtime($this->path);
 +			$size = $this->view->filesize($this->path);
 +			$mime = $this->view->getMimetype($this->path);
 +		} else {
 +			$fp = @fopen($this->path, 'rb');
 +			$mtime = filemtime($this->path);
 +			$size = filesize($this->path);
 +			$mime = \OC_Helper::getMimetype($this->path);
 +		}
 +		if ($fp) {
 +			\OC_Response::enableCaching();
 +			\OC_Response::setLastModifiedHeader($mtime);
 +			header('Content-Length: ' . $size);
 +			header('Content-Type: ' . $mime);
 +
 +			fpassthru($fp);
 +		} else {
 +			\OC_Response::setStatus(\OC_Response::STATUS_NOT_FOUND);
 +		}
 +	}
 +
 +	static public function removeHook($params) {
 +		$path = $params['path'];
 +		$user = \OCP\USER::getUser();
 +		$galleryDir = \OC_User::getHome($user) . '/gallery/';
 +		$thumbPath = $galleryDir . $path;
 +		if (is_dir($thumbPath)) {
 +			if (file_exists($thumbPath . '.png')) {
 +				unlink($thumbPath . '.png');
 +			}
 +		} else {
 +			if (file_exists($thumbPath)) {
 +				unlink($thumbPath);
 +			}
 +
 +			if (strrpos($path, '.')) {
 +				$extension = substr($path, strrpos($path, '.') + 1);
 +				$image = substr($path, 0, strrpos($path, '.'));
 +			} else {
 +				$extension = '';
 +				$image = $path;
 +			}
 +			$squareThumbPath = $galleryDir . $image . '.square.' . $extension;
 +			if (file_exists($squareThumbPath)) {
 +				unlink($squareThumbPath);
 +			}
 +		}
 +
 +		$parent = dirname($path);
 +		if ($parent !== DIRECTORY_SEPARATOR and $parent !== '' and $parent !== $path) {
 +			self::removeHook(array('path' => $parent));
 +		}
 +	}
 +
 +	static public function writeHook($params) {
 +		self::removeHook($params);
 +		//only create 5 thumbnails max in one request to prevent locking up the request
 +		if (self::$writeHookCount < 5) {
 +			$path = $params['path'];
 +			$mime = \OC\Files\Filesystem::getMimetype($path);
 +			if (substr($mime, 0, 6) === 'image/') {
 +				self::$writeHookCount++;
 +				new Thumbnail($path);
 +			}
 +		}
 +	}
 +}
 +
 +class AlbumThumbnail extends Thumbnail {
 +
 +	public function __construct($imagePath, $user = null, $square = false) {
 +		if (!\OC\Files\Filesystem::isValidPath($imagePath)) {
 +			return;
 +		}
 +		if (is_null($user)) {
 +			$this->view = \OC\Files\Filesystem::getView();
 +			$this->user = \OCP\USER::getUser();
 +		} else {
 +			$this->view = new \OC\Files\View('/' . $user . '/files');
 +			$this->user = $user;
 +		}
 +		$galleryDir = \OC_User::getHome($this->user) . '/gallery/' . $this->user . '/';
 +		$this->path = $galleryDir . $imagePath . '.png';
 +		if (!file_exists($this->path)) {
 +			self::create($imagePath, $square);
 +		}
 +	}
 +
 +	public function create($albumPath, $square = false) {
 +		$albumView = new \OC\Files\View($this->view->getRoot() . $albumPath);
 +		$images = $albumView->searchByMime('image', 10);
 +
 +		$count = min(count($images), 10);
 +		$thumbnail = imagecreatetruecolor($count * 200, 200);
 +		for ($i = 0; $i < $count; $i++) {
 +			$thumb = new Thumbnail($albumPath . $images[$i]['path'], $this->user, true);
 +			$image = $thumb->get();
 +			if ($image && $image->valid()) {
 +				imagecopy($thumbnail, $image->resource(), $i * 200, 0, 0, 0, 200, 200);
 +				$image->destroy();
 +			}
 +		}
 +
 +		imagepng($thumbnail, $this->path);
 +		imagedestroy($thumbnail);
 +	}
 +}
diff --cc apps/media/lib/ampache.php
index 761cd41,0000000..345263f
mode 100644,000000..100644
--- a/apps/media/lib/ampache.php
+++ b/apps/media/lib/ampache.php
@@@ -1,325 -1,0 +1,308 @@@
 +<?php
 +
 +/**
 + * Copyright (c) 2012 Robin Appelman <icewind at owncloud.com>
 + * This file is licensed under the Affero General Public License version 3 or
 + * later.
 + * See the COPYING-README file.
 + */
 +
 +namespace OCA\Media;
 +
 +//implementation of ampache's xml api
 +class Ampache {
 +	/**
 +	 * @var Collection $collection
 +	 */
 +	private $collection;
 +
 +	public function error($code, $msg) {
 +		$tmpl = new \OC_Template('media', 'ampache/error');
 +		$tmpl->assign('code', $code);
 +		$tmpl->assign('msg', $msg);
 +		$tmpl->printPage();
 +		die();
 +	}
 +
 +	/**
 +	 * do the initial handshake
 +	 *
 +	 * @param array $params
 +	 */
 +	public function handshake($params) {
 +		$auth = (isset($params['auth'])) ? $params['auth'] : false;
 +		$user = (isset($params['user'])) ? $params['user'] : false;
 +		$time = (isset($params['timestamp'])) ? $params['timestamp'] : false;
 +		$now = time();
 +		if ($now - $time > (10 * 60)) {
 +			$this->error(400, 'timestamp is more then 10 minutes old');
 +		}
 +		if ($auth and $user and $time) {
 +			$query = \OCP\DB::prepare("SELECT `user_id`, `user_password_sha256` FROM `*PREFIX*media_users` WHERE `user_id`=?");
 +			$result = $query->execute(array($user));
 +			if ($row = $result->fetchRow()) {
 +				$pass = $row['user_password_sha256'];
 +				$key = hash('sha256', $time . $pass);
 +				if ($key == $auth) {
 +					$token = hash('sha256', 'oc_media_' . $key);
 +					$this->collection = new Collection($row['user_id']);
 +					$date = date('c'); //todo proper update/add/clean dates
 +					$songs = $this->collection->getSongCount();
 +					$artists = $this->collection->getArtistCount();
 +					$albums = $this->collection->getAlbumCount();
 +					$query = \OCP\DB::prepare("INSERT INTO `*PREFIX*media_sessions` (`token`, `user_id`, `start`) VALUES (?, ?, now());");
 +					$query->execute(array($token, $user));
- 					$expire = date('c', time() + 600);
++					$expire = date('c', time() + 86400);
 +
 +					$tmpl = new \OC_Template('media', 'ampache/handshake');
 +					$tmpl->assign('token', $token);
 +					$tmpl->assign('date', $date);
 +					$tmpl->assign('songs', $songs);
 +					$tmpl->assign('artists', $artists);
 +					$tmpl->assign('albums', $albums);
 +					$tmpl->assign('expire', $expire);
 +					$tmpl->printPage();
 +					return;
 +				}
 +			}
 +			$this->error(400, 'Invalid login');
 +		} else {
 +			$this->error(400, 'Missing arguments');
 +		}
 +	}
 +
 +	public function ping($params) {
 +		if (isset($params['auth'])) {
 +			if ($this->checkAuth($params['auth'])) {
 +				$this->updateAuth($params['auth']);
 +			} else {
 +				$this->error(401, 'Invalid login');
 +				return;
 +			}
 +		}
 +		$tmpl = new \OC_Template('media', 'ampache/ping');
 +		$tmpl->printPage();
 +	}
 +
 +	public function checkAuth($auth) {
 +		if (is_array($auth)) {
 +			if (isset($auth['auth'])) {
 +				$auth = $auth['auth'];
 +			} else {
 +				return false;
 +			}
 +		}
 +		$CONFIG_DBTYPE = \OCP\Config::getSystemValue("dbtype", "sqlite");
 +		if ($CONFIG_DBTYPE == 'pgsql') {
- 			$interval = ' \'600s\'::interval ';
++			$interval = ' \'86400s\'::interval ';
 +		} else {
- 			$interval = '600';
++			$interval = '86400';
 +		}
 +		//remove old sessions
 +		$query = \OCP\DB::prepare("DELETE FROM `*PREFIX*media_sessions` WHERE `start`<(NOW() - " . $interval . ")");
 +		$query->execute();
 +
 +		$query = \OCP\DB::prepare("SELECT `user_id` FROM `*PREFIX*media_sessions` WHERE `token`=?");
 +		$result = $query->execute(array($auth));
 +		if ($row = $result->fetchRow()) {
 +			$this->collection = new Collection($row['user_id']);
 +			return true;
 +		} else {
 +			return false;
 +		}
 +	}
 +
 +	public function updateAuth($auth) {
 +		$query = \OCP\DB::prepare("UPDATE `*PREFIX*media_sessions` SET `start`=CURRENT_TIMESTAMP WHERE `token`=?");
 +		$query->execute(array($auth));
 +	}
 +
 +	private function printArtists($artists) {
 +		header('Content-Type:  text/xml');
 +		$tmpl = new \OC_Template('media', 'ampache/artists');
 +
 +		foreach ($artists as $artist) {
 +			$artistData = array();
 +			$artistData['albums'] = count($this->collection->getAlbums($artist['artist_id']));
 +			$artistData['songs'] = count($this->collection->getSongs($artist['artist_id']));
 +			$artistData['id'] = $artist['artist_id'];
 +			$artistData['name'] = xmlentities($artist['artist_name']);
 +			$tmpl->append('artists', $artistData);
 +		}
 +		$tmpl->printPage();
 +	}
 +
 +	private function printAlbums($albums, $artistName = false) {
 +		header('Content-Type:  text/xml');
 +		$tmpl = new \OC_Template('media', 'ampache/albums');
 +		foreach ($albums as $album) {
 +			$albumData = array();
 +			if ($artistName) {
 +				$albumData['artist_name'] = xmlentities($artistName);
 +			} else {
 +				$albumData['artist_name'] = xmlentities($this->collection->getArtistName($album['album_artist']));
 +			}
 +			$albumData['songs'] = count($this->collection->getSongs($album['album_artist'], $album['album_id']));
 +			$albumData['id'] = $album['album_id'];
 +			$albumData['name'] = xmlentities($album['album_name']);
 +			$albumData['artist'] = $album['album_artist'];
 +			$tmpl->append('albums', $albumData);
 +		}
 +		$tmpl->printPage();
 +	}
 +
 +	private function printSongs($songs, $artistName = false, $albumName = false) {
 +		header('Content-Type:  text/xml');
 +		$tmpl = new \OC_Template('media', 'ampache/songs');
 +
 +		foreach ($songs as $song) {
 +			$songData = array();
 +			if ($artistName) {
 +				$songData['artist_name'] = xmlentities($artistName);
 +			} else {
 +				$songData['artist_name'] = xmlentities($this->collection->getArtistName($song['song_artist']));
 +			}
 +			if ($albumName) {
 +				$songData['album_name'] = xmlentities($albumName);
 +			} else {
 +				$songData['album_name'] = xmlentities($this->collection->getAlbumName($song['song_album']));
 +			}
 +			$songData['id'] = $song['song_id'];
 +			$songData['name'] = xmlentities($song['song_name']);
 +			$songData['artist'] = $song['song_artist'];
 +			$songData['album'] = $song['song_album'];
 +			$songData['length'] = $song['song_length'];
 +			$songData['track'] = $song['song_track'];
 +			$songData['size'] = $song['song_size'];
 +			$url = \OCP\Util::linkToRemote('ampache') . 'server/xml.server.php/?action=play&song=' . $songData['id'] . '&auth=' . $_GET['auth'];
 +			$songData['url'] = xmlentities($url);
 +			$tmpl->append('songs', $songData);
 +		}
 +		$tmpl->printPage();
 +	}
 +
 +	public function artists($params) {
 +		if (!$this->checkAuth($params)) {
 +			$this->error(401, 'Invalid Login');
 +		}
 +		$filter = isset($params['filter']) ? $params['filter'] : '';
 +		$exact = isset($params['exact']) ? ($params['exact'] == 'true') : false;
 +		$artists = $this->collection->getArtists($filter, $exact);
 +		$this->printArtists($artists);
 +	}
 +
 +	public function artist_songs($params) {
 +		if (!$this->checkAuth($params)) {
 +			$this->error(401, 'Invalid Login');
 +		}
 +		$filter = isset($params['filter']) ? $params['filter'] : '';
 +		$songs = $this->collection->getSongs($filter);
 +		$artist = $this->collection->getArtistName($filter);
 +		$this->printSongs($songs, $artist);
 +	}
 +
 +	public function artist_albums($params) {
 +		if (!$this->checkAuth($params)) {
 +			$this->error(401, 'Invalid Login');
 +		}
 +		$filter = isset($params['filter']) ? $params['filter'] : '';
 +		$albums = $this->collection->getAlbums($filter);
 +		$artist = $this->collection->getArtistName($filter);
 +		$this->printAlbums($albums, $artist);
 +	}
 +
 +	public function albums($params) {
 +		if (!$this->checkAuth($params)) {
 +			$this->error(401, 'Invalid Login');
 +		}
 +		$filter = isset($params['filter']) ? $params['filter'] : '';
 +		$exact = isset($params['exact']) ? ($params['exact'] == 'true') : false;
 +		$albums = $this->collection->getAlbums(0, $filter, $exact);
 +		$this->printAlbums($albums, false);
 +	}
 +
 +	public function album_songs($params) {
 +		if (!$this->checkAuth($params)) {
 +			$this->error(401, 'Invalid Login');
 +		}
 +		$songs = $this->collection->getSongs(0, $params['filter']);
 +		if (count($songs) > 0) {
 +			$artist = $this->collection->getArtistName($songs[0]['song_artist']);
 +		} else {
 +			$artist = '';
 +		}
 +		$this->printSongs($songs, $artist);
 +	}
 +
 +	public function songs($params) {
 +		if (!$this->checkAuth($params)) {
 +			$this->error(401, 'Invalid Login');
 +		}
 +		$filter = isset($params['filter']) ? $params['filter'] : '';
 +		$exact = isset($params['exact']) ? ($params['exact'] == 'true') : false;
 +		$songs = $this->collection->getSongs(0, 0, $filter, $exact);
 +		$this->printSongs($songs);
 +	}
 +
 +	public function song($params) {
 +		if (!$this->checkAuth($params)) {
 +			$this->error(401, 'Invalid Login');
 +		}
 +		if ($song = $this->collection->getSong($params['filter'])) {
 +			$this->printSongs(array($song));
 +		}
 +	}
 +
 +	public function play($params) {
 +		if (!$this->checkAuth($params)) {
 +			$this->error(401, 'Invalid Login');
 +		}
 +		if ($song = $this->collection->getSong($params['song'])) {
 +			\OC_Util::setupFS($song["song_user"]);
 +
 +			header('Content-type: ' . \OC_Filesystem::getMimeType($song['song_path']));
++			header('Content-Length: ' . $song['song_size']);
 +			\OC_Filesystem::readfile($song['song_path']);
 +		}
 +	}
 +
 +	public function url_to_song($params) {
 +		if (!$this->checkAuth($params)) {
 +			$this->error(401, 'Invalid Login');
 +		}
 +		$url = $params['url'];
 +		$songId = substr($url, strrpos($url, 'song=') + 5);
 +		if ($song = $this->collection->getSong($songId)) {
 +			$this->printSongs(array($song));
 +		}
 +	}
 +
 +	public function search_songs($params) {
 +		if (!$this->checkAuth($params)) {
 +			$this->error(401, 'Invalid Login');
 +		}
 +		$filter = isset($params['filter']) ? $params['filter'] : '';
 +		$artists = $this->collection->getArtists($filter);
 +		$albums = $this->collection->getAlbums(0, $filter);
 +		$songs = $this->collection->getSongs(0, 0, $filter);
 +		foreach ($artists as $artist) {
 +			$songs = array_merge($songs, $this->collection->getSongs($artist['artist_id']));
 +		}
 +		foreach ($albums as $album) {
 +			$songs = array_merge($songs, $this->collection->getSongs($album['album_artist'], $album['album_id']));
 +		}
 +		$this->printSongs($songs);
 +	}
 +}
 +
- /**
-  * From http://dk1.php.net/manual/en/function.htmlentities.php#106535
-  */
- function get_xml_entity_at_index_0($CHAR) {
- 	if (!is_string($CHAR[0]) || (strlen($CHAR[0]) > 1)) {
- 		die("function: 'get_xml_entity_at_index_0' requires data type: 'char' (single character). '{$CHAR[0]}' does not match this type.");
- 	}
- 	switch ($CHAR[0]) {
- 		case "'":
- 		case '"':
- 		case '&':
- 		case '<':
- 		case '>':
- 			return htmlspecialchars($CHAR[0], ENT_QUOTES);
- 			break;
- 		default:
- 			return numeric_entity_4_char($CHAR[0]);
- 			break;
- 	}
++function xmlentities($text){
++    mb_regex_encoding("UTF-8");
++    //'&' => "&" must the first enty in the array
++    //to avoid double substitution
++    $repl = array('&' => "&", '"' => """, "'" => "'",
++                  "<" => "<", ">" => ">");
++    foreach ($repl as $search => $replace)
++        $text =  mb_ereg_replace($search, $replace, $text);
++    return($text);
 +}
 +
- function numeric_entity_4_char($char) {
- 	return "&#" . str_pad(ord($char), 3, '0', STR_PAD_LEFT) . ";";
- }
- 
- function xmlentities($string) {
- 	$not_in_list = "A-Z0-9a-z\s_-";
- 	return preg_replace_callback("/[^{$not_in_list}]/", '\OCA\Media\get_xml_entity_at_index_0', $string);
- }
diff --cc apps/media/lib/track.php
index 70d2297,0000000..89d94dd
mode 100644,000000..100644
--- a/apps/media/lib/track.php
+++ b/apps/media/lib/track.php
@@@ -1,197 -1,0 +1,205 @@@
 +<?php
 +
 +/**
 + * @author Victor Dubiniuk
 + * @copyright 2013 Victor Dubiniuk victor.dubiniuk at gmail.com
 + *
 + * This file is licensed under the Affero General Public License version 3 or
 + * later.
 + */
 +
 +namespace OCA\Media;
 + 
 +interface Extractable {
 +	public function getArtist();
 +	public function getAlbum();
 +	public function getTitle();
 +	public function getTrackNumber();
 +	public function getFileSize();
 +	public function getLength();
 +}
 +
 +
 +
 +class Track implements Extractable {
 +	
 +	/**
 +	* Path to the track
 +	*/
 +	protected $trackPath;
 +	
 +	/**
 +	* Array with raw getid3 data
 +	*/
 +	protected $trackData;
 +	
 +	/**
 +	* Preferred tags
 +	*/
 +	protected $tagType = 'id3v2';
 +	
 +	/**
 +	 * Constructor
 +	 *
 +	 * @param $trackData array
 +	 * @param $trackPath string
 +	 */
 +	public function __construct($trackPath) {
 +		$this->trackPath = $trackPath;
 +		$extractor = new Extractor_GetID3();
 +		$this->trackData = $extractor->extract($trackPath);
 +	}
 +	
 +	/**
 +	 * Get all tags as array
 +	 *
 +	 * @param none
 +	 * @return array
 +	 */
 +	public function getTags() {
 +		$tags = array(
 +			'artist' => $this->getArtist(),
 +			'album' => $this->getAlbum(),
 +			'title' => $this->getTitle(),
 +			'size' => $this->getFileSize(),
 +			'track' => $this->getTrackNumber(),
 +			'length' => $this->getLength(),
 +		);
 +		
 +		return $tags;
 +	}
 +	
 +	/**
 +	 * Get artist title
 +	 *
 +	 * @param none
 +	 * @return string
 +	 */
 +	public function getArtist() {
 +		$value = $this->getTagValue('artist');
 +		if (is_array($value)) {
 +			$value = $value[0];
 +		}
 +		if (!$value) {
 +			$value = 'Unknown';
 +		}
 +		return stripslashes($value);
 +	}
 +	
 +	/**
 +	 * Get album title
 +	 *
 +	 * @param none
 +	 * @return string
 +	 */
 +	public function getAlbum(){
 +		$value = $this->getTagValue('album');
 +		if (is_array($value)) {
 +			$value = $value[0];
 +		}
 +		if (!$value) {
 +			$value = 'Unknown';
 +		}
 +		return stripslashes($value);
 +	}
 +	
 +	/**
 +	 * Get track title
 +	 *
 +	 * @param none
 +	 * @return string
 +	 */
 +	public function getTitle(){
 +		$value = $this->getTagValue('title');
 +		if (is_array($value)) {
 +			$value = $value[0];
 +		}
 +		if (!$value) {
 +			$value = basename($this->trackPath);
 +		}
 +		return stripslashes($value);
 +	}
 +	
 +	/**
 +	 * Get track number
 +	 *
 +	 * @param none
 +	 * @return int
 +	 */
 +	public function getTrackNumber(){
 +		$value = $this->getTagValue('track');
 +		if (is_array($value)) {
 +			$value = $value[0];
 +		}
 +		if ($value !== false) {
 +			$value = $this->getTagValue('track_number');
 +			if (is_array($value)) {
 +				$value = $value[0];
 +			}
 +			if ($value !== false && preg_match('|\d+/|', $value)) {
 +				$value = preg_replace('|/.*$|', '', $value);
 +			}
 +		}
 +
 +		return (int) $value;
 +	}
 +	
 +	/**
 +	 * Get file size
 +	 *
 +	 * @param none
 +	 * @return int
 +	 */
 +	public function getFileSize(){
 +		$value = (int) $this->getTagValue('filesize');
 +		return $value;
 +	}
 +	
 +	/**
 +	 * Get track length in seconds
 +	 *
 +	 * @param none
 +	 * @return int
 +	 */
 +	public function getLength(){
 +		$value = (int) $this->getTagValue('playtime_seconds');
 +		return round($value);
 +	}
 +	
 +	/**
 +	 * Get tag value by it's name
 +	 *
 +	 * @param string
 +	 * @return string
 +	 */
 +	protected function getTagValue($tagname) {
 +		$value = false;
 +		if ($this->tagType) {
 +			if (isset($this->trackData[$this->tagType]['comments'][$tagname])) {
 +				$value = $this->trackData[$this->tagType]['comments'][$tagname];
++			} elseif (isset($this->trackData[$this->tagType][$tagname])) {
++				$value = $this->trackData[$this->tagType][$tagname];
 +			}
 +		}
 +		if ($value===false) {
 +			$value = $this->getDefaultTagValue($tagname);
 +		}
 +		return $value;
 +	}
 +	
 +	/**
 +	 * Get default tag value by it's name
 +	 *
 +	 * @param string
 +	 * @return string
 +	 */
 +	protected function getDefaultTagValue($tagname) {
- 		return (isset($this->trackData['comments'][$tagname]) ? $this->trackData['comments'][$tagname] : false);
++		if (isset($this->trackData['comments'][$tagname])) {
++			return $this->trackData['comments'][$tagname];
++		} elseif (isset($this->trackData[$tagname])) {
++			return $this->trackData[$tagname];
++		} else {
++			return false;
++		}
 +	}
 +}
diff --cc apps/media/templates/ampache/albums.php
index d016328,0000000..048052f
mode 100644,000000..100644
--- a/apps/media/templates/ampache/albums.php
+++ b/apps/media/templates/ampache/albums.php
@@@ -1,18 -1,0 +1,18 @@@
 +<?php
 +header('Content-Type: text/xml');
 +print '<?xml version="1.0" encoding="UTF-8" standalone="yes"?>';
 +?>
 +<root>
 +	<?php foreach ($_['albums'] as $album): ?>
 +		<album id='<?php p($album['id'])?>'>
 +			<name><?php print_unescaped($album['name'])?></name>
- 			<artist id='<?php p($album['artist'])?>'><?php p($album['artist_name'])?></artist>
++			<artist id='<?php p($album['artist'])?>'><?php print_unescaped($album['artist_name'])?></artist>
 +			<tracks><?php p($album['songs'])?></tracks>
 +			<rating>0</rating>
 +			<year>0</year>
 +			<disk>1</disk>
 +			<art> </art>
 +			<preciserating>0</preciserating>
 +		</album>
 +	<?php endforeach;?>
 +</root>
diff --cc apps/media/templates/ampache/handshake.php
index ff9d60f,0000000..64de9ce
mode 100644,000000..100644
--- a/apps/media/templates/ampache/handshake.php
+++ b/apps/media/templates/ampache/handshake.php
@@@ -1,18 -1,0 +1,18 @@@
 +<?php
 +header('Content-Type: text/xml');
 +print '<?xml version="1.0" encoding="UTF-8" standalone="yes"?>';
 +?>
 +<root>
 +	<auth><?php p($_['token']);?></auth>
 +	<version>350001</version>
 +	<update><?php p($_['date']);?></update>
 +	<add><?php p($_['date']);?></add>
 +	<clean><?php p($_['date']);?></clean>
 +	<songs><?php p($_['songs']);?></songs>
 +	<artists><?php p($_['artists']);?></artists>
 +	<albums><?php p($_['albums']);?></albums>
- 	<session_length>600</session_length>
++	<session_length>86400</session_length>
 +	<session_expire><?php p($_['expire']);?></session_expire>
 +	<tags>0</tags>
 +	<videos>0</videos>
 +</root>
diff --cc apps/search_lucene/ajax/lucene.php
index 0499548,0000000..53dc1da
mode 100644,000000..100644
--- a/apps/search_lucene/ajax/lucene.php
+++ b/apps/search_lucene/ajax/lucene.php
@@@ -1,83 -1,0 +1,85 @@@
 +<?php
 +
 +OCP\JSON::checkLoggedIn();
 +OCP\JSON::callCheck();
 +OCP\JSON::checkAppEnabled('search_lucene');
 +session_write_close();
 +
++//FIXME refactor this into the lib folder
++
 +function index() {
 +	$fileIds = OCA\Search_Lucene\Indexer::getUnindexed();
 +
 +	$eventSource = new OC_EventSource();
 +	$eventSource->send('count', count($fileIds));
 +
 +	$skippedDirs = explode(';', OCP\Config::getUserValue(OCP\User::getUser(), 'search_lucene', 'skipped_dirs', '.git;.svn;.CVS;.bzr'));
 +
 +	$query = OC_DB::prepare('INSERT INTO `*PREFIX*lucene_status` VALUES (?,?)');
 +
 +	foreach ($fileIds as $id) {
 +		$skipped = false;
 +
 +		try{
 +			//before we start mark the file as error so we know there was a problem when the php execution dies
 +			$result = $query->execute(array($id, 'E'));
 +
 +			$path = OC\Files\Filesystem::getPath($id);
 +			$eventSource->send('indexing', $path);
 +
 +			//clean jobs for indexed file
 +			$param=json_encode(array('path'=>$path,'user'=>OCP\User::getUser()));
 +			$cleanjobquery = OC_DB::prepare('DELETE FROM `*PREFIX*queuedtasks` WHERE `app`=? AND `parameters`=?');
 +			$cleanjobquery->execute(array('search_lucene',$param));
 +
 +			foreach ($skippedDirs as $skippedDir) {
 +				if (strpos($path, '/' . $skippedDir . '/') !== false //contains dir
 +					|| strrpos($path, '/' . $skippedDir) === strlen($path) - (strlen($skippedDir) + 1) // ends with dir
 +				) {
 +					$result = $query->execute(array($id, 'S'));
 +					$skipped = true;
 +					break;
 +				}
 +			}
 +			if (!$skipped) {
 +				if (OCA\Search_Lucene\Indexer::indexFile($path, OCP\User::getUser())) {
 +					$result = $query->execute(array($id, 'I'));
 +				}
 +			}
 +
 +			if (!$result) {
 +				OC_JSON::error(array('message' => 'Could not index file.'));
 +				$eventSource->send('error', $path);
 +			}
- 		} catch (PDOException $e) { //sqlite might report database locked errors when stock filescan is in progress
++		} catch (Exception $e) { //sqlite might report database locked errors when stock filescan is in progress
 +			//this also catches db locked exception that might come up when using sqlite
 +			\OCP\Util::writeLog('search_lucene',
 +				$e->getMessage() . ' Trace:\n' . $e->getTraceAsString(),
 +				\OCP\Util::ERROR);
 +			OC_JSON::error(array('message' => 'Could not index file.'));
 +			$eventSource->send('error', $e->getMessage());
 +			//try to mark the file as new to let it reindex
 +			$query->execute(array($id, 'N')); // Add UI to trigger rescan of files with status 'E'rror?
 +		}
 +	}
 +
 +	$eventSource->send('done', '');
 +	$eventSource->close();
 +}
 +
 +function handleOptimize() {
 +	OCA\Search_Lucene\Lucene::optimizeIndex();
 +}
 +
 +if ($_GET['operation']) {
 +	switch ($_GET['operation']) {
 +		case 'index':
 +			index();
 +			break;
 +		case 'optimize':
 +			handleOptimize();
 +			break;
 +		default:
 +			OCP\JSON::error(array('cause' => 'Unknown operation'));
 +	}
 +}
diff --cc apps/search_lucene/appinfo/database.xml
index 52dc2cf,0000000..12c0cf7
mode 100644,000000..100644
--- a/apps/search_lucene/appinfo/database.xml
+++ b/apps/search_lucene/appinfo/database.xml
@@@ -1,31 -1,0 +1,38 @@@
 +<?xml version="1.0" encoding="ISO-8859-1" ?>
 +<database>
 +	 <name>*dbname*</name>
 +	 <create>true</create>
 +	 <overwrite>false</overwrite>
 +	 <charset>utf8</charset>
 +	 <table>
 +		<name>*dbprefix*lucene_status</name>
 +		<declaration>
 +			<field>
 +				<name>fileid</name>
 +				<type>integer</type>
 +				<default>0</default>
 +				<notnull>true</notnull>
 +				<length>4</length>
 +			</field>
 +			<field>
 +				<name>status</name>
 +				<type>text</type>
 +				<notnull>false</notnull>
 +				<length>1</length>
 +			</field>
 +			<index>
++				<name>status_fileid</name>
++				<primary>true</primary>
++				<field>
++					<name>fileid</name>
++				</field>
++			</index>
++			<index>
 +				<name>status_index</name>
 +				<field>
 +					<name>status</name>
 +				</field>
 +			</index>
 +		</declaration>
 +	</table>
 +</database>
diff --cc apps/search_lucene/appinfo/version
index 79a2734,0000000..5d4294b
mode 100644,000000..100644
--- a/apps/search_lucene/appinfo/version
+++ b/apps/search_lucene/appinfo/version
@@@ -1,1 -1,0 +1,1 @@@
- 0.5.0
++0.5.1
diff --cc apps/search_lucene/lib/indexer.php
index 3a75403,0000000..2615b5a
mode 100644,000000..100644
--- a/apps/search_lucene/lib/indexer.php
+++ b/apps/search_lucene/lib/indexer.php
@@@ -1,245 -1,0 +1,265 @@@
 +<?php
 +
 +namespace OCA\Search_Lucene;
 +
 +use \OC\Files\Filesystem;
 +use \OCP\Util;
 +
 +/**
 + * @author Jörn Dreyer <jfd at butonic.de>
 + */
 +class Indexer {
 +
 +	/**
 +	 * classname which used for hooks handling
 +	 * used as signalclass in OC_Hooks::emit()
 +	 */
 +	const CLASSNAME = 'Indexer';
 +
 +	/**
 +	 * index a file
 +	 *
 +	 * @author Jörn Dreyer <jfd at butonic.de>
 +	 *
 +	 * @param string $path the path of the file
 +	 *
 +	 * @return bool
 +	 */
 +	static public function indexFile($path = '', $user = null) {
 +
 +		if (!Filesystem::isValidPath($path)) {
 +			return;
 +		}
 +		if ($path === '') {
 +			//ignore the empty path element
 +			return false;
 +		}
 +
 +		if (is_null($user)) {
 +			$view = Filesystem::getView();
 +			$user = \OCP\User::getUser();
 +		} else {
 +			$view = new \OC\Files\View('/' . $user . '/files');
 +		}
 +
 +		if ( ! $view ) {
 +			Util::writeLog('search_lucene',
 +				'could not resolve filesystem view',
 +				Util::WARN);
 +			return false;
 +		}
 +
 +		$root = $view->getRoot();
 +		$pk = md5($root . $path);
 +
 +		// the cache already knows mime and other basic stuff
 +		$data = $view->getFileInfo($path);
 +		if (isset($data['mimetype'])) {
 +			$mimetype = $data['mimetype'];
 +			if ('text/html' === $mimetype) {
 +				$doc = \Zend_Search_Lucene_Document_Html::loadHTML($view->file_get_contents($path));
 +			} else if ('application/msword' === $mimetype) {
 +				// FIXME uses ZipArchive ... make compatible with OC\Files\Filesystem
 +				//$doc = Zend_Search_Lucene_Document_Docx::loadDocxFile(OC\Files\Filesystem::file_get_contents($path));
 +
 +				//no special treatment yet
 +				$doc = new \Zend_Search_Lucene_Document();
 +			} else {
 +				$doc = new \Zend_Search_Lucene_Document();
 +			}
 +
 +			// store fscacheid as unique id to lookup by when deleting
 +			$doc->addField(\Zend_Search_Lucene_Field::Keyword('pk', $pk));
 +
 +			// Store document URL to identify it in the search results
 +			$doc->addField(\Zend_Search_Lucene_Field::Text('path', $path));
 +
 +			$doc->addField(\Zend_Search_Lucene_Field::unIndexed('size', $data['size']));
 +
 +			$doc->addField(\Zend_Search_Lucene_Field::unIndexed('mimetype', $mimetype));
 +
 +			self::extractMetadata($doc, $path, $view, $mimetype);
 +
 +			Lucene::updateFile($doc, $path, $user);
 +
 +			return true;
 +
 +		} else {
-             Util::writeLog('search_lucene',
++			Util::writeLog(
++				'search_lucene',
 +				'need mimetype for content extraction',
-                 Util::ERROR);
++				Util::ERROR
++			);
 +			return false;
 +		}
 +	}
 +
 +
 +	/**
 +	 * extract the metadata from a file
 +	 *
 +	 * uses getid3 to extract metadata.
 +	 * if possible also adds content (currently only for plain text files)
 +	 * hint: use OC\Files\Filesystem::getFileInfo($path) to get metadata for the last param
 +	 *
 +	 * @author Jörn Dreyer <jfd at butonic.de>
 +	 *
 +	 * @param Zend_Search_Lucene_Document $doc      to add the metadata to
 +	 * @param string                      $path     path of the file to extract metadata from
 +	 * @param string                      $mimetype depending on the mimetype different extractions are performed
 +	 *
 +	 * @return void
 +	 */
- 	private static function extractMetadata(\Zend_Search_Lucene_Document $doc, $path, \OC\Files\View $view, $mimetype) {
++	private static function extractMetadata(
++		\Zend_Search_Lucene_Document $doc,
++		$path,
++		\OC\Files\View $view,
++		$mimetype
++	) {
 +
 +		$file = $view->getLocalFile($path);
 +		$getID3 = new \getID3();
 +		$getID3->encoding = 'UTF-8';
 +		$data = $getID3->analyze($file);
 +
 +		// TODO index meta information from media files?
 +
 +		//show me what you got
 +		/*foreach ($data as $key => $value) {
 +			Util::writeLog('search_lucene',
 +						'getid3 extracted '.$key.': '.$value,
 +						Util::DEBUG);
 +			if (is_array($value)) {
 +				foreach ($value as $k => $v) {
 +					Util::writeLog('search_lucene',
 +							'  ' . $value .'-' .$k.': '.$v,
 +							Util::DEBUG);
 +				}
 +			}
 +		}*/
 +
 +		// filename _should_ always work, so log if it does not
 +		if (isset($data['filename'])) {
 +			$doc->addField(\Zend_Search_Lucene_Field::Text('filename', $data['filename']));
 +		} else {
-             Util::writeLog('search_lucene',
++			Util::writeLog(
++				'search_lucene',
 +				'failed to extract meta information for ' . $view->getAbsolutePath($path) . ': ' . $data['error']['0'],
-                 Util::WARN);
++				Util::WARN
++			);
 +		}
 +
 +		//content
 +
-         Util::writeLog('search_lucene',
++		Util::writeLog(
++			'search_lucene',
 +			'indexer extracting content for ' . $view->getAbsolutePath($path) . ' (' . $mimetype . ')',
-             Util::DEBUG);
++			Util::DEBUG
++		);
 +
 +		$body = '';
 +
 +		if ('text/plain' === $mimetype) {
 +			$body = $view->file_get_contents($path);
 +		} else if ('text/html' === $mimetype) {
 +			// getid3 does not handle html, but that's not a real error
 +			unset($data['error']);
 +
 +		} else if ('httpd/unix-directory' === $mimetype) {
 +			// getid3 does not handle directories, ignore the error
 +			unset($data['error']);
 +
 +		} else if ('application/pdf' === $mimetype) {
 +			try {
 +				$zendpdf = \Zend_Pdf::parse($view->file_get_contents($path));
 +
 +				//we currently only display the filename, so we only index metadata here
 +				if (isset($zendpdf->properties['Title'])) {
 +					$doc->addField(\Zend_Search_Lucene_Field::UnStored('title', $zendpdf->properties['Title']));
 +				}
 +				if (isset($zendpdf->properties['Author'])) {
 +					$doc->addField(\Zend_Search_Lucene_Field::UnStored('author', $zendpdf->properties['Author']));
 +				}
 +				if (isset($zendpdf->properties['Subject'])) {
 +					$doc->addField(\Zend_Search_Lucene_Field::UnStored('subject', $zendpdf->properties['Subject']));
 +				}
 +				if (isset($zendpdf->properties['Keywords'])) {
 +					$doc->addField(\Zend_Search_Lucene_Field::UnStored('keywords', $zendpdf->properties['Keywords']));
 +				}
 +				//TODO handle PDF 1.6 metadata Zend_Pdf::getMetadata()
 +
 +				//do the content extraction
 +				$pdfParse = new \App_Search_Helper_PdfParser();
 +				$body = $pdfParse->pdf2txt($zendpdf->render());
 +
 +			} catch (Exception $e) {
 +				Util::writeLog('search_lucene',
 +					$e->getMessage() . ' Trace:\n' . $e->getTraceAsString(),
 +					Util::ERROR);
 +			}
 +
 +		}
 +
 +		if ($body != '') {
 +			$doc->addField(\Zend_Search_Lucene_Field::UnStored('body', $body));
 +		}
 +
 +		if (isset($data['error'])) {
-             Util::writeLog('search_lucene',
++			Util::writeLog(
++				'search_lucene',
 +				'failed to extract meta information for ' . $view->getAbsolutePath($path) . ': ' . $data['error']['0'],
-                 Util::WARN);
++				Util::WARN
++			);
 +
 +			return;
 +		}
 +	}
 +
 +	/**
 +	 * get the list of all unindexed files for the user
 +	 *
 +	 * @return array
 +	 */
 +	static public function getUnindexed() {
 +		$files = array();
 +		$absoluteRoot = Filesystem::getView()->getAbsolutePath('/');
 +		$mounts = \OC\Files\Mount::findIn($absoluteRoot);
 +		$mount = \OC\Files\Mount::find($absoluteRoot);
 +		if (!in_array($mount, $mounts)) {
 +			$mounts[] = $mount;
 +		}
 +
- 		$query = \OC_DB::prepare('SELECT `*PREFIX*filecache`.`fileid`'
- 			. ' FROM `*PREFIX*filecache`'
- 			. ' LEFT JOIN `*PREFIX*lucene_status`'
- 			. ' ON `*PREFIX*filecache`.`fileid` = `*PREFIX*lucene_status`.`fileid`'
- 			. ' WHERE `storage` = ?'
- 			. ' AND `status` is null OR `status` = "N"');
++		$query = \OC_DB::prepare('
++			SELECT `*PREFIX*filecache`.`fileid`
++			FROM `*PREFIX*filecache`
++			LEFT JOIN `*PREFIX*lucene_status`
++			ON `*PREFIX*filecache`.`fileid` = `*PREFIX*lucene_status`.`fileid`
++			WHERE `storage` = ?
++			AND `status` is null OR `status` = \'N\'
++		');
 +
 +		foreach ($mounts as $mount) {
 +			$storage = $mount->getStorage();
 +			//only index local files for now
 +			if ($storage instanceof \OC\Files\Storage\Local) {
 +				$cache = $storage->getCache();
 +				$numericId = $cache->getNumericStorageId();
 +
 +				$result = $query->execute(array($numericId));
- 				if (!$result) {
++				if (\OC_DB::isError($result)) {
++					Util::writeLog(
++						'search_lucene',
++						'failed to find unindexed files: '.\OC_DB::getErrorMessage($result),
++						Util::WARN
++					);
 +					return false;
 +				}
 +				while ($row = $result->fetchRow()) {
 +					$files[] = $row['fileid'];
 +				}
 +			}
 +		}
 +		return $files;
 +	}
 +
 +}
diff --cc apps/search_lucene/lib/lucene.php
index f3a5822,0000000..dd00320
mode 100644,000000..100644
--- a/apps/search_lucene/lib/lucene.php
+++ b/apps/search_lucene/lib/lucene.php
@@@ -1,293 -1,0 +1,316 @@@
 +<?php
 +
 +namespace OCA\Search_Lucene;
 +
 +use \OC\Files\Filesystem;
 +use \OCP\User;
 +use \OCP\Util;
 +
 +/**
 + * @author Jörn Dreyer <jfd at butonic.de>
 + */
 +class Lucene extends \OC_Search_Provider {
 +
 +	/**
 +	 * classname which used for hooks handling
 +	 * used as signalclass in OC_Hooks::emit()
 +	 */
 +	const CLASSNAME = 'Lucene';
 +
 +	/**
 +	 * opens or creates the users lucene index
 +	 * 
 +	 * stores the index in <datadirectory>/<user>/lucene_index
 +	 * 
 +	 * @author Jörn Dreyer <jfd at butonic.de>
 +	 *
 +	 * @return Zend_Search_Lucene_Interface 
 +	 */
 +	public static function openOrCreate($user = null) {
 +
 +		if ($user == null) {
 +			$user = User::getUser();
 +		}
 +
 +		try {
 +			
 +			\Zend_Search_Lucene_Analysis_Analyzer::setDefault(
 +				new \Zend_Search_Lucene_Analysis_Analyzer_Common_TextNum_CaseInsensitive()
 +			); //let lucene search for numbers as well as words
 +			
 +			// Create index
 +			//$ocFilesystemView = OC_App::getStorage('search_lucene'); // encrypt the index on logout, decrypt on login
 +
 +			$indexUrl = \OC_User::getHome($user) . '/lucene_index';
 +			if (file_exists($indexUrl)) {
 +				$index = \Zend_Search_Lucene::open($indexUrl);
 +			} else {
 +				$index = \Zend_Search_Lucene::create($indexUrl);
 +				//todo index all user files
- 				
 +			}
 +		} catch ( Exception $e ) {
-             Util::writeLog('search_lucene',
- 					$e->getMessage().' Trace:\n'.$e->getTraceAsString(),
-                 	Util::ERROR);
++			Util::writeLog(
++				'search_lucene',
++				$e->getMessage().' Trace:\n'.$e->getTraceAsString(),
++				Util::ERROR
++			);
 +			return null;
 +		}
 +		
 +
 +		return $index;
 +	}
 +
 +	/**
 +	 * optimizes the lucene index
 +	 * 
 +	 * 
 +	 * @author Jörn Dreyer <jfd at butonic.de>
 +	 * 
 +	 * @param Zend_Search_Lucene_Interface $index an optional index
 +	 * 
 +	 * @return void
 +	 */
- 	static public function optimizeIndex(\Zend_Search_Lucene_Interface $index = null) {
++	static public function optimizeIndex(
++		\Zend_Search_Lucene_Interface $index = null
++	) {
 +
 +		if ($index === null) {
 +			$index = self::openOrCreate();
 +		}
 +
- 		Util::writeLog('search_lucene',
- 					   'optimizing index ',
- 						Util::DEBUG);
++		Util::writeLog(
++			'search_lucene',
++			'optimizing index ',
++			Util::DEBUG
++		);
 +
 +		$index->optimize();
 +
 +	}
 +
 +	/**
 +	 * upates a file in the lucene index
 +	 * 
 +	 * 1. the file is deleted from the index
 +	 * 2. the file is readded to the index
 +	 * 3. the file is marked as index in the status table
 +	 * 
 +	 * @author Jörn Dreyer <jfd at butonic.de>
 +	 * 
 +	 * @param Zend_Search_Lucene_Document $doc  the document to store for the path
 +	 * @param string                      $path path to the document to update
 +	 * 
 +	 * @return void
 +	 */
- 	static public function updateFile(\Zend_Search_Lucene_Document $doc,
- 									  $path = '',
- 									  $user = null,
- 									  \Zend_Search_Lucene_Interface $index = null) {
++	static public function updateFile(
++		\Zend_Search_Lucene_Document $doc,
++		$path = '',
++		$user = null,
++		\Zend_Search_Lucene_Interface $index = null
++	) {
 +
 +		if ($index === null) {
 +			$index = self::openOrCreate($user);
 +		}
 +		
 +		// TODO profile perfomance for searching before adding to index
 +		self::deleteFile($path, $user, $index);
 +
- 		Util::writeLog('search_lucene',
- 					   'adding ' . $path ,
- 					   Util::DEBUG);
++		Util::writeLog(
++			'search_lucene',
++			'adding ' . $path ,
++			Util::DEBUG
++		);
 +		
 +		// Add document to the index
 +		$index->addDocument($doc);
 +
 +		$index->commit();
 +
 +	}
 +
 +	/**
 +	 * removes a file frome the lucene index
 +	 * 
 +	 * @author Jörn Dreyer <jfd at butonic.de>
 +	 * 
 +	 * @param string                       $path  path to the document to remove from the index
 +	 * @param Zend_Search_Lucene_Interface $index optional can be passed ro reuse an existing instance
 +	 * 
 +	 * @return void
 +	 */
- 	static public function deleteFile($path, $user = null, \Zend_Search_Lucene_Interface $index = null) {
++	static public function deleteFile(
++		$path,
++		$user = null,
++		\Zend_Search_Lucene_Interface $index = null
++	) {
 +
 +		if ( $path === '' ) {
 +			//ignore the empty path element
 +			return;
 +		}
 +
 +		if (is_null($user)) {
 +			$view = Filesystem::getView();
 +			$user = \OCP\User::getUser();
 +		} else {
 +			$view = new \OC\Files\View('/' . $user . '/files');
 +		}
 +
 +		if ( ! $view ) {
- 			Util::writeLog('search_lucene',
++			Util::writeLog(
++				'search_lucene',
 +				'could not resolve filesystem view',
- 				Util::WARN);
++				Util::WARN
++			);
 +			return false;
 +		}
 +
 +		if ($index === null) {
 +			$index = self::openOrCreate($user);
 +		}
 +
 +		$root= $view->getRoot();
 +		$pk = md5($root.$path);
 +
- 		Util::writeLog('search_lucene',
- 					  'searching hits for pk:' . $pk,
- 					  Util::DEBUG);
++		Util::writeLog(
++			'search_lucene',
++			'searching hits for pk:' . $pk,
++			Util::DEBUG
++		);
 +
 +
 +		$hits = $index->find( 'pk:' . $pk ); //id would be internal to lucene
 +
- 		Util::writeLog('search_lucene',
- 					  'found ' . count($hits) . ' hits ',
- 					  Util::DEBUG);
++		Util::writeLog(
++			'search_lucene',
++			'found ' . count($hits) . ' hits ',
++			Util::DEBUG
++		);
 +
 +		foreach ($hits as $hit) {
- 			Util::writeLog('search_lucene',
- 						'removing ' . $hit->id . ':' . $hit->path . ' from index',
- 						Util::DEBUG);
++			Util::writeLog(
++				'search_lucene',
++				'removing ' . $hit->id . ':' . $hit->path . ' from index',
++				Util::DEBUG
++			);
 +			$index->delete($hit);
 +		}
 +	}
 +
 +	/**
 +	 * performs a search on the users index
 +	 * 
 +	 * @author Jörn Dreyer <jfd at butonic.de>
 +	 * 
 +	 * @param string $query lucene search query
 +	 * @return array of OC_Search_Result
 +	 */
 +	public function search($query){
 +		$results=array();
 +		if ( $query !== null ) {
 +			// * query * kills performance for bigger indexes
 +			// query * works ok
 +			// query is still best
 +			//FIXME emulates the old search but breaks all the nice lucene search query options
 +			//$query = '*' . $query . '*';
 +			//if (strpos($query, '*')===false) {
 +			//	$query = $query.='*'; // append query *, works ok
 +			//	TODO add end user guide for search terms ... 
 +			//}
 +			try {
 +				$index = self::openOrCreate(); 
 +				//default is 3, 0 needed to keep current search behaviour
 +				//Zend_Search_Lucene_Search_Query_Wildcard::setMinPrefixLength(0); 
 +				
 +				//$term  = new Zend_Search_Lucene_Index_Term($query);
 +				//$query = new Zend_Search_Lucene_Search_Query_Term($term);
 +				
 +				$hits = $index->find($query);
 +
 +				//limit results. we cant show more than ~30 anyway. TODO use paging later
 +				for ($i = 0; $i < 30 && $i < count($hits); $i++) {
 +					$results[] = self::asOCSearchResult($hits[$i]);
 +				}
 +
 +			} catch ( Exception $e ) {
- 				Util::writeLog('search_lucene',
- 							$e->getMessage().' Trace:\n'.$e->getTraceAsString(),
- 							Util::ERROR);
++				Util::writeLog(
++					'search_lucene',
++					$e->getMessage().' Trace:\n'.$e->getTraceAsString(),
++					Util::ERROR
++				);
 +			}
 +
 +		}
 +		return $results;
 +	}
 +
 +	/**
 +	 * converts a zend lucene search object to a OC_SearchResult
 +	 *
 +	 * Example:
 +	 * 
 +	 * Text | Some Document.txt
 +	 *      | /path/to/file, 148kb, Score: 0.55
 +	 * 
 +	 * @author Jörn Dreyer <jfd at butonic.de>
 +	 *
 +	 * @param Zend_Search_Lucene_Search_QueryHit $hit The Lucene Search Result
 +	 * @return OC_Search_Result an OC_Search_Result
 +	 */
 +	private static function asOCSearchResult(\Zend_Search_Lucene_Search_QueryHit $hit) {
 +
 +		$mimeBase = self::baseTypeOf($hit->mimetype);
 +
 +		switch($mimeBase){
 +			case 'audio':
 +				$type='Music';
 +				break;
 +			case 'text':
 +				$type='Text';
 +				break;
 +			case 'image':
 +				$type='Images';
 +				break;
 +			default:
 +				if ($hit->mimetype=='application/xml') {
 +					$type='Text';
 +				} else {
 +					$type='Files';
 +				}
 +		}
 +
 +		switch ($hit->mimetype) {
 +			case 'httpd/unix-directory':
 +				$url = Util::linkTo('files', 'index.php') . '?dir='.$hit->path;
 +				break;
 +			default:
 +				$url = \OC::getRouter()->generate('download', array('file'=>$hit->path));
 +		}
 +		
 +		return new \OC_Search_Result(
- 				basename($hit->path),
- 				dirname($hit->path)
- 					. ', ' . \OC_Helper::humanFileSize($hit->size)
- 					. ', Score: ' . number_format($hit->score, 2),
- 				$url,
- 				$type
++			basename($hit->path),
++			dirname($hit->path)
++				. ', ' . \OC_Helper::humanFileSize($hit->size)
++				. ', Score: ' . number_format($hit->score, 2),
++			$url,
++			$type
 +		);
 +	}
 +
 +	/**
 +	 * get the base type of a mimetype string
 +	 * 
 +	 * returns 'text' for 'text/plain'
 +	 * 
 +	 * @author Jörn Dreyer <jfd at butonic.de>
 +	 * 
 +	 * @param string $mimetype mimetype
 +	 * @return string basetype 
 +	 */
 +	public static function baseTypeOf($mimetype) {
 +		return substr($mimetype, 0, strpos($mimetype, '/'));
 +	}
 +
 +}
diff --cc apps/updater/js/controllers.js
index ea44be8,0000000..2549997
mode 100644,000000..100644
--- a/apps/updater/js/controllers.js
+++ b/apps/updater/js/controllers.js
@@@ -1,59 -1,0 +1,59 @@@
 +function indexCtrl($scope) {
 +
 +}
 +;
 +
 +function updateCtrl($scope, $routeParams) {
 +	$scope.update = function() {
 +		$('#upd-progress').show();
 +		$('#updater-start').hide();
 +		var updateEventSource = new OC.EventSource(OC.filePath('updater', 'ajax', 'update.php'));
 +		updateEventSource.listen('success', function(message) {
 +			$('<span></span>').append(message).append('<br />').appendTo($('#upd-progress'));
 +		});
 +		updateEventSource.listen('error', function(message) {
 +			$('<span></span>').addClass('error').append(message).append('<br />').appendTo($('#upd-progress'));
- 			message .= 'Please fix this and retry.';
++			message = message + 'Please fix this and retry.';
 +			$('<span></span>').addClass('error').append(message).append('<br />').appendTo($('#upd-progress'));
 +			updateEventSource.close();
 +		});
 +		updateEventSource.listen('failure', function(message) {
 +			$('<span></span>').addClass('error').append(message).append('<br />').appendTo($('#upd-progress'));
 +			$('<span></span>')
 +					.addClass('error bold')
 +					.append('<br />')
 +					.append(t('updater', 'The update was unsuccessful. Please report this issue to the <a href="https://github.com/owncloud/apps/issues" target="_blank">ownCloud community</a>.'))
 +					.appendTo($('#upd-progress'));
 +		});
 +		updateEventSource.listen('done', function(message) {
 +			$('<span></span>').addClass('bold').append('<br />').append('<a href="' + OC.webroot + '">' + OC.webroot + '</a>').appendTo($('#upd-progress'));
 +		});
 +	};
 +}
 +;
 +
 +function backupCtrl($scope, $http) {
 +	$http.get(OC.filePath('updater', 'ajax', 'backup/list.php'), {headers: {'requesttoken': oc_requesttoken}})
 +			.success(function(data) {
 +		$scope.entries = data.data;
 +	});
 +
 +	$scope.doDelete = function(name) {
 +		$http.get(OC.filePath('updater', 'ajax', 'backup/delete.php'), {
 +			headers: {'requesttoken': oc_requesttoken},
 +			params: {'filename': name}
 +		}).success(function(data) {
 +			$http.get(OC.filePath('updater', 'ajax', 'backup/list.php'), {headers: {'requesttoken': oc_requesttoken}})
 +					.success(function(data) {
 +				$scope.entries = data.data;
 +			});
 +		});
 +	}
 +	$scope.doDownload = function(name) {
 +		window.open(OC.filePath('updater', 'ajax', 'backup/download.php')
 +				+ '?requesttoken=' + oc_requesttoken
 +				+ '&filename=' + name
 +				);
 +	}
 +}
 +;
diff --cc core/doc/admin/_sources/configuration/auth_ldap.txt
index 217faf7,0000000..580266c
mode 100644,000000..100644
--- a/core/doc/admin/_sources/configuration/auth_ldap.txt
+++ b/core/doc/admin/_sources/configuration/auth_ldap.txt
@@@ -1,463 -1,0 +1,464 @@@
 +User Authentication with LDAP
 +=============================
 +
 +ownCloud ships an LDAP backend, which allows full use of ownCloud for user
 +logging in with LDAP credentials including:
 +
 +* LDAP group support
 +* File sharing with users and groups
 +* Access via WebDAV and of course ownCloud Desktop Client
 +* Versioning, external Storages and all other ownCloud Goodies
 +
 +To connect to an LDAP server the configuration needs to be set up properly.
 +Once the LDAP backend is activated (Settings→Apps, choose **LDAP user and group
 +backend**, click on **Enable**) the configuration can be found on
 +Settings→Admin. Read on for a detailed description of the configuration fields.
 +
 +Basic Settings
 +--------------
 +
 +The basic settings are all you need. However, if you have a larger directory,
 +custom requirements or need to connect to Active Directory (AD) you want to have
 +a look on the advanced settings afterwards. The basic part allows you to set up
 +a working connection to your LDAP server and use it with ownCloud.
 +
 +.. figure:: ../images/ldap-basic-settings-oc5.png
 +
 +   LDAP Basic Settings
 +
 +Note that a hint will be shown on the right hand side, when hovering with the
 +mouse over an input field. This gives you more context information while
 +filling out the settings.
 +
 +Settings Details
 +~~~~~~~~~~~~~~~~
 +
 +Server configuration:
 +  ownCloud can be configured to connect to multiple LDAP servers. Using this
 +  control you can pick a configuration you want to edit or add a new one. The
 +  button **Delete Configuration** deletes the current configuration.
 +
 +  * *Example: 1. Server*
 +
 +Host:
 +  The host name of the LDAP server. It can also be a **ldaps://** URI, for
 +  instance.
 +
 +  * Example: *directory.my-company.com*
 +
 +Base DN:
 +  The base DN of LDAP, from where all users and groups can be reached.
 +  Separated Base DNs for users and groups can be set in the Advanced
 +  tab. Nevertheless, this field is mandatory.
 +
 +  * Example: *dc=my-company,dc=com*
 +
 +User DN:
 +  The name as DN of a user who is able to do searches in the LDAP
 +  directory. Let it empty for anonymous access. It is recommended to have a
 +  special system user for ownCloud.
 +
 +  * Example: *uid=owncloudsystemuser,cn=sysusers,dc=my-company,dc=com*
 +
 +Password:
 +  The password for the user given above. Empty for anonymous access.
 +
 +User Login Filter:
 +  The filter to use when a users tries to login. Use **%uid** as placeholder
 +  for the user name. Note, that login applies this filter only, but not User
 +  List Filter. This may change in future.
 +
 +  * Example (allows login with user name and email address): *(|(uid=%uid)(email=$uid))*
 +
 +User List Filter:
 +  The filter to use when a search for users will be executed.
 +
 +  * Example: *objectClass=posixAccount*
 +
 +Group Filter:
 +  The filter to use when a search for groups will be executed. In
 +  case you do not want to use LDAP groups in ownCloud, leave it empty.
 +
 +  * Example: *objectClass=posixGroup*
 +
 +Advanced Settings
 +-----------------
 +
 +In the LDAP Advanced settings section you can define options, that are less
 +common to set. They are not needed for a working connection, unless you use a
 +non-standard Port, e.g. It can also have a positive effect on the performance
 +to specify distinguished bases for user and group searches.
 +
 +The Advanced Settings are structured into three parts:
 +* Connection Settings
 +* Directory Settings
 +* Special Attributes
 +
 +.. figure:: ../images/ldap-advanced-settings-oc5.png
 +
 +   LDAP Advanced Settings
 +
 +Connection Settings
 +~~~~~~~~~~~~~~~~~~~
 +
 +.. figure:: ../images/ldap-advanced-settings-connection-settings-oc5.png
 +
 +   LDAP Advanced Settings, section Connection Settings
 +
 +Configuration Active:
 +  Enables or Disables the current configuration. Disabled configuration will not
 +  connect to the LDAP server.
 +
 +  * Example: *[X]*
 +
 +Port:
 +  The port on which to connect to the LDAP server.
 +
 +  * Example: *389*
 +
 +Backup (Replica) Host:
 +  A backup server can be defined here. ownCloud tries to connect to the backup
 +  server automatically, when the main host (as specified in basic settings)
 +  cannot be reached. It is import that the backup server is a replica of the
 +  main server, because the object UUIDs must match.
 +
 +  * Example: *directory2.my-company.com*
 +
 +Backup (Replica) Port:
 +  The port on which to connect to the backup LDAP server. If no port is given,
 +  but a host, then the main port (as specified above) will be used.
 +
 +  * Example: *389*
 +
 +Disable Main Server:
 +  You can manually override the main server and make ownCloud only connect to
 +  the backup server. It may be handy for planned downtimes.
 +
 +  * Example: *[ ]*
 +
 +Use TLS:
 +  Whether to use TLS encrypted connection to the LDAP server.  This will be
 +  ignored when "ldaps://" protocol is specified in the host entries.
 +
 +  * Example: *[ ]*
 +
 +Case insensitive LDAP server (Windows):
 +  Whether the LDAP server is running on a Windows Host
 +
 +  * Example: *[ ]*
 +
 +Turn off SSL certificate validation:
 +  Turns of check of valid SSL certificates. Use it – if needed –
 +  for testing, only!
 +
 +  * Example: *[ ]*
 +
 +Cache Time-To-Live:
 +  A cache is introduced to avoid unnecessary LDAP traffic,
 +  for example lookups check whether the users exists on every page request or
 +  WebDAV interaction. It is also supposed to speed up the Admin → User page or
 +  list of users to share with, once it is populated. Saving the configuration
 +  empties the cache (changes are not necessary). The time is given in seconds.
 +
 +  Note that almost every PHP request would require to build up a new connection
 +  to the LDAP server. If you require a most up-to-dateness it is recommended not
 +  to totally switch off the cache, but define a minimum life time of 15s.
 +
 +  * Example (10 min): *600*
 +
 +Directory Settings
 +~~~~~~~~~~~~~~~~~~~
 +
 +.. figure:: ../images/ldap-advanced-settings-directory-settings-oc5.png
 +
 +   LDAP Advanced Settings, section Directory Settings
 +
 +User Display Name Field:
 +  The attribute that should be used as display name in ownCloud. Prior to
 +  ownCloud 5 it was used as internal user name. This is not the case anymore.
 +  It also means that display names are not permanent in ownCloud, i.e. if the
 +  attribute's value changes in LDAP, it changes in ownCloud too. Display names
 +  to not need to be unique, but you rather want to specify a more or less unique
 +  attribute here to avoid confusion.
 +
 +  *  Example: *displayName*
 +
 +Base User Tree:
 +  The base DN of LDAP, from where all users can be reached. It needs to be given
 +  completely despite to the Base DN from the Basic settings. You can specifiy
 +  multiple base trees, one in each line.
 +
 +  * Example:
 +
 +    | *cn=programmers,dc=my-company,dc=com*
 +    | *cn=designers,dc=my-company,dc=com*
 +
 +User Search Attributes:
 +  These attributes are used when a search for users with a search string is
 +  done. This happens, for instance, in the share dialogue. By default the user
 +  display name attribute as specified above is being used. Multiple attributes
 +  can be given, one in each line.
 +
 +  * Example:
 +
 +    | *displayName*
 +    | *mail*
 +
 +Group Display Name Field:
 +  The attribute that should be used as ownCloud group name. ownCloud allows a
 +  limited set of characters (a-zA-Z0-9.-_@), every other character will be
 +  replaced in ownCloud. Once a group name is assigned, it will not be changed,
 +  i.e. changing this value will only have effect to new LDAP groups.
 +
 +  * Example: *cn*
 +
 +Base Group Tree:
 +  The base DN of LDAP, from where all groups can be reached.
 +  It needs to be given completely despite to the Base DN from the Basic
 +  settings. You can specifiy multiple base trees, one in each line.
 +
 +  * Example:
 +
 +    | *cn=barcelona,dc=my-company,dc=com*
 +    | *cn=madrid,dc=my-company,dc=com*
 +
 +Group Search Attributes:
 +  These attributes are used when a search for groups with a search string is
 +  done. This happens, for instance, in the share dialogue. By default the group
 +  display name attribute as specified above is being used. Multiple attributes
 +  can be given, one in each line.
 +
 +  * Example:
 +
 +    | *cn*
 +    | *description*
 +
 +Group Member association:
 +  The attribute that is used to indicate group memberships, i.e. the attribute
 +  used by LDAP groups to refer to their users.
 +
 +  * Example: *uniquemember*
 +
 +Special Attributes
 +~~~~~~~~~~~~~~~~~~
 +
 +.. figure:: ../images/ldap-advanced-settings-special-attributes-oc5.png
 +
 +   LDAP Advanced Settings, section Special Attributes
 +
 +Quota Field:
 +  ownCloud can read an LDAP attribute and set the user quota according to its
 +  value. Specify the attribute here, otherwise keep it empty. The attribute
 +  shall return human readable values, e.g. "2 GB".
 +
 +  * Example: *ownCloudQuota*
 +
 +Quota Default:
 +  Override ownCloud default quota for LDAP users who do not
 +  have a quota set in the attribute given above.
 +
 +  * Example: *15 GB*
 +
 +Email Field:
 +  ownCloud can read an LDAP attribute and set the user email
 +  there from. Specify the attribute here, otherwise keep it empty.
 +
 +  * Example: *mail*
 +
 +User Home Folder Naming Rule:
 +  By default, the ownCloud creates the user
 +  directory, where all files and meta data are kept, according to the ownCloud
 +  user name. You may want to override this setting and name it after an
 +  attribute's value. The attribute given can also return an absolute path, e.g.
 +  ``/mnt/storage43/alice``. Leave it empty for default behavior.
 +
 +  * Example: *cn*
 +
 +Expert Settings (>= ownCloud 5.0.7)
 +---------------------------------------
 +
 +.. figure:: ../images/ldap-expert-settings-oc5.png
 +
 +In the Expert Settings fundamental behavior can be adjusted to your needs. The
 +configuration should be done before starting production use or when testing the
 +installation.
 +
 +Internal Username:
 +  The internal username is the identifier in ownCloud for LDAP users. By default
 +  it will be created from the UUID attribute. By using the UUID attribute it is
 +  made sure that the username is unique and characters do not need to be
 +  converted. The internal username has the restriction that only these
 +  characters are allowed: [\a-\zA-\Z0-\9_. at -]. Other characters are replaced with
 +  their ASCII correspondence or are simply omitted.
 +
 +  The LDAP backend ensures that there are no duplicate internal usernames in
 +  ownCloud, i.e. that it is checking all other activated user backends
 +  (including local ownCloud users). On collisions a random number (between 1000
 +  and 9999) will be attached to the retrieved value. For example, if "alice"
 +  exists, the next username may be "alice_1337".
 +
 +  The internal username is also the default name for the user home folder in
 +  ownCloud. It is also a part of remote URLs, for instance for all \*DAV services.
 +  With this setting the default behaviour can be overriden. To achieve a similar
 +  behaviour as before ownCloud 5 enter the user display name attribute in the
 +  following field.
 +
 +  Leave it empty for default behaviour. Changes will have effect only on newly
 +  mapped (added) LDAP users.
 +
 +  * Example: *uid*
 +
 +Override UUID detection
 +  By default, ownCloud autodetects the UUID attribute. The UUID attribute is
 +  used to doubtlessly identify LDAP users and groups. Also, the internal
 +  username will be created based on the UUID, if not specified otherwise above.
 +
 +  You can override the setting and pass an attribute of your choice. You must
 +  make sure that the attribute of your choice can be fetched for both users and
 +  groups and it is unique. Leave it empty for default behaviour. Changes will
 +  have effect only on newly mapped (added) LDAP users and groups. It also will
 +  have effect when a user's or group's DN changes and an old UUID was cached: It
 +  will result in a new user. Because of this, the setting should be applied
 +  before putting ownCloud in production use and cleaning the bindings
 +  (see below).
 +
 +  The default behaviour does not differ from ownCloud 4.5. You do not want to
 +  change this after upgrading from ownCloud 4.5 unless you update the mapping
 +  tables yourself.
 +
 +  * Example: *cn*
 +
 +Username-LDAP User Mapping
 +  ownCloud uses the usernames as key to store and assign data. In order to
 +  precisely identify and recognize users, each LDAP user will have a internal
 +  username in ownCloud. This requires a mapping from ownCloud username to LDAP
 +  user. The created username is mapped to the UUID of the LDAP user.
 +  Additionally the DN is cached as well to reduce LDAP interaction, but it is
 +  not used for identification. If the DN changes, the change will be detected by
 +  ownCloud by checking the UUID value.
 +
 +  The same is valid for groups.
 +
 +  The internal ownCloud name is used all over in ownCloud. Clearing the Mappings
 +  will have leftovers everywhere. Do never clear the mappings
 +  in a production environment. Only clear mappings in a testing or experimental
 +  stage.
 +
 +  **Clearing the Mappings is not configuration sensitive, it affects all LDAP
 +  configurations!**
 +
 +
 +
 +Testing the configuration
 +-------------------------
 +
 +In this version we introduced the **Test Configuration** button on the bottom
 +of the LDAP settings section. It will always check the values as currently
 +given in the input fields. You do not need to save before testing. By clicking
 +on the button, ownCloud will try to bind to the ownCloud server with the
 +settings currently given in the input fields. The response will look like this:
 +
 +.. figure:: ../images/ldap-settings-invalid-oc45.png
 +
 +   Failure
 +
 +In case the configuration fails, you can see details in ownCloud's log, which
 +is in the data directory and called **owncloud.log** or on the bottom the
 +**Settings →  Admin page**. Unfortunately it requires a reload – sorry for the
 +inconvenience.
 +
 +.. figure:: ../images/ldap-settings-valid-oc45.png
 +
 +   Success
 +
 +In this case, Save the settings. You can check if the users and groups are
 +fetched correctly on the Settings → Users page.
 +
 +Troubleshooting, Tips and Tricks
 +--------------------------------
 +
 +SSL Certificate Verification (LDAPS, TLS)
 +-----------------------------------------
 +
 +A common mistake with SSL certificates is that they may not be known to PHP.
 +If you have trouble with certificate validation make sure that
 +
 +* you have the certificate of the server installed on the ownCloud server
 +* the certificate is announced in the system's LDAP configuration file (usually
 +  */etc/ldap/ldap.conf* on Linux, *C:\\openldap\\sysconf\\ldap.conf* or
 +  *C:\\ldap.conf* on Windows) using a **TLS_CACERT /path/to/cert** line.
 +* Using LDAPS, also make sure that the port is correctly configured (by default
 +  686)
 +
 +Microsoft Active Directory
 +--------------------------
 +
 +In case you want to connect to a Windows AD, you must change some values in the Advanced tab.
 +
++* The default login filter will not work with AD. Use “samaccountname=%uid” instead.
 +* The default in User Display Name Field will not work with Active Directory.
 +* The Group Member association must be set to “member (AD)”
 +* Check Case insensitive LDAP server (Windows)
 +
 +Duplicating Server Configurations
 +---------------------------------
 +
 +In case you have a working configuration and want to create a similar one or
 +"snapshot" configurations before modifying them you can do the following:
 +
 +#. Go to the **LDAP Basic** tab
 +#. On **Server Configuration** choose *Add Server Configuration*
 +#. Answer the question *Take over settings from recent server configuration?*
 +   with *yes*.
 +#. (optional) Switch to **Advanced** tab and uncheck **Configuration Active**
 +   in the *Connection Settings*, so the new configuration is not used on Save
 +#. Click on **Save**
 +
 +Now you can modify the configuration and enable it if you wish.
 +
 +ownCloud LDAP Internals
 +-----------------------
 +
 +Some parts of how the LDAP backend works are described here. May it be helpful.
 +
 +User and Group Mapping
 +----------------------
 +
 +In ownCloud the user or group name is used to have all relevant information in
 +the database assigned. To work reliably a permanent internal user name and
 +group name is created and mapped to the LDAP DN and UUID. If the DN changes in
 +LDAP it will be detected, there will be no conflicts.
 +
 +Those mappings are done in the database table ldap_user_mapping and
 +ldap_group_mapping. The user name is also used for the user's folder (except
 +something else is specified in *User Home Folder Naming Rule*), which
 +contains files and meta data.
 +
 +As of ownCloud 5 internal user name and a visible display name are separated.
 +This is not the case for group names, yet, i.e. group cannot be altered.
 +
 +That means that your LDAP configuration should be good and ready before putting
 +it into production. The mapping tables are filled early, but as long as you are
 +testing, you can empty the tables any time. Do not do this in production. If you
 +want to rename a group, be very careful. Do not rename the user's internal name.
 +
 +Caching
 +-------
 +
 +For performance reasons a cache has been introduced to ownCloud. He we store
 +all users and groups, group memberships or internal userExists-requests. Since
 +ownCloud is written in PHP and each and every page request (also done by Ajax)
 +loads ownCloud and would execute one or more LDAP queries again, you do want to
 +have some of those queries cached and save those requests and traffic. It is
 +highly recommended to have the cache filled for a small amount of time, which
 +comes also very handy when using the sync client, as it is yet another request
 +for PHP.
 +
 +Handling with Backup Server
 +---------------------------
 +
 +When ownCloud is not able to contact the main server, he will be treated as
 +offline and no connection attempts will be done for the time specified in
 +**Cache Time-To-Live**. If a backup server is configured, it will be connected
 +instead. If you plan a maintained downtime, check **Disable Main Server** for
 +the time being to avoid unnecessary connection attempts every now and then.
diff --cc core/doc/admin/configuration/auth_ldap.html
index 482023d,0000000..e537a86
mode 100644,000000..100644
--- a/core/doc/admin/configuration/auth_ldap.html
+++ b/core/doc/admin/configuration/auth_ldap.html
@@@ -1,663 -1,0 +1,664 @@@
 +
 +<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
 +  "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
 +
 +
 +<html xmlns="http://www.w3.org/1999/xhtml">
 +  <head>
 +    <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
 +    
 +    <title>User Authentication with LDAP — ownCloud Administrators Manual 5.0 documentation</title>
 +    
 +    <link rel="stylesheet" href="../_static/style.css" type="text/css" />
 +    <link rel="stylesheet" href="../_static/pygments.css" type="text/css" />
 +    <link rel="stylesheet" href="../_static/style.css" type="text/css" />
 +    <link rel="stylesheet" href="../_static/bootstrap-sphinx.css" type="text/css" />
 +    
 +    <script type="text/javascript">
 +      var DOCUMENTATION_OPTIONS = {
 +        URL_ROOT:    '../',
 +        VERSION:     '5.0',
 +        COLLAPSE_INDEX: false,
 +        FILE_SUFFIX: '.html',
 +        HAS_SOURCE:  true
 +      };
 +    </script>
 +    <script type="text/javascript" src="../_static/jquery.js"></script>
 +    <script type="text/javascript" src="../_static/underscore.js"></script>
 +    <script type="text/javascript" src="../_static/doctools.js"></script>
 +    <script type="text/javascript" src="../_static/bootstrap.js"></script>
 +    <link rel="top" title="ownCloud Administrators Manual 5.0 documentation" href="../index.html" />
 +    <link rel="up" title="Configuration" href="index.html" />
 +    <link rel="next" title="Background Jobs" href="background_jobs.html" />
 +    <link rel="prev" title="Configuration" href="index.html" />
 +<script type="text/javascript">
 +(function () {
 +  /**
 +   * Patch TOC list.
 +   *
 +   * Will mutate the underlying span to have a correct ul for nav.
 +   *
 +   * @param $span: Span containing nested UL's to mutate.
 +   * @param minLevel: Starting level for nested lists. (1: global, 2: local).
 +   */
 +  var patchToc = function ($ul, minLevel) {
 +    var findA;
 +
 +    // Find all a "internal" tags, traversing recursively.
 +    findA = function ($elem, level) {
 +      var level = level || 0,
 +        $items = $elem.find("> li > a.internal, > ul, > li > ul");
 +
 +      // Iterate everything in order.
 +      $items.each(function (index, item) {
 +        var $item = $(item),
 +          tag = item.tagName.toLowerCase(),
 +          pad = 15 + ((level - minLevel) * 10);
 +
 +        if (tag === 'a' && level >= minLevel) {
 +          // Add to existing padding.
 +          $item.css('padding-left', pad + "px");
 +          console.log(level, $item, 'padding-left', pad + "px");
 +        } else if (tag === 'ul') {
 +          // Recurse.
 +          findA($item, level + 1);
 +        }
 +      });
 +    };
 +
 +    console.log("HERE");
 +    findA($ul);
 +  };
 +
 +  $(document).ready(function () {
 +    // Add styling, structure to TOC's.
 +    $(".dropdown-menu").each(function () {
 +      $(this).find("ul").each(function (index, item){
 +        var $item = $(item);
 +        $item.addClass('unstyled');
 +      });
 +      $(this).find("li").each(function () {
 +        $(this).parent().append(this);
 +      });
 +    });
 +
 +    // Patch in level.
 +    patchToc($("ul.globaltoc"), 2);
 +    patchToc($("ul.localtoc"), 2);
 +
 +    // Enable dropdown.
 +    $('.dropdown-toggle').dropdown();
 +  });
 +}());
 +</script>
 +
 +  </head>
 +  <body>
 +  
 +
 +<div class="container">
 +  <div class="content">
 +    <div class="page-header">
 +      <h1><a href="../contents.html">ownCloud Administrators Manual</a></h1>
 +
 +    </div>
 +    
 +			<div class="row">
 +				<div class="span3">
 +					<div class="sidebar">
 +						<div class="well">
 +							<div class="menu-support-container">
 +								<ul id="menu-support" class="menu">
 +									<ul>
 +										<li><a href="../contents.html">Overview</a></li>
 +									</ul>
 +                  <ul>
 +<li class="toctree-l1"><a class="reference internal" href="../index.html">Admin Documentation</a></li>
 +</ul>
 +<ul class="current">
 +<li class="toctree-l1"><a class="reference internal" href="../installation/index.html">Installation</a></li>
 +<li class="toctree-l1 current"><a class="reference internal" href="index.html">Configuration</a><ul class="current">
 +<li class="toctree-l2 current"><a class="current reference internal" href="">User Authentication with LDAP</a><ul>
 +<li class="toctree-l3"><a class="reference internal" href="#basic-settings">Basic Settings</a></li>
 +<li class="toctree-l3"><a class="reference internal" href="#advanced-settings">Advanced Settings</a></li>
 +<li class="toctree-l3"><a class="reference internal" href="#expert-settings-owncloud-5-0-7">Expert Settings (>= ownCloud 5.0.7)</a></li>
 +<li class="toctree-l3"><a class="reference internal" href="#testing-the-configuration">Testing the configuration</a></li>
 +<li class="toctree-l3"><a class="reference internal" href="#troubleshooting-tips-and-tricks">Troubleshooting, Tips and Tricks</a></li>
 +<li class="toctree-l3"><a class="reference internal" href="#ssl-certificate-verification-ldaps-tls">SSL Certificate Verification (LDAPS, TLS)</a></li>
 +<li class="toctree-l3"><a class="reference internal" href="#microsoft-active-directory">Microsoft Active Directory</a></li>
 +<li class="toctree-l3"><a class="reference internal" href="#duplicating-server-configurations">Duplicating Server Configurations</a></li>
 +<li class="toctree-l3"><a class="reference internal" href="#owncloud-ldap-internals">ownCloud LDAP Internals</a></li>
 +<li class="toctree-l3"><a class="reference internal" href="#user-and-group-mapping">User and Group Mapping</a></li>
 +<li class="toctree-l3"><a class="reference internal" href="#caching">Caching</a></li>
 +<li class="toctree-l3"><a class="reference internal" href="#handling-with-backup-server">Handling with Backup Server</a></li>
 +</ul>
 +</li>
 +<li class="toctree-l2"><a class="reference internal" href="background_jobs.html">Background Jobs</a></li>
 +<li class="toctree-l2"><a class="reference internal" href="configuration_3rdparty.html">3rd-Party Configuration</a></li>
 +<li class="toctree-l2"><a class="reference internal" href="configuration_apps.html">Apps Configuration</a></li>
 +<li class="toctree-l2"><a class="reference internal" href="configuration_automation.html">Automatic Configuration</a></li>
 +<li class="toctree-l2"><a class="reference internal" href="configuration_custom_clients.html">Custom Client Configuration</a></li>
 +<li class="toctree-l2"><a class="reference internal" href="configuration_database.html">Database Configuration</a></li>
 +<li class="toctree-l2"><a class="reference internal" href="configuration_encryption.html">Use Server-Side Encryption</a></li>
 +<li class="toctree-l2"><a class="reference internal" href="configuration_knowledgebase.html">Knowledge Base Configuration</a></li>
 +<li class="toctree-l2"><a class="reference internal" href="configuration_language.html">Language Configuration</a></li>
 +<li class="toctree-l2"><a class="reference internal" href="configuration_logging.html">Logging Configuration</a></li>
 +<li class="toctree-l2"><a class="reference internal" href="configuration_mail.html">Mail Configuration</a></li>
 +<li class="toctree-l2"><a class="reference internal" href="configuration_maintenance.html">Maintenance Mode Configuration</a></li>
 +<li class="toctree-l2"><a class="reference internal" href="configuration_reverseproxy.html">Reverse Proxy Configuration</a></li>
 +<li class="toctree-l2"><a class="reference internal" href="configuring_big_file_upload.html">Uploading big files > 512MB (as set by default)</a></li>
 +<li class="toctree-l2"><a class="reference internal" href="configuring_big_file_upload.html#enabling-uploading-big-files">Enabling uploading big files</a></li>
 +<li class="toctree-l2"><a class="reference internal" href="custom_mount_config_gui.html">Custom Mount Configuration Web-GUI</a></li>
 +<li class="toctree-l2"><a class="reference internal" href="custom_mount_config.html">Custom Mount Configuration</a></li>
 +<li class="toctree-l2"><a class="reference internal" href="custom_user_backend.html">Custom User Backend Configuration</a></li>
 +<li class="toctree-l2"><a class="reference internal" href="xsendfile.html">Serving static files via web server</a></li>
 +</ul>
 +</li>
 +<li class="toctree-l1"><a class="reference internal" href="../maintenance/index.html">Maintenance</a></li>
 +<li class="toctree-l1"><a class="reference internal" href="../issues/index.html">Issues</a></li>
 +</ul>
 +
 +								</ul>
 +							</div>
 +						</div>
 +					</div>
 +				</div>
 +        
 +
 +				<div class="span9">
 +					<div class="page-content">
 +						
 +  <div class="section" id="user-authentication-with-ldap">
 +<h1>User Authentication with LDAP<a class="headerlink" href="#user-authentication-with-ldap" title="Permalink to this headline">¶</a></h1>
 +<p>ownCloud ships an LDAP backend, which allows full use of ownCloud for user
 +logging in with LDAP credentials including:</p>
 +<ul class="simple">
 +<li>LDAP group support</li>
 +<li>File sharing with users and groups</li>
 +<li>Access via WebDAV and of course ownCloud Desktop Client</li>
 +<li>Versioning, external Storages and all other ownCloud Goodies</li>
 +</ul>
 +<p>To connect to an LDAP server the configuration needs to be set up properly.
 +Once the LDAP backend is activated (Settings→Apps, choose <strong>LDAP user and group
 +backend</strong>, click on <strong>Enable</strong>) the configuration can be found on
 +Settings→Admin. Read on for a detailed description of the configuration fields.</p>
 +<div class="section" id="basic-settings">
 +<h2>Basic Settings<a class="headerlink" href="#basic-settings" title="Permalink to this headline">¶</a></h2>
 +<p>The basic settings are all you need. However, if you have a larger directory,
 +custom requirements or need to connect to Active Directory (AD) you want to have
 +a look on the advanced settings afterwards. The basic part allows you to set up
 +a working connection to your LDAP server and use it with ownCloud.</p>
 +<div class="figure">
 +<img alt="../_images/ldap-basic-settings-oc5.png" src="../_images/ldap-basic-settings-oc5.png" />
 +<p class="caption">LDAP Basic Settings</p>
 +</div>
 +<p>Note that a hint will be shown on the right hand side, when hovering with the
 +mouse over an input field. This gives you more context information while
 +filling out the settings.</p>
 +<div class="section" id="settings-details">
 +<h3>Settings Details<a class="headerlink" href="#settings-details" title="Permalink to this headline">¶</a></h3>
 +<dl class="docutils">
 +<dt>Server configuration:</dt>
 +<dd><p class="first">ownCloud can be configured to connect to multiple LDAP servers. Using this
 +control you can pick a configuration you want to edit or add a new one. The
 +button <strong>Delete Configuration</strong> deletes the current configuration.</p>
 +<ul class="last simple">
 +<li><em>Example: 1. Server</em></li>
 +</ul>
 +</dd>
 +<dt>Host:</dt>
 +<dd><p class="first">The host name of the LDAP server. It can also be a <strong>ldaps://</strong> URI, for
 +instance.</p>
 +<ul class="last simple">
 +<li>Example: <em>directory.my-company.com</em></li>
 +</ul>
 +</dd>
 +<dt>Base DN:</dt>
 +<dd><p class="first">The base DN of LDAP, from where all users and groups can be reached.
 +Separated Base DNs for users and groups can be set in the Advanced
 +tab. Nevertheless, this field is mandatory.</p>
 +<ul class="last simple">
 +<li>Example: <em>dc=my-company,dc=com</em></li>
 +</ul>
 +</dd>
 +<dt>User DN:</dt>
 +<dd><p class="first">The name as DN of a user who is able to do searches in the LDAP
 +directory. Let it empty for anonymous access. It is recommended to have a
 +special system user for ownCloud.</p>
 +<ul class="last simple">
 +<li>Example: <em>uid=owncloudsystemuser,cn=sysusers,dc=my-company,dc=com</em></li>
 +</ul>
 +</dd>
 +<dt>Password:</dt>
 +<dd>The password for the user given above. Empty for anonymous access.</dd>
 +<dt>User Login Filter:</dt>
 +<dd><p class="first">The filter to use when a users tries to login. Use <strong>%uid</strong> as placeholder
 +for the user name. Note, that login applies this filter only, but not User
 +List Filter. This may change in future.</p>
 +<ul class="last simple">
 +<li>Example (allows login with user name and email address): <em>(|(uid=%uid)(email=$uid))</em></li>
 +</ul>
 +</dd>
 +<dt>User List Filter:</dt>
 +<dd><p class="first">The filter to use when a search for users will be executed.</p>
 +<ul class="last simple">
 +<li>Example: <em>objectClass=posixAccount</em></li>
 +</ul>
 +</dd>
 +<dt>Group Filter:</dt>
 +<dd><p class="first">The filter to use when a search for groups will be executed. In
 +case you do not want to use LDAP groups in ownCloud, leave it empty.</p>
 +<ul class="last simple">
 +<li>Example: <em>objectClass=posixGroup</em></li>
 +</ul>
 +</dd>
 +</dl>
 +</div>
 +</div>
 +<div class="section" id="advanced-settings">
 +<h2>Advanced Settings<a class="headerlink" href="#advanced-settings" title="Permalink to this headline">¶</a></h2>
 +<p>In the LDAP Advanced settings section you can define options, that are less
 +common to set. They are not needed for a working connection, unless you use a
 +non-standard Port, e.g. It can also have a positive effect on the performance
 +to specify distinguished bases for user and group searches.</p>
 +<p>The Advanced Settings are structured into three parts:
 +* Connection Settings
 +* Directory Settings
 +* Special Attributes</p>
 +<div class="figure">
 +<img alt="../_images/ldap-advanced-settings-oc5.png" src="../_images/ldap-advanced-settings-oc5.png" />
 +<p class="caption">LDAP Advanced Settings</p>
 +</div>
 +<div class="section" id="connection-settings">
 +<h3>Connection Settings<a class="headerlink" href="#connection-settings" title="Permalink to this headline">¶</a></h3>
 +<div class="figure">
 +<img alt="../_images/ldap-advanced-settings-connection-settings-oc5.png" src="../_images/ldap-advanced-settings-connection-settings-oc5.png" />
 +<p class="caption">LDAP Advanced Settings, section Connection Settings</p>
 +</div>
 +<dl class="docutils">
 +<dt>Configuration Active:</dt>
 +<dd><p class="first">Enables or Disables the current configuration. Disabled configuration will not
 +connect to the LDAP server.</p>
 +<ul class="last simple">
 +<li>Example: <em>[X]</em></li>
 +</ul>
 +</dd>
 +<dt>Port:</dt>
 +<dd><p class="first">The port on which to connect to the LDAP server.</p>
 +<ul class="last simple">
 +<li>Example: <em>389</em></li>
 +</ul>
 +</dd>
 +<dt>Backup (Replica) Host:</dt>
 +<dd><p class="first">A backup server can be defined here. ownCloud tries to connect to the backup
 +server automatically, when the main host (as specified in basic settings)
 +cannot be reached. It is import that the backup server is a replica of the
 +main server, because the object UUIDs must match.</p>
 +<ul class="last simple">
 +<li>Example: <em>directory2.my-company.com</em></li>
 +</ul>
 +</dd>
 +<dt>Backup (Replica) Port:</dt>
 +<dd><p class="first">The port on which to connect to the backup LDAP server. If no port is given,
 +but a host, then the main port (as specified above) will be used.</p>
 +<ul class="last simple">
 +<li>Example: <em>389</em></li>
 +</ul>
 +</dd>
 +<dt>Disable Main Server:</dt>
 +<dd><p class="first">You can manually override the main server and make ownCloud only connect to
 +the backup server. It may be handy for planned downtimes.</p>
 +<ul class="last simple">
 +<li>Example: <em>[ ]</em></li>
 +</ul>
 +</dd>
 +<dt>Use TLS:</dt>
 +<dd><p class="first">Whether to use TLS encrypted connection to the LDAP server.  This will be
 +ignored when “ldaps://” protocol is specified in the host entries.</p>
 +<ul class="last simple">
 +<li>Example: <em>[ ]</em></li>
 +</ul>
 +</dd>
 +<dt>Case insensitive LDAP server (Windows):</dt>
 +<dd><p class="first">Whether the LDAP server is running on a Windows Host</p>
 +<ul class="last simple">
 +<li>Example: <em>[ ]</em></li>
 +</ul>
 +</dd>
 +<dt>Turn off SSL certificate validation:</dt>
 +<dd><p class="first">Turns of check of valid SSL certificates. Use it – if needed –
 +for testing, only!</p>
 +<ul class="last simple">
 +<li>Example: <em>[ ]</em></li>
 +</ul>
 +</dd>
 +<dt>Cache Time-To-Live:</dt>
 +<dd><p class="first">A cache is introduced to avoid unnecessary LDAP traffic,
 +for example lookups check whether the users exists on every page request or
 +WebDAV interaction. It is also supposed to speed up the Admin → User page or
 +list of users to share with, once it is populated. Saving the configuration
 +empties the cache (changes are not necessary). The time is given in seconds.</p>
 +<p>Note that almost every PHP request would require to build up a new connection
 +to the LDAP server. If you require a most up-to-dateness it is recommended not
 +to totally switch off the cache, but define a minimum life time of 15s.</p>
 +<ul class="last simple">
 +<li>Example (10 min): <em>600</em></li>
 +</ul>
 +</dd>
 +</dl>
 +</div>
 +<div class="section" id="directory-settings">
 +<h3>Directory Settings<a class="headerlink" href="#directory-settings" title="Permalink to this headline">¶</a></h3>
 +<div class="figure">
 +<img alt="../_images/ldap-advanced-settings-directory-settings-oc5.png" src="../_images/ldap-advanced-settings-directory-settings-oc5.png" />
 +<p class="caption">LDAP Advanced Settings, section Directory Settings</p>
 +</div>
 +<dl class="docutils">
 +<dt>User Display Name Field:</dt>
 +<dd><p class="first">The attribute that should be used as display name in ownCloud. Prior to
 +ownCloud 5 it was used as internal user name. This is not the case anymore.
 +It also means that display names are not permanent in ownCloud, i.e. if the
 +attribute’s value changes in LDAP, it changes in ownCloud too. Display names
 +to not need to be unique, but you rather want to specify a more or less unique
 +attribute here to avoid confusion.</p>
 +<ul class="last simple">
 +<li>Example: <em>displayName</em></li>
 +</ul>
 +</dd>
 +<dt>Base User Tree:</dt>
 +<dd><p class="first">The base DN of LDAP, from where all users can be reached. It needs to be given
 +completely despite to the Base DN from the Basic settings. You can specifiy
 +multiple base trees, one in each line.</p>
 +<ul class="last">
 +<li><p class="first">Example:</p>
 +<div class="line-block">
 +<div class="line"><em>cn=programmers,dc=my-company,dc=com</em></div>
 +<div class="line"><em>cn=designers,dc=my-company,dc=com</em></div>
 +</div>
 +</li>
 +</ul>
 +</dd>
 +<dt>User Search Attributes:</dt>
 +<dd><p class="first">These attributes are used when a search for users with a search string is
 +done. This happens, for instance, in the share dialogue. By default the user
 +display name attribute as specified above is being used. Multiple attributes
 +can be given, one in each line.</p>
 +<ul class="last">
 +<li><p class="first">Example:</p>
 +<div class="line-block">
 +<div class="line"><em>displayName</em></div>
 +<div class="line"><em>mail</em></div>
 +</div>
 +</li>
 +</ul>
 +</dd>
 +<dt>Group Display Name Field:</dt>
 +<dd><p class="first">The attribute that should be used as ownCloud group name. ownCloud allows a
 +limited set of characters (a-zA-Z0-9.-_@), every other character will be
 +replaced in ownCloud. Once a group name is assigned, it will not be changed,
 +i.e. changing this value will only have effect to new LDAP groups.</p>
 +<ul class="last simple">
 +<li>Example: <em>cn</em></li>
 +</ul>
 +</dd>
 +<dt>Base Group Tree:</dt>
 +<dd><p class="first">The base DN of LDAP, from where all groups can be reached.
 +It needs to be given completely despite to the Base DN from the Basic
 +settings. You can specifiy multiple base trees, one in each line.</p>
 +<ul class="last">
 +<li><p class="first">Example:</p>
 +<div class="line-block">
 +<div class="line"><em>cn=barcelona,dc=my-company,dc=com</em></div>
 +<div class="line"><em>cn=madrid,dc=my-company,dc=com</em></div>
 +</div>
 +</li>
 +</ul>
 +</dd>
 +<dt>Group Search Attributes:</dt>
 +<dd><p class="first">These attributes are used when a search for groups with a search string is
 +done. This happens, for instance, in the share dialogue. By default the group
 +display name attribute as specified above is being used. Multiple attributes
 +can be given, one in each line.</p>
 +<ul class="last">
 +<li><p class="first">Example:</p>
 +<div class="line-block">
 +<div class="line"><em>cn</em></div>
 +<div class="line"><em>description</em></div>
 +</div>
 +</li>
 +</ul>
 +</dd>
 +<dt>Group Member association:</dt>
 +<dd><p class="first">The attribute that is used to indicate group memberships, i.e. the attribute
 +used by LDAP groups to refer to their users.</p>
 +<ul class="last simple">
 +<li>Example: <em>uniquemember</em></li>
 +</ul>
 +</dd>
 +</dl>
 +</div>
 +<div class="section" id="special-attributes">
 +<h3>Special Attributes<a class="headerlink" href="#special-attributes" title="Permalink to this headline">¶</a></h3>
 +<div class="figure">
 +<img alt="../_images/ldap-advanced-settings-special-attributes-oc5.png" src="../_images/ldap-advanced-settings-special-attributes-oc5.png" />
 +<p class="caption">LDAP Advanced Settings, section Special Attributes</p>
 +</div>
 +<dl class="docutils">
 +<dt>Quota Field:</dt>
 +<dd><p class="first">ownCloud can read an LDAP attribute and set the user quota according to its
 +value. Specify the attribute here, otherwise keep it empty. The attribute
 +shall return human readable values, e.g. “2 GB”.</p>
 +<ul class="last simple">
 +<li>Example: <em>ownCloudQuota</em></li>
 +</ul>
 +</dd>
 +<dt>Quota Default:</dt>
 +<dd><p class="first">Override ownCloud default quota for LDAP users who do not
 +have a quota set in the attribute given above.</p>
 +<ul class="last simple">
 +<li>Example: <em>15 GB</em></li>
 +</ul>
 +</dd>
 +<dt>Email Field:</dt>
 +<dd><p class="first">ownCloud can read an LDAP attribute and set the user email
 +there from. Specify the attribute here, otherwise keep it empty.</p>
 +<ul class="last simple">
 +<li>Example: <em>mail</em></li>
 +</ul>
 +</dd>
 +<dt>User Home Folder Naming Rule:</dt>
 +<dd><p class="first">By default, the ownCloud creates the user
 +directory, where all files and meta data are kept, according to the ownCloud
 +user name. You may want to override this setting and name it after an
 +attribute’s value. The attribute given can also return an absolute path, e.g.
 +<tt class="docutils literal"><span class="pre">/mnt/storage43/alice</span></tt>. Leave it empty for default behavior.</p>
 +<ul class="last simple">
 +<li>Example: <em>cn</em></li>
 +</ul>
 +</dd>
 +</dl>
 +</div>
 +</div>
 +<div class="section" id="expert-settings-owncloud-5-0-7">
 +<h2>Expert Settings (>= ownCloud 5.0.7)<a class="headerlink" href="#expert-settings-owncloud-5-0-7" title="Permalink to this headline">¶</a></h2>
 +<div class="figure">
 +<img alt="../_images/ldap-expert-settings-oc5.png" src="../_images/ldap-expert-settings-oc5.png" />
 +</div>
 +<p>In the Expert Settings fundamental behavior can be adjusted to your needs. The
 +configuration should be done before starting production use or when testing the
 +installation.</p>
 +<dl class="docutils">
 +<dt>Internal Username:</dt>
 +<dd><p class="first">The internal username is the identifier in ownCloud for LDAP users. By default
 +it will be created from the UUID attribute. By using the UUID attribute it is
 +made sure that the username is unique and characters do not need to be
 +converted. The internal username has the restriction that only these
 +characters are allowed: [a-zA-Z0-9_.@-]. Other characters are replaced with
 +their ASCII correspondence or are simply omitted.</p>
 +<p>The LDAP backend ensures that there are no duplicate internal usernames in
 +ownCloud, i.e. that it is checking all other activated user backends
 +(including local ownCloud users). On collisions a random number (between 1000
 +and 9999) will be attached to the retrieved value. For example, if “alice”
 +exists, the next username may be “alice_1337”.</p>
 +<p>The internal username is also the default name for the user home folder in
 +ownCloud. It is also a part of remote URLs, for instance for all *DAV services.
 +With this setting the default behaviour can be overriden. To achieve a similar
 +behaviour as before ownCloud 5 enter the user display name attribute in the
 +following field.</p>
 +<p>Leave it empty for default behaviour. Changes will have effect only on newly
 +mapped (added) LDAP users.</p>
 +<ul class="last simple">
 +<li>Example: <em>uid</em></li>
 +</ul>
 +</dd>
 +<dt>Override UUID detection</dt>
 +<dd><p class="first">By default, ownCloud autodetects the UUID attribute. The UUID attribute is
 +used to doubtlessly identify LDAP users and groups. Also, the internal
 +username will be created based on the UUID, if not specified otherwise above.</p>
 +<p>You can override the setting and pass an attribute of your choice. You must
 +make sure that the attribute of your choice can be fetched for both users and
 +groups and it is unique. Leave it empty for default behaviour. Changes will
 +have effect only on newly mapped (added) LDAP users and groups. It also will
 +have effect when a user’s or group’s DN changes and an old UUID was cached: It
 +will result in a new user. Because of this, the setting should be applied
 +before putting ownCloud in production use and cleaning the bindings
 +(see below).</p>
 +<p>The default behaviour does not differ from ownCloud 4.5. You do not want to
 +change this after upgrading from ownCloud 4.5 unless you update the mapping
 +tables yourself.</p>
 +<ul class="last simple">
 +<li>Example: <em>cn</em></li>
 +</ul>
 +</dd>
 +<dt>Username-LDAP User Mapping</dt>
 +<dd><p class="first">ownCloud uses the usernames as key to store and assign data. In order to
 +precisely identify and recognize users, each LDAP user will have a internal
 +username in ownCloud. This requires a mapping from ownCloud username to LDAP
 +user. The created username is mapped to the UUID of the LDAP user.
 +Additionally the DN is cached as well to reduce LDAP interaction, but it is
 +not used for identification. If the DN changes, the change will be detected by
 +ownCloud by checking the UUID value.</p>
 +<p>The same is valid for groups.</p>
 +<p>The internal ownCloud name is used all over in ownCloud. Clearing the Mappings
 +will have leftovers everywhere. Do never clear the mappings
 +in a production environment. Only clear mappings in a testing or experimental
 +stage.</p>
 +<p class="last"><strong>Clearing the Mappings is not configuration sensitive, it affects all LDAP
 +configurations!</strong></p>
 +</dd>
 +</dl>
 +</div>
 +<div class="section" id="testing-the-configuration">
 +<h2>Testing the configuration<a class="headerlink" href="#testing-the-configuration" title="Permalink to this headline">¶</a></h2>
 +<p>In this version we introduced the <strong>Test Configuration</strong> button on the bottom
 +of the LDAP settings section. It will always check the values as currently
 +given in the input fields. You do not need to save before testing. By clicking
 +on the button, ownCloud will try to bind to the ownCloud server with the
 +settings currently given in the input fields. The response will look like this:</p>
 +<div class="figure">
 +<img alt="../_images/ldap-settings-invalid-oc45.png" src="../_images/ldap-settings-invalid-oc45.png" />
 +<p class="caption">Failure</p>
 +</div>
 +<p>In case the configuration fails, you can see details in ownCloud’s log, which
 +is in the data directory and called <strong>owncloud.log</strong> or on the bottom the
 +<strong>Settings →  Admin page</strong>. Unfortunately it requires a reload – sorry for the
 +inconvenience.</p>
 +<div class="figure">
 +<img alt="../_images/ldap-settings-valid-oc45.png" src="../_images/ldap-settings-valid-oc45.png" />
 +<p class="caption">Success</p>
 +</div>
 +<p>In this case, Save the settings. You can check if the users and groups are
 +fetched correctly on the Settings → Users page.</p>
 +</div>
 +<div class="section" id="troubleshooting-tips-and-tricks">
 +<h2>Troubleshooting, Tips and Tricks<a class="headerlink" href="#troubleshooting-tips-and-tricks" title="Permalink to this headline">¶</a></h2>
 +</div>
 +<div class="section" id="ssl-certificate-verification-ldaps-tls">
 +<h2>SSL Certificate Verification (LDAPS, TLS)<a class="headerlink" href="#ssl-certificate-verification-ldaps-tls" title="Permalink to this headline">¶</a></h2>
 +<p>A common mistake with SSL certificates is that they may not be known to PHP.
 +If you have trouble with certificate validation make sure that</p>
 +<ul class="simple">
 +<li>you have the certificate of the server installed on the ownCloud server</li>
 +<li>the certificate is announced in the system’s LDAP configuration file (usually
 +<em>/etc/ldap/ldap.conf</em> on Linux, <em>C:\openldap\sysconf\ldap.conf</em> or
 +<em>C:\ldap.conf</em> on Windows) using a <strong>TLS_CACERT /path/to/cert</strong> line.</li>
 +<li>Using LDAPS, also make sure that the port is correctly configured (by default
 +686)</li>
 +</ul>
 +</div>
 +<div class="section" id="microsoft-active-directory">
 +<h2>Microsoft Active Directory<a class="headerlink" href="#microsoft-active-directory" title="Permalink to this headline">¶</a></h2>
 +<p>In case you want to connect to a Windows AD, you must change some values in the Advanced tab.</p>
 +<ul class="simple">
++<li>The default login filter will not work with AD. Use “samaccountname=%uid” instead.</li>
 +<li>The default in User Display Name Field will not work with Active Directory.</li>
 +<li>The Group Member association must be set to “member (AD)”</li>
 +<li>Check Case insensitive LDAP server (Windows)</li>
 +</ul>
 +</div>
 +<div class="section" id="duplicating-server-configurations">
 +<h2>Duplicating Server Configurations<a class="headerlink" href="#duplicating-server-configurations" title="Permalink to this headline">¶</a></h2>
 +<p>In case you have a working configuration and want to create a similar one or
 +“snapshot” configurations before modifying them you can do the following:</p>
 +<ol class="arabic simple">
 +<li>Go to the <strong>LDAP Basic</strong> tab</li>
 +<li>On <strong>Server Configuration</strong> choose <em>Add Server Configuration</em></li>
 +<li>Answer the question <em>Take over settings from recent server configuration?</em>
 +with <em>yes</em>.</li>
 +<li>(optional) Switch to <strong>Advanced</strong> tab and uncheck <strong>Configuration Active</strong>
 +in the <em>Connection Settings</em>, so the new configuration is not used on Save</li>
 +<li>Click on <strong>Save</strong></li>
 +</ol>
 +<p>Now you can modify the configuration and enable it if you wish.</p>
 +</div>
 +<div class="section" id="owncloud-ldap-internals">
 +<h2>ownCloud LDAP Internals<a class="headerlink" href="#owncloud-ldap-internals" title="Permalink to this headline">¶</a></h2>
 +<p>Some parts of how the LDAP backend works are described here. May it be helpful.</p>
 +</div>
 +<div class="section" id="user-and-group-mapping">
 +<h2>User and Group Mapping<a class="headerlink" href="#user-and-group-mapping" title="Permalink to this headline">¶</a></h2>
 +<p>In ownCloud the user or group name is used to have all relevant information in
 +the database assigned. To work reliably a permanent internal user name and
 +group name is created and mapped to the LDAP DN and UUID. If the DN changes in
 +LDAP it will be detected, there will be no conflicts.</p>
 +<p>Those mappings are done in the database table ldap_user_mapping and
 +ldap_group_mapping. The user name is also used for the user’s folder (except
 +something else is specified in <em>User Home Folder Naming Rule</em>), which
 +contains files and meta data.</p>
 +<p>As of ownCloud 5 internal user name and a visible display name are separated.
 +This is not the case for group names, yet, i.e. group cannot be altered.</p>
 +<p>That means that your LDAP configuration should be good and ready before putting
 +it into production. The mapping tables are filled early, but as long as you are
 +testing, you can empty the tables any time. Do not do this in production. If you
 +want to rename a group, be very careful. Do not rename the user’s internal name.</p>
 +</div>
 +<div class="section" id="caching">
 +<h2>Caching<a class="headerlink" href="#caching" title="Permalink to this headline">¶</a></h2>
 +<p>For performance reasons a cache has been introduced to ownCloud. He we store
 +all users and groups, group memberships or internal userExists-requests. Since
 +ownCloud is written in PHP and each and every page request (also done by Ajax)
 +loads ownCloud and would execute one or more LDAP queries again, you do want to
 +have some of those queries cached and save those requests and traffic. It is
 +highly recommended to have the cache filled for a small amount of time, which
 +comes also very handy when using the sync client, as it is yet another request
 +for PHP.</p>
 +</div>
 +<div class="section" id="handling-with-backup-server">
 +<h2>Handling with Backup Server<a class="headerlink" href="#handling-with-backup-server" title="Permalink to this headline">¶</a></h2>
 +<p>When ownCloud is not able to contact the main server, he will be treated as
 +offline and no connection attempts will be done for the time specified in
 +<strong>Cache Time-To-Live</strong>. If a backup server is configured, it will be connected
 +instead. If you plan a maintained downtime, check <strong>Disable Main Server</strong> for
 +the time being to avoid unnecessary connection attempts every now and then.</p>
 +</div>
 +</div>
 +
 +
 +					</div>
 +				</div>
 +			</div>
 +    
 +  </div>
 +</div>
 +  </body>
 +</html>
diff --cc core/doc/admin/searchindex.js
index 11ec1c6,0000000..607ce18
mode 100644,000000..100644
--- a/core/doc/admin/searchindex.js
+++ b/core/doc/admin/searchindex.js
@@@ -1,1 -1,0 +1,1 @@@
- Search.setIndex({objects:{},terms:{retain:[17,34],four:27,token_secret:26,sorri:20,loginfilt:31,aur:24,swap:13,under:[21,18,16,13],worth:18,everi:[31,18,5,20,30],affect:[24,20],upload:[11,32,2,21,13,8,10],verif:[10,20],seper:31,"10g":32,enjoi:31,second:[31,20],even:[17,21,32],dialogu:20,followsymlink:24,asid:21,"10t15":6,"new":[11,24,31,4,30,20],subtre:31,abov:[14,24,4,5,13,30,20],never:[30,20],here:[14,0,32,2,21,20],studio:1,path:[14,22,23,2,32,25,26,28,20],apps_path:25,uuidattribut:31 [...]
++Search.setIndex({objects:{},terms:{retain:[17,34],four:27,token_secret:26,sorri:20,loginfilt:31,aur:24,swap:13,under:[21,18,16,13],worth:18,everi:[31,18,5,20,30],affect:[24,20],upload:[11,32,2,21,13,8,10],verif:[10,20],seper:31,"10g":32,enjoi:31,second:[31,20],even:[17,21,32],dialogu:20,followsymlink:24,asid:21,"10t15":6,"new":[11,24,31,4,30,20],subtre:31,abov:[14,24,4,5,13,30,20],never:[30,20],here:[14,0,32,2,21,20],studio:1,path:[14,22,23,2,32,25,26,28,20],apps_path:25,uuidattribut:31 [...]
diff --cc lib/request.php
index df33217,0abc6e7..0abc6e7
mode 100644,100755..100644
--- a/lib/request.php
+++ b/lib/request.php
diff --cc lib/util.php
index 97e0fdb,aa2bc07..aa2bc07
mode 100644,100755..100644
--- a/lib/util.php
+++ b/lib/util.php

-- 
Alioth's /usr/local/bin/git-commit-notice on /srv/git.debian.org/git/pkg-owncloud/owncloud.git



More information about the Pkg-owncloud-commits mailing list