From 79a6d89bccf4c5d4ce934c88bc544988c47b8e98 Mon Sep 17 00:00:00 2001 From: Luke Policinski Date: Wed, 19 Feb 2014 21:23:39 +0000 Subject: [PATCH 001/187] Feature Added : Ability to drag and drop in Chrome --- apps/files/ajax/upload.php | 26 ++++++++++++++++++-------- apps/files/js/file-upload.js | 3 ++- apps/files/js/filelist.js | 32 ++++++++++++++++++++++++++++---- lib/private/files/view.php | 11 +++++++++++ 4 files changed, 59 insertions(+), 13 deletions(-) diff --git a/apps/files/ajax/upload.php b/apps/files/ajax/upload.php index 145f40c50d..b2aa7a9920 100644 --- a/apps/files/ajax/upload.php +++ b/apps/files/ajax/upload.php @@ -103,22 +103,32 @@ if ($maxUploadFileSize >= 0 and $totalSize > $maxUploadFileSize) { } $result = array(); +$directory = ''; if (strpos($dir, '..') === false) { $fileCount = count($files['name']); for ($i = 0; $i < $fileCount; $i++) { + + // Get the files directory + if(isset($_POST['file_directory']) === true) { + $directory = '/'.$_POST['file_directory']; + } + // $path needs to be normalized - this failed within drag'n'drop upload to a sub-folder if (isset($_POST['resolution']) && $_POST['resolution']==='autorename') { // append a number in brackets like 'filename (2).ext' - $target = OCP\Files::buildNotExistingFileName(stripslashes($dir), $files['name'][$i]); + $target = OCP\Files::buildNotExistingFileName(stripslashes($dir.$directory), $files['name'][$i]); } else { - $target = \OC\Files\Filesystem::normalizePath(stripslashes($dir).'/'.$files['name'][$i]); + $target = \OC\Files\Filesystem::normalizePath(stripslashes($dir.$directory).'/'.$files['name'][$i]); } - - $directory = \OC\Files\Filesystem::normalizePath(stripslashes($dir)); - if (isset($public_directory)) { - // If we are uploading from the public app, - // we want to send the relative path in the ajax request. - $directory = $public_directory; + + if(empty($directory) === true) + { + $directory = \OC\Files\Filesystem::normalizePath(stripslashes($dir)); + if (isset($public_directory)) { + // If we are uploading from the public app, + // we want to send the relative path in the ajax request. + $directory = $public_directory; + } } if ( ! \OC\Files\Filesystem::file_exists($target) diff --git a/apps/files/js/file-upload.js b/apps/files/js/file-upload.js index f962a7044a..ae6fdc654e 100644 --- a/apps/files/js/file-upload.js +++ b/apps/files/js/file-upload.js @@ -327,7 +327,8 @@ $(document).ready(function() { // noone set update parameters, we set the minimum data.formData = { requesttoken: oc_requesttoken, - dir: $('#dir').val() + dir: $('#dir').val(), + file_directory: data.files[0]['relativePath'], }; } }, diff --git a/apps/files/js/filelist.js b/apps/files/js/filelist.js index d6cffde05d..2631812c2e 100644 --- a/apps/files/js/filelist.js +++ b/apps/files/js/filelist.js @@ -879,7 +879,8 @@ $(document).ready(function() { data.formData = function(form) { return [ {name: 'dir', value: dir}, - {name: 'requesttoken', value: oc_requesttoken} + {name: 'requesttoken', value: oc_requesttoken}, + {name: 'file_directory', value: data.files[0]['relativePath']} ]; }; } @@ -935,7 +936,7 @@ $(document).ready(function() { var file = result[0]; if (data.context && data.context.data('type') === 'dir') { - + // update upload counter ui var uploadtext = data.context.find('.uploadtext'); var currentUploads = parseInt(uploadtext.attr('currentUploads')); @@ -956,10 +957,33 @@ $(document).ready(function() { size += parseInt(file.size); data.context.attr('data-size', size); data.context.find('td.filesize').text(humanFileSize(size)); - - } else { + } + else { + // only append new file if uploaded into the current folder if (file.directory !== FileList.getCurrentDirectory()) { + + file_directory = file.directory.replace('/','').replace(/\/$/, "").split('/'); + + if (file_directory.length == 1) { + file_directory = file_directory[0]; + + // Get the directory + if ($('tr[data-file="'+file_directory+'"]').length == 0) + { + FileList.addDir(file_directory, 0, new Date(), false); + } + } + else { + file_directory = file_directory[0]; + } + + // update folder size + var size = parseInt($('tr[data-file="'+file_directory+'"]').attr('data-size')); + size += parseInt(file.size); + $('tr[data-file="'+file_directory+'"]').attr('data-size', size); + $('tr[data-file="'+file_directory+'"]').find('td.filesize').text(humanFileSize(size)); + return; } diff --git a/lib/private/files/view.php b/lib/private/files/view.php index 530aa8f751..7977272ee1 100644 --- a/lib/private/files/view.php +++ b/lib/private/files/view.php @@ -617,10 +617,21 @@ class View { } public function fromTmpFile($tmpFile, $path) { + if (Filesystem::isValidPath($path)) { + + // Get directory that the file is going into + $file_path = \OC_User::getHome(\OC_User::getUser()) . '/files'.substr($path, 0, strrpos($path,'/')); + + // Create the directories if any + if(empty($file_path) === false) { + mkdir($file_path, 0770, true); + } + if (!$tmpFile) { debug_print_backtrace(); } + $source = fopen($tmpFile, 'r'); if ($source) { $this->file_put_contents($path, $source); From b01492fecd5019a09cd7b1265a9cef43fc42616f Mon Sep 17 00:00:00 2001 From: Luke Policinski Date: Wed, 19 Feb 2014 21:28:32 +0000 Subject: [PATCH 002/187] Feature Added : Ability to drag and drop in Chrome --- apps/files/ajax/upload.php | 4 ++-- apps/files/js/filelist.js | 12 ++++++------ lib/private/files/view.php | 10 +++++----- 3 files changed, 13 insertions(+), 13 deletions(-) diff --git a/apps/files/ajax/upload.php b/apps/files/ajax/upload.php index b2aa7a9920..d189386e05 100644 --- a/apps/files/ajax/upload.php +++ b/apps/files/ajax/upload.php @@ -107,12 +107,12 @@ $directory = ''; if (strpos($dir, '..') === false) { $fileCount = count($files['name']); for ($i = 0; $i < $fileCount; $i++) { - + // Get the files directory if(isset($_POST['file_directory']) === true) { $directory = '/'.$_POST['file_directory']; } - + // $path needs to be normalized - this failed within drag'n'drop upload to a sub-folder if (isset($_POST['resolution']) && $_POST['resolution']==='autorename') { // append a number in brackets like 'filename (2).ext' diff --git a/apps/files/js/filelist.js b/apps/files/js/filelist.js index 2631812c2e..17846c23b7 100644 --- a/apps/files/js/filelist.js +++ b/apps/files/js/filelist.js @@ -936,7 +936,7 @@ $(document).ready(function() { var file = result[0]; if (data.context && data.context.data('type') === 'dir') { - + // update upload counter ui var uploadtext = data.context.find('.uploadtext'); var currentUploads = parseInt(uploadtext.attr('currentUploads')); @@ -962,12 +962,12 @@ $(document).ready(function() { // only append new file if uploaded into the current folder if (file.directory !== FileList.getCurrentDirectory()) { - + file_directory = file.directory.replace('/','').replace(/\/$/, "").split('/'); - + if (file_directory.length == 1) { file_directory = file_directory[0]; - + // Get the directory if ($('tr[data-file="'+file_directory+'"]').length == 0) { @@ -977,13 +977,13 @@ $(document).ready(function() { else { file_directory = file_directory[0]; } - + // update folder size var size = parseInt($('tr[data-file="'+file_directory+'"]').attr('data-size')); size += parseInt(file.size); $('tr[data-file="'+file_directory+'"]').attr('data-size', size); $('tr[data-file="'+file_directory+'"]').find('td.filesize').text(humanFileSize(size)); - + return; } diff --git a/lib/private/files/view.php b/lib/private/files/view.php index 7977272ee1..ddf8c3038b 100644 --- a/lib/private/files/view.php +++ b/lib/private/files/view.php @@ -617,21 +617,21 @@ class View { } public function fromTmpFile($tmpFile, $path) { - + if (Filesystem::isValidPath($path)) { - + // Get directory that the file is going into $file_path = \OC_User::getHome(\OC_User::getUser()) . '/files'.substr($path, 0, strrpos($path,'/')); - + // Create the directories if any if(empty($file_path) === false) { mkdir($file_path, 0770, true); } - + if (!$tmpFile) { debug_print_backtrace(); } - + $source = fopen($tmpFile, 'r'); if ($source) { $this->file_put_contents($path, $source); From 089052b13af7a5e27d225ab345616511f4eaf2a9 Mon Sep 17 00:00:00 2001 From: Luke Policinski Date: Thu, 20 Feb 2014 20:18:27 +0000 Subject: [PATCH 003/187] Fixed issue with drag and drop not in the root directory, for folders and files --- apps/files/js/filelist.js | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/apps/files/js/filelist.js b/apps/files/js/filelist.js index 17846c23b7..64315c0362 100644 --- a/apps/files/js/filelist.js +++ b/apps/files/js/filelist.js @@ -958,10 +958,9 @@ $(document).ready(function() { data.context.attr('data-size', size); data.context.find('td.filesize').text(humanFileSize(size)); } - else { - + else { // only append new file if uploaded into the current folder - if (file.directory !== FileList.getCurrentDirectory()) { + if (file.directory != '/' && file.directory !== FileList.getCurrentDirectory()) { file_directory = file.directory.replace('/','').replace(/\/$/, "").split('/'); @@ -977,12 +976,14 @@ $(document).ready(function() { else { file_directory = file_directory[0]; } + + file_directory = FileList.findFileEl(file_directory); // update folder size - var size = parseInt($('tr[data-file="'+file_directory+'"]').attr('data-size')); + var size = parseInt(file_directory.attr('data-size')); size += parseInt(file.size); - $('tr[data-file="'+file_directory+'"]').attr('data-size', size); - $('tr[data-file="'+file_directory+'"]').find('td.filesize').text(humanFileSize(size)); + file_directory.attr('data-size', size); + file_directory.find('td.filesize').text(humanFileSize(size)); return; } From 3a21755963d8d9897a48ab58292345c0b710e239 Mon Sep 17 00:00:00 2001 From: Robin Appelman Date: Tue, 25 Feb 2014 16:23:09 +0100 Subject: [PATCH 004/187] Pass the filesystem view as argument in the sabredav connectors and use the fileinfo object --- apps/files/appinfo/remote.php | 22 ++- .../sabre/aborteduploaddetectionplugin.php | 33 ++-- lib/private/connector/sabre/directory.php | 35 ++-- lib/private/connector/sabre/file.php | 64 +++---- lib/private/connector/sabre/node.php | 168 ++++++------------ lib/private/connector/sabre/objecttree.php | 78 ++++---- lib/private/connector/sabre/quotaplugin.php | 30 ++-- lib/private/connector/sabre/server.php | 80 ++++++--- 8 files changed, 232 insertions(+), 278 deletions(-) diff --git a/apps/files/appinfo/remote.php b/apps/files/appinfo/remote.php index ef22fe9218..1922bc4fcd 100644 --- a/apps/files/appinfo/remote.php +++ b/apps/files/appinfo/remote.php @@ -34,12 +34,8 @@ $authBackend = new OC_Connector_Sabre_Auth(); $lockBackend = new OC_Connector_Sabre_Locks(); $requestBackend = new OC_Connector_Sabre_Request(); -// Create ownCloud Dir -$rootDir = new OC_Connector_Sabre_Directory(''); -$objectTree = new \OC\Connector\Sabre\ObjectTree($rootDir); - // Fire up server -$server = new OC_Connector_Sabre_Server($objectTree); +$server = new OC_Connector_Sabre_Server(); $server->httpRequest = $requestBackend; $server->setBaseUri($baseuri); @@ -49,10 +45,22 @@ $server->addPlugin(new Sabre_DAV_Auth_Plugin($authBackend, $defaults->getName()) $server->addPlugin(new Sabre_DAV_Locks_Plugin($lockBackend)); $server->addPlugin(new Sabre_DAV_Browser_Plugin(false)); // Show something in the Browser, but no upload $server->addPlugin(new OC_Connector_Sabre_FilesPlugin()); -$server->addPlugin(new OC_Connector_Sabre_AbortedUploadDetectionPlugin()); -$server->addPlugin(new OC_Connector_Sabre_QuotaPlugin()); $server->addPlugin(new OC_Connector_Sabre_MaintenancePlugin()); $server->addPlugin(new OC_Connector_Sabre_ExceptionLoggerPlugin('webdav')); +// wait with registering these until auth is handled and the filesystem is setup +$server->subscribeEvent('beforeMethod', function () use ($server) { + $view = \OC\Files\Filesystem::getView(); + $rootInfo = $view->getFileInfo(''); + + // Create ownCloud Dir + $rootDir = new OC_Connector_Sabre_Directory($view, $rootInfo); + $objectTree = new \OC\Connector\Sabre\ObjectTree($rootDir, $view); + $server->setObjectTree($objectTree); + + $server->addPlugin(new OC_Connector_Sabre_AbortedUploadDetectionPlugin($view)); + $server->addPlugin(new OC_Connector_Sabre_QuotaPlugin($view)); +}, 30); // priority 30: after auth (10) and acl(20), before lock(50) and handling the request + // And off we go! $server->exec(); diff --git a/lib/private/connector/sabre/aborteduploaddetectionplugin.php b/lib/private/connector/sabre/aborteduploaddetectionplugin.php index ad759d1d84..1a092a59a8 100644 --- a/lib/private/connector/sabre/aborteduploaddetectionplugin.php +++ b/lib/private/connector/sabre/aborteduploaddetectionplugin.php @@ -22,11 +22,16 @@ class OC_Connector_Sabre_AbortedUploadDetectionPlugin extends Sabre_DAV_ServerPl private $server; /** - * is kept public to allow overwrite for unit testing - * * @var \OC\Files\View */ - public $fileView; + private $fileView; + + /** + * @param \OC\Files\View $view + */ + public function __construct($view) { + $this->fileView = $view; + } /** * This initializes the plugin. @@ -55,7 +60,7 @@ class OC_Connector_Sabre_AbortedUploadDetectionPlugin extends Sabre_DAV_ServerPl // we should only react on PUT which is used for upload // e.g. with LOCK this will not work, but LOCK uses createFile() as well - if ($this->server->httpRequest->getMethod() !== 'PUT' ) { + if ($this->server->httpRequest->getMethod() !== 'PUT') { return; } @@ -70,9 +75,9 @@ class OC_Connector_Sabre_AbortedUploadDetectionPlugin extends Sabre_DAV_ServerPl if (!$expected) { return; } - $actual = $this->getFileView()->filesize($filePath); + $actual = $this->fileView->filesize($filePath); if ($actual != $expected) { - $this->getFileView()->unlink($filePath); + $this->fileView->unlink($filePath); throw new Sabre_DAV_Exception_BadRequest('expected filesize ' . $expected . ' got ' . $actual); } @@ -81,8 +86,7 @@ class OC_Connector_Sabre_AbortedUploadDetectionPlugin extends Sabre_DAV_ServerPl /** * @return string */ - public function getLength() - { + public function getLength() { $req = $this->server->httpRequest; $length = $req->getHeader('X-Expected-Entity-Length'); if (!$length) { @@ -91,17 +95,4 @@ class OC_Connector_Sabre_AbortedUploadDetectionPlugin extends Sabre_DAV_ServerPl return $length; } - - /** - * @return \OC\Files\View - */ - public function getFileView() - { - if (is_null($this->fileView)) { - // initialize fileView - $this->fileView = \OC\Files\Filesystem::getView(); - } - - return $this->fileView; - } } diff --git a/lib/private/connector/sabre/directory.php b/lib/private/connector/sabre/directory.php index 02d1a9f4ba..619aec21ea 100644 --- a/lib/private/connector/sabre/directory.php +++ b/lib/private/connector/sabre/directory.php @@ -60,20 +60,22 @@ class OC_Connector_Sabre_Directory extends OC_Connector_Sabre_Node implements Sa // exit if we can't create a new file and we don't updatable existing file $info = OC_FileChunking::decodeName($name); - if (!\OC\Files\Filesystem::isCreatable($this->path) && - !\OC\Files\Filesystem::isUpdatable($this->path . '/' . $info['name'])) { + if (!$this->fileView->isCreatable($this->path) && + !$this->fileView->isUpdatable($this->path . '/' . $info['name'])) { throw new \Sabre_DAV_Exception_Forbidden(); } } else { // For non-chunked upload it is enough to check if we can create a new file - if (!\OC\Files\Filesystem::isCreatable($this->path)) { + if (!$this->fileView->isCreatable($this->path)) { throw new \Sabre_DAV_Exception_Forbidden(); } } $path = $this->path . '/' . $name; - $node = new OC_Connector_Sabre_File($path); + // using a dummy FileInfo is acceptable here since it will be refreshed after the put is complete + $info = new \OC\Files\FileInfo($path, null, null, array()); + $node = new OC_Connector_Sabre_File($this->fileView, $info); return $node->put($data); } @@ -90,12 +92,12 @@ class OC_Connector_Sabre_Directory extends OC_Connector_Sabre_Node implements Sa throw new \Sabre_DAV_Exception_Forbidden(); } - if (!\OC\Files\Filesystem::isCreatable($this->path)) { + if (!$this->fileView->isCreatable($this->path)) { throw new \Sabre_DAV_Exception_Forbidden(); } $newPath = $this->path . '/' . $name; - if(!\OC\Files\Filesystem::mkdir($newPath)) { + if(!$this->fileView->mkdir($newPath)) { throw new Sabre_DAV_Exception_Forbidden('Could not create directory '.$newPath); } @@ -105,6 +107,7 @@ class OC_Connector_Sabre_Directory extends OC_Connector_Sabre_Node implements Sa * Returns a specific child node, referenced by its name * * @param string $name + * @param \OCP\Files\FileInfo $info * @throws Sabre_DAV_Exception_FileNotFound * @return Sabre_DAV_INode */ @@ -112,7 +115,7 @@ class OC_Connector_Sabre_Directory extends OC_Connector_Sabre_Node implements Sa $path = $this->path . '/' . $name; if (is_null($info)) { - $info = \OC\Files\Filesystem::getFileInfo($path); + $info = $this->fileView->getFileInfo($path); } if (!$info) { @@ -120,12 +123,10 @@ class OC_Connector_Sabre_Directory extends OC_Connector_Sabre_Node implements Sa } if ($info['mimetype'] == 'httpd/unix-directory') { - $node = new OC_Connector_Sabre_Directory($path); + $node = new OC_Connector_Sabre_Directory($this->fileView, $info); } else { - $node = new OC_Connector_Sabre_File($path); + $node = new OC_Connector_Sabre_File($this->fileView, $info); } - - $node->setFileinfoCache($info); return $node; } @@ -136,7 +137,7 @@ class OC_Connector_Sabre_Directory extends OC_Connector_Sabre_Node implements Sa */ public function getChildren() { - $folder_content = \OC\Files\Filesystem::getDirectoryContent($this->path); + $folder_content = $this->fileView->getDirectoryContent($this->path); $paths = array(); foreach($folder_content as $info) { $paths[] = $this->path.'/'.$info['name']; @@ -167,7 +168,7 @@ class OC_Connector_Sabre_Directory extends OC_Connector_Sabre_Node implements Sa $nodes = array(); foreach($folder_content as $info) { - $node = $this->getChild($info['name'], $info); + $node = $this->getChild($info->getName(), $info); $node->setPropertyCache($properties[$this->path.'/'.$info['name']]); $nodes[] = $node; } @@ -183,7 +184,7 @@ class OC_Connector_Sabre_Directory extends OC_Connector_Sabre_Node implements Sa public function childExists($name) { $path = $this->path . '/' . $name; - return \OC\Files\Filesystem::file_exists($path); + return $this->fileView->file_exists($path); } @@ -199,11 +200,11 @@ class OC_Connector_Sabre_Directory extends OC_Connector_Sabre_Node implements Sa throw new \Sabre_DAV_Exception_Forbidden(); } - if (!\OC\Files\Filesystem::isDeletable($this->path)) { + if (!$this->info->isDeletable()) { throw new \Sabre_DAV_Exception_Forbidden(); } - \OC\Files\Filesystem::rmdir($this->path); + $this->fileView->rmdir($this->path); } @@ -235,7 +236,7 @@ class OC_Connector_Sabre_Directory extends OC_Connector_Sabre_Node implements Sa public function getProperties($properties) { $props = parent::getProperties($properties); if (in_array(self::GETETAG_PROPERTYNAME, $properties) && !isset($props[self::GETETAG_PROPERTYNAME])) { - $props[self::GETETAG_PROPERTYNAME] = $this->getETagPropertyForPath($this->path); + $props[self::GETETAG_PROPERTYNAME] = $this->info->getEtag(); } return $props; } diff --git a/lib/private/connector/sabre/file.php b/lib/private/connector/sabre/file.php index ef6caaf22a..cbecd90b7d 100644 --- a/lib/private/connector/sabre/file.php +++ b/lib/private/connector/sabre/file.php @@ -45,11 +45,8 @@ class OC_Connector_Sabre_File extends OC_Connector_Sabre_Node implements Sabre_D * @return string|null */ public function put($data) { - - $fs = $this->getFS(); - - if ($fs->file_exists($this->path) && - !$fs->isUpdatable($this->path)) { + if ($this->info && $this->fileView->file_exists($this->path) && + !$this->info->isUpdateable()) { throw new \Sabre_DAV_Exception_Forbidden(); } @@ -79,10 +76,10 @@ class OC_Connector_Sabre_File extends OC_Connector_Sabre_Node implements Sabre_D } try { - $putOkay = $fs->file_put_contents($partpath, $data); + $putOkay = $this->fileView->file_put_contents($partpath, $data); if ($putOkay === false) { \OC_Log::write('webdav', '\OC\Files\Filesystem::file_put_contents() failed', \OC_Log::ERROR); - $fs->unlink($partpath); + $this->fileView->unlink($partpath); // because we have no clue about the cause we can only throw back a 500/Internal Server Error throw new Sabre_DAV_Exception('Could not write file contents'); } @@ -105,29 +102,30 @@ class OC_Connector_Sabre_File extends OC_Connector_Sabre_Node implements Sabre_D } // rename to correct path - $renameOkay = $fs->rename($partpath, $this->path); - $fileExists = $fs->file_exists($this->path); + $renameOkay = $this->fileView->rename($partpath, $this->path); + $fileExists = $this->fileView->file_exists($this->path); if ($renameOkay === false || $fileExists === false) { \OC_Log::write('webdav', '\OC\Files\Filesystem::rename() failed', \OC_Log::ERROR); - $fs->unlink($partpath); + $this->fileView->unlink($partpath); throw new Sabre_DAV_Exception('Could not rename part file to final file'); } // allow sync clients to send the mtime along in a header $mtime = OC_Request::hasModificationTime(); if ($mtime !== false) { - if($fs->touch($this->path, $mtime)) { + if($this->fileView->touch($this->path, $mtime)) { header('X-OC-MTime: accepted'); } } + $this->refreshInfo(); - return $this->getETagPropertyForPath($this->path); + return '"' . $this->info->getEtag() . '"'; } /** * Returns the data * - * @return string + * @return string | resource */ public function get() { @@ -135,7 +133,7 @@ class OC_Connector_Sabre_File extends OC_Connector_Sabre_Node implements Sabre_D if (\OC_Util::encryptedFiles()) { throw new \Sabre_DAV_Exception_ServiceUnavailable(); } else { - return \OC\Files\Filesystem::fopen($this->path, 'rb'); + return $this->fileView->fopen($this->path, 'rb'); } } @@ -147,16 +145,14 @@ class OC_Connector_Sabre_File extends OC_Connector_Sabre_Node implements Sabre_D * @throws Sabre_DAV_Exception_Forbidden */ public function delete() { - $fs = $this->getFS(); - if ($this->path === 'Shared') { throw new \Sabre_DAV_Exception_Forbidden(); } - if (!$fs->isDeletable($this->path)) { + if (!$this->info->isDeletable()) { throw new \Sabre_DAV_Exception_Forbidden(); } - $fs->unlink($this->path); + $this->fileView->unlink($this->path); // remove properties $this->removeProperties(); @@ -169,12 +165,7 @@ class OC_Connector_Sabre_File extends OC_Connector_Sabre_Node implements Sabre_D * @return int */ public function getSize() { - $this->getFileinfoCache(); - if ($this->fileinfo_cache['size'] > -1) { - return $this->fileinfo_cache['size']; - } else { - return null; - } + return $this->info->getSize(); } /** @@ -189,11 +180,7 @@ class OC_Connector_Sabre_File extends OC_Connector_Sabre_Node implements Sabre_D * @return mixed */ public function getETag() { - $properties = $this->getProperties(array(self::GETETAG_PROPERTYNAME)); - if (isset($properties[self::GETETAG_PROPERTYNAME])) { - return $properties[self::GETETAG_PROPERTYNAME]; - } - return null; + return $this->info->getEtag(); } /** @@ -204,12 +191,7 @@ class OC_Connector_Sabre_File extends OC_Connector_Sabre_Node implements Sabre_D * @return mixed */ public function getContentType() { - if (isset($this->fileinfo_cache['mimetype'])) { - return $this->fileinfo_cache['mimetype']; - } - - return \OC\Files\Filesystem::getMimeType($this->path); - + return $this->info->getMimetype(); } /** @@ -245,15 +227,14 @@ class OC_Connector_Sabre_File extends OC_Connector_Sabre_Node implements Sabre_D $chunk_handler->file_assemble($partFile); // here is the final atomic rename - $fs = $this->getFS(); $targetPath = $path . '/' . $info['name']; - $renameOkay = $fs->rename($partFile, $targetPath); - $fileExists = $fs->file_exists($targetPath); + $renameOkay = $this->fileView->rename($partFile, $targetPath); + $fileExists = $this->fileView->file_exists($targetPath); if ($renameOkay === false || $fileExists === false) { \OC_Log::write('webdav', '\OC\Files\Filesystem::rename() failed', \OC_Log::ERROR); // only delete if an error occurred and the target file was already created if ($fileExists) { - $fs->unlink($targetPath); + $this->fileView->unlink($targetPath); } throw new Sabre_DAV_Exception('Could not rename part file assembled from chunks'); } @@ -261,12 +242,13 @@ class OC_Connector_Sabre_File extends OC_Connector_Sabre_Node implements Sabre_D // allow sync clients to send the mtime along in a header $mtime = OC_Request::hasModificationTime(); if ($mtime !== false) { - if($fs->touch($targetPath, $mtime)) { + if($this->fileView->touch($targetPath, $mtime)) { header('X-OC-MTime: accepted'); } } - return OC_Connector_Sabre_Node::getETagPropertyForPath($targetPath); + $info = $this->fileView->getFileInfo($targetPath); + return $info->getEtag(); } return null; diff --git a/lib/private/connector/sabre/node.php b/lib/private/connector/sabre/node.php index 5807c5c7f7..3a5c721dda 100644 --- a/lib/private/connector/sabre/node.php +++ b/lib/private/connector/sabre/node.php @@ -20,7 +20,6 @@ * License along with this library. If not, see . * */ - abstract class OC_Connector_Sabre_Node implements Sabre_DAV_INode, Sabre_DAV_IProperties { const GETETAG_PROPERTYNAME = '{DAV:}getetag'; const LASTMODIFIED_PROPERTYNAME = '{DAV:}lastmodified'; @@ -29,15 +28,13 @@ abstract class OC_Connector_Sabre_Node implements Sabre_DAV_INode, Sabre_DAV_IPr * Allow configuring the method used to generate Etags * * @var array(class_name, function_name) - */ + */ public static $ETagFunction = null; /** - * is kept public to allow overwrite for unit testing - * * @var \OC\Files\View */ - public $fileView; + protected $fileView; /** * The path to the current node @@ -46,53 +43,53 @@ abstract class OC_Connector_Sabre_Node implements Sabre_DAV_INode, Sabre_DAV_IPr */ protected $path; - /** - * node fileinfo cache - * @var array - */ - protected $fileinfo_cache; /** * node properties cache + * * @var array */ protected $property_cache = null; /** - * @brief Sets up the node, expects a full path name - * @param string $path - * @return void + * @var \OCP\Files\FileInfo */ - public function __construct($path) { - $this->path = $path; + protected $info; + + /** + * @brief Sets up the node, expects a full path name + * @param \OC\Files\View $view + * @param \OCP\Files\FileInfo $info + */ + public function __construct($view, $info) { + $this->fileView = $view; + $this->path = $this->fileView->getRelativePath($info->getPath()); + $this->info = $info; } - + protected function refreshInfo() { + $this->info = $this->fileView->getFileInfo($this->path); + } /** * @brief Returns the name of the node * @return string */ public function getName() { - - list(, $name) = Sabre_DAV_URLUtil::splitPath($this->path); - return $name; - + return $this->info->getName(); } /** * @brief Renames the node * @param string $name The new name - * @return void */ public function setName($name) { - $fs = $this->getFS(); // rename is only allowed if the update privilege is granted - if (!$fs->isUpdatable($this->path)) { + if (!$this->info->isUpdateable()) { throw new \Sabre_DAV_Exception_Forbidden(); } - list($parentPath, ) = Sabre_DAV_URLUtil::splitPath($this->path); + list($parentPath,) = Sabre_DAV_URLUtil::splitPath($this->path); list(, $newName) = Sabre_DAV_URLUtil::splitPath($name); if (!\OCP\Util::isValidFileName($newName)) { @@ -102,38 +99,17 @@ abstract class OC_Connector_Sabre_Node implements Sabre_DAV_INode, Sabre_DAV_IPr $newPath = $parentPath . '/' . $newName; $oldPath = $this->path; - $fs->rename($this->path, $newPath); + $this->fileView->rename($this->path, $newPath); $this->path = $newPath; - $query = OC_DB::prepare( 'UPDATE `*PREFIX*properties` SET `propertypath` = ?' - .' WHERE `userid` = ? AND `propertypath` = ?' ); - $query->execute( array( $newPath, OC_User::getUser(), $oldPath )); - + $query = OC_DB::prepare('UPDATE `*PREFIX*properties` SET `propertypath` = ?' + . ' WHERE `userid` = ? AND `propertypath` = ?'); + $query->execute(array($newPath, OC_User::getUser(), $oldPath)); + $this->refreshInfo(); } - public function setFileinfoCache($fileinfo_cache) - { - $this->fileinfo_cache = $fileinfo_cache; - } - - /** - * @brief Ensure that the fileinfo cache is filled - * @note Uses OC_FileCache or a direct stat - */ - protected function getFileinfoCache() { - if (!isset($this->fileinfo_cache)) { - if ($fileinfo_cache = \OC\Files\Filesystem::getFileInfo($this->path)) { - } else { - $fileinfo_cache = \OC\Files\Filesystem::stat($this->path); - } - - $this->fileinfo_cache = $fileinfo_cache; - } - } - - public function setPropertyCache($property_cache) - { + public function setPropertyCache($property_cache) { $this->property_cache = $property_cache; } @@ -142,8 +118,7 @@ abstract class OC_Connector_Sabre_Node implements Sabre_DAV_INode, Sabre_DAV_IPr * @return int */ public function getLastModified() { - $this->getFileinfoCache(); - return $this->fileinfo_cache['mtime']; + return $this->info->getMtime(); } @@ -153,7 +128,8 @@ abstract class OC_Connector_Sabre_Node implements Sabre_DAV_INode, Sabre_DAV_IPr * Even if the modification time is set to a custom value the access time is set to now. */ public function touch($mtime) { - \OC\Files\Filesystem::touch($this->path, $mtime); + $this->fileView->touch($this->path, $mtime); + $this->refreshInfo(); } /** @@ -163,29 +139,28 @@ abstract class OC_Connector_Sabre_Node implements Sabre_DAV_INode, Sabre_DAV_IPr */ public function updateProperties($properties) { $existing = $this->getProperties(array()); - foreach($properties as $propertyName => $propertyValue) { + foreach ($properties as $propertyName => $propertyValue) { // If it was null, we need to delete the property if (is_null($propertyValue)) { - if(array_key_exists( $propertyName, $existing )) { - $query = OC_DB::prepare( 'DELETE FROM `*PREFIX*properties`' - .' WHERE `userid` = ? AND `propertypath` = ? AND `propertyname` = ?' ); - $query->execute( array( OC_User::getUser(), $this->path, $propertyName )); + if (array_key_exists($propertyName, $existing)) { + $query = OC_DB::prepare('DELETE FROM `*PREFIX*properties`' + . ' WHERE `userid` = ? AND `propertypath` = ? AND `propertyname` = ?'); + $query->execute(array(OC_User::getUser(), $this->path, $propertyName)); } - } - else { - if( strcmp( $propertyName, self::GETETAG_PROPERTYNAME) === 0 ) { - \OC\Files\Filesystem::putFileInfo($this->path, array('etag'=> $propertyValue)); - } elseif( strcmp( $propertyName, self::LASTMODIFIED_PROPERTYNAME) === 0 ) { + } else { + if (strcmp($propertyName, self::GETETAG_PROPERTYNAME) === 0) { + \OC\Files\Filesystem::putFileInfo($this->path, array('etag' => $propertyValue)); + } elseif (strcmp($propertyName, self::LASTMODIFIED_PROPERTYNAME) === 0) { $this->touch($propertyValue); } else { - if(!array_key_exists( $propertyName, $existing )) { - $query = OC_DB::prepare( 'INSERT INTO `*PREFIX*properties`' - .' (`userid`,`propertypath`,`propertyname`,`propertyvalue`) VALUES(?,?,?,?)' ); - $query->execute( array( OC_User::getUser(), $this->path, $propertyName,$propertyValue )); + if (!array_key_exists($propertyName, $existing)) { + $query = OC_DB::prepare('INSERT INTO `*PREFIX*properties`' + . ' (`userid`,`propertypath`,`propertyname`,`propertyvalue`) VALUES(?,?,?,?)'); + $query->execute(array(OC_User::getUser(), $this->path, $propertyName, $propertyValue)); } else { - $query = OC_DB::prepare( 'UPDATE `*PREFIX*properties` SET `propertyvalue` = ?' - .' WHERE `userid` = ? AND `propertypath` = ? AND `propertyname` = ?' ); - $query->execute( array( $propertyValue,OC_User::getUser(), $this->path, $propertyName )); + $query = OC_DB::prepare('UPDATE `*PREFIX*properties` SET `propertyvalue` = ?' + . ' WHERE `userid` = ? AND `propertypath` = ? AND `propertyname` = ?'); + $query->execute(array($propertyValue, OC_User::getUser(), $this->path, $propertyName)); } } } @@ -199,9 +174,9 @@ abstract class OC_Connector_Sabre_Node implements Sabre_DAV_INode, Sabre_DAV_IPr * removes all properties for this node and user */ public function removeProperties() { - $query = OC_DB::prepare( 'DELETE FROM `*PREFIX*properties`' - .' WHERE `userid` = ? AND `propertypath` = ?' ); - $query->execute( array( OC_User::getUser(), $this->path)); + $query = OC_DB::prepare('DELETE FROM `*PREFIX*properties`' + . ' WHERE `userid` = ? AND `propertypath` = ?'); + $query->execute(array(OC_User::getUser(), $this->path)); $this->setPropertyCache(null); } @@ -219,29 +194,23 @@ abstract class OC_Connector_Sabre_Node implements Sabre_DAV_INode, Sabre_DAV_IPr if (is_null($this->property_cache)) { $sql = 'SELECT * FROM `*PREFIX*properties` WHERE `userid` = ? AND `propertypath` = ?'; - $result = OC_DB::executeAudited( $sql, array( OC_User::getUser(), $this->path ) ); + $result = OC_DB::executeAudited($sql, array(OC_User::getUser(), $this->path)); $this->property_cache = array(); - while( $row = $result->fetchRow()) { + while ($row = $result->fetchRow()) { $this->property_cache[$row['propertyname']] = $row['propertyvalue']; } - // Don't call the static getETagPropertyForPath, its result is not cached - $this->getFileinfoCache(); - if ($this->fileinfo_cache['etag']) { - $this->property_cache[self::GETETAG_PROPERTYNAME] = '"'.$this->fileinfo_cache['etag'].'"'; - } else { - $this->property_cache[self::GETETAG_PROPERTYNAME] = null; - } + $this->property_cache[self::GETETAG_PROPERTYNAME] = '"' . $this->info->getEtag() . '"'; } // if the array was empty, we need to return everything - if(count($properties) == 0) { + if (count($properties) == 0) { return $this->property_cache; } $props = array(); - foreach($properties as $property) { + foreach ($properties as $property) { if (isset($this->property_cache[$property])) { $props[$property] = $this->property_cache[$property]; } @@ -250,36 +219,13 @@ abstract class OC_Connector_Sabre_Node implements Sabre_DAV_INode, Sabre_DAV_IPr return $props; } - /** - * Returns the ETag surrounded by double-quotes for this path. - * @param string $path Path of the file - * @return string|null Returns null if the ETag can not effectively be determined - */ - protected function getETagPropertyForPath($path) { - $data = $this->getFS()->getFileInfo($path); - if (isset($data['etag'])) { - return '"'.$data['etag'].'"'; - } - return null; - } - - protected function getFS() { - if (is_null($this->fileView)) { - $this->fileView = \OC\Files\Filesystem::getView(); - } - return $this->fileView; - } - /** * @return string|null */ - public function getFileId() - { - $this->getFileinfoCache(); - - if (isset($this->fileinfo_cache['fileid'])) { + public function getFileId() { + if ($this->info->getId()) { $instanceId = OC_Util::getInstanceId(); - $id = sprintf('%08d', $this->fileinfo_cache['fileid']); + $id = sprintf('%08d', $this->info->getId()); return $id . $instanceId; } diff --git a/lib/private/connector/sabre/objecttree.php b/lib/private/connector/sabre/objecttree.php index d2fa425b22..d176d3333f 100644 --- a/lib/private/connector/sabre/objecttree.php +++ b/lib/private/connector/sabre/objecttree.php @@ -8,16 +8,28 @@ namespace OC\Connector\Sabre; +use OC\Files\FileInfo; use OC\Files\Filesystem; class ObjectTree extends \Sabre_DAV_ObjectTree { /** - * keep this public to allow mock injection during unit test - * * @var \OC\Files\View */ - public $fileView; + protected $fileView; + + /** + * Creates the object + * + * This method expects the rootObject to be passed as a parameter + * + * @param \Sabre_DAV_ICollection $rootNode + * @param \OC\Files\View $view + */ + public function __construct(\Sabre_DAV_ICollection $rootNode, $view) { + parent::__construct($rootNode); + $this->fileView = $view; + } /** * Returns the INode object for the requested path @@ -40,31 +52,34 @@ class ObjectTree extends \Sabre_DAV_ObjectTree { if (pathinfo($path, PATHINFO_EXTENSION) === 'part') { // read from storage - $absPath = $this->getFileView()->getAbsolutePath($path); + $absPath = $this->fileView->getAbsolutePath($path); list($storage, $internalPath) = Filesystem::resolvePath('/' . $absPath); if ($storage) { + /** + * @var \OC\Files\Storage\Storage $storage + */ $scanner = $storage->getScanner($internalPath); // get data directly - $info = $scanner->getData($internalPath); + $data = $scanner->getData($internalPath); + $info = new FileInfo($absPath, $storage, $internalPath, $data); + } else { + $info = null; } - } - else { + } else { // read from cache - $info = $this->getFileView()->getFileInfo($path); + $info = $this->fileView->getFileInfo($path); } if (!$info) { throw new \Sabre_DAV_Exception_NotFound('File with name ' . $path . ' could not be located'); } - if ($info['mimetype'] === 'httpd/unix-directory') { - $node = new \OC_Connector_Sabre_Directory($path); + if ($info->getType() === 'dir') { + $node = new \OC_Connector_Sabre_Directory($this->fileView, $info); } else { - $node = new \OC_Connector_Sabre_File($path); + $node = new \OC_Connector_Sabre_File($this->fileView, $info); } - $node->setFileinfoCache($info); - $this->cache[$path] = $node; return $node; @@ -88,19 +103,18 @@ class ObjectTree extends \Sabre_DAV_ObjectTree { list($destinationDir,) = \Sabre_DAV_URLUtil::splitPath($destinationPath); // check update privileges - $fs = $this->getFileView(); - if (!$fs->isUpdatable($sourcePath)) { + if (!$this->fileView->isUpdatable($sourcePath)) { throw new \Sabre_DAV_Exception_Forbidden(); } if ($sourceDir !== $destinationDir) { // for a full move we need update privileges on sourcePath and sourceDir as well as destinationDir - if (!$fs->isUpdatable($sourceDir)) { + if (!$this->fileView->isUpdatable($sourceDir)) { throw new \Sabre_DAV_Exception_Forbidden(); } - if (!$fs->isUpdatable($destinationDir)) { + if (!$this->fileView->isUpdatable($destinationDir)) { throw new \Sabre_DAV_Exception_Forbidden(); } - if (!$fs->isDeletable($sourcePath)) { + if (!$this->fileView->isDeletable($sourcePath)) { throw new \Sabre_DAV_Exception_Forbidden(); } } @@ -110,15 +124,15 @@ class ObjectTree extends \Sabre_DAV_ObjectTree { throw new \Sabre_DAV_Exception_BadRequest(); } - $renameOkay = $fs->rename($sourcePath, $destinationPath); + $renameOkay = $this->fileView->rename($sourcePath, $destinationPath); if (!$renameOkay) { throw new \Sabre_DAV_Exception_Forbidden(''); } // update properties - $query = \OC_DB::prepare( 'UPDATE `*PREFIX*properties` SET `propertypath` = ?' - .' WHERE `userid` = ? AND `propertypath` = ?' ); - $query->execute( array( $destinationPath, \OC_User::getUser(), $sourcePath )); + $query = \OC_DB::prepare('UPDATE `*PREFIX*properties` SET `propertypath` = ?' + . ' WHERE `userid` = ? AND `propertypath` = ?'); + $query->execute(array($destinationPath, \OC_User::getUser(), $sourcePath)); $this->markDirty($sourceDir); $this->markDirty($destinationDir); @@ -137,12 +151,12 @@ class ObjectTree extends \Sabre_DAV_ObjectTree { */ public function copy($source, $destination) { - if (Filesystem::is_file($source)) { - Filesystem::copy($source, $destination); + if ($this->fileView->is_file($source)) { + $this->fileView->copy($source, $destination); } else { - Filesystem::mkdir($destination); - $dh = Filesystem::opendir($source); - if(is_resource($dh)) { + $this->fileView->mkdir($destination); + $dh = $this->fileView->opendir($source); + if (is_resource($dh)) { while (($subnode = readdir($dh)) !== false) { if ($subnode == '.' || $subnode == '..') continue; @@ -155,14 +169,4 @@ class ObjectTree extends \Sabre_DAV_ObjectTree { list($destinationDir,) = \Sabre_DAV_URLUtil::splitPath($destination); $this->markDirty($destinationDir); } - - /** - * @return \OC\Files\View - */ - public function getFileView() { - if (is_null($this->fileView)) { - $this->fileView = \OC\Files\Filesystem::getView(); - } - return $this->fileView; - } } diff --git a/lib/private/connector/sabre/quotaplugin.php b/lib/private/connector/sabre/quotaplugin.php index 8099794f67..13fb187eed 100644 --- a/lib/private/connector/sabre/quotaplugin.php +++ b/lib/private/connector/sabre/quotaplugin.php @@ -9,6 +9,11 @@ */ class OC_Connector_Sabre_QuotaPlugin extends Sabre_DAV_ServerPlugin { + /** + * @var \OC\Files\View + */ + private $view; + /** * Reference to main server object * @@ -17,11 +22,11 @@ class OC_Connector_Sabre_QuotaPlugin extends Sabre_DAV_ServerPlugin { private $server; /** - * is kept public to allow overwrite for unit testing - * - * @var \OC\Files\View + * @param \OC\Files\View $view */ - public $fileView; + public function __construct($view) { + $this->view = $view; + } /** * This initializes the plugin. @@ -52,8 +57,8 @@ class OC_Connector_Sabre_QuotaPlugin extends Sabre_DAV_ServerPlugin { public function checkQuota($uri, $data = null) { $length = $this->getLength(); if ($length) { - if (substr($uri, 0, 1)!=='/') { - $uri='/'.$uri; + if (substr($uri, 0, 1) !== '/') { + $uri = '/' . $uri; } list($parentUri, $newName) = Sabre_DAV_URLUtil::splitPath($uri); $freeSpace = $this->getFreeSpace($parentUri); @@ -64,8 +69,7 @@ class OC_Connector_Sabre_QuotaPlugin extends Sabre_DAV_ServerPlugin { return true; } - public function getLength() - { + public function getLength() { $req = $this->server->httpRequest; $length = $req->getHeader('X-Expected-Entity-Length'); if (!$length) { @@ -84,14 +88,8 @@ class OC_Connector_Sabre_QuotaPlugin extends Sabre_DAV_ServerPlugin { * @param $parentUri * @return mixed */ - public function getFreeSpace($parentUri) - { - if (is_null($this->fileView)) { - // initialize fileView - $this->fileView = \OC\Files\Filesystem::getView(); - } - - $freeSpace = $this->fileView->free_space($parentUri); + public function getFreeSpace($parentUri) { + $freeSpace = $this->view->free_space($parentUri); return $freeSpace; } } diff --git a/lib/private/connector/sabre/server.php b/lib/private/connector/sabre/server.php index 2660b043f4..bb7a7171d1 100644 --- a/lib/private/connector/sabre/server.php +++ b/lib/private/connector/sabre/server.php @@ -27,6 +27,22 @@ * @see Sabre_DAV_Server */ class OC_Connector_Sabre_Server extends Sabre_DAV_Server { + /** + * Sets up the server + * + * Unlike Sabre_DAV_Server's constructor this does not take an INode or ObjectTree as argument, + * the object tree needs to be set later with setObjectTree + */ + public function __construct() { + $this->httpResponse = new Sabre_HTTP_Response(); + $this->httpRequest = new Sabre_HTTP_Request(); + + } + + public function setObjectTree($tree) { + $this->tree = $tree; + } + /** * @see Sabre_DAV_Server @@ -40,22 +56,22 @@ class OC_Connector_Sabre_Server extends Sabre_DAV_Server { // The only two options for the depth of a propfind is 0 or 1 // if ($depth!=0) $depth = 1; - $newProperties = $this->getPropertiesForPath($uri,$requestedProperties,$depth); + $newProperties = $this->getPropertiesForPath($uri, $requestedProperties, $depth); // This is a multi-status response $this->httpResponse->sendStatus(207); - $this->httpResponse->setHeader('Content-Type','application/xml; charset=utf-8'); - $this->httpResponse->setHeader('Vary','Brief,Prefer'); + $this->httpResponse->setHeader('Content-Type', 'application/xml; charset=utf-8'); + $this->httpResponse->setHeader('Vary', 'Brief,Prefer'); // Normally this header is only needed for OPTIONS responses, however.. // iCal seems to also depend on these being set for PROPFIND. Since // this is not harmful, we'll add it. - $features = array('1','3', 'extended-mkcol'); - foreach($this->plugins as $plugin) { - $features = array_merge($features,$plugin->getFeatures()); + $features = array('1', '3', 'extended-mkcol'); + foreach ($this->plugins as $plugin) { + $features = array_merge($features, $plugin->getFeatures()); } - $this->httpResponse->setHeader('DAV',implode(', ',$features)); + $this->httpResponse->setHeader('DAV', implode(', ', $features)); $prefer = $this->getHTTPPrefer(); $minimal = $prefer['return-minimal']; @@ -67,10 +83,11 @@ class OC_Connector_Sabre_Server extends Sabre_DAV_Server { /** * Small helper to support PROPFIND with DEPTH_INFINITY. + * * @param string $path */ private function addPathNodesRecursively(&$nodes, $path) { - foreach($this->tree->getChildren($path) as $childNode) { + foreach ($this->tree->getChildren($path) as $childNode) { $nodes[$path . '/' . $childNode->getName()] = $childNode; if ($childNode instanceof Sabre_DAV_ICollection) $this->addPathNodesRecursively($nodes, $path . '/' . $childNode->getName()); @@ -81,7 +98,7 @@ class OC_Connector_Sabre_Server extends Sabre_DAV_Server { // if ($depth!=0) $depth = 1; - $path = rtrim($path,'/'); + $path = rtrim($path, '/'); $returnPropertyList = array(); @@ -89,9 +106,10 @@ class OC_Connector_Sabre_Server extends Sabre_DAV_Server { $nodes = array( $path => $parentNode ); - if ($depth==1 && $parentNode instanceof Sabre_DAV_ICollection) { - foreach($this->tree->getChildren($path) as $childNode) + if ($depth == 1 && $parentNode instanceof Sabre_DAV_ICollection) { + foreach ($this->tree->getChildren($path) as $childNode) { $nodes[$path . '/' . $childNode->getName()] = $childNode; + } } else if ($depth == self::DEPTH_INFINITY && $parentNode instanceof Sabre_DAV_ICollection) { $this->addPathNodesRecursively($nodes, $path); } @@ -99,9 +117,9 @@ class OC_Connector_Sabre_Server extends Sabre_DAV_Server { // If the propertyNames array is empty, it means all properties are requested. // We shouldn't actually return everything we know though, and only return a // sensible list. - $allProperties = count($propertyNames)==0; + $allProperties = count($propertyNames) == 0; - foreach($nodes as $myPath=>$node) { + foreach ($nodes as $myPath => $node) { $currentPropertyNames = $propertyNames; @@ -128,15 +146,15 @@ class OC_Connector_Sabre_Server extends Sabre_DAV_Server { // to make certain decisions about the entry. // WebDAV dictates we should add a / and the end of href's for collections $removeRT = false; - if (!in_array('{DAV:}resourcetype',$currentPropertyNames)) { + if (!in_array('{DAV:}resourcetype', $currentPropertyNames)) { $currentPropertyNames[] = '{DAV:}resourcetype'; $removeRT = true; } - $result = $this->broadcastEvent('beforeGetProperties',array($myPath, $node, &$currentPropertyNames, &$newProperties)); + $result = $this->broadcastEvent('beforeGetProperties', array($myPath, $node, &$currentPropertyNames, &$newProperties)); // If this method explicitly returned false, we must ignore this // node as it is inaccessible. - if ($result===false) continue; + if ($result === false) continue; if (count($currentPropertyNames) > 0) { @@ -149,7 +167,7 @@ class OC_Connector_Sabre_Server extends Sabre_DAV_Server { // So as we loop through this list, we will only take the // properties that were actually requested and discard the // rest. - foreach($currentPropertyNames as $k=>$currentPropertyName) { + foreach ($currentPropertyNames as $k => $currentPropertyName) { if (isset($nodeProperties[$currentPropertyName])) { unset($currentPropertyNames[$k]); $newProperties[200][$currentPropertyName] = $nodeProperties[$currentPropertyName]; @@ -160,12 +178,14 @@ class OC_Connector_Sabre_Server extends Sabre_DAV_Server { } - foreach($currentPropertyNames as $prop) { + foreach ($currentPropertyNames as $prop) { if (isset($newProperties[200][$prop])) continue; - switch($prop) { - case '{DAV:}getlastmodified' : if ($node->getLastModified()) $newProperties[200][$prop] = new Sabre_DAV_Property_GetLastModified($node->getLastModified()); break; + switch ($prop) { + case '{DAV:}getlastmodified' : + if ($node->getLastModified()) $newProperties[200][$prop] = new Sabre_DAV_Property_GetLastModified($node->getLastModified()); + break; case '{DAV:}getcontentlength' : if ($node instanceof Sabre_DAV_IFile) { $size = $node->getSize(); @@ -186,18 +206,22 @@ class OC_Connector_Sabre_Server extends Sabre_DAV_Server { $newProperties[200][$prop] = $quotaInfo[1]; } break; - case '{DAV:}getetag' : if ($node instanceof Sabre_DAV_IFile && $etag = $node->getETag()) $newProperties[200][$prop] = $etag; break; - case '{DAV:}getcontenttype' : if ($node instanceof Sabre_DAV_IFile && $ct = $node->getContentType()) $newProperties[200][$prop] = $ct; break; + case '{DAV:}getetag' : + if ($node instanceof Sabre_DAV_IFile && $etag = $node->getETag()) $newProperties[200][$prop] = $etag; + break; + case '{DAV:}getcontenttype' : + if ($node instanceof Sabre_DAV_IFile && $ct = $node->getContentType()) $newProperties[200][$prop] = $ct; + break; case '{DAV:}supported-report-set' : $reports = array(); - foreach($this->plugins as $plugin) { + foreach ($this->plugins as $plugin) { $reports = array_merge($reports, $plugin->getSupportedReportSet($myPath)); } $newProperties[200][$prop] = new Sabre_DAV_Property_SupportedReportSet($reports); break; case '{DAV:}resourcetype' : $newProperties[200]['{DAV:}resourcetype'] = new Sabre_DAV_Property_ResourceType(); - foreach($this->resourceTypeMapping as $className => $resourceType) { + foreach ($this->resourceTypeMapping as $className => $resourceType) { if ($node instanceof $className) $newProperties[200]['{DAV:}resourcetype']->add($resourceType); } break; @@ -209,16 +233,16 @@ class OC_Connector_Sabre_Server extends Sabre_DAV_Server { } - $this->broadcastEvent('afterGetProperties',array(trim($myPath,'/'),&$newProperties, $node)); + $this->broadcastEvent('afterGetProperties', array(trim($myPath, '/'), &$newProperties, $node)); - $newProperties['href'] = trim($myPath,'/'); + $newProperties['href'] = trim($myPath, '/'); // Its is a WebDAV recommendation to add a trailing slash to collectionnames. // Apple's iCal also requires a trailing slash for principals (rfc 3744), though this is non-standard. - if ($myPath!='' && isset($newProperties[200]['{DAV:}resourcetype'])) { + if ($myPath != '' && isset($newProperties[200]['{DAV:}resourcetype'])) { $rt = $newProperties[200]['{DAV:}resourcetype']; if ($rt->is('{DAV:}collection') || $rt->is('{DAV:}principal')) { - $newProperties['href'] .='/'; + $newProperties['href'] .= '/'; } } From 9231195c98b5fc709903e8a0b639b882aa3f9ec8 Mon Sep 17 00:00:00 2001 From: Robin Appelman Date: Mon, 3 Mar 2014 13:51:49 +0100 Subject: [PATCH 005/187] Fix FileInfo->getType --- lib/private/files/fileinfo.php | 8 ++++++-- lib/public/files/fileinfo.php | 2 +- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/lib/private/files/fileinfo.php b/lib/private/files/fileinfo.php index 2dbdd80a26..916346b608 100644 --- a/lib/private/files/fileinfo.php +++ b/lib/private/files/fileinfo.php @@ -144,10 +144,14 @@ class FileInfo implements \OCP\Files\FileInfo, \ArrayAccess { * @return \OCP\Files\FileInfo::TYPE_FILE | \OCP\Files\FileInfo::TYPE_FOLDER */ public function getType() { - return $this->data['type']; + if ($this->data['type']) { + return $this->data['type']; + } else { + return $this->getMimetype() === 'httpd/unix-directory' ? self::TYPE_FOLDER : self::TYPE_FILE; + } } - public function getData(){ + public function getData() { return $this->data; } diff --git a/lib/public/files/fileinfo.php b/lib/public/files/fileinfo.php index 68ce45d3fa..37162e0933 100644 --- a/lib/public/files/fileinfo.php +++ b/lib/public/files/fileinfo.php @@ -9,7 +9,7 @@ namespace OCP\Files; interface FileInfo { const TYPE_FILE = 'file'; - const TYPE_FOLDER = 'folder'; + const TYPE_FOLDER = 'dir'; /** * Get the Etag of the file or folder From fe994669cdec336f6c4a3e724a9d468fd8f69dfe Mon Sep 17 00:00:00 2001 From: Robin Appelman Date: Mon, 3 Mar 2014 13:57:08 +0100 Subject: [PATCH 006/187] Make path for dummy fileinfo absolute --- lib/private/connector/sabre/directory.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/private/connector/sabre/directory.php b/lib/private/connector/sabre/directory.php index 619aec21ea..641354e763 100644 --- a/lib/private/connector/sabre/directory.php +++ b/lib/private/connector/sabre/directory.php @@ -72,7 +72,7 @@ class OC_Connector_Sabre_Directory extends OC_Connector_Sabre_Node implements Sa } } - $path = $this->path . '/' . $name; + $path = $this->fileView->getAbsolutePath($this->path) . '/' . $name; // using a dummy FileInfo is acceptable here since it will be refreshed after the put is complete $info = new \OC\Files\FileInfo($path, null, null, array()); $node = new OC_Connector_Sabre_File($this->fileView, $info); From 331fc55e2d876123580986264f47318afcf9352b Mon Sep 17 00:00:00 2001 From: Robin Appelman Date: Mon, 3 Mar 2014 14:27:24 +0100 Subject: [PATCH 007/187] Update unit tests to use the new injection --- .../sabre/aborteduploaddetectionplugin.php | 27 +++---- tests/lib/connector/sabre/directory.php | 22 ++++- tests/lib/connector/sabre/file.php | 81 +++++++++++++++---- tests/lib/connector/sabre/objecttree.php | 14 +++- tests/lib/connector/sabre/quotaplugin.php | 23 +++--- 5 files changed, 120 insertions(+), 47 deletions(-) diff --git a/tests/lib/connector/sabre/aborteduploaddetectionplugin.php b/tests/lib/connector/sabre/aborteduploaddetectionplugin.php index 201f126386..60d141e72b 100644 --- a/tests/lib/connector/sabre/aborteduploaddetectionplugin.php +++ b/tests/lib/connector/sabre/aborteduploaddetectionplugin.php @@ -1,11 +1,11 @@ * This file is licensed under the Affero General Public License version 3 or * later. * See the COPYING-README file. */ - class Test_OC_Connector_Sabre_AbortedUploadDetectionPlugin extends PHPUnit_Framework_TestCase { /** @@ -18,17 +18,18 @@ class Test_OC_Connector_Sabre_AbortedUploadDetectionPlugin extends PHPUnit_Frame */ private $plugin; - public function setUp() { + private function init($view) { $this->server = new Sabre_DAV_Server(); - $this->plugin = new OC_Connector_Sabre_AbortedUploadDetectionPlugin(); + $this->plugin = new OC_Connector_Sabre_AbortedUploadDetectionPlugin($view); $this->plugin->initialize($this->server); } /** * @dataProvider lengthProvider */ - public function testLength($expected, $headers) - { + public function testLength($expected, $headers) { + $this->init(null); + $this->server->httpRequest = new Sabre_HTTP_Request($headers); $length = $this->plugin->getLength(); $this->assertEquals($expected, $length); @@ -37,9 +38,8 @@ class Test_OC_Connector_Sabre_AbortedUploadDetectionPlugin extends PHPUnit_Frame /** * @dataProvider verifyContentLengthProvider */ - public function testVerifyContentLength($method, $fileSize, $headers) - { - $this->plugin->fileView = $this->buildFileViewMock($fileSize); + public function testVerifyContentLength($method, $fileSize, $headers) { + $this->init($this->buildFileViewMock($fileSize)); $headers['REQUEST_METHOD'] = $method; $this->server->httpRequest = new Sabre_HTTP_Request($headers); @@ -51,12 +51,11 @@ class Test_OC_Connector_Sabre_AbortedUploadDetectionPlugin extends PHPUnit_Frame * @dataProvider verifyContentLengthFailedProvider * @expectedException Sabre_DAV_Exception_BadRequest */ - public function testVerifyContentLengthFailed($method, $fileSize, $headers) - { - $this->plugin->fileView = $this->buildFileViewMock($fileSize); - + public function testVerifyContentLengthFailed($method, $fileSize, $headers) { + $view = $this->buildFileViewMock($fileSize); + $this->init($view); // we expect unlink to be called - $this->plugin->fileView->expects($this->once())->method('unlink'); + $view->expects($this->once())->method('unlink'); $headers['REQUEST_METHOD'] = $method; $this->server->httpRequest = new Sabre_HTTP_Request($headers); @@ -92,7 +91,7 @@ class Test_OC_Connector_Sabre_AbortedUploadDetectionPlugin extends PHPUnit_Frame private function buildFileViewMock($fileSize) { // mock filesystem - $view = $this->getMock('\OC\Files\View', array('filesize', 'unlink'), array(), '', FALSE); + $view = $this->getMock('\OC\Files\View', array('filesize', 'unlink'), array(), '', false); $view->expects($this->any())->method('filesize')->withAnyParameters()->will($this->returnValue($fileSize)); return $view; diff --git a/tests/lib/connector/sabre/directory.php b/tests/lib/connector/sabre/directory.php index c501521b60..b2bf0d4a6d 100644 --- a/tests/lib/connector/sabre/directory.php +++ b/tests/lib/connector/sabre/directory.php @@ -1,18 +1,32 @@ * This file is licensed under the Affero General Public License version 3 or * later. * See the COPYING-README file. */ - class Test_OC_Connector_Sabre_Directory extends PHPUnit_Framework_TestCase { + private function getRootDir() { + $view = $this->getMock('OC\Files\View', array(), array(), '', false); + $view->expects($this->once()) + ->method('getRelativePath') + ->will($this->returnValue('')); + + $info = $this->getMock('OC\Files\FileInfo', array(), array(), '', false); + $info->expects($this->once()) + ->method('getPath') + ->will($this->returnValue('')); + + return new OC_Connector_Sabre_Directory($view, $info); + } + /** * @expectedException Sabre_DAV_Exception_Forbidden */ public function testCreateSharedFileFails() { - $dir = new OC_Connector_Sabre_Directory(''); + $dir = $this->getRootDir(); $dir->createFile('Shared'); } @@ -20,7 +34,7 @@ class Test_OC_Connector_Sabre_Directory extends PHPUnit_Framework_TestCase { * @expectedException Sabre_DAV_Exception_Forbidden */ public function testCreateSharedFolderFails() { - $dir = new OC_Connector_Sabre_Directory(''); + $dir = $this->getRootDir(); $dir->createDirectory('Shared'); } @@ -28,7 +42,7 @@ class Test_OC_Connector_Sabre_Directory extends PHPUnit_Framework_TestCase { * @expectedException Sabre_DAV_Exception_Forbidden */ public function testDeleteSharedFolderFails() { - $dir = new OC_Connector_Sabre_Directory('Shared'); + $dir = $this->getRootDir(); $dir->delete(); } } diff --git a/tests/lib/connector/sabre/file.php b/tests/lib/connector/sabre/file.php index c2f0ffa12d..011f8ffb6e 100644 --- a/tests/lib/connector/sabre/file.php +++ b/tests/lib/connector/sabre/file.php @@ -13,9 +13,20 @@ class Test_OC_Connector_Sabre_File extends PHPUnit_Framework_TestCase { */ public function testSimplePutFails() { // setup - $file = new OC_Connector_Sabre_File('/test.txt'); - $file->fileView = $this->getMock('\OC\Files\View', array('file_put_contents'), array(), '', FALSE); - $file->fileView->expects($this->any())->method('file_put_contents')->withAnyParameters()->will($this->returnValue(false)); + $view = $this->getMock('\OC\Files\View', array('file_put_contents', 'getRelativePath'), array(), '', false); + $view->expects($this->any()) + ->method('file_put_contents') + ->will($this->returnValue(false)); + + $view->expects($this->any()) + ->method('getRelativePath') + ->will($this->returnValue('/test.txt')); + + $info = new \OC\Files\FileInfo('/test.txt', null, null, array( + 'permissions'=>\OCP\PERMISSION_ALL + )); + + $file = new OC_Connector_Sabre_File($view, $info); // action $etag = $file->put('test data'); @@ -26,10 +37,25 @@ class Test_OC_Connector_Sabre_File extends PHPUnit_Framework_TestCase { */ public function testSimplePutFailsOnRename() { // setup - $file = new OC_Connector_Sabre_File('/test.txt'); - $file->fileView = $this->getMock('\OC\Files\View', array('file_put_contents', 'rename'), array(), '', FALSE); - $file->fileView->expects($this->any())->method('file_put_contents')->withAnyParameters()->will($this->returnValue(true)); - $file->fileView->expects($this->any())->method('rename')->withAnyParameters()->will($this->returnValue(false)); + $view = $this->getMock('\OC\Files\View', array('file_put_contents', 'rename', 'getRelativePath'), array(), '', false); + $view->expects($this->any()) + ->method('file_put_contents') + ->withAnyParameters() + ->will($this->returnValue(true)); + $view->expects($this->any()) + ->method('rename') + ->withAnyParameters() + ->will($this->returnValue(false)); + + $view->expects($this->any()) + ->method('getRelativePath') + ->will($this->returnValue('/test.txt')); + + $info = new \OC\Files\FileInfo('/test.txt', null, null, array( + 'permissions' => \OCP\PERMISSION_ALL + )); + + $file = new OC_Connector_Sabre_File($view, $info); // action $etag = $file->put('test data'); @@ -40,9 +66,19 @@ class Test_OC_Connector_Sabre_File extends PHPUnit_Framework_TestCase { */ public function testSimplePutInvalidChars() { // setup - $file = new OC_Connector_Sabre_File('/super*star.txt'); - $file->fileView = $this->getMock('\OC\Files\View', array('file_put_contents'), array(), '', FALSE); - $file->fileView->expects($this->any())->method('file_put_contents')->withAnyParameters()->will($this->returnValue(false)); + $view = $this->getMock('\OC\Files\View', array('file_put_contents', 'getRelativePath'), array(), '', false); + $view->expects($this->any()) + ->method('file_put_contents') + ->will($this->returnValue(false)); + + $view->expects($this->any()) + ->method('getRelativePath') + ->will($this->returnValue('/super*star.txt')); + + $info = new \OC\Files\FileInfo('/super*star.txt', null, null, array( + 'permissions' => \OCP\PERMISSION_ALL + )); + $file = new OC_Connector_Sabre_File($view, $info); // action $etag = $file->put('test data'); @@ -54,9 +90,16 @@ class Test_OC_Connector_Sabre_File extends PHPUnit_Framework_TestCase { */ public function testSetNameInvalidChars() { // setup - $file = new OC_Connector_Sabre_File('/test.txt'); - $file->fileView = $this->getMock('\OC\Files\View', array('isUpdatable'), array(), '', FALSE); - $file->fileView->expects($this->any())->method('isUpdatable')->withAnyParameters()->will($this->returnValue(true)); + $view = $this->getMock('\OC\Files\View', array('getRelativePath'), array(), '', false); + + $view->expects($this->any()) + ->method('getRelativePath') + ->will($this->returnValue('/super*star.txt')); + + $info = new \OC\Files\FileInfo('/super*star.txt', null, null, array( + 'permissions' => \OCP\PERMISSION_ALL + )); + $file = new OC_Connector_Sabre_File($view, $info); $file->setName('/super*star.txt'); } @@ -64,7 +107,17 @@ class Test_OC_Connector_Sabre_File extends PHPUnit_Framework_TestCase { * @expectedException Sabre_DAV_Exception_Forbidden */ public function testDeleteSharedFails() { - $file = new OC_Connector_Sabre_File('Shared'); + $view = $this->getMock('\OC\Files\View', array('getRelativePath'), array(), '', false); + + $view->expects($this->any()) + ->method('getRelativePath') + ->will($this->returnValue('Shared')); + + $info = new \OC\Files\FileInfo('/Shared', null, null, array( + 'permissions' => \OCP\PERMISSION_ALL + )); + + $file = new OC_Connector_Sabre_File($view, $info); $file->delete(); } } diff --git a/tests/lib/connector/sabre/objecttree.php b/tests/lib/connector/sabre/objecttree.php index fb50c736ed..876334cf68 100644 --- a/tests/lib/connector/sabre/objecttree.php +++ b/tests/lib/connector/sabre/objecttree.php @@ -9,6 +9,7 @@ namespace Test\OC\Connector\Sabre; +use OC\Files\FileInfo; use OC_Connector_Sabre_Directory; use PHPUnit_Framework_TestCase; use Sabre_DAV_Exception_Forbidden; @@ -32,6 +33,10 @@ class TestDoubleFileView extends \OC\Files\View{ public function rename($path1, $path2) { return $this->canRename; } + + public function getRelativePath($path){ + return $path; + } } class ObjectTree extends PHPUnit_Framework_TestCase { @@ -91,10 +96,14 @@ class ObjectTree extends PHPUnit_Framework_TestCase { * @param $updatables */ private function moveTest($source, $dest, $updatables, $deletables) { - $rootDir = new OC_Connector_Sabre_Directory(''); + $view = new TestDoubleFileView($updatables, $deletables); + + $info = new FileInfo('', null, null, array()); + + $rootDir = new OC_Connector_Sabre_Directory($view, $info); $objectTree = $this->getMock('\OC\Connector\Sabre\ObjectTree', array('nodeExists', 'getNodeForPath'), - array($rootDir)); + array($rootDir, $view)); $objectTree->expects($this->once()) ->method('getNodeForPath') @@ -102,7 +111,6 @@ class ObjectTree extends PHPUnit_Framework_TestCase { ->will($this->returnValue(false)); /** @var $objectTree \OC\Connector\Sabre\ObjectTree */ - $objectTree->fileView = new TestDoubleFileView($updatables, $deletables); $objectTree->move($source, $dest); } diff --git a/tests/lib/connector/sabre/quotaplugin.php b/tests/lib/connector/sabre/quotaplugin.php index 1186de2874..6781b970a4 100644 --- a/tests/lib/connector/sabre/quotaplugin.php +++ b/tests/lib/connector/sabre/quotaplugin.php @@ -1,11 +1,11 @@ * This file is licensed under the Affero General Public License version 3 or * later. * See the COPYING-README file. */ - class Test_OC_Connector_Sabre_QuotaPlugin extends PHPUnit_Framework_TestCase { /** @@ -18,17 +18,18 @@ class Test_OC_Connector_Sabre_QuotaPlugin extends PHPUnit_Framework_TestCase { */ private $plugin; - public function setUp() { + private function init($quota) { + $view = $this->buildFileViewMock($quota); $this->server = new Sabre_DAV_Server(); - $this->plugin = new OC_Connector_Sabre_QuotaPlugin(); + $this->plugin = new OC_Connector_Sabre_QuotaPlugin($view); $this->plugin->initialize($this->server); } /** * @dataProvider lengthProvider */ - public function testLength($expected, $headers) - { + public function testLength($expected, $headers) { + $this->init(0); $this->server->httpRequest = new Sabre_HTTP_Request($headers); $length = $this->plugin->getLength(); $this->assertEquals($expected, $length); @@ -37,9 +38,8 @@ class Test_OC_Connector_Sabre_QuotaPlugin extends PHPUnit_Framework_TestCase { /** * @dataProvider quotaOkayProvider */ - public function testCheckQuota($quota, $headers) - { - $this->plugin->fileView = $this->buildFileViewMock($quota); + public function testCheckQuota($quota, $headers) { + $this->init($quota); $this->server->httpRequest = new Sabre_HTTP_Request($headers); $result = $this->plugin->checkQuota(''); @@ -50,9 +50,8 @@ class Test_OC_Connector_Sabre_QuotaPlugin extends PHPUnit_Framework_TestCase { * @expectedException Sabre_DAV_Exception_InsufficientStorage * @dataProvider quotaExceededProvider */ - public function testCheckExceededQuota($quota, $headers) - { - $this->plugin->fileView = $this->buildFileViewMock($quota); + public function testCheckExceededQuota($quota, $headers) { + $this->init($quota); $this->server->httpRequest = new Sabre_HTTP_Request($headers); $this->plugin->checkQuota(''); @@ -92,7 +91,7 @@ class Test_OC_Connector_Sabre_QuotaPlugin extends PHPUnit_Framework_TestCase { private function buildFileViewMock($quota) { // mock filesysten - $view = $this->getMock('\OC\Files\View', array('free_space'), array(), '', FALSE); + $view = $this->getMock('\OC\Files\View', array('free_space'), array(), '', false); $view->expects($this->any())->method('free_space')->withAnyParameters()->will($this->returnValue($quota)); return $view; From 5ef37c28d1dc73c1fc16a731954b84b46c1943f4 Mon Sep 17 00:00:00 2001 From: Robin Appelman Date: Tue, 4 Mar 2014 13:28:48 +0100 Subject: [PATCH 008/187] Remove the need for a custom SabreDav server constructor --- apps/files/appinfo/remote.php | 8 ++++---- lib/private/connector/sabre/objecttree.php | 19 ++++++++++++++++--- lib/private/connector/sabre/server.php | 11 ----------- 3 files changed, 20 insertions(+), 18 deletions(-) diff --git a/apps/files/appinfo/remote.php b/apps/files/appinfo/remote.php index 1922bc4fcd..d9c667c010 100644 --- a/apps/files/appinfo/remote.php +++ b/apps/files/appinfo/remote.php @@ -35,7 +35,8 @@ $lockBackend = new OC_Connector_Sabre_Locks(); $requestBackend = new OC_Connector_Sabre_Request(); // Fire up server -$server = new OC_Connector_Sabre_Server(); +$objectTree = new \OC\Connector\Sabre\ObjectTree(); +$server = new OC_Connector_Sabre_Server($objectTree); $server->httpRequest = $requestBackend; $server->setBaseUri($baseuri); @@ -49,14 +50,13 @@ $server->addPlugin(new OC_Connector_Sabre_MaintenancePlugin()); $server->addPlugin(new OC_Connector_Sabre_ExceptionLoggerPlugin('webdav')); // wait with registering these until auth is handled and the filesystem is setup -$server->subscribeEvent('beforeMethod', function () use ($server) { +$server->subscribeEvent('beforeMethod', function () use ($server, $objectTree) { $view = \OC\Files\Filesystem::getView(); $rootInfo = $view->getFileInfo(''); // Create ownCloud Dir $rootDir = new OC_Connector_Sabre_Directory($view, $rootInfo); - $objectTree = new \OC\Connector\Sabre\ObjectTree($rootDir, $view); - $server->setObjectTree($objectTree); + $objectTree->init($rootDir, $view); $server->addPlugin(new OC_Connector_Sabre_AbortedUploadDetectionPlugin($view)); $server->addPlugin(new OC_Connector_Sabre_QuotaPlugin($view)); diff --git a/lib/private/connector/sabre/objecttree.php b/lib/private/connector/sabre/objecttree.php index d176d3333f..76f4817d3b 100644 --- a/lib/private/connector/sabre/objecttree.php +++ b/lib/private/connector/sabre/objecttree.php @@ -22,12 +22,16 @@ class ObjectTree extends \Sabre_DAV_ObjectTree { * Creates the object * * This method expects the rootObject to be passed as a parameter - * + */ + public function __construct() { + } + + /** * @param \Sabre_DAV_ICollection $rootNode * @param \OC\Files\View $view */ - public function __construct(\Sabre_DAV_ICollection $rootNode, $view) { - parent::__construct($rootNode); + public function init(\Sabre_DAV_ICollection $rootNode, \OC\Files\View $view) { + $this->rootNode = $rootNode; $this->fileView = $view; } @@ -39,6 +43,9 @@ class ObjectTree extends \Sabre_DAV_ObjectTree { * @return \Sabre_DAV_INode */ public function getNodeForPath($path) { + if (!$this->fileView) { + throw new \Sabre_DAV_Exception_ServiceUnavailable('filesystem not setup'); + } $path = trim($path, '/'); if (isset($this->cache[$path])) { @@ -94,6 +101,9 @@ class ObjectTree extends \Sabre_DAV_ObjectTree { * @return int */ public function move($sourcePath, $destinationPath) { + if (!$this->fileView) { + throw new \Sabre_DAV_Exception_ServiceUnavailable('filesystem not setup'); + } $sourceNode = $this->getNodeForPath($sourcePath); if ($sourceNode instanceof \Sabre_DAV_ICollection and $this->nodeExists($destinationPath)) { @@ -150,6 +160,9 @@ class ObjectTree extends \Sabre_DAV_ObjectTree { * @return void */ public function copy($source, $destination) { + if (!$this->fileView) { + throw new \Sabre_DAV_Exception_ServiceUnavailable('filesystem not setup'); + } if ($this->fileView->is_file($source)) { $this->fileView->copy($source, $destination); diff --git a/lib/private/connector/sabre/server.php b/lib/private/connector/sabre/server.php index bb7a7171d1..b09237fb3c 100644 --- a/lib/private/connector/sabre/server.php +++ b/lib/private/connector/sabre/server.php @@ -27,17 +27,6 @@ * @see Sabre_DAV_Server */ class OC_Connector_Sabre_Server extends Sabre_DAV_Server { - /** - * Sets up the server - * - * Unlike Sabre_DAV_Server's constructor this does not take an INode or ObjectTree as argument, - * the object tree needs to be set later with setObjectTree - */ - public function __construct() { - $this->httpResponse = new Sabre_HTTP_Response(); - $this->httpRequest = new Sabre_HTTP_Request(); - - } public function setObjectTree($tree) { $this->tree = $tree; From 52115662057408d095249a237a78c2207ab1b821 Mon Sep 17 00:00:00 2001 From: Robin Appelman Date: Tue, 4 Mar 2014 16:36:03 +0100 Subject: [PATCH 009/187] update test case --- tests/lib/connector/sabre/objecttree.php | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/lib/connector/sabre/objecttree.php b/tests/lib/connector/sabre/objecttree.php index 876334cf68..bc8ec98fae 100644 --- a/tests/lib/connector/sabre/objecttree.php +++ b/tests/lib/connector/sabre/objecttree.php @@ -111,6 +111,7 @@ class ObjectTree extends PHPUnit_Framework_TestCase { ->will($this->returnValue(false)); /** @var $objectTree \OC\Connector\Sabre\ObjectTree */ + $objectTree->init($rootDir, $view); $objectTree->move($source, $dest); } From a687498547448d6ff11eccc0acddcf0647873ce8 Mon Sep 17 00:00:00 2001 From: Robin Appelman Date: Wed, 5 Mar 2014 13:19:08 +0100 Subject: [PATCH 010/187] Fix encryption webdav tests --- apps/files_encryption/tests/util.php | 6 +++++- apps/files_encryption/tests/webdav.php | 19 ++++++++++++++----- 2 files changed, 19 insertions(+), 6 deletions(-) diff --git a/apps/files_encryption/tests/util.php b/apps/files_encryption/tests/util.php index f70e30c4d7..5fa96f6ad6 100755 --- a/apps/files_encryption/tests/util.php +++ b/apps/files_encryption/tests/util.php @@ -510,7 +510,11 @@ class Test_Encryption_Util extends \PHPUnit_Framework_TestCase { */ public static function loginHelper($user, $create = false, $password = false) { if ($create) { - \OC_User::createUser($user, $user); + try { + \OC_User::createUser($user, $user); + } catch(\Exception $e) { // catch username is already being used from previous aborted runs + + } } if ($password === false) { diff --git a/apps/files_encryption/tests/webdav.php b/apps/files_encryption/tests/webdav.php index 8e8b9c53ce..1f23be3e79 100755 --- a/apps/files_encryption/tests/webdav.php +++ b/apps/files_encryption/tests/webdav.php @@ -33,6 +33,7 @@ use OCA\Encryption; /** * Class Test_Encryption_Webdav + * * @brief this class provide basic webdav tests for PUT,GET and DELETE */ class Test_Encryption_Webdav extends \PHPUnit_Framework_TestCase { @@ -48,6 +49,8 @@ class Test_Encryption_Webdav extends \PHPUnit_Framework_TestCase { public $dataShort; public $stateFilesTrashbin; + private static $storage; + public static function setUpBeforeClass() { // reset backend \OC_User::clearBackends(); @@ -65,6 +68,8 @@ class Test_Encryption_Webdav extends \PHPUnit_Framework_TestCase { // create test user \Test_Encryption_Util::loginHelper(\Test_Encryption_Webdav::TEST_ENCRYPTION_WEBDAV_USER1, true); + + self::$storage = new \OC\Files\Storage\Temporary(array()); } function setUp() { @@ -96,8 +101,7 @@ class Test_Encryption_Webdav extends \PHPUnit_Framework_TestCase { // reset app files_trashbin if ($this->stateFilesTrashbin) { OC_App::enable('files_trashbin'); - } - else { + } else { OC_App::disable('files_trashbin'); } } @@ -153,7 +157,7 @@ class Test_Encryption_Webdav extends \PHPUnit_Framework_TestCase { $this->assertTrue(Encryption\Crypt::isCatfileContent($encryptedContent)); // get decrypted file contents - $decrypt = file_get_contents('crypt:///' . $this->userId . '/files'. $filename); + $decrypt = file_get_contents('crypt:///' . $this->userId . '/files' . $filename); // check if file content match with the written content $this->assertEquals($this->dataShort, $decrypt); @@ -225,7 +229,12 @@ class Test_Encryption_Webdav extends \PHPUnit_Framework_TestCase { $requestBackend = new OC_Connector_Sabre_Request(); // Create ownCloud Dir - $publicDir = new OC_Connector_Sabre_Directory(''); + $root = '/' . $this->userId . '/files'; + \OC\Files\Filesystem::mount(self::$storage, array(), $root); + $view = new \OC\Files\View($root); + $publicDir = new OC_Connector_Sabre_Directory($view, $view->getFileInfo('')); + $objectTree = new \OC\Connector\Sabre\ObjectTree(); + $objectTree->init($publicDir, $view); // Fire up server $server = new Sabre_DAV_Server($publicDir); @@ -236,7 +245,7 @@ class Test_Encryption_Webdav extends \PHPUnit_Framework_TestCase { $server->addPlugin(new Sabre_DAV_Auth_Plugin($authBackend, 'ownCloud')); $server->addPlugin(new Sabre_DAV_Locks_Plugin($lockBackend)); $server->addPlugin(new Sabre_DAV_Browser_Plugin(false)); // Show something in the Browser, but no upload - $server->addPlugin(new OC_Connector_Sabre_QuotaPlugin()); + $server->addPlugin(new OC_Connector_Sabre_QuotaPlugin($view)); $server->addPlugin(new OC_Connector_Sabre_MaintenancePlugin()); // And off we go! From 8fcc29ee8b7b5ed600c8ec7116f52486718467f6 Mon Sep 17 00:00:00 2001 From: root Date: Tue, 10 Dec 2013 17:50:45 +0100 Subject: [PATCH 011/187] only check ldap_errno if an ldap_* function actually fails fix wizard errors reset search paging right after the search, not before some of the possible next search operations cookie contains binary characters, don't log it (avoids json_encode(): Invalid UTF-8 sequence in argument errors) --- apps/user_ldap/lib/access.php | 10 ++++++---- apps/user_ldap/lib/ldap.php | 6 ++++-- apps/user_ldap/lib/wizard.php | 1 + 3 files changed, 11 insertions(+), 6 deletions(-) diff --git a/apps/user_ldap/lib/access.php b/apps/user_ldap/lib/access.php index b7e4023dd7..018cf13c91 100644 --- a/apps/user_ldap/lib/access.php +++ b/apps/user_ldap/lib/access.php @@ -63,7 +63,6 @@ class Access extends LDAPUtility { return false; } //all or nothing! otherwise we get in trouble with. - $this->initPagedSearch($filter, array($dn), $attr, 99999, 0); $dn = $this->DNasBaseParameter($dn); $rr = @$this->ldap->read($cr, $dn, $filter, array($attr)); if(!$this->ldap->isResource($rr)) { @@ -707,6 +706,9 @@ class Access extends LDAPUtility { $linkResources = array_pad(array(), count($base), $cr); $sr = $this->ldap->search($linkResources, $base, $filter, $attr); $error = $this->ldap->errno($cr); + if ($pagedSearchOK) { + $this->ldap->controlPagedResult($cr, 0, false, ""); + } if(!is_array($sr) || $error !== 0) { \OCP\Util::writeLog('user_ldap', 'Error when searching: '.$this->ldap->error($cr). @@ -1236,9 +1238,9 @@ class Access extends LDAPUtility { if($this->connection->hasPagedResultSupport && !is_null($limit)) { $offset = intval($offset); //can be null \OCP\Util::writeLog('user_ldap', - 'initializing paged search for Filter'.$filter.' base '.print_r($bases, true) + 'initializing paged search for Filter '.$filter.' base '.print_r($bases, true) .' attr '.print_r($attr, true). ' limit ' .$limit.' offset '.$offset, - \OCP\Util::INFO); + \OCP\Util::DEBUG); //get the cookie from the search for the previous search, required by LDAP foreach($bases as $base) { @@ -1260,7 +1262,7 @@ class Access extends LDAPUtility { } if(!is_null($cookie)) { if($offset > 0) { - \OCP\Util::writeLog('user_ldap', 'Cookie '.$cookie, \OCP\Util::INFO); + \OCP\Util::writeLog('user_ldap', 'Cookie '.CRC32($cookie), \OCP\Util::INFO); } $pagedSearchOK = $this->ldap->controlPagedResult( $this->connection->getConnectionResource(), $limit, diff --git a/apps/user_ldap/lib/ldap.php b/apps/user_ldap/lib/ldap.php index de9b7481c1..0e5f78cd28 100644 --- a/apps/user_ldap/lib/ldap.php +++ b/apps/user_ldap/lib/ldap.php @@ -78,7 +78,7 @@ class LDAP implements ILDAPWrapper { } public function nextEntry($link, $result) { - return $this->invokeLDAPMethod('next_entry', $link, $result); + return ldap_next_entry($link, $result); } public function read($link, $baseDN, $filter, $attr) { @@ -139,7 +139,9 @@ class LDAP implements ILDAPWrapper { if(function_exists($func)) { $this->preFunctionCall($func, $arguments); $result = call_user_func_array($func, $arguments); - $this->postFunctionCall(); + if ($result === FALSE) { + $this->postFunctionCall(); + } return $result; } } diff --git a/apps/user_ldap/lib/wizard.php b/apps/user_ldap/lib/wizard.php index e79090febc..04ea87d53e 100644 --- a/apps/user_ldap/lib/wizard.php +++ b/apps/user_ldap/lib/wizard.php @@ -799,6 +799,7 @@ class Wizard extends LDAPUtility { \OCP\Util::writeLog('user_ldap', 'Wiz: Setting LDAP Options ', \OCP\Util::DEBUG); //set LDAP options $this->ldap->setOption($cr, LDAP_OPT_PROTOCOL_VERSION, 3); + $this->ldap->setOption($cr, LDAP_OPT_REFERRALS, 0); $this->ldap->setOption($cr, LDAP_OPT_NETWORK_TIMEOUT, self::LDAP_NW_TIMEOUT); if($tls) { $isTlsWorking = @$this->ldap->startTls($cr); From 6585bd5a500292a284490e96250d465280f3c34b Mon Sep 17 00:00:00 2001 From: root Date: Wed, 11 Dec 2013 11:00:53 +0100 Subject: [PATCH 012/187] "reset" ldap pagedSearch control by setting it to a high value call nextEntry via invokeLDAPMethod --- apps/user_ldap/lib/ldap.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/user_ldap/lib/ldap.php b/apps/user_ldap/lib/ldap.php index 0e5f78cd28..d1ca91045b 100644 --- a/apps/user_ldap/lib/ldap.php +++ b/apps/user_ldap/lib/ldap.php @@ -78,7 +78,7 @@ class LDAP implements ILDAPWrapper { } public function nextEntry($link, $result) { - return ldap_next_entry($link, $result); + return $this->invokeLDAPMethod('next_entry', $link, $result); } public function read($link, $baseDN, $filter, $attr) { From f850fcfac51080baf1af90d59a8d94b80721c819 Mon Sep 17 00:00:00 2001 From: Alexander Bergolth Date: Thu, 6 Mar 2014 12:23:20 +0100 Subject: [PATCH 013/187] "reset" ldap pagedSearch control by setting it to a high value --- apps/user_ldap/lib/access.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/user_ldap/lib/access.php b/apps/user_ldap/lib/access.php index 018cf13c91..01bfbb4cbf 100644 --- a/apps/user_ldap/lib/access.php +++ b/apps/user_ldap/lib/access.php @@ -707,7 +707,7 @@ class Access extends LDAPUtility { $sr = $this->ldap->search($linkResources, $base, $filter, $attr); $error = $this->ldap->errno($cr); if ($pagedSearchOK) { - $this->ldap->controlPagedResult($cr, 0, false, ""); + $this->ldap->controlPagedResult($cr, 999999, false, ""); } if(!is_array($sr) || $error !== 0) { \OCP\Util::writeLog('user_ldap', From 08a46e30806434bcd854f7868c3c9c6553d4ac11 Mon Sep 17 00:00:00 2001 From: adrien Date: Thu, 6 Mar 2014 17:57:09 +0100 Subject: [PATCH 014/187] add cache for single users --- lib/private/user/database.php | 78 ++++++++++++++++++++--------------- 1 file changed, 45 insertions(+), 33 deletions(-) diff --git a/lib/private/user/database.php b/lib/private/user/database.php index 15e6643dfb..911073c133 100644 --- a/lib/private/user/database.php +++ b/lib/private/user/database.php @@ -42,7 +42,9 @@ class OC_User_Database extends OC_User_Backend { /** * @var PasswordHash */ - static private $hasher = null; + private static $hasher = null; + + protected static $cache = array(); private function getHasher() { if (!self::$hasher) { @@ -135,11 +137,9 @@ class OC_User_Database extends OC_User_Backend { * @return string display name */ public function getDisplayName($uid) { - $query = OC_DB::prepare('SELECT `displayname` FROM `*PREFIX*users` WHERE `uid` = ?'); - $result = $query->execute(array($uid))->fetchAll(); - $displayName = trim($result[0]['displayname'], ' '); - if (!empty($displayName)) { - return $displayName; + $this->loadUser($uid); + if (!empty(self::$cache['uid']['displayname'])) { + return self::$cache['uid']['displayname']; } else { return $uid; } @@ -183,23 +183,41 @@ class OC_User_Database extends OC_User_Backend { $storedHash = $row['password']; if ($storedHash[0] == '$') { //the new phpass based hashing $hasher = $this->getHasher(); - if ($hasher->CheckPassword($password . OC_Config::getValue('passwordsalt', ''), $storedHash)) { + if ($hasher->CheckPassword($password . OC_Config::getValue('passwordsalt', ''), $storedHash)) return $row['uid']; - } else { - return false; - } - } else { //old sha1 based hashing - if (sha1($password) == $storedHash) { - //upgrade to new hashing - $this->setPassword($row['uid'], $password); - return $row['uid']; - } else { - return false; - } + + //old sha1 based hashing + } elseif (sha1($password) == $storedHash) { + //upgrade to new hashing + $this->setPassword($row['uid'], $password); + return $row['uid']; } - } else { - return false; } + + return false; + } + + /** + * @brief Load an user in the cache + * @returns boolean + */ + protected function loadUser($uid) { + if (empty(self::$cache[$uid])) { + $query = OC_DB::prepare('SELECT `uid`, `displayname` FROM `*PREFIX*users` WHERE LOWER(`uid`) = LOWER(?)'); + $result = $query->execute(array($uid)); + + if (OC_DB::isError($result)) { + OC_Log::write('core', OC_DB::getErrorMessage($result), OC_Log::ERROR); + return false; + } + + while ($row = $result->fetchRow()) { + self::$cache[$uid]['uid'] = $row['uid']; + self::$cache[$uid]['displayname'] = $row['displayname']; + } + } + + return true; } /** @@ -224,26 +242,20 @@ class OC_User_Database extends OC_User_Backend { * @return boolean */ public function userExists($uid) { - $query = OC_DB::prepare('SELECT COUNT(*) FROM `*PREFIX*users` WHERE LOWER(`uid`) = LOWER(?)'); - $result = $query->execute(array($uid)); - if (OC_DB::isError($result)) { - OC_Log::write('core', OC_DB::getErrorMessage($result), OC_Log::ERROR); - return false; - } - return $result->fetchOne() > 0; + $this->loadUser($uid); + return empty(self::$cache[$uid]) ? false : true; } /** * @brief get the user's home directory * @param string $uid the username - * @return string|false + * @return boolean */ public function getHome($uid) { - if ($this->userExists($uid)) { + if ($this->userExists($uid)) return OC_Config::getValue("datadirectory", OC::$SERVERROOT . "/data") . '/' . $uid; - } else { - return false; - } + + return false; } /** @@ -256,7 +268,7 @@ class OC_User_Database extends OC_User_Backend { /** * counts the users in the database * - * @return false|string | bool + * @return int | bool */ public function countUsers() { $query = OC_DB::prepare('SELECT COUNT(*) FROM `*PREFIX*users`'); From 43e79f41f34b71499b9bac85d27b87b53a569da0 Mon Sep 17 00:00:00 2001 From: ben-denham Date: Fri, 28 Feb 2014 15:07:25 +1300 Subject: [PATCH 015/187] Added improved version of patch by @blizzz in https://github.com/owncloud/core/issues/6651#issuecomment-32261257 to stop filter settings from being reset under a race condition. --- apps/user_ldap/js/settings.js | 192 +++++++++++++++++++++------------- 1 file changed, 118 insertions(+), 74 deletions(-) diff --git a/apps/user_ldap/js/settings.js b/apps/user_ldap/js/settings.js index acf88ef58a..0470ee079f 100644 --- a/apps/user_ldap/js/settings.js +++ b/apps/user_ldap/js/settings.js @@ -138,6 +138,108 @@ var LdapConfiguration = { } }; + +// LdapFilter object. + +function LdapFilter(target) { + this.locked = true; + this.target = false; + this.mode = LdapWizard.filterModeAssisted; + this.lazyRunCompose = false; + + if( target === 'User' || + target === 'Login' || + target === 'Group') { + this.target = target; + this.determineMode(); + } +} + +LdapFilter.prototype.compose = function() { + var action; + + if(this.locked) { + this.lazyRunCompose = true; + return false; + } + + if(this.target === 'User') { + action = 'getUserListFilter'; + } else if(this.target === 'Login') { + action = 'getUserLoginFilter'; + } else if(this.target === 'Group') { + action = 'getGroupFilter'; + } + + if(!$('#raw'+this.target+'FilterContainer').hasClass('invisible')) { + //Raw filter editing, i.e. user defined filter, don't compose + return; + } + + var param = 'action='+action+ + '&ldap_serverconfig_chooser='+ + encodeURIComponent($('#ldap_serverconfig_chooser').val()); + + var filter = this; + + LdapWizard.ajax(param, + function(result) { + LdapWizard.applyChanges(result); + if(filter.target === 'User') { + LdapWizard.countUsers(); + } else if(filter.target === 'Group') { + LdapWizard.countGroups(); + LdapWizard.detectGroupMemberAssoc(); + } + }, + function (result) { + // error handling + } + ); +} + +LdapFilter.prototype.determineMode = function() { + var param = 'action=get'+encodeURIComponent(this.target)+'FilterMode'+ + '&ldap_serverconfig_chooser='+ + encodeURIComponent($('#ldap_serverconfig_chooser').val()); + + var filter = this; + LdapWizard.ajax(param, + function(result) { + property = 'ldap' + filter.target + 'FilterMode'; + filter.mode = result.changes[property]; + if(filter.mode == LdapWizard.filterModeRaw + && $('#raw'+filter.target+'FilterContainer').hasClass('invisible')) { + LdapWizard['toggleRaw'+filter.target+'Filter'](); + } else if(filter.mode == LdapWizard.filterModeAssisted + && !$('#raw'+filter.target+'FilterContainer').hasClass('invisible')) { + LdapWizard['toggleRaw'+filter.target+'Filter'](); + } + filter.unlock(); + }, + function (result) { + //on error case get back to default i.e. Assisted + if(!$('#raw'+filter.target+'FilterContainer').hasClass('invisible')) { + LdapWizard['toggleRaw'+filter.target+'Filter'](); + filter.mode = LdapWizard.filterModeAssisted; + } + filter.unlock(); + } + ); + +} + +LdapFilter.prototype.unlock = function() { + this.locked = false; + if(this.lazyRunCompose) { + this.lazyRunCompose = false; + this.compose(); + } +} + +// end of LdapFilter object. + + var LdapWizard = { checkPortInfoShown: false, saveBlacklist: {}, @@ -145,6 +247,9 @@ var LdapWizard = { spinner: '', filterModeAssisted: 0, filterModeRaw: 1, + userFilter: false, + loginFilter: false, + groupFilter: false, ajax: function(param, fnOnSuccess, fnOnError) { $.post( @@ -275,41 +380,6 @@ var LdapWizard = { } }, - composeFilter: function(type) { - subject = type.charAt(0).toUpperCase() + type.substr(1); - if(!$('#raw'+subject+'FilterContainer').hasClass('invisible')) { - //Raw filter editing, i.e. user defined filter, don't compose - return; - } - - if(type == 'user') { - action = 'getUserListFilter'; - } else if(type == 'login') { - action = 'getUserLoginFilter'; - } else if(type == 'group') { - action = 'getGroupFilter'; - } - - param = 'action='+action+ - '&ldap_serverconfig_chooser='+ - encodeURIComponent($('#ldap_serverconfig_chooser').val()); - - LdapWizard.ajax(param, - function(result) { - LdapWizard.applyChanges(result); - if(type == 'user') { - LdapWizard.countUsers(); - } else if(type == 'group') { - LdapWizard.countGroups(); - LdapWizard.detectGroupMemberAssoc(); - } - }, - function (result) { - // error handling - } - ); - }, - controlBack: function() { curTabIndex = $('#ldapSettings').tabs('option', 'active'); if(curTabIndex == 0) { @@ -559,7 +629,7 @@ var LdapWizard = { }, initGroupFilter: function() { - LdapWizard.regardFilterMode('Group'); + LdapWizard.groupFilter = new LdapFilter('Group'); LdapWizard.findObjectClasses('ldap_groupfilter_objectclass', 'Group'); LdapWizard.findAvailableGroups('ldap_groupfilter_groups', 'Groups'); LdapWizard.countGroups(); @@ -568,13 +638,13 @@ var LdapWizard = { /** init login filter tab section **/ initLoginFilter: function() { - LdapWizard.regardFilterMode('Login'); + LdapWizard.loginFilter = new LdapFilter('Login'); LdapWizard.findAttributes(); }, postInitLoginFilter: function() { if($('#rawLoginFilterContainer').hasClass('invisible')) { - LdapWizard.composeFilter('login'); + LdapWizard.loginFilter.compose(); } }, @@ -600,7 +670,7 @@ var LdapWizard = { initUserFilter: function() { LdapWizard.userFilterObjectClassesHasRun = false; LdapWizard.userFilterAvailableGroupsHasRun = false; - LdapWizard.regardFilterMode('User'); + LdapWizard.userFilter = new LdapFilter('User'); LdapWizard.findObjectClasses('ldap_userfilter_objectclass', 'User'); LdapWizard.findAvailableGroups('ldap_userfilter_groups', 'Users'); }, @@ -608,7 +678,7 @@ var LdapWizard = { postInitUserFilter: function() { if(LdapWizard.userFilterObjectClassesHasRun && LdapWizard.userFilterAvailableGroupsHasRun) { - LdapWizard.composeFilter('user'); + LdapWizard.userFilter.compose(); LdapWizard.countUsers(); } }, @@ -657,7 +727,7 @@ var LdapWizard = { if(triggerObj.id == 'ldap_loginfilter_username' || triggerObj.id == 'ldap_loginfilter_email') { - LdapWizard.composeFilter('login'); + LdapWizard.loginFilter.compose(); } if($('#ldapSettings').tabs('option', 'active') == 0) { @@ -666,32 +736,6 @@ var LdapWizard = { } }, - regardFilterMode: function(subject) { - param = 'action=get'+encodeURIComponent(subject)+'FilterMode'+ - '&ldap_serverconfig_chooser='+ - encodeURIComponent($('#ldap_serverconfig_chooser').val()); - - LdapWizard.ajax(param, - function(result) { - property = 'ldap' + subject + 'FilterMode'; - mode = result.changes[property]; - if(mode == LdapWizard.filterModeRaw - && $('#raw'+subject+'FilterContainer').hasClass('invisible')) { - LdapWizard['toggleRaw'+subject+'Filter'](); - } else if(mode == LdapWizard.filterModeAssisted - && !$('#raw'+subject+'FilterContainer').hasClass('invisible')) { - LdapWizard['toggleRaw'+subject+'Filter'](); - } - }, - function (result) { - //on error case get back to default i.e. Assisted - if(!$('#raw'+subject+'FilterContainer').hasClass('invisible')) { - LdapWizard['toggleRaw'+subject+'Filter'](); - } - } - ); - }, - save: function(inputObj) { if(LdapWizard.blacklistRemove(inputObj.id)) { return; @@ -713,15 +757,15 @@ var LdapWizard = { LdapWizard._save($('#'+originalObj)[0], $.trim(values)); if(originalObj == 'ldap_userfilter_objectclass' || originalObj == 'ldap_userfilter_groups') { - LdapWizard.composeFilter('user'); + LdapWizard.userFilter.compose(); //when user filter is changed afterwards, login filter needs to //be adjusted, too - LdapWizard.composeFilter('login'); + LdapWizard.loginFilter.compose(); } else if(originalObj == 'ldap_loginfilter_attributes') { - LdapWizard.composeFilter('login'); + LdapWizard.loginFilter.compose(); } else if(originalObj == 'ldap_groupfilter_objectclass' || originalObj == 'ldap_groupfilter_groups') { - LdapWizard.composeFilter('group'); + LdapWizard.groupFilter.compose(); } }, @@ -777,10 +821,10 @@ var LdapWizard = { LdapWizard._save({ id: modeKey }, LdapWizard.filterModeAssisted); if(moc.indexOf('user') >= 0) { LdapWizard.blacklistRemove('ldap_userlist_filter'); - LdapWizard.composeFilter('user'); + LdapWizard.userFilter.compose(); } else { LdapWizard.blacklistRemove('ldap_group_filter'); - LdapWizard.composeFilter('group'); + LdapWizard.groupFilter.compose(); } } }, @@ -814,7 +858,7 @@ var LdapWizard = { $('#ldap_loginfilter_username').prop('disabled', property); LdapWizard._save({ id: 'ldapLoginFilterMode' }, mode); if(action == 'enable') { - LdapWizard.composeFilter('login'); + LdapWizard.loginFilter.compose(); } }, From ef65381a0b41a1d6519e7d5bf39d3aac75127a1e Mon Sep 17 00:00:00 2001 From: ben-denham Date: Fri, 7 Mar 2014 10:10:42 +1300 Subject: [PATCH 016/187] Moved LdapFilter into a separate js file in user_ldap. --- apps/user_ldap/js/ldapFilter.js | 95 +++++++++++++++++++++++++++++ apps/user_ldap/js/settings.js | 102 -------------------------------- apps/user_ldap/settings.php | 1 + 3 files changed, 96 insertions(+), 102 deletions(-) create mode 100644 apps/user_ldap/js/ldapFilter.js diff --git a/apps/user_ldap/js/ldapFilter.js b/apps/user_ldap/js/ldapFilter.js new file mode 100644 index 0000000000..71f8cd6c7c --- /dev/null +++ b/apps/user_ldap/js/ldapFilter.js @@ -0,0 +1,95 @@ +function LdapFilter(target) { + this.locked = true; + this.target = false; + this.mode = LdapWizard.filterModeAssisted; + this.lazyRunCompose = false; + + if( target === 'User' || + target === 'Login' || + target === 'Group') { + this.target = target; + this.determineMode(); + } +} + +LdapFilter.prototype.compose = function() { + var action; + + if(this.locked) { + this.lazyRunCompose = true; + return false; + } + + if(this.target === 'User') { + action = 'getUserListFilter'; + } else if(this.target === 'Login') { + action = 'getUserLoginFilter'; + } else if(this.target === 'Group') { + action = 'getGroupFilter'; + } + + if(!$('#raw'+this.target+'FilterContainer').hasClass('invisible')) { + //Raw filter editing, i.e. user defined filter, don't compose + return; + } + + var param = 'action='+action+ + '&ldap_serverconfig_chooser='+ + encodeURIComponent($('#ldap_serverconfig_chooser').val()); + + var filter = this; + + LdapWizard.ajax(param, + function(result) { + LdapWizard.applyChanges(result); + if(filter.target === 'User') { + LdapWizard.countUsers(); + } else if(filter.target === 'Group') { + LdapWizard.countGroups(); + LdapWizard.detectGroupMemberAssoc(); + } + }, + function (result) { + // error handling + } + ); +} + +LdapFilter.prototype.determineMode = function() { + var param = 'action=get'+encodeURIComponent(this.target)+'FilterMode'+ + '&ldap_serverconfig_chooser='+ + encodeURIComponent($('#ldap_serverconfig_chooser').val()); + + var filter = this; + LdapWizard.ajax(param, + function(result) { + property = 'ldap' + filter.target + 'FilterMode'; + filter.mode = result.changes[property]; + if(filter.mode == LdapWizard.filterModeRaw + && $('#raw'+filter.target+'FilterContainer').hasClass('invisible')) { + LdapWizard['toggleRaw'+filter.target+'Filter'](); + } else if(filter.mode == LdapWizard.filterModeAssisted + && !$('#raw'+filter.target+'FilterContainer').hasClass('invisible')) { + LdapWizard['toggleRaw'+filter.target+'Filter'](); + } + filter.unlock(); + }, + function (result) { + //on error case get back to default i.e. Assisted + if(!$('#raw'+filter.target+'FilterContainer').hasClass('invisible')) { + LdapWizard['toggleRaw'+filter.target+'Filter'](); + filter.mode = LdapWizard.filterModeAssisted; + } + filter.unlock(); + } + ); + +} + +LdapFilter.prototype.unlock = function() { + this.locked = false; + if(this.lazyRunCompose) { + this.lazyRunCompose = false; + this.compose(); + } +} diff --git a/apps/user_ldap/js/settings.js b/apps/user_ldap/js/settings.js index 0470ee079f..57f61b7d68 100644 --- a/apps/user_ldap/js/settings.js +++ b/apps/user_ldap/js/settings.js @@ -138,108 +138,6 @@ var LdapConfiguration = { } }; - -// LdapFilter object. - -function LdapFilter(target) { - this.locked = true; - this.target = false; - this.mode = LdapWizard.filterModeAssisted; - this.lazyRunCompose = false; - - if( target === 'User' || - target === 'Login' || - target === 'Group') { - this.target = target; - this.determineMode(); - } -} - -LdapFilter.prototype.compose = function() { - var action; - - if(this.locked) { - this.lazyRunCompose = true; - return false; - } - - if(this.target === 'User') { - action = 'getUserListFilter'; - } else if(this.target === 'Login') { - action = 'getUserLoginFilter'; - } else if(this.target === 'Group') { - action = 'getGroupFilter'; - } - - if(!$('#raw'+this.target+'FilterContainer').hasClass('invisible')) { - //Raw filter editing, i.e. user defined filter, don't compose - return; - } - - var param = 'action='+action+ - '&ldap_serverconfig_chooser='+ - encodeURIComponent($('#ldap_serverconfig_chooser').val()); - - var filter = this; - - LdapWizard.ajax(param, - function(result) { - LdapWizard.applyChanges(result); - if(filter.target === 'User') { - LdapWizard.countUsers(); - } else if(filter.target === 'Group') { - LdapWizard.countGroups(); - LdapWizard.detectGroupMemberAssoc(); - } - }, - function (result) { - // error handling - } - ); -} - -LdapFilter.prototype.determineMode = function() { - var param = 'action=get'+encodeURIComponent(this.target)+'FilterMode'+ - '&ldap_serverconfig_chooser='+ - encodeURIComponent($('#ldap_serverconfig_chooser').val()); - - var filter = this; - LdapWizard.ajax(param, - function(result) { - property = 'ldap' + filter.target + 'FilterMode'; - filter.mode = result.changes[property]; - if(filter.mode == LdapWizard.filterModeRaw - && $('#raw'+filter.target+'FilterContainer').hasClass('invisible')) { - LdapWizard['toggleRaw'+filter.target+'Filter'](); - } else if(filter.mode == LdapWizard.filterModeAssisted - && !$('#raw'+filter.target+'FilterContainer').hasClass('invisible')) { - LdapWizard['toggleRaw'+filter.target+'Filter'](); - } - filter.unlock(); - }, - function (result) { - //on error case get back to default i.e. Assisted - if(!$('#raw'+filter.target+'FilterContainer').hasClass('invisible')) { - LdapWizard['toggleRaw'+filter.target+'Filter'](); - filter.mode = LdapWizard.filterModeAssisted; - } - filter.unlock(); - } - ); - -} - -LdapFilter.prototype.unlock = function() { - this.locked = false; - if(this.lazyRunCompose) { - this.lazyRunCompose = false; - this.compose(); - } -} - -// end of LdapFilter object. - - var LdapWizard = { checkPortInfoShown: false, saveBlacklist: {}, diff --git a/apps/user_ldap/settings.php b/apps/user_ldap/settings.php index d077eafdde..6b7d8e6f53 100644 --- a/apps/user_ldap/settings.php +++ b/apps/user_ldap/settings.php @@ -25,6 +25,7 @@ OC_Util::checkAdminUser(); +OCP\Util::addScript('user_ldap', 'ldapFilter'); OCP\Util::addScript('user_ldap', 'settings'); OCP\Util::addScript('core', 'jquery.multiselect'); OCP\Util::addStyle('user_ldap', 'settings'); From dde4f2f91720a5eb4ad7db4fdb15dfc115f0e8c0 Mon Sep 17 00:00:00 2001 From: adrien Date: Thu, 6 Mar 2014 22:23:17 +0100 Subject: [PATCH 017/187] upgrade the cache user --- lib/private/user/database.php | 52 +++++++++++++++++++++++++---------- 1 file changed, 38 insertions(+), 14 deletions(-) diff --git a/lib/private/user/database.php b/lib/private/user/database.php index 911073c133..1e3d457eb5 100644 --- a/lib/private/user/database.php +++ b/lib/private/user/database.php @@ -39,13 +39,15 @@ require_once 'phpass/PasswordHash.php'; * Class for user management in a SQL Database (e.g. MySQL, SQLite) */ class OC_User_Database extends OC_User_Backend { + + protected static $cache = array(); + protected static $cache_complete = true; + /** * @var PasswordHash */ private static $hasher = null; - protected static $cache = array(); - private function getHasher() { if (!self::$hasher) { //we don't want to use DES based crypt(), since it doesn't return a hash with a recognisable prefix @@ -199,6 +201,7 @@ class OC_User_Database extends OC_User_Backend { /** * @brief Load an user in the cache + * @param string $uid the username * @returns boolean */ protected function loadUser($uid) { @@ -220,6 +223,32 @@ class OC_User_Database extends OC_User_Backend { return true; } + /** + * @brief Load an user in the cache + * @param string $uid the username + * @returns boolean + */ + protected function loadUsers() { + if (!self::$cache_complete) { + $query = OC_DB::prepare('SELECT `uid`, `displayname` FROM `*PREFIX*users` ORDER BY `uid`'); + $result = $query->execute(array($uid)); + + if (OC_DB::isError($result)) { + OC_Log::write('core', OC_DB::getErrorMessage($result), OC_Log::ERROR); + return false; + } + + while ($row = $result->fetchRow()) { + self::$cache[$uid]['uid'] = $row['uid']; + self::$cache[$uid]['displayname'] = $row['displayname']; + } + + self::$cache_complete = true; + } + + return true; + } + /** * @brief Get a list of all users * @returns array with all uids @@ -227,12 +256,12 @@ class OC_User_Database extends OC_User_Backend { * Get a list of all users. */ public function getUsers($search = '', $limit = null, $offset = null) { - $query = OC_DB::prepare('SELECT `uid` FROM `*PREFIX*users` WHERE LOWER(`uid`) LIKE LOWER(?)', $limit, $offset); - $result = $query->execute(array($search . '%')); + $this->loadUsers(); + $users = array(); - while ($row = $result->fetchRow()) { - $users[] = $row['uid']; - } + foreach (self::$cache as $uid => $value) + $users[] = $uid; + return $users; } @@ -271,13 +300,8 @@ class OC_User_Database extends OC_User_Backend { * @return int | bool */ public function countUsers() { - $query = OC_DB::prepare('SELECT COUNT(*) FROM `*PREFIX*users`'); - $result = $query->execute(); - if (OC_DB::isError($result)) { - OC_Log::write('core', OC_DB::getErrorMessage($result), OC_Log::ERROR); - return false; - } - return $result->fetchOne(); + $this->loadUsers(); + return count(self::$cache); } } From 5cdfc56867d0fe8dc5be9e2112c53e3ddf212104 Mon Sep 17 00:00:00 2001 From: adrien Date: Thu, 6 Mar 2014 22:34:43 +0100 Subject: [PATCH 018/187] update the cache when add user --- lib/private/user/database.php | 29 ++++++++++++++++------------- 1 file changed, 16 insertions(+), 13 deletions(-) diff --git a/lib/private/user/database.php b/lib/private/user/database.php index 1e3d457eb5..bbd72fc65e 100644 --- a/lib/private/user/database.php +++ b/lib/private/user/database.php @@ -39,15 +39,14 @@ require_once 'phpass/PasswordHash.php'; * Class for user management in a SQL Database (e.g. MySQL, SQLite) */ class OC_User_Database extends OC_User_Backend { - - protected static $cache = array(); - protected static $cache_complete = true; - /** * @var PasswordHash */ private static $hasher = null; + protected static $cache = array(); + protected static $cache_complete = false; + private function getHasher() { if (!self::$hasher) { //we don't want to use DES based crypt(), since it doesn't return a hash with a recognisable prefix @@ -68,16 +67,19 @@ class OC_User_Database extends OC_User_Backend { * itself, not in its subclasses. */ public function createUser($uid, $password) { - if ($this->userExists($uid)) { - return false; - } else { + if (!$this->userExists($uid)) { $hasher = $this->getHasher(); $hash = $hasher->HashPassword($password . OC_Config::getValue('passwordsalt', '')); $query = OC_DB::prepare('INSERT INTO `*PREFIX*users` ( `uid`, `password` ) VALUES( ?, ? )'); $result = $query->execute(array($uid, $hash)); - - return $result ? true : false; + + if ($result) { + self::$cache[$uid]['uid'] = $uid; + return true; + } } + + return false; } /** @@ -127,10 +129,11 @@ class OC_User_Database extends OC_User_Backend { if ($this->userExists($uid)) { $query = OC_DB::prepare('UPDATE `*PREFIX*users` SET `displayname` = ? WHERE LOWER(`uid`) = ?'); $query->execute(array($displayName, $uid)); + self::$cache[$uid]['displayname'] = $displayName; return true; - } else { - return false; } + + return false; } /** @@ -140,8 +143,8 @@ class OC_User_Database extends OC_User_Backend { */ public function getDisplayName($uid) { $this->loadUser($uid); - if (!empty(self::$cache['uid']['displayname'])) { - return self::$cache['uid']['displayname']; + if (!empty(self::$cache[$uid]['displayname'])) { + return self::$cache[$uid]['displayname']; } else { return $uid; } From fbde24c89ae61294bb6327ccc2b5b150b2ec4928 Mon Sep 17 00:00:00 2001 From: adrien Date: Fri, 7 Mar 2014 08:46:34 +0100 Subject: [PATCH 019/187] fix undefined in loadUsers --- lib/private/user/database.php | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/lib/private/user/database.php b/lib/private/user/database.php index bbd72fc65e..80d52feffc 100644 --- a/lib/private/user/database.php +++ b/lib/private/user/database.php @@ -54,7 +54,6 @@ class OC_User_Database extends OC_User_Backend { self::$hasher = new PasswordHash(8, $forcePortable); } return self::$hasher; - } /** @@ -112,9 +111,9 @@ class OC_User_Database extends OC_User_Backend { $query->execute(array($hash, $uid)); return true; - } else { - return false; } + + return false; } /** @@ -188,8 +187,9 @@ class OC_User_Database extends OC_User_Backend { $storedHash = $row['password']; if ($storedHash[0] == '$') { //the new phpass based hashing $hasher = $this->getHasher(); - if ($hasher->CheckPassword($password . OC_Config::getValue('passwordsalt', ''), $storedHash)) + if ($hasher->CheckPassword($password . OC_Config::getValue('passwordsalt', ''), $storedHash)) { return $row['uid']; + } //old sha1 based hashing } elseif (sha1($password) == $storedHash) { @@ -234,7 +234,7 @@ class OC_User_Database extends OC_User_Backend { protected function loadUsers() { if (!self::$cache_complete) { $query = OC_DB::prepare('SELECT `uid`, `displayname` FROM `*PREFIX*users` ORDER BY `uid`'); - $result = $query->execute(array($uid)); + $result = $query->execute(); if (OC_DB::isError($result)) { OC_Log::write('core', OC_DB::getErrorMessage($result), OC_Log::ERROR); @@ -262,8 +262,9 @@ class OC_User_Database extends OC_User_Backend { $this->loadUsers(); $users = array(); - foreach (self::$cache as $uid => $value) + foreach (self::$cache as $uid => $value) { $users[] = $uid; + } return $users; } @@ -281,11 +282,12 @@ class OC_User_Database extends OC_User_Backend { /** * @brief get the user's home directory * @param string $uid the username - * @return boolean + * @return string|false */ public function getHome($uid) { - if ($this->userExists($uid)) + if ($this->userExists($uid)) { return OC_Config::getValue("datadirectory", OC::$SERVERROOT . "/data") . '/' . $uid; + } return false; } From db6fb198fe7b8fa854050730b32d58d3787505b3 Mon Sep 17 00:00:00 2001 From: Robin Appelman Date: Fri, 7 Mar 2014 14:27:23 +0100 Subject: [PATCH 020/187] don't throw errors in getType --- lib/private/files/fileinfo.php | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/lib/private/files/fileinfo.php b/lib/private/files/fileinfo.php index 916346b608..26ea0aab56 100644 --- a/lib/private/files/fileinfo.php +++ b/lib/private/files/fileinfo.php @@ -53,6 +53,9 @@ class FileInfo implements \OCP\Files\FileInfo, \ArrayAccess { } public function offsetGet($offset) { + if ($offset === 'type') { + return $this->getType(); + } return $this->data[$offset]; } @@ -144,7 +147,7 @@ class FileInfo implements \OCP\Files\FileInfo, \ArrayAccess { * @return \OCP\Files\FileInfo::TYPE_FILE | \OCP\Files\FileInfo::TYPE_FOLDER */ public function getType() { - if ($this->data['type']) { + if (isset($this->data['type'])) { return $this->data['type']; } else { return $this->getMimetype() === 'httpd/unix-directory' ? self::TYPE_FOLDER : self::TYPE_FILE; From d8843f6cd3b37bc7e95b5baa437d7087b09fc6fd Mon Sep 17 00:00:00 2001 From: nishiki Date: Sun, 9 Mar 2014 12:01:35 +0100 Subject: [PATCH 021/187] minor clean code --- lib/private/user/database.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/private/user/database.php b/lib/private/user/database.php index 80d52feffc..c472c5445b 100644 --- a/lib/private/user/database.php +++ b/lib/private/user/database.php @@ -276,7 +276,7 @@ class OC_User_Database extends OC_User_Backend { */ public function userExists($uid) { $this->loadUser($uid); - return empty(self::$cache[$uid]) ? false : true; + return !empty(self::$cache[$uid]); } /** From 75011c2e09e2ee7e9c76a86e938c4d8656225352 Mon Sep 17 00:00:00 2001 From: nishiki Date: Sun, 9 Mar 2014 12:22:47 +0100 Subject: [PATCH 022/187] add query result (boolean) for update or delete --- lib/private/user/database.php | 15 ++++++--------- 1 file changed, 6 insertions(+), 9 deletions(-) diff --git a/lib/private/user/database.php b/lib/private/user/database.php index c472c5445b..57ecdda9da 100644 --- a/lib/private/user/database.php +++ b/lib/private/user/database.php @@ -91,8 +91,9 @@ class OC_User_Database extends OC_User_Backend { public function deleteUser($uid) { // Delete user-group-relation $query = OC_DB::prepare('DELETE FROM `*PREFIX*users` WHERE `uid` = ?'); - $query->execute(array($uid)); - return true; + $result = $query->execute(array($uid)); + + return $result ? true : false; } /** @@ -108,9 +109,9 @@ class OC_User_Database extends OC_User_Backend { $hasher = $this->getHasher(); $hash = $hasher->HashPassword($password . OC_Config::getValue('passwordsalt', '')); $query = OC_DB::prepare('UPDATE `*PREFIX*users` SET `password` = ? WHERE `uid` = ?'); - $query->execute(array($hash, $uid)); + $result = $query->execute(array($hash, $uid)); - return true; + return $result ? true : false; } return false; @@ -142,11 +143,7 @@ class OC_User_Database extends OC_User_Backend { */ public function getDisplayName($uid) { $this->loadUser($uid); - if (!empty(self::$cache[$uid]['displayname'])) { - return self::$cache[$uid]['displayname']; - } else { - return $uid; - } + return empty(self::$cache[$uid]['displayname']) ? $uid : self::$cache[$uid]['displayname']; } /** From ba9d8f7c1a448e0a769ee31d20ee75c1616b22e7 Mon Sep 17 00:00:00 2001 From: nishiki Date: Sun, 9 Mar 2014 12:47:19 +0100 Subject: [PATCH 023/187] fix undifined uid --- lib/private/user/database.php | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/lib/private/user/database.php b/lib/private/user/database.php index 57ecdda9da..591ea2a5c9 100644 --- a/lib/private/user/database.php +++ b/lib/private/user/database.php @@ -239,7 +239,8 @@ class OC_User_Database extends OC_User_Backend { } while ($row = $result->fetchRow()) { - self::$cache[$uid]['uid'] = $row['uid']; + $uid = $row['uid']; + self::$cache[$uid]['uid'] = $uid; self::$cache[$uid]['displayname'] = $row['displayname']; } From 415b1d03bca12c082ff70e44d3b7b8b3fbc8f347 Mon Sep 17 00:00:00 2001 From: adrien Date: Mon, 10 Mar 2014 17:27:51 +0100 Subject: [PATCH 024/187] fix cache when remove an user --- lib/private/user/database.php | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/lib/private/user/database.php b/lib/private/user/database.php index 591ea2a5c9..4a9ad1b825 100644 --- a/lib/private/user/database.php +++ b/lib/private/user/database.php @@ -93,7 +93,14 @@ class OC_User_Database extends OC_User_Backend { $query = OC_DB::prepare('DELETE FROM `*PREFIX*users` WHERE `uid` = ?'); $result = $query->execute(array($uid)); - return $result ? true : false; + if ($result) { + if (isset(self::$cache[$uid])) { + unset(self::$cache[$uid]); + } + return true; + } + + return false; } /** From f827761e7111d4f3b082074facef31efd74939e7 Mon Sep 17 00:00:00 2001 From: adrien Date: Tue, 11 Mar 2014 11:56:46 +0100 Subject: [PATCH 025/187] remove static variable, add limit and offset --- lib/private/user/database.php | 80 ++++++++++++++++++++++------------- 1 file changed, 51 insertions(+), 29 deletions(-) diff --git a/lib/private/user/database.php b/lib/private/user/database.php index 4a9ad1b825..5df9c9e2b4 100644 --- a/lib/private/user/database.php +++ b/lib/private/user/database.php @@ -44,8 +44,8 @@ class OC_User_Database extends OC_User_Backend { */ private static $hasher = null; - protected static $cache = array(); - protected static $cache_complete = false; + private $cache = array(); + private $cache_complete = false; private function getHasher() { if (!self::$hasher) { @@ -73,7 +73,7 @@ class OC_User_Database extends OC_User_Backend { $result = $query->execute(array($uid, $hash)); if ($result) { - self::$cache[$uid]['uid'] = $uid; + $this->cache[$uid]['uid'] = $uid; return true; } } @@ -94,8 +94,8 @@ class OC_User_Database extends OC_User_Backend { $result = $query->execute(array($uid)); if ($result) { - if (isset(self::$cache[$uid])) { - unset(self::$cache[$uid]); + if (isset($this->cache[$uid])) { + unset($this->cache[$uid]); } return true; } @@ -136,7 +136,8 @@ class OC_User_Database extends OC_User_Backend { if ($this->userExists($uid)) { $query = OC_DB::prepare('UPDATE `*PREFIX*users` SET `displayname` = ? WHERE LOWER(`uid`) = ?'); $query->execute(array($displayName, $uid)); - self::$cache[$uid]['displayname'] = $displayName; + $this->cache[$uid]['displayname'] = $displayName; + return true; } @@ -150,7 +151,7 @@ class OC_User_Database extends OC_User_Backend { */ public function getDisplayName($uid) { $this->loadUser($uid); - return empty(self::$cache[$uid]['displayname']) ? $uid : self::$cache[$uid]['displayname']; + return empty($this->cache[$uid]['displayname']) ? $uid : $this->cache[$uid]['displayname']; } /** @@ -159,15 +160,24 @@ class OC_User_Database extends OC_User_Backend { * * Get a list of all display names and user ids. */ - public function getDisplayNames($search = '', $limit = null, $offset = null) { + public function getDisplayNames($search = '', $limit = null, $offset = 0) { + $this->loadUsers(); + + $search = strtolower($search); + $i = 0; + $displayNames = array(); - $query = OC_DB::prepare('SELECT `uid`, `displayname` FROM `*PREFIX*users`' - . ' WHERE LOWER(`displayname`) LIKE LOWER(?) OR ' - . 'LOWER(`uid`) LIKE LOWER(?)', $limit, $offset); - $result = $query->execute(array($search . '%', $search . '%')); - $users = array(); - while ($row = $result->fetchRow()) { - $displayNames[$row['uid']] = $row['displayname']; + foreach ($this->cache as $uid => $value) { + if ((preg_match('/^.*'.$search.'.*/', strtolower($uid)) || preg_match('/^.*'.$search.'.*/', strtolower($value['displayname']))) && $offset <= $i) { + $displayNames[$uid] = $value['displayname']; + if (!is_null($limit)) { + $limit--; + if ($limit <= 0) { + break; + } + } + } + $i++; } return $displayNames; @@ -211,8 +221,8 @@ class OC_User_Database extends OC_User_Backend { * @param string $uid the username * @returns boolean */ - protected function loadUser($uid) { - if (empty(self::$cache[$uid])) { + private function loadUser($uid) { + if (empty($this->cache[$uid])) { $query = OC_DB::prepare('SELECT `uid`, `displayname` FROM `*PREFIX*users` WHERE LOWER(`uid`) = LOWER(?)'); $result = $query->execute(array($uid)); @@ -222,8 +232,8 @@ class OC_User_Database extends OC_User_Backend { } while ($row = $result->fetchRow()) { - self::$cache[$uid]['uid'] = $row['uid']; - self::$cache[$uid]['displayname'] = $row['displayname']; + $this->cache[$uid]['uid'] = $row['uid']; + $this->cache[$uid]['displayname'] = $row['displayname']; } } @@ -235,8 +245,8 @@ class OC_User_Database extends OC_User_Backend { * @param string $uid the username * @returns boolean */ - protected function loadUsers() { - if (!self::$cache_complete) { + private function loadUsers() { + if (!$this->cache_complete) { $query = OC_DB::prepare('SELECT `uid`, `displayname` FROM `*PREFIX*users` ORDER BY `uid`'); $result = $query->execute(); @@ -247,11 +257,11 @@ class OC_User_Database extends OC_User_Backend { while ($row = $result->fetchRow()) { $uid = $row['uid']; - self::$cache[$uid]['uid'] = $uid; - self::$cache[$uid]['displayname'] = $row['displayname']; + $this->cache[$uid]['uid'] = $uid; + $this->cache[$uid]['displayname'] = $row['displayname']; } - self::$cache_complete = true; + $this->cache_complete = true; } return true; @@ -263,12 +273,24 @@ class OC_User_Database extends OC_User_Backend { * * Get a list of all users. */ - public function getUsers($search = '', $limit = null, $offset = null) { + public function getUsers($search = '', $limit = null, $offset = 0) { $this->loadUsers(); + $search = strtolower($search); + $i = 0; + $users = array(); - foreach (self::$cache as $uid => $value) { - $users[] = $uid; + foreach ($this->cache as $uid => $value) { + if (preg_match('/^'.$search.'.*/', strtolower($uid)) && $offset <= $i) { + $users[] = $uid; + if (!is_null($limit)) { + $limit--; + if ($limit <= 0) { + break; + } + } + } + $i++; } return $users; @@ -281,7 +303,7 @@ class OC_User_Database extends OC_User_Backend { */ public function userExists($uid) { $this->loadUser($uid); - return !empty(self::$cache[$uid]); + return !empty($this->cache[$uid]); } /** @@ -311,7 +333,7 @@ class OC_User_Database extends OC_User_Backend { */ public function countUsers() { $this->loadUsers(); - return count(self::$cache); + return count($this->cache); } } From ea6f8ba352ac7b3c9069407d6f64664ddee81c4d Mon Sep 17 00:00:00 2001 From: adrien Date: Tue, 11 Mar 2014 16:58:10 +0100 Subject: [PATCH 026/187] fix remove cache when delete --- lib/private/user/database.php | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/lib/private/user/database.php b/lib/private/user/database.php index 5df9c9e2b4..683ba90e55 100644 --- a/lib/private/user/database.php +++ b/lib/private/user/database.php @@ -93,14 +93,11 @@ class OC_User_Database extends OC_User_Backend { $query = OC_DB::prepare('DELETE FROM `*PREFIX*users` WHERE `uid` = ?'); $result = $query->execute(array($uid)); - if ($result) { - if (isset($this->cache[$uid])) { - unset($this->cache[$uid]); - } - return true; + if (isset($this->cache[$uid])) { + unset($this->cache[$uid]); } - return false; + return $result ? true : false; } /** From 8a8a32cf43efad6fdb2089097cb7fadbe0472456 Mon Sep 17 00:00:00 2001 From: ben-denham Date: Fri, 14 Mar 2014 09:17:39 +1300 Subject: [PATCH 027/187] Changed conditions in user_ldap's ldapFilter.js to use ===, fixed indentation. --- apps/user_ldap/js/ldapFilter.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/apps/user_ldap/js/ldapFilter.js b/apps/user_ldap/js/ldapFilter.js index 71f8cd6c7c..70f24f0d4a 100644 --- a/apps/user_ldap/js/ldapFilter.js +++ b/apps/user_ldap/js/ldapFilter.js @@ -65,10 +65,10 @@ LdapFilter.prototype.determineMode = function() { function(result) { property = 'ldap' + filter.target + 'FilterMode'; filter.mode = result.changes[property]; - if(filter.mode == LdapWizard.filterModeRaw + if(filter.mode === LdapWizard.filterModeRaw && $('#raw'+filter.target+'FilterContainer').hasClass('invisible')) { LdapWizard['toggleRaw'+filter.target+'Filter'](); - } else if(filter.mode == LdapWizard.filterModeAssisted + } else if(filter.mode === LdapWizard.filterModeAssisted && !$('#raw'+filter.target+'FilterContainer').hasClass('invisible')) { LdapWizard['toggleRaw'+filter.target+'Filter'](); } @@ -80,7 +80,7 @@ LdapFilter.prototype.determineMode = function() { LdapWizard['toggleRaw'+filter.target+'Filter'](); filter.mode = LdapWizard.filterModeAssisted; } - filter.unlock(); + filter.unlock(); } ); From 0da61a26ee842e611f33d32109572c70bbc170b1 Mon Sep 17 00:00:00 2001 From: adrien Date: Fri, 21 Mar 2014 15:50:25 +0100 Subject: [PATCH 028/187] remove cache all user --- lib/private/user/database.php | 92 +++++++++-------------------------- 1 file changed, 22 insertions(+), 70 deletions(-) diff --git a/lib/private/user/database.php b/lib/private/user/database.php index 683ba90e55..706982bb75 100644 --- a/lib/private/user/database.php +++ b/lib/private/user/database.php @@ -45,7 +45,6 @@ class OC_User_Database extends OC_User_Backend { private static $hasher = null; private $cache = array(); - private $cache_complete = false; private function getHasher() { if (!self::$hasher) { @@ -71,11 +70,8 @@ class OC_User_Database extends OC_User_Backend { $hash = $hasher->HashPassword($password . OC_Config::getValue('passwordsalt', '')); $query = OC_DB::prepare('INSERT INTO `*PREFIX*users` ( `uid`, `password` ) VALUES( ?, ? )'); $result = $query->execute(array($uid, $hash)); - - if ($result) { - $this->cache[$uid]['uid'] = $uid; - return true; - } + + return $result ? true : false; } return false; @@ -157,24 +153,15 @@ class OC_User_Database extends OC_User_Backend { * * Get a list of all display names and user ids. */ - public function getDisplayNames($search = '', $limit = null, $offset = 0) { - $this->loadUsers(); - - $search = strtolower($search); - $i = 0; - + public function getDisplayNames($search = '', $limit = null, $offset = null) { $displayNames = array(); - foreach ($this->cache as $uid => $value) { - if ((preg_match('/^.*'.$search.'.*/', strtolower($uid)) || preg_match('/^.*'.$search.'.*/', strtolower($value['displayname']))) && $offset <= $i) { - $displayNames[$uid] = $value['displayname']; - if (!is_null($limit)) { - $limit--; - if ($limit <= 0) { - break; - } - } - } - $i++; + $query = OC_DB::prepare('SELECT `uid`, `displayname` FROM `*PREFIX*users`' + . ' WHERE LOWER(`displayname`) LIKE LOWER(?) OR ' + . 'LOWER(`uid`) LIKE LOWER(?)', $limit, $offset); + $result = $query->execute(array($search . '%', $search . '%')); + $users = array(); + while ($row = $result->fetchRow()) { + $displayNames[$row['uid']] = $row['displayname']; } return $displayNames; @@ -237,59 +224,19 @@ class OC_User_Database extends OC_User_Backend { return true; } - /** - * @brief Load an user in the cache - * @param string $uid the username - * @returns boolean - */ - private function loadUsers() { - if (!$this->cache_complete) { - $query = OC_DB::prepare('SELECT `uid`, `displayname` FROM `*PREFIX*users` ORDER BY `uid`'); - $result = $query->execute(); - - if (OC_DB::isError($result)) { - OC_Log::write('core', OC_DB::getErrorMessage($result), OC_Log::ERROR); - return false; - } - - while ($row = $result->fetchRow()) { - $uid = $row['uid']; - $this->cache[$uid]['uid'] = $uid; - $this->cache[$uid]['displayname'] = $row['displayname']; - } - - $this->cache_complete = true; - } - - return true; - } - /** * @brief Get a list of all users * @returns array with all uids * * Get a list of all users. */ - public function getUsers($search = '', $limit = null, $offset = 0) { - $this->loadUsers(); - - $search = strtolower($search); - $i = 0; - + public function getUsers($search = '', $limit = null, $offset = null) { + $query = OC_DB::prepare('SELECT `uid` FROM `*PREFIX*users` WHERE LOWER(`uid`) LIKE LOWER(?)', $limit, $offset); + $result = $query->execute(array($search . '%')); $users = array(); - foreach ($this->cache as $uid => $value) { - if (preg_match('/^'.$search.'.*/', strtolower($uid)) && $offset <= $i) { - $users[] = $uid; - if (!is_null($limit)) { - $limit--; - if ($limit <= 0) { - break; - } - } - } - $i++; + while ($row = $result->fetchRow()) { + $users[] = $row['uid']; } - return $users; } @@ -329,8 +276,13 @@ class OC_User_Database extends OC_User_Backend { * @return int | bool */ public function countUsers() { - $this->loadUsers(); - return count($this->cache); + $query = OC_DB::prepare('SELECT COUNT(*) FROM `*PREFIX*users`'); + $result = $query->execute(); + if (OC_DB::isError($result)) { + OC_Log::write('core', OC_DB::getErrorMessage($result), OC_Log::ERROR); + return false; + } + return $result->fetchOne(); } } From 9c8596e95b81f9d5ddcd7132ac0134b0ffed67e5 Mon Sep 17 00:00:00 2001 From: Robin Appelman Date: Tue, 1 Apr 2014 15:43:27 +0200 Subject: [PATCH 029/187] Keep cached urls with different hostnames or baseurls seperate --- lib/private/route/cachingrouter.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/private/route/cachingrouter.php b/lib/private/route/cachingrouter.php index ad25372391..63bfd3e053 100644 --- a/lib/private/route/cachingrouter.php +++ b/lib/private/route/cachingrouter.php @@ -31,7 +31,7 @@ class CachingRouter extends Router { * @return string */ public function generate($name, $parameters = array(), $absolute = false) { - $key = $name . json_encode($parameters) . $absolute; + $key = $this->context->getHost() . $this->context->getBaseUrl() . $name . json_encode($parameters) . $absolute; if ($this->cache->hasKey($key)) { return $this->cache->get($key); } else { From c926e98ea96f4782bc75b97e4db5996dacd6e0d4 Mon Sep 17 00:00:00 2001 From: Jan-Christoph Borchardt Date: Tue, 1 Apr 2014 17:18:03 +0200 Subject: [PATCH 030/187] fix log in input sizing caused by core input field switch to px --- core/css/styles.css | 42 +++++++++++++++++++++++++++++++----------- 1 file changed, 31 insertions(+), 11 deletions(-) diff --git a/core/css/styles.css b/core/css/styles.css index 8a12057529..f750c72077 100644 --- a/core/css/styles.css +++ b/core/css/styles.css @@ -402,14 +402,32 @@ input[type="submit"].enabled { } /* Icons for username and password fields to better recognize them */ -#adminlogin, #adminpass, #user, #password { width:11.7em!important; padding-left:1.8em; } -#adminlogin+label+img, #adminpass-icon, #user+label+img, #password-icon { - position:absolute; left:1.25em; top:1.65em; - -ms-filter:"progid:DXImageTransform.Microsoft.Alpha(Opacity=30)"; filter:alpha(opacity=30); opacity:.3; +#adminlogin, #adminpass, #user, #password { + width: 223px !important; + padding-left: 36px !important; +} +#adminlogin+label+img, +#adminpass-icon, +#user+label+img, +#password-icon { + position: absolute; + left: 1.25em; + top: 1.65em; + -ms-filter: "progid:DXImageTransform.Microsoft.Alpha(Opacity=30)"; + filter: alpha(opacity=30); + opacity: .3; +} +#adminpass-icon, #password-icon { + top: 1.1em; +} +input[name="password-clone"] { + padding-left: 1.8em; + width: 11.7em !important; +} +input[name="adminpass-clone"] { + padding-left: 1.8em; + width: 11.7em !important; } -#adminpass-icon, #password-icon { top:1.1em; } -input[name="password-clone"] { padding-left:1.8em; width:11.7em !important; } -input[name="adminpass-clone"] { padding-left:1.8em; width:11.7em !important; } /* General new input field look */ #body-login input[type="text"], @@ -456,7 +474,7 @@ label.infield { cursor:text !important; top:1.05em; left:.85em; } #body-login form input[type="checkbox"]+label { position: relative; margin: 0; - font-size: 1em; + font-size: 13px; padding: 14px; padding-left: 28px; margin-left: -28px; @@ -610,11 +628,13 @@ label.infield { cursor:text !important; top:1.05em; left:.85em; } /* Log in and install button */ #body-login input { - font-size: 1.5em; + font-size: 20px; + margin: 5px; + padding: 12px 10px 8px; } #body-login input[type="text"], #body-login input[type="password"] { - width: 13em; + width: 249px; } #body-login input.login { width: auto; @@ -624,7 +644,7 @@ label.infield { cursor:text !important; top:1.05em; left:.85em; } padding: 10px 20px; /* larger log in and installation buttons */ } #remember_login { - margin: 18px 5px 0 18px; + margin: 24px 5px 0 16px !important; vertical-align: text-bottom; } From 0c441724bc3031e50715169e9fd5541b347b2878 Mon Sep 17 00:00:00 2001 From: Jan-Christoph Borchardt Date: Tue, 1 Apr 2014 17:21:05 +0200 Subject: [PATCH 031/187] make infield labels use less space so pasting in is easier --- core/css/styles.css | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/core/css/styles.css b/core/css/styles.css index f750c72077..7af80e31ff 100644 --- a/core/css/styles.css +++ b/core/css/styles.css @@ -466,9 +466,14 @@ input[name="adminpass-clone"] { p.infield { position:relative; } label.infield { cursor:text !important; top:1.05em; left:.85em; } #body-login form label.infield { /* labels are ellipsized when too long, keep them short */ - position:absolute; width:90%; padding-left:1.4em; - font-size:19px; color:#aaa; - white-space:nowrap; overflow:hidden; text-overflow:ellipsis; + position: absolute; + width: auto; + margin-left: 26px; + font-size: 19px; + color: #aaa; + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; } #body-login #databaseField .infield { padding-left:0; } #body-login form input[type="checkbox"]+label { From 51fbf0bcbc2c3c68f7b4b36128304f4ab9df98e7 Mon Sep 17 00:00:00 2001 From: Robin Appelman Date: Mon, 7 Apr 2014 13:27:18 +0200 Subject: [PATCH 032/187] add delimiter between host and baseurl --- lib/private/route/cachingrouter.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/private/route/cachingrouter.php b/lib/private/route/cachingrouter.php index 63bfd3e053..766c67c73a 100644 --- a/lib/private/route/cachingrouter.php +++ b/lib/private/route/cachingrouter.php @@ -31,7 +31,7 @@ class CachingRouter extends Router { * @return string */ public function generate($name, $parameters = array(), $absolute = false) { - $key = $this->context->getHost() . $this->context->getBaseUrl() . $name . json_encode($parameters) . $absolute; + $key = $this->context->getHost() . '#' . $this->context->getBaseUrl() . $name . json_encode($parameters) . $absolute; if ($this->cache->hasKey($key)) { return $this->cache->get($key); } else { From 6d373e97c32ec51bd649eeafdfcc3596067776f8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thomas=20M=C3=BCller?= Date: Mon, 7 Apr 2014 23:02:49 +0200 Subject: [PATCH 033/187] remove unused exit() --- apps/files/ajax/upload.php | 1 - 1 file changed, 1 deletion(-) diff --git a/apps/files/ajax/upload.php b/apps/files/ajax/upload.php index 38175fa500..a5ce7b257d 100644 --- a/apps/files/ajax/upload.php +++ b/apps/files/ajax/upload.php @@ -196,7 +196,6 @@ if (strpos($dir, '..') === false) { if ($error === false) { OCP\JSON::encodedPrint($result); - exit(); } else { OCP\JSON::error(array(array('data' => array_merge(array('message' => $error, 'code' => $errorCode), $storageStats)))); } From 971a311feec6c4c2de905bdbbe71c2cf60296cb2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thomas=20M=C3=BCller?= Date: Mon, 7 Apr 2014 23:03:19 +0200 Subject: [PATCH 034/187] adding parentId to file info array --- apps/files/lib/helper.php | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/apps/files/lib/helper.php b/apps/files/lib/helper.php index b765fdaf3e..f9515d6715 100644 --- a/apps/files/lib/helper.php +++ b/apps/files/lib/helper.php @@ -73,13 +73,14 @@ class Helper /** * Formats the file info to be returned as JSON to the client. * - * @param \OCP\Files\FileInfo file info + * @param \OCP\Files\FileInfo $i * @return array formatted file info */ public static function formatFileInfo($i) { $entry = array(); $entry['id'] = $i['fileid']; + $entry['parentId'] = $i['parent']; $entry['date'] = \OCP\Util::formatDate($i['mtime']); $entry['mtime'] = $i['mtime'] * 1000; // only pick out the needed attributes From d15a4719d2bc58827d7cbe616ab25aaf376c8293 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thomas=20M=C3=BCller?= Date: Mon, 7 Apr 2014 23:04:08 +0200 Subject: [PATCH 035/187] fix folder upload in js --- apps/files/js/filelist.js | 37 +++++++++++++++++++++---------------- 1 file changed, 21 insertions(+), 16 deletions(-) diff --git a/apps/files/js/filelist.js b/apps/files/js/filelist.js index 69c97e8b32..4a59508330 100644 --- a/apps/files/js/filelist.js +++ b/apps/files/js/filelist.js @@ -1103,33 +1103,38 @@ $(document).ready(function() { size += parseInt(file.size); data.context.attr('data-size', size); data.context.find('td.filesize').text(humanFileSize(size)); - } - else { + } else { // only append new file if uploaded into the current folder - if (file.directory != '/' && file.directory !== FileList.getCurrentDirectory()) { + if (file.directory !== '/' && file.directory !== FileList.getCurrentDirectory()) { - file_directory = file.directory.replace('/','').replace(/\/$/, "").split('/'); + var fileDirectory = file.directory.replace('/','').replace(/\/$/, "").split('/'); - if (file_directory.length == 1) { - file_directory = file_directory[0]; + if (fileDirectory.length === 1) { + fileDirectory = fileDirectory[0]; // Get the directory - if ($('tr[data-file="'+file_directory+'"]').length == 0) - { - FileList.addDir(file_directory, 0, new Date(), false); + if ($('tr[data-file="'+fileDirectory+'"]').length === 0) { + var dir = { + name: fileDirectory, + type: 'dir', + mimetype: 'httpd/unix-directory', + permissions: file.permissions, + size: 0, + id: file.parentId + }; + FileList.add(dir, {insert: true}); } - } - else { - file_directory = file_directory[0]; + } else { + fileDirectory = fileDirectory[0]; } - file_directory = FileList.findFileEl(file_directory); + fileDirectory = FileList.findFileEl(fileDirectory); // update folder size - var size = parseInt(file_directory.attr('data-size')); + var size = parseInt(fileDirectory.attr('data-size')); size += parseInt(file.size); - file_directory.attr('data-size', size); - file_directory.find('td.filesize').text(humanFileSize(size)); + fileDirectory.attr('data-size', size); + fileDirectory.find('td.filesize').text(humanFileSize(size)); return; } From 7dd00746556aa4be26e41a6f3b66095a29b02c45 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thomas=20M=C3=BCller?= Date: Mon, 7 Apr 2014 23:17:35 +0200 Subject: [PATCH 036/187] fixing mkdir code to respect external file systems as well --- lib/private/files/view.php | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/private/files/view.php b/lib/private/files/view.php index 0dce90aec4..519ed250b1 100644 --- a/lib/private/files/view.php +++ b/lib/private/files/view.php @@ -633,11 +633,11 @@ class View { if (Filesystem::isValidPath($path)) { // Get directory that the file is going into - $file_path = \OC_User::getHome(\OC_User::getUser()) . '/files'.substr($path, 0, strrpos($path,'/')); + $filePath = dirname($path); // Create the directories if any - if(empty($file_path) === false) { - mkdir($file_path, 0770, true); + if (!$this->file_exists($filePath)) { + $this->mkdir($filePath); } if (!$tmpFile) { From 68b7822cf50a670619659ae2082633f4c62fb5f9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thomas=20M=C3=BCller?= Date: Mon, 7 Apr 2014 23:45:35 +0200 Subject: [PATCH 037/187] js style fixes according to our jshintrc rules --- apps/files/js/filelist.js | 114 +++++++++++++++++++------------------- 1 file changed, 56 insertions(+), 58 deletions(-) diff --git a/apps/files/js/filelist.js b/apps/files/js/filelist.js index 4a59508330..ceaaed264a 100644 --- a/apps/files/js/filelist.js +++ b/apps/files/js/filelist.js @@ -9,7 +9,7 @@ */ /* global OC, t, n, FileList, FileActions, Files, BreadCrumb */ -/* global procesSelection, dragOptions, SVGSupport */ +/* global procesSelection, dragOptions, folderDropOptions, SVGSupport */ window.FileList = { appName: t('files', 'Files'), isEmpty: true, @@ -228,7 +228,7 @@ window.FileList = { linkUrl = Files.getDownloadUrl(name, FileList.getCurrentDirectory()); } td.append(''); - var link_elem = $('').attr({ + var linkElem = $('').attr({ "class": "name", "href": linkUrl }); @@ -244,19 +244,19 @@ window.FileList = { basename = name; extension = false; } - var name_span=$('').addClass('nametext').text(basename); - link_elem.append(name_span); + var nameSpan=$('').addClass('nametext').text(basename); + linkElem.append(nameSpan); if (extension) { - name_span.append($('').addClass('extension').text(extension)); + nameSpan.append($('').addClass('extension').text(extension)); } // dirs can show the number of uploaded files if (type === 'dir') { - link_elem.append($('').attr({ + linkElem.append($('').attr({ 'class': 'uploadtext', 'currentUploads': 0 })); } - td.append(link_elem); + td.append(linkElem); tr.append(td); // size column @@ -266,7 +266,7 @@ window.FileList = { } else { simpleSize = t('files', 'Pending'); } - var lastModifiedTime = Math.round(mtime / 1000); + td = $('').attr({ "class": "filesize", "style": 'color:rgb(' + sizeColor + ',' + sizeColor + ',' + sizeColor + ')' @@ -453,8 +453,6 @@ window.FileList = { }); }, reloadCallback: function(result) { - var $controls = $('#controls'); - delete this._reloadCall; this.hideMask(); @@ -822,10 +820,9 @@ window.FileList = { var info = t('files', '{dirs} and {files}', infoVars); // don't show the filesize column, if filesize is NaN (e.g. in trashbin) - if (isNaN(summary.totalSize)) { - var fileSize = ''; - } else { - var fileSize = ''+humanFileSize(summary.totalSize)+''; + var fileSize = ''; + if (!isNaN(summary.totalSize)) { + fileSize = ''+humanFileSize(summary.totalSize)+''; } var $summary = $(''+info+''+fileSize+''); @@ -911,7 +908,6 @@ window.FileList = { } }, updateEmptyContent: function() { - var $fileList = $('#fileList'); var permissions = $('#permissions').val(); var isCreatable = (permissions & OC.PERMISSION_CREATE) !== 0; $('#emptycontent').toggleClass('hidden', !isCreatable || !FileList.isEmpty); @@ -949,13 +945,13 @@ window.FileList = { }, scrollTo:function(file) { //scroll to and highlight preselected file - var $scrolltorow = FileList.findFileEl(file); - if ($scrolltorow.exists()) { - $scrolltorow.addClass('searchresult'); - $(window).scrollTop($scrolltorow.position().top); + var $scrollToRow = FileList.findFileEl(file); + if ($scrollToRow.exists()) { + $scrollToRow.addClass('searchresult'); + $(window).scrollTop($scrollToRow.position().top); //remove highlight when hovered over - $scrolltorow.one('hover', function() { - $scrolltorow.removeClass('searchresult'); + $scrollToRow.one('hover', function() { + $scrollToRow.removeClass('searchresult'); }); } }, @@ -991,9 +987,9 @@ $(document).ready(function() { FileList.initialize(); // handle upload events - var file_upload_start = $('#file_upload_start'); + var fileUploadStart = $('#file_upload_start'); - file_upload_start.on('fileuploaddrop', function(e, data) { + fileUploadStart.on('fileuploaddrop', function(e, data) { OC.Upload.log('filelist handle fileuploaddrop', e, data); var dropTarget = $(e.originalEvent.target).closest('tr, .crumb'); @@ -1021,7 +1017,7 @@ $(document).ready(function() { return [ {name: 'dir', value: dir}, {name: 'requesttoken', value: oc_requesttoken}, - {name: 'file_directory', value: data.files[0]['relativePath']} + {name: 'file_directory', value: data.files[0].relativePath} ]; }; } else { @@ -1032,7 +1028,7 @@ $(document).ready(function() { } } }); - file_upload_start.on('fileuploadadd', function(e, data) { + fileUploadStart.on('fileuploadadd', function(e, data) { OC.Upload.log('filelist handle fileuploadadd', e, data); //finish delete if we are uploading a deleted file @@ -1045,19 +1041,19 @@ $(document).ready(function() { // add to existing folder // update upload counter ui - var uploadtext = data.context.find('.uploadtext'); - var currentUploads = parseInt(uploadtext.attr('currentUploads')); + var uploadText = data.context.find('.uploadtext'); + var currentUploads = parseInt(uploadText.attr('currentUploads'), 10); currentUploads += 1; - uploadtext.attr('currentUploads', currentUploads); + uploadText.attr('currentUploads', currentUploads); var translatedText = n('files', 'Uploading %n file', 'Uploading %n files', currentUploads); if (currentUploads === 1) { var img = OC.imagePath('core', 'loading.gif'); data.context.find('td.filename').attr('style','background-image:url('+img+')'); - uploadtext.text(translatedText); - uploadtext.show(); + uploadText.text(translatedText); + uploadText.show(); } else { - uploadtext.text(translatedText); + uploadText.text(translatedText); } } @@ -1066,7 +1062,7 @@ $(document).ready(function() { * when file upload done successfully add row to filelist * update counter when uploading to sub folder */ - file_upload_start.on('fileuploaddone', function(e, data) { + fileUploadStart.on('fileuploaddone', function(e, data) { OC.Upload.log('filelist handle fileuploaddone', e, data); var response; @@ -1080,27 +1076,28 @@ $(document).ready(function() { if (typeof result[0] !== 'undefined' && result[0].status === 'success') { var file = result[0]; + var size = 0; if (data.context && data.context.data('type') === 'dir') { // update upload counter ui - var uploadtext = data.context.find('.uploadtext'); - var currentUploads = parseInt(uploadtext.attr('currentUploads')); + var uploadText = data.context.find('.uploadtext'); + var currentUploads = parseInt(uploadText.attr('currentUploads'), 10); currentUploads -= 1; - uploadtext.attr('currentUploads', currentUploads); + uploadText.attr('currentUploads', currentUploads); var translatedText = n('files', 'Uploading %n file', 'Uploading %n files', currentUploads); if (currentUploads === 0) { var img = OC.imagePath('core', 'filetypes/folder'); data.context.find('td.filename').attr('style','background-image:url('+img+')'); - uploadtext.text(translatedText); - uploadtext.hide(); + uploadText.text(translatedText); + uploadText.hide(); } else { - uploadtext.text(translatedText); + uploadText.text(translatedText); } // update folder size - var size = parseInt(data.context.data('size')); - size += parseInt(file.size); + size = parseInt(data.context.data('size'), 10); + size += parseInt(file.size, 10); data.context.attr('data-size', size); data.context.find('td.filesize').text(humanFileSize(size)); } else { @@ -1131,8 +1128,8 @@ $(document).ready(function() { fileDirectory = FileList.findFileEl(fileDirectory); // update folder size - var size = parseInt(fileDirectory.attr('data-size')); - size += parseInt(file.size); + size = parseInt(fileDirectory.attr('data-size'), 10); + size += parseInt(file.size, 10); fileDirectory.attr('data-size', size); fileDirectory.find('td.filesize').text(humanFileSize(size)); @@ -1140,7 +1137,7 @@ $(document).ready(function() { } // add as stand-alone row to filelist - var size=t('files', 'Pending'); + size = t('files', 'Pending'); if (data.files[0].size>=0) { size=data.files[0].size; } @@ -1152,37 +1149,40 @@ $(document).ready(function() { } } }); - file_upload_start.on('fileuploadstop', function(e, data) { + fileUploadStart.on('fileuploadstop', function(e, data) { OC.Upload.log('filelist handle fileuploadstop', e, data); //if user pressed cancel hide upload chrome if (data.errorThrown === 'abort') { //cleanup uploading to a dir - var uploadtext = $('tr .uploadtext'); + var uploadText = $('tr .uploadtext'); var img = OC.imagePath('core', 'filetypes/folder'); - uploadtext.parents('td.filename').attr('style','background-image:url('+img+')'); - uploadtext.fadeOut(); - uploadtext.attr('currentUploads', 0); + uploadText.parents('td.filename').attr('style','background-image:url('+img+')'); + uploadText.fadeOut(); + uploadText.attr('currentUploads', 0); } }); - file_upload_start.on('fileuploadfail', function(e, data) { + fileUploadStart.on('fileuploadfail', function(e, data) { OC.Upload.log('filelist handle fileuploadfail', e, data); //if user pressed cancel hide upload chrome if (data.errorThrown === 'abort') { //cleanup uploading to a dir - var uploadtext = $('tr .uploadtext'); + var uploadText = $('tr .uploadtext'); var img = OC.imagePath('core', 'filetypes/folder'); - uploadtext.parents('td.filename').attr('style','background-image:url('+img+')'); - uploadtext.fadeOut(); - uploadtext.attr('currentUploads', 0); + uploadText.parents('td.filename').attr('style','background-image:url('+img+')'); + uploadText.fadeOut(); + uploadText.attr('currentUploads', 0); } }); $('#notification').hide(); $('#notification:first-child').on('click', '.replace', function() { OC.Notification.hide(function() { - FileList.replace($('#notification > span').attr('data-oldName'), $('#notification > span').attr('data-newName'), $('#notification > span').attr('data-isNewFile')); + FileList.replace( + $('#notification > span').attr('data-oldName'), + $('#notification > span').attr('data-newName'), + $('#notification > span').attr('data-isNewFile')); }); }); $('#notification:first-child').on('click', '.suggest', function() { @@ -1212,8 +1212,7 @@ $(document).ready(function() { function parseHashQuery() { var hash = window.location.hash, - pos = hash.indexOf('?'), - query; + pos = hash.indexOf('?'); if (pos >= 0) { return hash.substr(pos + 1); } @@ -1222,8 +1221,7 @@ $(document).ready(function() { function parseCurrentDirFromUrl() { var query = parseHashQuery(), - params, - dir = '/'; + params; // try and parse from URL hash first if (query) { params = OC.parseQueryString(decodeQuery(query)); From 8bb27551bde2d5aa516f4df2918ca3ed0ccd18b0 Mon Sep 17 00:00:00 2001 From: Arthur Schiwon Date: Thu, 20 Mar 2014 00:21:14 +0100 Subject: [PATCH 038/187] LDAP: make sure cache key for paged result cookie matches when limit or offset is null or 0 --- apps/user_ldap/lib/access.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/apps/user_ldap/lib/access.php b/apps/user_ldap/lib/access.php index 4d187bab8d..b8e7e9bb67 100644 --- a/apps/user_ldap/lib/access.php +++ b/apps/user_ldap/lib/access.php @@ -1184,7 +1184,7 @@ class Access extends LDAPUtility { } $offset -= $limit; //we work with cache here - $cachekey = 'lc' . crc32($base) . '-' . crc32($filter) . '-' . $limit . '-' . $offset; + $cachekey = 'lc' . crc32($base) . '-' . crc32($filter) . '-' . intval($limit) . '-' . intval($offset); $cookie = ''; if(isset($this->cookies[$cachekey])) { $cookie = $this->cookies[$cachekey]; @@ -1206,7 +1206,7 @@ class Access extends LDAPUtility { */ private function setPagedResultCookie($base, $filter, $limit, $offset, $cookie) { if(!empty($cookie)) { - $cachekey = 'lc' . crc32($base) . '-' . crc32($filter) . '-' .$limit . '-' . $offset; + $cachekey = 'lc' . crc32($base) . '-' . crc32($filter) . '-' .intval($limit) . '-' . intval($offset); $this->cookies[$cachekey] = $cookie; } } From 40f9875f11e1bd35dd96ebd8fc8cf718be49689c Mon Sep 17 00:00:00 2001 From: Arthur Schiwon Date: Thu, 20 Mar 2014 00:21:51 +0100 Subject: [PATCH 039/187] LDAP: fix user report i.e. count for LDAP servers with really many users --- apps/user_ldap/lib/access.php | 42 ++++++++++++++++++++++------------- 1 file changed, 27 insertions(+), 15 deletions(-) diff --git a/apps/user_ldap/lib/access.php b/apps/user_ldap/lib/access.php index b8e7e9bb67..7640a5b1bc 100644 --- a/apps/user_ldap/lib/access.php +++ b/apps/user_ldap/lib/access.php @@ -659,7 +659,7 @@ class Access extends LDAPUtility { * @param string $filter */ public function countUsers($filter, $attr = array('dn'), $limit = null, $offset = null) { - return $this->count($filter, $this->connection->ldapBaseGroups, $attr, $limit, $offset); + return $this->count($filter, $this->connection->ldapBaseUsers, $attr, $limit, $offset); } /** @@ -775,22 +775,34 @@ class Access extends LDAPUtility { */ private function count($filter, $base, $attr = null, $limit = null, $offset = null, $skipHandling = false) { \OCP\Util::writeLog('user_ldap', 'Count filter: '.print_r($filter, true), \OCP\Util::DEBUG); - $search = $this->executeSearch($filter, $base, $attr, $limit, $offset); - if($search === false) { - return false; - } - list($sr, $pagedSearchOK) = $search; - $cr = $this->connection->getConnectionResource(); - $counter = 0; - foreach($sr as $key => $res) { - $count = $this->ldap->countEntries($cr, $res); - if($count !== false) { - $counter += $count; - } + + if(is_null($limit)) { + //TODO replace 400 with $this->connection->ldapPagingSize; once PR 6221 is merged and move it to callee countUsers() + $limit = 400; } - $this->processPagedSearchStatus($sr, $filter, $base, $counter, $limit, + $counter = 0; + $cr = $this->connection->getConnectionResource(); + + do { + $search = $this->executeSearch($filter, $base, $attr, + $limit, $offset); + if($search === false) { + return $counter > 0 ? $counter : false; + } + list($sr, $pagedSearchOK) = $search; + + foreach($sr as $key => $res) { + $count = $this->ldap->countEntries($cr, $res); + if($count !== false) { + $counter += $count; + } + } + + $this->processPagedSearchStatus($sr, $filter, $base, $count, $limit, $offset, $pagedSearchOK, $skipHandling); + $offset += $limit; + } while($count === $limit); return $counter; } @@ -891,7 +903,7 @@ class Access extends LDAPUtility { //we slice the findings, when //a) paged search insuccessful, though attempted //b) no paged search, but limit set - if((!$this->pagedSearchedSuccessful + if((!$this->getPagedSearchResultState() && $pagedSearchOK) || ( !$pagedSearchOK From 43402c56ddbe5e4041fdcdc0605d16badeadb69b Mon Sep 17 00:00:00 2001 From: Arthur Schiwon Date: Thu, 20 Mar 2014 09:14:41 +0100 Subject: [PATCH 040/187] initialize variable --- apps/user_ldap/lib/access.php | 1 + 1 file changed, 1 insertion(+) diff --git a/apps/user_ldap/lib/access.php b/apps/user_ldap/lib/access.php index 7640a5b1bc..1af993677c 100644 --- a/apps/user_ldap/lib/access.php +++ b/apps/user_ldap/lib/access.php @@ -782,6 +782,7 @@ class Access extends LDAPUtility { } $counter = 0; + $count = null; $cr = $this->connection->getConnectionResource(); do { From bdc418d1f99738d45b2a0e4fab07c974d5b58681 Mon Sep 17 00:00:00 2001 From: Arthur Schiwon Date: Thu, 20 Mar 2014 10:59:58 +0100 Subject: [PATCH 041/187] var count is assigned in the inner loop so it must be checked inside there to be properly used as part of the exit condition of the outer loop --- apps/user_ldap/lib/access.php | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/apps/user_ldap/lib/access.php b/apps/user_ldap/lib/access.php index 1af993677c..d135ee5b73 100644 --- a/apps/user_ldap/lib/access.php +++ b/apps/user_ldap/lib/access.php @@ -786,6 +786,7 @@ class Access extends LDAPUtility { $cr = $this->connection->getConnectionResource(); do { + $continue = false; $search = $this->executeSearch($filter, $base, $attr, $limit, $offset); if($search === false) { @@ -798,12 +799,15 @@ class Access extends LDAPUtility { if($count !== false) { $counter += $count; } + if($count === $limit) { + $continue = true; + } } $this->processPagedSearchStatus($sr, $filter, $base, $count, $limit, $offset, $pagedSearchOK, $skipHandling); $offset += $limit; - } while($count === $limit); + } while($continue); return $counter; } From 2e5adadad7b779f0794a6ecddb2b0f51b9214b4a Mon Sep 17 00:00:00 2001 From: Arthur Schiwon Date: Thu, 20 Mar 2014 11:38:05 +0100 Subject: [PATCH 042/187] Put inner loop into own method, let's see whether it makes scrutinizer happier --- apps/user_ldap/lib/access.php | 27 ++++++++++++++++++--------- 1 file changed, 18 insertions(+), 9 deletions(-) diff --git a/apps/user_ldap/lib/access.php b/apps/user_ldap/lib/access.php index d135ee5b73..ce97aaff14 100644 --- a/apps/user_ldap/lib/access.php +++ b/apps/user_ldap/lib/access.php @@ -794,15 +794,8 @@ class Access extends LDAPUtility { } list($sr, $pagedSearchOK) = $search; - foreach($sr as $key => $res) { - $count = $this->ldap->countEntries($cr, $res); - if($count !== false) { - $counter += $count; - } - if($count === $limit) { - $continue = true; - } - } + $count = $this->countEntriesInSearchResults($sr, $limit, $continue); + $counter += $count; $this->processPagedSearchStatus($sr, $filter, $base, $count, $limit, $offset, $pagedSearchOK, $skipHandling); @@ -812,6 +805,22 @@ class Access extends LDAPUtility { return $counter; } + private function countEntriesInSearchResults($searchResults, $limit, + &$hasHitLimit) { + $cr = $this->connection->getConnectionResource(); + $count = 0; + + foreach($searchResults as $res) { + $count = intval($this->ldap->countEntries($cr, $res)); + $counter += $count; + if($count === $limit) { + $hasHitLimit = true; + } + } + + return $counter; + } + /** * @brief executes an LDAP search * @param $filter the LDAP filter for the search From ee3368d2ad8c362e27d904bbb22c7b741b09cff8 Mon Sep 17 00:00:00 2001 From: Arthur Schiwon Date: Wed, 9 Apr 2014 11:38:51 +0200 Subject: [PATCH 043/187] replace hardcoded limit of 400 with user controlled ldapPagingSize value --- apps/user_ldap/lib/access.php | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/apps/user_ldap/lib/access.php b/apps/user_ldap/lib/access.php index ce97aaff14..0b3ff4aa15 100644 --- a/apps/user_ldap/lib/access.php +++ b/apps/user_ldap/lib/access.php @@ -777,8 +777,7 @@ class Access extends LDAPUtility { \OCP\Util::writeLog('user_ldap', 'Count filter: '.print_r($filter, true), \OCP\Util::DEBUG); if(is_null($limit)) { - //TODO replace 400 with $this->connection->ldapPagingSize; once PR 6221 is merged and move it to callee countUsers() - $limit = 400; + $limit = $this->connection->ldapPagingSize; } $counter = 0; From 015b9b1dac1e7f90e4e28d2b19697bedf83e56fe Mon Sep 17 00:00:00 2001 From: Joas Schilling Date: Wed, 2 Apr 2014 12:54:41 +0200 Subject: [PATCH 044/187] Add option to getUsersSharingFile() to get the paths for the shared users --- lib/private/share/share.php | 61 ++++++++++++++++++++++++++++++------- lib/public/share.php | 7 +++-- 2 files changed, 54 insertions(+), 14 deletions(-) diff --git a/lib/private/share/share.php b/lib/private/share/share.php index 7bab98b00b..3d22f6fd45 100644 --- a/lib/private/share/share.php +++ b/lib/private/share/share.php @@ -91,23 +91,25 @@ class Share extends \OC\Share\Constants { /** * Find which users can access a shared item - * @param $path to the file - * @param $user owner of the file - * @param include owner to the list of users with access to the file + * @param string $path to the file + * @param string $ownerUser owner of the file + * @param bool $includeOwner include owner to the list of users with access to the file + * @param bool $returnUserPaths Return an array with the user => path map * @return array * @note $path needs to be relative to user data dir, e.g. 'file.txt' * not '/admin/data/file.txt' */ - public static function getUsersSharingFile($path, $user, $includeOwner = false) { + public static function getUsersSharingFile($path, $ownerUser, $includeOwner = false, $returnUserPaths = false) { - $shares = array(); + $shares = $sharePaths = $fileTargets = array(); $publicShare = false; $source = -1; $cache = false; - $view = new \OC\Files\View('/' . $user . '/files'); + $view = new \OC\Files\View('/' . $ownerUser . '/files'); if ($view->file_exists($path)) { $meta = $view->getFileInfo($path); + $path = substr($meta->getPath(), strlen('/' . $ownerUser . '/files')); } else { // if the file doesn't exists yet we start with the parent folder $meta = $view->getFileInfo(dirname($path)); @@ -119,10 +121,9 @@ class Share extends \OC\Share\Constants { } while ($source !== -1) { - // Fetch all shares with another user $query = \OC_DB::prepare( - 'SELECT `share_with` + 'SELECT `share_with`, `file_source`, `file_target` FROM `*PREFIX*share` WHERE @@ -136,12 +137,15 @@ class Share extends \OC\Share\Constants { } else { while ($row = $result->fetchRow()) { $shares[] = $row['share_with']; + if ($returnUserPaths) { + $fileTargets[(int) $row['file_source']][$row['share_with']] = $row; + } } } - // We also need to take group shares into account + // We also need to take group shares into account $query = \OC_DB::prepare( - 'SELECT `share_with` + 'SELECT `share_with`, `file_source`, `file_target` FROM `*PREFIX*share` WHERE @@ -156,6 +160,11 @@ class Share extends \OC\Share\Constants { while ($row = $result->fetchRow()) { $usersInGroup = \OC_Group::usersInGroup($row['share_with']); $shares = array_merge($shares, $usersInGroup); + if ($returnUserPaths) { + foreach ($usersInGroup as $user) { + $fileTargets[(int) $row['file_source']][$user] = $row; + } + } } } @@ -188,9 +197,39 @@ class Share extends \OC\Share\Constants { $source = -1; } } + // Include owner in list of users, if requested if ($includeOwner) { - $shares[] = $user; + $shares[] = $ownerUser; + if ($returnUserPaths) { + $sharePaths[$ownerUser] = $path; + } + } + + if ($returnUserPaths) { + $fileTargetIDs = array_keys($fileTargets); + $fileTargetIDs = array_unique($fileTargetIDs); + + $query = \OC_DB::prepare( + 'SELECT `fileid`, `path` + FROM `*PREFIX*filecache` + WHERE `fileid` IN (' . implode(',', $fileTargetIDs) . ')' + ); + $result = $query->execute(); + + if (\OCP\DB::isError($result)) { + \OCP\Util::writeLog('OCP\Share', \OC_DB::getErrorMessage($result), \OC_Log::ERROR); + } else { + while ($row = $result->fetchRow()) { + foreach ($fileTargets[$row['fileid']] as $uid => $shareData) { + $sharedPath = '/Shared' . $shareData['file_target']; + $sharedPath .= substr($path, strlen($row['path']) -5); + $sharePaths[$uid] = $sharedPath; + } + } + } + + return $sharePaths; } return array("users" => array_unique($shares), "public" => $publicShare); diff --git a/lib/public/share.php b/lib/public/share.php index 564839e86b..c694314ad0 100644 --- a/lib/public/share.php +++ b/lib/public/share.php @@ -64,14 +64,15 @@ class Share extends \OC\Share\Constants { /** * Find which users can access a shared item * @param string $path to the file - * @param string $user owner of the file + * @param string $ownerUser owner of the file * @param bool $includeOwner include owner to the list of users with access to the file + * @param bool $returnUserPaths Return an array with the user => path map * @return array * @note $path needs to be relative to user data dir, e.g. 'file.txt' * not '/admin/data/file.txt' */ - public static function getUsersSharingFile($path, $user, $includeOwner = false) { - return \OC\Share\Share::getUsersSharingFile($path, $user, $includeOwner); + public static function getUsersSharingFile($path, $ownerUser, $includeOwner = false, $returnUserPaths = false) { + return \OC\Share\Share::getUsersSharingFile($path, $ownerUser, $includeOwner, $returnUserPaths); } /** From d418e176ce4ce3ee07a5b9e632150470c0387f87 Mon Sep 17 00:00:00 2001 From: Joas Schilling Date: Wed, 9 Apr 2014 15:01:39 +0200 Subject: [PATCH 045/187] Do not query when the list is empty --- lib/private/share/share.php | 30 ++++++++++++++++-------------- 1 file changed, 16 insertions(+), 14 deletions(-) diff --git a/lib/private/share/share.php b/lib/private/share/share.php index 3d22f6fd45..59826d0303 100644 --- a/lib/private/share/share.php +++ b/lib/private/share/share.php @@ -210,21 +210,23 @@ class Share extends \OC\Share\Constants { $fileTargetIDs = array_keys($fileTargets); $fileTargetIDs = array_unique($fileTargetIDs); - $query = \OC_DB::prepare( - 'SELECT `fileid`, `path` - FROM `*PREFIX*filecache` - WHERE `fileid` IN (' . implode(',', $fileTargetIDs) . ')' - ); - $result = $query->execute(); + if (!empty($fileTargetIDs)) { + $query = \OC_DB::prepare( + 'SELECT `fileid`, `path` + FROM `*PREFIX*filecache` + WHERE `fileid` IN (' . implode(',', $fileTargetIDs) . ')' + ); + $result = $query->execute(); - if (\OCP\DB::isError($result)) { - \OCP\Util::writeLog('OCP\Share', \OC_DB::getErrorMessage($result), \OC_Log::ERROR); - } else { - while ($row = $result->fetchRow()) { - foreach ($fileTargets[$row['fileid']] as $uid => $shareData) { - $sharedPath = '/Shared' . $shareData['file_target']; - $sharedPath .= substr($path, strlen($row['path']) -5); - $sharePaths[$uid] = $sharedPath; + if (\OCP\DB::isError($result)) { + \OCP\Util::writeLog('OCP\Share', \OC_DB::getErrorMessage($result), \OC_Log::ERROR); + } else { + while ($row = $result->fetchRow()) { + foreach ($fileTargets[$row['fileid']] as $uid => $shareData) { + $sharedPath = '/Shared' . $shareData['file_target']; + $sharedPath .= substr($path, strlen($row['path']) -5); + $sharePaths[$uid] = $sharedPath; + } } } } From 2fc9e27ceddf9d632d79916a71f9100138bb685f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thomas=20M=C3=BCller?= Date: Wed, 9 Apr 2014 22:16:41 +0200 Subject: [PATCH 046/187] use FileList.findFileEl --- apps/files/js/filelist.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/apps/files/js/filelist.js b/apps/files/js/filelist.js index 8ee89aad3e..9c749bb8f3 100644 --- a/apps/files/js/filelist.js +++ b/apps/files/js/filelist.js @@ -1098,7 +1098,8 @@ $(document).ready(function() { fileDirectory = fileDirectory[0]; // Get the directory - if ($('tr[data-file="'+fileDirectory+'"]').length === 0) { + var fd = FileList.findFileEl(fileDirectory); + if (fd.length === 0) { var dir = { name: fileDirectory, type: 'dir', From e8be2ac55492a7e4f141d3bb7e6698783bef454c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thomas=20M=C3=BCller?= Date: Wed, 9 Apr 2014 23:32:12 +0200 Subject: [PATCH 047/187] In cases folder drag and drop is not supported a proper message is displayed --- apps/files/js/file-upload.js | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) diff --git a/apps/files/js/file-upload.js b/apps/files/js/file-upload.js index 46a5320840..070314a118 100644 --- a/apps/files/js/file-upload.js +++ b/apps/files/js/file-upload.js @@ -242,11 +242,18 @@ OC.Upload = { data.errorThrown = errorMessage; } - if (file.type === '' && file.size === 4096) { - data.textStatus = 'dirorzero'; - data.errorThrown = t('files', 'Unable to upload {filename} as it is a directory or has 0 bytes', - {filename: file.name} - ); + // in case folder drag and drop is not supported file will point to a directory + if (!file.type && file.size%4096 === 0 && file.size <= 102400) { + try { + reader = new FileReader(); + reader.readAsBinaryString(f); + } catch (NS_ERROR_FILE_ACCESS_DENIED) { + //file is a directory + data.textStatus = 'dirorzero'; + data.errorThrown = t('files', 'Unable to upload {filename} as it is a directory or has 0 bytes', + {filename: file.name} + ); + } } // add size From 0edacf372c5b2e1f1b64210ceff5e757494c959b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thomas=20M=C3=BCller?= Date: Thu, 10 Apr 2014 18:07:36 +0200 Subject: [PATCH 048/187] fix single file upload in firefox --- apps/files/js/file-upload.js | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/apps/files/js/file-upload.js b/apps/files/js/file-upload.js index 070314a118..516812563a 100644 --- a/apps/files/js/file-upload.js +++ b/apps/files/js/file-upload.js @@ -333,11 +333,15 @@ OC.Upload = { submit: function(e, data) { OC.Upload.rememberUpload(data); if ( ! data.formData ) { + var fileDirectory = ''; + if(typeof data.files[0].relativePath !== 'undefined') { + fileDirectory = data.files[0].relativePath; + } // noone set update parameters, we set the minimum data.formData = { requesttoken: oc_requesttoken, dir: $('#dir').val(), - file_directory: data.files[0]['relativePath'] + file_directory: fileDirectory }; } }, From 9e9c109390c265cdb63b5788365ce74034009a6c Mon Sep 17 00:00:00 2001 From: Volkan Gezer Date: Thu, 10 Apr 2014 21:54:25 +0200 Subject: [PATCH 049/187] Expiration date is hidden smoothly From now on, it is hidden smoothly as the other fields do. --- core/js/share.js | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/core/js/share.js b/core/js/share.js index ef71cc7999..19d71a6cbd 100644 --- a/core/js/share.js +++ b/core/js/share.js @@ -480,15 +480,16 @@ OC.Share={ $('#emailPrivateLink #email').hide(); $('#emailPrivateLink #emailButton').hide(); $('#allowPublicUploadWrapper').hide(); + $('#expirationDate').hide(); }, dirname:function(path) { return path.replace(/\\/g,'/').replace(/\/[^\/]*$/, ''); }, showExpirationDate:function(date) { $('#expirationCheckbox').attr('checked', true); - $('#expirationDate').before('
'); $('#expirationDate').val(date); - $('#expirationDate').show(); + $('#expirationDate').show('blind'); + $('#expirationDate').css('display','block'); $('#expirationDate').datepicker({ dateFormat : 'dd-mm-yy' }); @@ -558,7 +559,7 @@ $(document).ready(function() { OC.Share.itemShares[shareType].splice(index, 1); OC.Share.updateIcon(itemType, itemSource); if (typeof OC.Share.statuses[itemSource] === 'undefined') { - $('#expiration').hide(); + $('#expiration').hide('blind'); } }); return false; @@ -618,7 +619,7 @@ $(document).ready(function() { OC.Share.itemShares[OC.Share.SHARE_TYPE_LINK] = false; OC.Share.updateIcon(itemType, itemSource); if (typeof OC.Share.statuses[itemSource] === 'undefined') { - $('#expiration').hide(); + $('#expiration').hide('blind'); } }); } @@ -710,7 +711,7 @@ $(document).ready(function() { if (!result || result.status !== 'success') { OC.dialogs.alert(t('core', 'Error unsetting expiration date'), t('core', 'Error')); } - $('#expirationDate').hide(); + $('#expirationDate').hide('blind'); }); } }); From e28227ecdc0f898868701fe131326d8c15ef60d7 Mon Sep 17 00:00:00 2001 From: Arthur Schiwon Date: Fri, 11 Apr 2014 13:56:06 +0200 Subject: [PATCH 050/187] fix comparison in determineMode, fixes problems with restoring the filter mode (assisted or manually) on page refresh --- apps/user_ldap/js/ldapFilter.js | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/apps/user_ldap/js/ldapFilter.js b/apps/user_ldap/js/ldapFilter.js index 70f24f0d4a..d0fee6db7f 100644 --- a/apps/user_ldap/js/ldapFilter.js +++ b/apps/user_ldap/js/ldapFilter.js @@ -64,13 +64,16 @@ LdapFilter.prototype.determineMode = function() { LdapWizard.ajax(param, function(result) { property = 'ldap' + filter.target + 'FilterMode'; - filter.mode = result.changes[property]; + filter.mode = parseInt(result.changes[property]); if(filter.mode === LdapWizard.filterModeRaw && $('#raw'+filter.target+'FilterContainer').hasClass('invisible')) { LdapWizard['toggleRaw'+filter.target+'Filter'](); } else if(filter.mode === LdapWizard.filterModeAssisted && !$('#raw'+filter.target+'FilterContainer').hasClass('invisible')) { LdapWizard['toggleRaw'+filter.target+'Filter'](); + } else { + console.log('LDAP Wizard determineMode: returned mode was »' + + filter.mode + '« of type ' + typeof filter.mode); } filter.unlock(); }, From 1c57ffa16fab8cfb132978865d819e98cb143a24 Mon Sep 17 00:00:00 2001 From: Arthur Schiwon Date: Fri, 11 Apr 2014 14:08:12 +0200 Subject: [PATCH 051/187] Give hint when composing filter failed --- apps/user_ldap/js/ldapFilter.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/apps/user_ldap/js/ldapFilter.js b/apps/user_ldap/js/ldapFilter.js index d0fee6db7f..0d69164794 100644 --- a/apps/user_ldap/js/ldapFilter.js +++ b/apps/user_ldap/js/ldapFilter.js @@ -50,7 +50,8 @@ LdapFilter.prototype.compose = function() { } }, function (result) { - // error handling + console.log('LDAP Wizard: could not compose filter. '+ + 'Please check owncloud.log'); } ); } From 07ea57465ba23dd31dae943b44f4c04ccabb5e60 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thomas=20M=C3=BCller?= Date: Fri, 11 Apr 2014 14:54:13 +0200 Subject: [PATCH 052/187] drop folder support on public shared folders --- apps/files_sharing/js/public.js | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/apps/files_sharing/js/public.js b/apps/files_sharing/js/public.js index 9ce8985f1f..ae2412f6a3 100644 --- a/apps/files_sharing/js/public.js +++ b/apps/files_sharing/js/public.js @@ -62,11 +62,17 @@ $(document).ready(function() { var file_upload_start = $('#file_upload_start'); file_upload_start.on('fileuploadadd', function(e, data) { + var fileDirectory = ''; + if(typeof data.files[0].relativePath !== 'undefined') { + fileDirectory = data.files[0].relativePath; + } + // Add custom data to the upload handler data.formData = { requesttoken: $('#publicUploadRequestToken').val(), dirToken: $('#dirToken').val(), - subdir: $('input#dir').val() + subdir: $('input#dir').val(), + file_directory: fileDirectory }; }); From 6343391e580b0a686f2b2ee2b0b43ca675473be8 Mon Sep 17 00:00:00 2001 From: Lukas Reschke Date: Fri, 11 Apr 2014 20:38:27 +0200 Subject: [PATCH 053/187] Change from showHTML to show There is no need to use `showHTML` here. --- apps/files/js/files.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/files/js/files.js b/apps/files/js/files.js index ac10191618..9f38263bef 100644 --- a/apps/files/js/files.js +++ b/apps/files/js/files.js @@ -135,7 +135,7 @@ var Files = { return; } if (initStatus === '1') { // encryption tried to init but failed - OC.Notification.showHtml(t('files', 'Invalid private key for Encryption App. Please update your private key password in your personal settings to recover access to your encrypted files.')); + OC.Notification.show(t('files', 'Invalid private key for Encryption App. Please update your private key password in your personal settings to recover access to your encrypted files.')); return; } if (encryptedFiles === '1') { From df67a04385e64d2e14a7c0385289904d04b53301 Mon Sep 17 00:00:00 2001 From: Lukas Reschke Date: Sun, 13 Apr 2014 11:51:03 +0200 Subject: [PATCH 054/187] Move security headers to base.php Some headers were currently only added to the templates but not to other components (e.g. SabreDAV / JSON / etc...) The migration to base.php ensures that the headers are served to all requests passing base.php --- lib/base.php | 31 +++++++++++++++++++++++++++++++ lib/private/template.php | 26 +------------------------- 2 files changed, 32 insertions(+), 25 deletions(-) diff --git a/lib/base.php b/lib/base.php index 6ea77aa7a5..34baba69d4 100644 --- a/lib/base.php +++ b/lib/base.php @@ -213,6 +213,36 @@ class OC { } } + /* + * This function adds some security related headers to all requests + * served via base.php + * The implementation of this function as to happen here to ensure that + * all third-party components (e.g. SabreDAV) also benefit from this + * headers + */ + public static function addSecurityHeaders() { + header('X-XSS-Protection: 1; mode=block'); // Enforce browser based XSS filters + header('X-Content-Type-Options: nosniff'); // Disable sniffing the content type for IE + + // iFrame Restriction Policy + $xFramePolicy = OC_Config::getValue('xframe_restriction', true); + if($xFramePolicy) { + header('X-Frame-Options: Sameorigin'); // Disallow iFraming from other domains + } + + // Content Security Policy + // If you change the standard policy, please also change it in config.sample.php + $policy = OC_Config::getValue('custom_csp_policy', + 'default-src \'self\'; ' + .'script-src \'self\' \'unsafe-eval\'; ' + .'style-src \'self\' \'unsafe-inline\'; ' + .'frame-src *; ' + .'img-src *; ' + .'font-src \'self\' data:; ' + .'media-src *'); + header('Content-Security-Policy:'.$policy); + } + public static function checkSSL() { // redirect to https site if configured if (OC_Config::getValue("forcessl", false)) { @@ -512,6 +542,7 @@ class OC { self::checkConfig(); self::checkInstalled(); self::checkSSL(); + self::addSecurityHeaders(); $errors = OC_Util::checkServer(); if (count($errors) > 0) { diff --git a/lib/private/template.php b/lib/private/template.php index c6851c6cc8..b7db569095 100644 --- a/lib/private/template.php +++ b/lib/private/template.php @@ -64,31 +64,7 @@ class OC_Template extends \OC\Template\Base { $this->path = $path; parent::__construct($template, $requesttoken, $l10n, $themeDefaults); - - // Some headers to enhance security - header('X-XSS-Protection: 1; mode=block'); // Enforce browser based XSS filters - header('X-Content-Type-Options: nosniff'); // Disable sniffing the content type for IE - - // iFrame Restriction Policy - $xFramePolicy = OC_Config::getValue('xframe_restriction', true); - if($xFramePolicy) { - header('X-Frame-Options: Sameorigin'); // Disallow iFraming from other domains - } - - // Content Security Policy - // If you change the standard policy, please also change it in config.sample.php - $policy = OC_Config::getValue('custom_csp_policy', - 'default-src \'self\'; ' - .'script-src \'self\' \'unsafe-eval\'; ' - .'style-src \'self\' \'unsafe-inline\'; ' - .'frame-src *; ' - .'img-src *; ' - .'font-src \'self\' data:; ' - .'media-src *'); - header('Content-Security-Policy:'.$policy); // Standard - - } - +} /** * autodetect the formfactor of the used device * default -> the normal desktop browser interface From a2a850dd91664cbfeba50186d5219d90d62eadd2 Mon Sep 17 00:00:00 2001 From: Lukas Reschke Date: Sun, 13 Apr 2014 11:52:31 +0200 Subject: [PATCH 055/187] Fix indentation --- lib/private/template.php | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/lib/private/template.php b/lib/private/template.php index b7db569095..610d5fbc8e 100644 --- a/lib/private/template.php +++ b/lib/private/template.php @@ -64,7 +64,8 @@ class OC_Template extends \OC\Template\Base { $this->path = $path; parent::__construct($template, $requesttoken, $l10n, $themeDefaults); -} + } + /** * autodetect the formfactor of the used device * default -> the normal desktop browser interface From b04d95b1160673dbaa80ce214f22e931e8c93178 Mon Sep 17 00:00:00 2001 From: Lukas Reschke Date: Sun, 13 Apr 2014 12:48:16 +0200 Subject: [PATCH 056/187] Remove uneeded usages of nosniff --- lib/private/json.php | 2 -- lib/public/appframework/http/jsonresponse.php | 1 - tests/lib/appframework/http/JSONResponseTest.php | 7 ------- 3 files changed, 10 deletions(-) diff --git a/lib/private/json.php b/lib/private/json.php index 4ccdb490a6..34f81c3b8c 100644 --- a/lib/private/json.php +++ b/lib/private/json.php @@ -119,8 +119,6 @@ class OC_JSON{ * Encode and print $data in json format */ public static function encodedPrint($data, $setContentType=true) { - // Disable mimesniffing, don't move this to setContentTypeHeader! - header( 'X-Content-Type-Options: nosniff' ); if($setContentType) { self::setContentTypeHeader(); } diff --git a/lib/public/appframework/http/jsonresponse.php b/lib/public/appframework/http/jsonresponse.php index 6628c4514d..6d029b7464 100644 --- a/lib/public/appframework/http/jsonresponse.php +++ b/lib/public/appframework/http/jsonresponse.php @@ -49,7 +49,6 @@ class JSONResponse extends Response { public function __construct($data=array(), $statusCode=Http::STATUS_OK) { $this->data = $data; $this->setStatus($statusCode); - $this->addHeader('X-Content-Type-Options', 'nosniff'); $this->addHeader('Content-type', 'application/json; charset=utf-8'); } diff --git a/tests/lib/appframework/http/JSONResponseTest.php b/tests/lib/appframework/http/JSONResponseTest.php index b9b7c7d638..fbaae1b922 100644 --- a/tests/lib/appframework/http/JSONResponseTest.php +++ b/tests/lib/appframework/http/JSONResponseTest.php @@ -79,13 +79,6 @@ class JSONResponseTest extends \PHPUnit_Framework_TestCase { $this->assertEquals($expected, $this->json->render()); } - - public function testShouldHaveXContentHeaderByDefault() { - $headers = $this->json->getHeaders(); - $this->assertEquals('nosniff', $headers['X-Content-Type-Options']); - } - - public function testConstructorAllowsToSetData() { $data = array('hi'); $code = 300; From 387d46cb988ba2e617de9ede0ff6fb8430e59758 Mon Sep 17 00:00:00 2001 From: Lukas Reschke Date: Sun, 13 Apr 2014 12:54:26 +0200 Subject: [PATCH 057/187] Typo + Line breaks --- lib/base.php | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/lib/base.php b/lib/base.php index 34baba69d4..5fb7b0b467 100644 --- a/lib/base.php +++ b/lib/base.php @@ -214,11 +214,9 @@ class OC { } /* - * This function adds some security related headers to all requests - * served via base.php - * The implementation of this function as to happen here to ensure that - * all third-party components (e.g. SabreDAV) also benefit from this - * headers + * This function adds some security related headers to all requests served via base.php + * The implementation of this function as hto happen here to ensure that all third-party + * components (e.g. SabreDAV) also benefit from this headers. */ public static function addSecurityHeaders() { header('X-XSS-Protection: 1; mode=block'); // Enforce browser based XSS filters From fa8814902e9012880179e61ea846a7d85e3079f8 Mon Sep 17 00:00:00 2001 From: Robin McCorkell Date: Sun, 13 Apr 2014 13:47:08 +0100 Subject: [PATCH 058/187] Adjust logic for deciding if trashbin empty Any error in opening the trashbin directory returns 'true' for isEmpty. An error is prevented by checking if the directory exists before trying to open it. --- apps/files_trashbin/lib/trashbin.php | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/apps/files_trashbin/lib/trashbin.php b/apps/files_trashbin/lib/trashbin.php index 7b14a4ec08..bf8d465e5d 100644 --- a/apps/files_trashbin/lib/trashbin.php +++ b/apps/files_trashbin/lib/trashbin.php @@ -921,13 +921,11 @@ class Trashbin { public static function isEmpty($user) { $view = new \OC\Files\View('/' . $user . '/files_trashbin'); - $dh = $view->opendir('/files'); - if (!$dh) { - return false; - } - while ($file = readdir($dh)) { - if ($file !== '.' and $file !== '..') { - return false; + if ($view->is_dir('/files') && $dh = $view->opendir('/files')) { + while ($file = readdir($dh)) { + if ($file !== '.' and $file !== '..') { + return false; + } } } return true; From 869e7a51f0ed5b24c1ee0714112edb71e7dd1b62 Mon Sep 17 00:00:00 2001 From: Robin McCorkell Date: Sun, 13 Apr 2014 14:46:37 +0100 Subject: [PATCH 059/187] Prevent error in files_trashbin `glob` can return FALSE when the directory is empty, instead of an empty array, causing an error at `foreach`. "Note: On some systems it is impossible to distinguish between empty match and an error." See http://www.php.net/manual/en/function.glob.php -> Return Values --- apps/files_trashbin/lib/trashbin.php | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/apps/files_trashbin/lib/trashbin.php b/apps/files_trashbin/lib/trashbin.php index bf8d465e5d..9b931333b7 100644 --- a/apps/files_trashbin/lib/trashbin.php +++ b/apps/files_trashbin/lib/trashbin.php @@ -824,13 +824,15 @@ class Trashbin { $matches = glob($escapedVersionsName . '*'); } - foreach ($matches as $ma) { - if ($timestamp) { - $parts = explode('.v', substr($ma, 0, $offset)); - $versions[] = (end($parts)); - } else { - $parts = explode('.v', $ma); - $versions[] = (end($parts)); + if (is_array($matches)) { + foreach ($matches as $ma) { + if ($timestamp) { + $parts = explode('.v', substr($ma, 0, $offset)); + $versions[] = (end($parts)); + } else { + $parts = explode('.v', $ma); + $versions[] = (end($parts)); + } } } return $versions; From eeee9eacea333035e22ef3ed938e36f56bc762cd Mon Sep 17 00:00:00 2001 From: Robin McCorkell Date: Sun, 13 Apr 2014 16:17:13 +0100 Subject: [PATCH 060/187] Prevent error from cache update on deleted files --- lib/private/files/storage/local.php | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/lib/private/files/storage/local.php b/lib/private/files/storage/local.php index 571bf7f97c..ff2949d33b 100644 --- a/lib/private/files/storage/local.php +++ b/lib/private/files/storage/local.php @@ -305,7 +305,11 @@ if (\OC_Util::runningOnWindows()) { * @return bool */ public function hasUpdated($path, $time) { - return $this->filemtime($path) > $time; + if ($this->file_exists($path)) { + return $this->filemtime($path) > $time; + } else { + return true; + } } /** From 8bb003868c9f319e387bf464a035a3b2fd123531 Mon Sep 17 00:00:00 2001 From: Robin McCorkell Date: Sun, 13 Apr 2014 16:49:23 +0100 Subject: [PATCH 061/187] Fix user_ldap default setting value --- apps/user_ldap/templates/settings.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/user_ldap/templates/settings.php b/apps/user_ldap/templates/settings.php index 0a111225a7..03f2b8db09 100644 --- a/apps/user_ldap/templates/settings.php +++ b/apps/user_ldap/templates/settings.php @@ -37,7 +37,7 @@

-

+

t('Special Attributes'));?>

From c123dc7de4d9dde46bd31dbf6211704f1b4bb82d Mon Sep 17 00:00:00 2001 From: Lukas Reschke Date: Mon, 14 Apr 2014 10:15:31 +0200 Subject: [PATCH 062/187] Fix typo Thanks @DeepDiver1975 --- lib/base.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/base.php b/lib/base.php index 5fb7b0b467..7098f480e2 100644 --- a/lib/base.php +++ b/lib/base.php @@ -215,7 +215,7 @@ class OC { /* * This function adds some security related headers to all requests served via base.php - * The implementation of this function as hto happen here to ensure that all third-party + * The implementation of this function has to happen here to ensure that all third-party * components (e.g. SabreDAV) also benefit from this headers. */ public static function addSecurityHeaders() { From 51e47319ef2ab37d458dc96ba33a26bb851102be Mon Sep 17 00:00:00 2001 From: Thomas Tanghus Date: Mon, 14 Apr 2014 17:17:50 +0200 Subject: [PATCH 063/187] White-list known secure mime types. Refs. #8184 --- lib/private/connector/sabre/file.php | 6 +- lib/private/files/type/detection.php | 40 ++++- lib/private/helper.php | 10 ++ lib/private/mimetypes.list.php | 210 +++++++++++++-------------- tests/lib/helper.php | 12 ++ 5 files changed, 166 insertions(+), 112 deletions(-) diff --git a/lib/private/connector/sabre/file.php b/lib/private/connector/sabre/file.php index ef6caaf22a..750d646a8f 100644 --- a/lib/private/connector/sabre/file.php +++ b/lib/private/connector/sabre/file.php @@ -205,10 +205,12 @@ class OC_Connector_Sabre_File extends OC_Connector_Sabre_Node implements Sabre_D */ public function getContentType() { if (isset($this->fileinfo_cache['mimetype'])) { - return $this->fileinfo_cache['mimetype']; + $mimeType = $this->fileinfo_cache['mimetype']; + } else { + $mimeType = \OC\Files\Filesystem::getMimeType($this->path); } - return \OC\Files\Filesystem::getMimeType($this->path); + return \OC_Helper::getSecureMimeType($mimeType); } diff --git a/lib/private/files/type/detection.php b/lib/private/files/type/detection.php index 11e439032c..8ee5391781 100644 --- a/lib/private/files/type/detection.php +++ b/lib/private/files/type/detection.php @@ -17,24 +17,40 @@ namespace OC\Files\Type; */ class Detection { protected $mimetypes = array(); + protected $secureMimeTypes = array(); /** - * add an extension -> mimetype mapping + * Add an extension -> mimetype mapping + * + * $mimetype is the assumed correct mime type + * The optional $secureMimeType is an alternative to send to send + * to avoid potential XSS. * * @param string $extension * @param string $mimetype + * @param string|null $secureMimeType */ - public function registerType($extension, $mimetype) { - $this->mimetypes[$extension] = $mimetype; + public function registerType($extension, $mimetype, $secureMimeType = null) { + $this->mimetypes[$extension] = array($mimetype, $secureMimeType); + $this->secureMimeTypes[$mimetype] = $secureMimeType ?: $mimetype; } /** - * add an array of extension -> mimetype mappings + * Add an array of extension -> mimetype mappings + * + * The mimetype value is in itself an array where the first index is + * the assumed correct mimetype and the second is either a secure alternative + * or null if the correct is considered secure. * * @param array $types */ public function registerTypeArray($types) { $this->mimetypes = array_merge($this->mimetypes, $types); + + // Update the alternative mimetypes to avoid having to look them up each time. + foreach ($this->mimetypes as $mimeType) { + $this->secureMimeTypes[$mimeType[0]] = $mimeType[1] ?: $mimeType[0]; + } } /** @@ -48,7 +64,9 @@ class Detection { //try to guess the type by the file extension $extension = strtolower(strrchr(basename($path), ".")); $extension = substr($extension, 1); //remove leading . - return (isset($this->mimetypes[$extension])) ? $this->mimetypes[$extension] : 'application/octet-stream'; + return (isset($this->mimetypes[$extension]) && isset($this->mimetypes[$extension][0])) + ? $this->mimetypes[$extension][0] + : 'application/octet-stream'; } else { return 'application/octet-stream'; } @@ -123,4 +141,16 @@ class Detection { return $mime; } } + + /** + * Get a secure mimetype that won't expose potential XSS. + * + * @param string $mimeType + * @return string + */ + public function getSecureMimeType($mimeType) { + return isset($this->secureMimeTypes[$mimeType]) + ? $this->secureMimeTypes[$mimeType] + : 'application/octet-stream'; + } } diff --git a/lib/private/helper.php b/lib/private/helper.php index da3d3cd1c6..d5214823de 100644 --- a/lib/private/helper.php +++ b/lib/private/helper.php @@ -430,6 +430,16 @@ class OC_Helper { return self::getMimetypeDetector()->detect($path); } + /** + * Get a secure mimetype that won't expose potential XSS. + * + * @param string $mimeType + * @return string + */ + static function getSecureMimeType($mimeType) { + return self::getMimetypeDetector()->getSecureMimeType($mimeType); + } + /** * get the mimetype form a data string * diff --git a/lib/private/mimetypes.list.php b/lib/private/mimetypes.list.php index 91bcf58426..cdfd2ec0fd 100644 --- a/lib/private/mimetypes.list.php +++ b/lib/private/mimetypes.list.php @@ -24,109 +24,109 @@ * Array mapping file extensions to mimetypes (in alphabetical order). */ return array( - '7z' => 'application/x-7z-compressed', - 'accdb' => 'application/msaccess', - 'ai' => 'application/illustrator', - 'avi' => 'video/x-msvideo', - 'bash' => 'text/x-shellscript', - 'blend' => 'application/x-blender', - 'bin' => 'application/x-bin', - 'bmp' => 'image/bmp', - 'cb7' => 'application/x-cbr', - 'cba' => 'application/x-cbr', - 'cbr' => 'application/x-cbr', - 'cbt' => 'application/x-cbr', - 'cbtc' => 'application/x-cbr', - 'cbz' => 'application/x-cbr', - 'cc' => 'text/x-c', - 'cdr' => 'application/coreldraw', - 'cpp' => 'text/x-c++src', - 'css' => 'text/css', - 'csv' => 'text/csv', - 'cvbdl' => 'application/x-cbr', - 'c' => 'text/x-c', - 'c++' => 'text/x-c++src', - 'deb' => 'application/x-deb', - 'doc' => 'application/msword', - 'docx' => 'application/vnd.openxmlformats-officedocument.wordprocessingml.document', - 'dot' => 'application/msword', - 'dotx' => 'application/vnd.openxmlformats-officedocument.wordprocessingml.template', - 'dv' => 'video/dv', - 'eot' => 'application/vnd.ms-fontobject', - 'epub' => 'application/epub+zip', - 'exe' => 'application/x-ms-dos-executable', - 'flac' => 'audio/flac', - 'gif' => 'image/gif', - 'gz' => 'application/x-gzip', - 'gzip' => 'application/x-gzip', - 'html' => 'text/html', - 'htm' => 'text/html', - 'ical' => 'text/calendar', - 'ics' => 'text/calendar', - 'impress' => 'text/impress', - 'jpeg' => 'image/jpeg', - 'jpg' => 'image/jpeg', - 'js' => 'application/javascript', - 'json' => 'application/json', - 'keynote' => 'application/x-iwork-keynote-sffkey', - 'kra' => 'application/x-krita', - 'm2t' => 'video/mp2t', - 'm4v' => 'video/mp4', - 'markdown' => 'text/markdown', - 'mdown' => 'text/markdown', - 'md' => 'text/markdown', - 'mdb' => 'application/msaccess', - 'mdwn' => 'text/markdown', - 'mkv' => 'video/x-matroska', - 'mobi' => 'application/x-mobipocket-ebook', - 'mov' => 'video/quicktime', - 'mp3' => 'audio/mpeg', - 'mp4' => 'video/mp4', - 'mpeg' => 'video/mpeg', - 'mpg' => 'video/mpeg', - 'msi' => 'application/x-msi', - 'numbers' => 'application/x-iwork-numbers-sffnumbers', - 'odg' => 'application/vnd.oasis.opendocument.graphics', - 'odp' => 'application/vnd.oasis.opendocument.presentation', - 'ods' => 'application/vnd.oasis.opendocument.spreadsheet', - 'odt' => 'application/vnd.oasis.opendocument.text', - 'oga' => 'audio/ogg', - 'ogg' => 'audio/ogg', - 'ogv' => 'video/ogg', - 'otf' => 'font/opentype', - 'pages' => 'application/x-iwork-pages-sffpages', - 'pdf' => 'application/pdf', - 'php' => 'application/x-php', - 'pl' => 'application/x-perl', - 'png' => 'image/png', - 'ppt' => 'application/mspowerpoint', - 'pptx' => 'application/vnd.openxmlformats-officedocument.presentationml.presentation', - 'psd' => 'application/x-photoshop', - 'py' => 'text/x-python', - 'rar' => 'application/x-rar-compressed', - 'reveal' => 'text/reveal', - 'sgf' => 'application/sgf', - 'sh-lib' => 'text/x-shellscript', - 'sh' => 'text/x-shellscript', - 'svg' => 'image/svg+xml', - 'swf' => 'application/x-shockwave-flash', - 'tar' => 'application/x-tar', - 'tar.gz' => 'application/x-compressed', - 'tex' => 'application/x-tex', - 'tgz' => 'application/x-compressed', - 'tiff' => 'image/tiff', - 'tif' => 'image/tiff', - 'ttf' => 'application/x-font-ttf', - 'txt' => 'text/plain', - 'vcard' => 'text/vcard', - 'vcf' => 'text/vcard', - 'wav' => 'audio/wav', - 'webm' => 'video/webm', - 'woff' => 'application/font-woff', - 'wmv' => 'video/x-ms-asf', - 'xcf' => 'application/x-gimp', - 'xls' => 'application/msexcel', - 'xlsx' => 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet', - 'xml' => 'application/xml', - 'zip' => 'application/zip', + '7z' => array('application/x-7z-compressed', null), + 'accdb' => array('application/msaccess', null), + 'ai' => array('application/illustrator', null), + 'avi' => array('video/x-msvideo', null), + 'bash' => array('text/x-shellscript', null), + 'blend' => array('application/x-blender', null), + 'bin' => array('application/x-bin', null), + 'bmp' => array('image/bmp', null), + 'cb7' => array('application/x-cbr', null), + 'cba' => array('application/x-cbr', null), + 'cbr' => array('application/x-cbr', null), + 'cbt' => array('application/x-cbr', null), + 'cbtc' => array('application/x-cbr', null), + 'cbz' => array('application/x-cbr', null), + 'cc' => array('text/x-c', null), + 'cdr' => array('application/coreldraw', null), + 'cpp' => array('text/x-c++src', null), + 'css' => array('text/css', null), + 'csv' => array('text/csv', null), + 'cvbdl' => array('application/x-cbr', null), + 'c' => array('text/x-c', null), + 'c++' => array('text/x-c++src', null), + 'deb' => array('application/x-deb', null), + 'doc' => array('application/msword', null), + 'docx' => array('application/vnd.openxmlformats-officedocument.wordprocessingml.document', null), + 'dot' => array('application/msword', null), + 'dotx' => array('application/vnd.openxmlformats-officedocument.wordprocessingml.template', null), + 'dv' => array('video/dv', null), + 'eot' => array('application/vnd.ms-fontobject', null), + 'epub' => array('application/epub+zip', null), + 'exe' => array('application/x-ms-dos-executable', null), + 'flac' => array('audio/flac', null), + 'gif' => array('image/gif', null), + 'gz' => array('application/x-gzip', null), + 'gzip' => array('application/x-gzip', null), + 'html' => array('text/html', 'text/plain'), + 'htm' => array('text/html', 'text/plain'), + 'ical' => array('text/calendar', null), + 'ics' => array('text/calendar', null), + 'impress' => array('text/impress', null), + 'jpeg' => array('image/jpeg', null), + 'jpg' => array('image/jpeg', null), + 'js' => array('application/javascript', 'text/plain'), + 'json' => array('application/json', 'text/plain'), + 'keynote' => array('application/x-iwork-keynote-sffkey', null), + 'kra' => array('application/x-krita', null), + 'm2t' => array('video/mp2t', null), + 'm4v' => array('video/mp4', null), + 'markdown' => array('text/markdown', null), + 'mdown' => array('text/markdown', null), + 'md' => array('text/markdown', null), + 'mdb' => array('application/msaccess', null), + 'mdwn' => array('text/markdown', null), + 'mkv' => array('video/x-matroska', null), + 'mobi' => array('application/x-mobipocket-ebook', null), + 'mov' => array('video/quicktime', null), + 'mp3' => array('audio/mpeg', null), + 'mp4' => array('video/mp4', null), + 'mpeg' => array('video/mpeg', null), + 'mpg' => array('video/mpeg', null), + 'msi' => array('application/x-msi', null), + 'numbers' => array('application/x-iwork-numbers-sffnumbers', null), + 'odg' => array('application/vnd.oasis.opendocument.graphics', null), + 'odp' => array('application/vnd.oasis.opendocument.presentation', null), + 'ods' => array('application/vnd.oasis.opendocument.spreadsheet', null), + 'odt' => array('application/vnd.oasis.opendocument.text', null), + 'oga' => array('audio/ogg', null), + 'ogg' => array('audio/ogg', null), + 'ogv' => array('video/ogg', null), + 'otf' => array('font/opentype', null), + 'pages' => array('application/x-iwork-pages-sffpages', null), + 'pdf' => array('application/pdf', null), + 'php' => array('application/x-php', null), + 'pl' => array('application/x-perl', null), + 'png' => array('image/png', null), + 'ppt' => array('application/mspowerpoint', null), + 'pptx' => array('application/vnd.openxmlformats-officedocument.presentationml.presentation', null), + 'psd' => array('application/x-photoshop', null), + 'py' => array('text/x-python', null), + 'rar' => array('application/x-rar-compressed', null), + 'reveal' => array('text/reveal', null), + 'sgf' => array('application/sgf', null), + 'sh-lib' => array('text/x-shellscript', null), + 'sh' => array('text/x-shellscript', null), + 'svg' => array('image/svg+xml', 'text/plain'), + 'swf' => array('application/x-shockwave-flash', 'application/octet-stream'), + 'tar' => array('application/x-tar', null), + 'tar.gz' => array('application/x-compressed', null), + 'tex' => array('application/x-tex', null), + 'tgz' => array('application/x-compressed', null), + 'tiff' => array('image/tiff', null), + 'tif' => array('image/tiff', null), + 'ttf' => array('application/x-font-ttf', null), + 'txt' => array('text/plain', null), + 'vcard' => array('text/vcard', null), + 'vcf' => array('text/vcard', null), + 'wav' => array('audio/wav', null), + 'webm' => array('video/webm', null), + 'woff' => array('application/font-woff', null), + 'wmv' => array('video/x-ms-asf', null), + 'xcf' => array('application/x-gimp', null), + 'xls' => array('application/msexcel', null), + 'xlsx' => array('application/vnd.openxmlformats-officedocument.spreadsheetml.sheet', null), + 'xml' => array('application/xml', 'text/plain'), + 'zip' => array('application/zip', null), ); diff --git a/tests/lib/helper.php b/tests/lib/helper.php index 0943e6bc1b..5d319e40f0 100644 --- a/tests/lib/helper.php +++ b/tests/lib/helper.php @@ -71,6 +71,18 @@ class Test_Helper extends PHPUnit_Framework_TestCase { $this->assertEquals($result, $expected); } + function testGetSecureMimeType() { + $dir=OC::$SERVERROOT.'/tests/data'; + + $result = OC_Helper::getSecureMimeType('image/svg+xml'); + $expected = 'text/plain'; + $this->assertEquals($result, $expected); + + $result = OC_Helper::getSecureMimeType('image/png'); + $expected = 'image/png'; + $this->assertEquals($result, $expected); + } + function testGetFileNameMimeType() { $this->assertEquals('text/plain', OC_Helper::getFileNameMimeType('foo.txt')); $this->assertEquals('image/png', OC_Helper::getFileNameMimeType('foo.png')); From 2fb68c120b0b1f9c6d7ed9e3f10d595ffd080ddb Mon Sep 17 00:00:00 2001 From: Thomas Tanghus Date: Mon, 14 Apr 2014 18:21:19 +0200 Subject: [PATCH 064/187] Added explanation to mimetypes.list.php to avoid future confusion. --- lib/private/mimetypes.list.php | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/lib/private/mimetypes.list.php b/lib/private/mimetypes.list.php index cdfd2ec0fd..07e2391c11 100644 --- a/lib/private/mimetypes.list.php +++ b/lib/private/mimetypes.list.php @@ -22,6 +22,10 @@ /** * Array mapping file extensions to mimetypes (in alphabetical order). + * + * The first index in the mime type array is the assumed correct mimetype + * and the second is either a secure alternative or null if the correct + * is considered secure. */ return array( '7z' => array('application/x-7z-compressed', null), From 2bda3f9ae0893e94bd07a9c8916adcf06aa4a597 Mon Sep 17 00:00:00 2001 From: Lukas Reschke Date: Mon, 14 Apr 2014 20:16:52 +0200 Subject: [PATCH 065/187] Use direct link instead of JS MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Due to our CSP policy this link won’t work as it it considered as inline Javascript. This commit replaces the link with a static link to the files app. Reimplementation of #8067 - fixes #7742 --- lib/private/files.php | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/lib/private/files.php b/lib/private/files.php index bfe6d3c02d..e6135c4329 100644 --- a/lib/private/files.php +++ b/lib/private/files.php @@ -231,7 +231,7 @@ class OC_Files { OC_Template::printErrorPage( $l->t('ZIP download is turned off.'), $l->t('Files need to be downloaded one by one.') - . '
' . $l->t('Back to Files') . '' + . '
' . $l->t('Back to Files') . '' ); exit; } @@ -258,8 +258,7 @@ class OC_Files { OC_Template::printErrorPage( $l->t('Selected files too large to generate zip file.'), $l->t('Please download the files separately in smaller chunks or kindly ask your administrator.') - .'
' - . $l->t('Back to Files') . '' + . '
' . $l->t('Back to Files') . '' ); exit; } From 91d40f5033a4b8d30147a0f9673375f7aeea4271 Mon Sep 17 00:00:00 2001 From: Victor Dubiniuk Date: Mon, 14 Apr 2014 21:49:46 +0300 Subject: [PATCH 066/187] remove unneeded LOWER. Fixes #8202 --- lib/private/user/database.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/private/user/database.php b/lib/private/user/database.php index 15e6643dfb..994a47011e 100644 --- a/lib/private/user/database.php +++ b/lib/private/user/database.php @@ -121,7 +121,7 @@ class OC_User_Database extends OC_User_Backend { */ public function setDisplayName($uid, $displayName) { if ($this->userExists($uid)) { - $query = OC_DB::prepare('UPDATE `*PREFIX*users` SET `displayname` = ? WHERE LOWER(`uid`) = ?'); + $query = OC_DB::prepare('UPDATE `*PREFIX*users` SET `displayname` = ? WHERE LOWER(`uid`) = LOWER(?)'); $query->execute(array($displayName, $uid)); return true; } else { From 7f77b08098426801bfede696a88671efc1f82d83 Mon Sep 17 00:00:00 2001 From: Robin Appelman Date: Tue, 15 Apr 2014 15:48:02 +0200 Subject: [PATCH 067/187] Sort parameters and cast to int --- lib/private/route/cachingrouter.php | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/lib/private/route/cachingrouter.php b/lib/private/route/cachingrouter.php index 766c67c73a..6412ceb041 100644 --- a/lib/private/route/cachingrouter.php +++ b/lib/private/route/cachingrouter.php @@ -31,7 +31,8 @@ class CachingRouter extends Router { * @return string */ public function generate($name, $parameters = array(), $absolute = false) { - $key = $this->context->getHost() . '#' . $this->context->getBaseUrl() . $name . json_encode($parameters) . $absolute; + sort($parameters); + $key = $this->context->getHost() . '#' . $this->context->getBaseUrl() . $name . json_encode($parameters) . intval($absolute); if ($this->cache->hasKey($key)) { return $this->cache->get($key); } else { From 02726acbc8bfd436dff97eaff575eb2572ecd48b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thomas=20M=C3=BCller?= Date: Tue, 15 Apr 2014 16:26:12 +0200 Subject: [PATCH 068/187] adding checks and log messages regarding the assets folder --- lib/private/templatelayout.php | 31 ++++++++++++++++++++++++++++++- 1 file changed, 30 insertions(+), 1 deletion(-) diff --git a/lib/private/templatelayout.php b/lib/private/templatelayout.php index af17adb11c..0c0c8d9c8d 100644 --- a/lib/private/templatelayout.php +++ b/lib/private/templatelayout.php @@ -66,7 +66,7 @@ class OC_TemplateLayout extends OC_Template { } $versionParameter = '?v=' . md5(implode(OC_Util::getVersion())); - $useAssetPipeline = OC_Config::getValue('asset-pipeline.enabled', false); + $useAssetPipeline = $this->isAssetPipelineEnabled(); if ($useAssetPipeline) { $this->append( 'jsfiles', OC_Helper::linkToRoute('js_config') . $versionParameter); @@ -179,4 +179,33 @@ class OC_TemplateLayout extends OC_Template { sort($files); return hash('md5', implode('', $files)); } + + /** + * @return bool + */ + private function isAssetPipelineEnabled() { + // asset management enabled? + $useAssetPipeline = OC_Config::getValue('asset-pipeline.enabled', false); + if (!$useAssetPipeline) { + return false; + } + + // assets folder exists? + $assetDir = \OC::$SERVERROOT . '/assets'; + if (!is_dir($assetDir)) { + if (!mkdir($assetDir)) { + \OCP\Util::writeLog('assets', + "Folder <$assetDir> does not exist and/or could not be generated.", \OCP\Util::ERROR); + return false; + } + } + + // assets folder can be accessed? + if (!touch($assetDir."/.oc")) { + \OCP\Util::writeLog('assets', + "Folder <$assetDir> could not be accessed.", \OCP\Util::ERROR); + return false; + } + return $useAssetPipeline; + } } From 30168169b973eb1ca909a9a811b72d28588e1100 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thomas=20M=C3=BCller?= Date: Tue, 15 Apr 2014 16:56:45 +0200 Subject: [PATCH 069/187] Flush the Buffer Early - right after head --- core/templates/layout.base.php | 2 +- core/templates/layout.guest.php | 2 +- core/templates/layout.user.php | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/core/templates/layout.base.php b/core/templates/layout.base.php index b3e2f4da91..c519388fa3 100644 --- a/core/templates/layout.base.php +++ b/core/templates/layout.base.php @@ -30,7 +30,7 @@ ?> - + diff --git a/core/templates/layout.guest.php b/core/templates/layout.guest.php index 5788d1d5bd..1f89e3f040 100644 --- a/core/templates/layout.guest.php +++ b/core/templates/layout.guest.php @@ -32,7 +32,7 @@ ?> - +