[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