diff options
29 files changed, 488 insertions, 157 deletions
diff --git a/3rdparty b/3rdparty -Subproject 77dc3920c3e9f4d36dc72e8a3aa6ac67ff5cffc +Subproject 5cd078af93210ecbc21714369f60df3bb625f52 diff --git a/apps/encryption/command/migratekeys.php b/apps/encryption/command/migratekeys.php index e6e5e7b70b0..d0fc1573061 100644 --- a/apps/encryption/command/migratekeys.php +++ b/apps/encryption/command/migratekeys.php @@ -115,5 +115,7 @@ class MigrateKeys extends Command { } } + $migration->finalCleanUp(); + } } diff --git a/apps/encryption/lib/keymanager.php b/apps/encryption/lib/keymanager.php index 05d23873482..8c8c1f8fd78 100644 --- a/apps/encryption/lib/keymanager.php +++ b/apps/encryption/lib/keymanager.php @@ -406,19 +406,36 @@ class KeyManager { } /** - * @param $userId + * check if user has a private and a public key + * + * @param string $userId * @return bool + * @throws PrivateKeyMissingException + * @throws PublicKeyMissingException */ public function userHasKeys($userId) { + $privateKey = $publicKey = true; + try { $this->getPrivateKey($userId); - $this->getPublicKey($userId); } catch (PrivateKeyMissingException $e) { - return false; + $privateKey = false; + $exception = $e; + } + try { + $this->getPublicKey($userId); } catch (PublicKeyMissingException $e) { + $publicKey = false; + $exception = $e; + } + + if ($privateKey && $publicKey) { + return true; + } elseif (!$privateKey && !$publicKey) { return false; + } else { + throw $exception; } - return true; } /** diff --git a/apps/encryption/lib/migration.php b/apps/encryption/lib/migration.php index b5d5dc26568..26e2a143f69 100644 --- a/apps/encryption/lib/migration.php +++ b/apps/encryption/lib/migration.php @@ -50,7 +50,7 @@ class Migration { $this->config = $config; } - public function __destruct() { + public function finalCleanUp() { $this->view->deleteAll('files_encryption/public_keys'); $this->updateFileCache(); $this->config->deleteAppValue('files_encryption', 'installed_version'); diff --git a/apps/encryption/tests/lib/KeyManagerTest.php b/apps/encryption/tests/lib/KeyManagerTest.php index 2561b29462f..0bac5e0341b 100644 --- a/apps/encryption/tests/lib/KeyManagerTest.php +++ b/apps/encryption/tests/lib/KeyManagerTest.php @@ -182,18 +182,62 @@ class KeyManagerTest extends TestCase { ); } - public function testUserHasKeys() { + /** + * @dataProvider dataTestUserHasKeys + */ + public function testUserHasKeys($key, $expected) { $this->keyStorageMock->expects($this->exactly(2)) ->method('getUserKey') ->with($this->equalTo($this->userId), $this->anything()) - ->willReturn('key'); + ->willReturn($key); - $this->assertTrue( + $this->assertSame($expected, $this->instance->userHasKeys($this->userId) ); } + public function dataTestUserHasKeys() { + return [ + ['key', true], + ['', false] + ]; + } + + /** + * @expectedException \OCA\Encryption\Exceptions\PrivateKeyMissingException + */ + public function testUserHasKeysMissingPrivateKey() { + $this->keyStorageMock->expects($this->exactly(2)) + ->method('getUserKey') + ->willReturnCallback(function ($uid, $keyID, $encryptionModuleId) { + if ($keyID=== 'privateKey') { + return ''; + } + return 'key'; + }); + + $this->instance->userHasKeys($this->userId); + } + + /** + * @expectedException \OCA\Encryption\Exceptions\PublicKeyMissingException + */ + public function testUserHasKeysMissingPublicKey() { + $this->keyStorageMock->expects($this->exactly(2)) + ->method('getUserKey') + ->willReturnCallback(function ($uid, $keyID, $encryptionModuleId){ + if ($keyID === 'publicKey') { + return ''; + } + return 'key'; + }); + + $this->instance->userHasKeys($this->userId); + + } + + public function testInit() { $this->keyStorageMock->expects($this->any()) ->method('getUserKey') diff --git a/apps/files/js/filelist.js b/apps/files/js/filelist.js index a3fd605ff7e..183b5e909a6 100644 --- a/apps/files/js/filelist.js +++ b/apps/files/js/filelist.js @@ -685,6 +685,10 @@ if (type === 'dir') { mime = mime || 'httpd/unix-directory'; + + if (fileData.mountType && fileData.mountType.indexOf('external') === 0) { + icon = OC.MimeType.getIconUrl('dir-external'); + } } //containing tr diff --git a/apps/files_sharing/l10n/fr.js b/apps/files_sharing/l10n/fr.js index ca10ca211d1..0a8a19ed142 100644 --- a/apps/files_sharing/l10n/fr.js +++ b/apps/files_sharing/l10n/fr.js @@ -29,7 +29,7 @@ OC.L10N.register( "A file or folder has been <strong>shared</strong>" : "Un fichier ou un répertoire a été <strong>partagé</strong>", "A file or folder was shared from <strong>another server</strong>" : "Un fichier ou un répertoire a été partagé depuis <strong>un autre serveur</strong>", "A public shared file or folder was <strong>downloaded</strong>" : "Un fichier ou un répertoire partagé publiquement a été <strong>téléchargé</strong>", - "You received a new remote share %2$s from %1$s" : "L'utilisateur %1$s a partagé la ressource %2$s avec vous.", + "You received a new remote share %2$s from %1$s" : "L'utilisateur %1$s a partagé la ressource distante %2$s avec vous", "You received a new remote share from %s" : "Vous avez reçu un partage distant de %s", "%1$s accepted remote share %2$s" : "%1$s a accepté le partage distant %2$s", "%1$s declined remote share %2$s" : "%1$s a refusé le partage distant %2$s", diff --git a/apps/files_sharing/l10n/fr.json b/apps/files_sharing/l10n/fr.json index ed8b166729a..15ddb2d4187 100644 --- a/apps/files_sharing/l10n/fr.json +++ b/apps/files_sharing/l10n/fr.json @@ -27,7 +27,7 @@ "A file or folder has been <strong>shared</strong>" : "Un fichier ou un répertoire a été <strong>partagé</strong>", "A file or folder was shared from <strong>another server</strong>" : "Un fichier ou un répertoire a été partagé depuis <strong>un autre serveur</strong>", "A public shared file or folder was <strong>downloaded</strong>" : "Un fichier ou un répertoire partagé publiquement a été <strong>téléchargé</strong>", - "You received a new remote share %2$s from %1$s" : "L'utilisateur %1$s a partagé la ressource %2$s avec vous.", + "You received a new remote share %2$s from %1$s" : "L'utilisateur %1$s a partagé la ressource distante %2$s avec vous", "You received a new remote share from %s" : "Vous avez reçu un partage distant de %s", "%1$s accepted remote share %2$s" : "%1$s a accepté le partage distant %2$s", "%1$s declined remote share %2$s" : "%1$s a refusé le partage distant %2$s", diff --git a/apps/files_trashbin/lib/storage.php b/apps/files_trashbin/lib/storage.php index 006971fb242..4185fc6aec4 100644 --- a/apps/files_trashbin/lib/storage.php +++ b/apps/files_trashbin/lib/storage.php @@ -26,6 +26,7 @@ namespace OCA\Files_Trashbin; use OC\Files\Filesystem; use OC\Files\Storage\Wrapper\Wrapper; +use OCP\IUserManager; class Storage extends Wrapper { @@ -41,8 +42,12 @@ class Storage extends Wrapper { */ private static $disableTrash = false; - function __construct($parameters) { + /** @var IUserManager */ + private $userManager; + + function __construct($parameters, IUserManager $userManager = null) { $this->mountPoint = $parameters['mountPoint']; + $this->userManager = $userManager; parent::__construct($parameters); } @@ -101,6 +106,27 @@ class Storage extends Wrapper { } /** + * check if it is a file located in data/user/files only files in the + * 'files' directory should be moved to the trash + * + * @param $path + * @return bool + */ + protected function shouldMoveToTrash($path){ + $normalized = Filesystem::normalizePath($this->mountPoint . '/' . $path); + $parts = explode('/', $normalized); + if (count($parts) < 4) { + return false; + } + + if ($this->userManager->userExists($parts[1]) && $parts[2] == 'files') { + return true; + } + + return false; + } + + /** * Run the delete operation with the given method * * @param string $path path of file or folder to delete @@ -112,6 +138,7 @@ class Storage extends Wrapper { if (self::$disableTrash || !\OC_App::isEnabled('files_trashbin') || (pathinfo($path, PATHINFO_EXTENSION) === 'part') + || $this->shouldMoveToTrash($path) === false ) { return call_user_func_array([$this->storage, $method], [$path]); } @@ -144,7 +171,10 @@ class Storage extends Wrapper { */ public static function setupStorage() { \OC\Files\Filesystem::addStorageWrapper('oc_trashbin', function ($mountPoint, $storage) { - return new \OCA\Files_Trashbin\Storage(array('storage' => $storage, 'mountPoint' => $mountPoint)); + return new \OCA\Files_Trashbin\Storage( + array('storage' => $storage, 'mountPoint' => $mountPoint), + \OC::$server->getUserManager() + ); }, 1); } diff --git a/apps/files_trashbin/tests/storage.php b/apps/files_trashbin/tests/storage.php index 637543683dc..64540505d22 100644 --- a/apps/files_trashbin/tests/storage.php +++ b/apps/files_trashbin/tests/storage.php @@ -493,4 +493,34 @@ class Storage extends \Test\TestCase { $results = $this->rootView->getDirectoryContent($this->user . '/files_trashbin/files/'); $this->assertEquals(0, count($results)); } + + /** + * @dataProvider dataTestShouldMoveToTrash + */ + public function testShouldMoveToTrash($mountPoint, $path, $userExists, $expected) { + $tmpStorage = $this->getMockBuilder('\OC\Files\Storage\Temporary') + ->disableOriginalConstructor()->getMock(); + $userManager = $this->getMockBuilder('OCP\IUserManager') + ->disableOriginalConstructor()->getMock(); + $userManager->expects($this->any()) + ->method('userExists')->willReturn($userExists); + $storage = new \OCA\Files_Trashbin\Storage( + ['mountPoint' => $mountPoint, 'storage' => $tmpStorage], + $userManager + ); + + $this->assertSame($expected, + $this->invokePrivate($storage, 'shouldMoveToTrash', [$path]) + ); + + } + + public function dataTestShouldMoveToTrash() { + return [ + ['/schiesbn/', '/files/test.txt', true, true], + ['/schiesbn/', '/files/test.txt', false, false], + ['/schiesbn/', '/test.txt', true, false], + ['/schiesbn/', '/test.txt', false, false], + ]; + } } diff --git a/apps/user_ldap/l10n/fr.js b/apps/user_ldap/l10n/fr.js index 6613c7c5bd8..2c4f4bfb5fa 100644 --- a/apps/user_ldap/l10n/fr.js +++ b/apps/user_ldap/l10n/fr.js @@ -116,7 +116,7 @@ OC.L10N.register( "Disable Main Server" : "Désactiver le serveur principal", "Only connect to the replica server." : "Se connecter uniquement à la réplique", "Case insensitive LDAP server (Windows)" : "Serveur LDAP non sensible à la casse (Windows)", - "Turn off SSL certificate validation." : "Désactiver la validation des certificats SSL.", + "Turn off SSL certificate validation." : "Désactiver la validation des certificats SSL", "Not recommended, use it for testing only! If connection only works with this option, import the LDAP server's SSL certificate in your %s server." : "Non recommandé, à utiliser à des fins de tests uniquement. Si la connexion ne fonctionne qu'avec cette option, importez le certificat SSL du serveur LDAP dans le serveur %s.", "Cache Time-To-Live" : "Durée de vie du cache (TTL)", "in seconds. A change empties the cache." : "en secondes. Tout changement vide le cache.", diff --git a/apps/user_ldap/l10n/fr.json b/apps/user_ldap/l10n/fr.json index 7eb868483eb..43519556e12 100644 --- a/apps/user_ldap/l10n/fr.json +++ b/apps/user_ldap/l10n/fr.json @@ -114,7 +114,7 @@ "Disable Main Server" : "Désactiver le serveur principal", "Only connect to the replica server." : "Se connecter uniquement à la réplique", "Case insensitive LDAP server (Windows)" : "Serveur LDAP non sensible à la casse (Windows)", - "Turn off SSL certificate validation." : "Désactiver la validation des certificats SSL.", + "Turn off SSL certificate validation." : "Désactiver la validation des certificats SSL", "Not recommended, use it for testing only! If connection only works with this option, import the LDAP server's SSL certificate in your %s server." : "Non recommandé, à utiliser à des fins de tests uniquement. Si la connexion ne fonctionne qu'avec cette option, importez le certificat SSL du serveur LDAP dans le serveur %s.", "Cache Time-To-Live" : "Durée de vie du cache (TTL)", "in seconds. A change empties the cache." : "en secondes. Tout changement vide le cache.", diff --git a/autotest.sh b/autotest.sh index 960f03ec1f6..e9327c8f83e 100755 --- a/autotest.sh +++ b/autotest.sh @@ -167,8 +167,8 @@ function execute_tests { echo "Waiting for Oracle initialization ... " - # grep exits on the first match and then the script continues - docker logs -f "$DOCKER_CONTAINER_ID" 2>&1 | grep -q "Grant succeeded." + # grep exits on the first match and then the script continues - times out after 2 minutes + timeout 120 docker logs -f "$DOCKER_CONTAINER_ID" 2>&1 | grep -q "Grant succeeded." DATABASEUSER=autotest DATABASENAME='XE' diff --git a/core/js/apps.js b/core/js/apps.js index ecefa48caa1..71170bbc23a 100644 --- a/core/js/apps.js +++ b/core/js/apps.js @@ -58,7 +58,7 @@ if (!area.is(':animated')) { // button toggles the area - if (button === event.target.closest('[data-apps-slide-toggle]')) { + if ($(button).is($(event.target).closest('[data-apps-slide-toggle]'))) { if (area.is(':visible')) { hideArea(); } else { diff --git a/lib/l10n/ro.js b/lib/l10n/ro.js index fdaad0ad650..24a42ef68c0 100644 --- a/lib/l10n/ro.js +++ b/lib/l10n/ro.js @@ -25,6 +25,7 @@ OC.L10N.register( "seconds ago" : "secunde în urmă", "web services under your control" : "servicii web controlate de tine", "Empty filename is not allowed" : "Nu este permis fișier fără nume", + "File name contains at least one invalid character" : "Numele fișierului conține măcar un caracter invalid", "File name is too long" : "Numele fișierului este prea lung", "Application is not enabled" : "Aplicația nu este activată", "Authentication error" : "Eroare la autentificare", diff --git a/lib/l10n/ro.json b/lib/l10n/ro.json index 7ae016ead7b..23c946b93e3 100644 --- a/lib/l10n/ro.json +++ b/lib/l10n/ro.json @@ -23,6 +23,7 @@ "seconds ago" : "secunde în urmă", "web services under your control" : "servicii web controlate de tine", "Empty filename is not allowed" : "Nu este permis fișier fără nume", + "File name contains at least one invalid character" : "Numele fișierului conține măcar un caracter invalid", "File name is too long" : "Numele fișierului este prea lung", "Application is not enabled" : "Aplicația nu este activată", "Authentication error" : "Eroare la autentificare", diff --git a/lib/private/appframework/http/request.php b/lib/private/appframework/http/request.php index f826ef45bb5..baf2f0c4745 100644 --- a/lib/private/appframework/http/request.php +++ b/lib/private/appframework/http/request.php @@ -478,7 +478,8 @@ class Request implements \ArrayAccess, \Countable, IRequest { */ private function isOverwriteCondition($type = '') { $regex = '/' . $this->config->getSystemValue('overwritecondaddr', '') . '/'; - return $regex === '//' || preg_match($regex, $this->server['REMOTE_ADDR']) === 1 + $remoteAddr = isset($this->server['REMOTE_ADDR']) ? $this->server['REMOTE_ADDR'] : ''; + return $regex === '//' || preg_match($regex, $remoteAddr) === 1 || $type !== 'protocol'; } diff --git a/lib/private/encryption/util.php b/lib/private/encryption/util.php index 8bff65428d3..d0733941a35 100644 --- a/lib/private/encryption/util.php +++ b/lib/private/encryption/util.php @@ -128,35 +128,6 @@ class Util { } /** - * read header into array - * - * @param string $header - * @return array - */ - public function readHeader($header) { - - $result = array(); - - if (substr($header, 0, strlen(self::HEADER_START)) === self::HEADER_START) { - $endAt = strpos($header, self::HEADER_END); - if ($endAt !== false) { - $header = substr($header, 0, $endAt + strlen(self::HEADER_END)); - - // +1 to not start with an ':' which would result in empty element at the beginning - $exploded = explode(':', substr($header, strlen(self::HEADER_START)+1)); - - $element = array_shift($exploded); - while ($element !== self::HEADER_END) { - $result[$element] = array_shift($exploded); - $element = array_shift($exploded); - } - } - } - - return $result; - } - - /** * create header for encrypted file * * @param array $headerData diff --git a/lib/private/files/storage/dav.php b/lib/private/files/storage/dav.php index e02f971b38b..24cf3c29209 100644 --- a/lib/private/files/storage/dav.php +++ b/lib/private/files/storage/dav.php @@ -219,9 +219,9 @@ class DAV extends Common { $this->statCache->set($path, false); return false; } - $this->convertException($e); + $this->convertException($e, $path); } catch (\Exception $e) { - $this->convertException($e); + $this->convertException($e, $path); } return false; } @@ -286,9 +286,9 @@ class DAV extends Common { if ($e->getHttpStatus() === 404) { return false; } - $this->convertException($e); + $this->convertException($e, $path); } catch (\Exception $e) { - $this->convertException($e); + $this->convertException($e, $path); } return false; } @@ -311,9 +311,9 @@ class DAV extends Common { if ($e->getHttpStatus() === 404) { return false; } - $this->convertException($e); + $this->convertException($e, $path); } catch (\Exception $e) { - $this->convertException($e); + $this->convertException($e, $path); } return false; } @@ -363,6 +363,9 @@ class DAV extends Common { $statusCode = curl_getinfo($curl, CURLINFO_HTTP_CODE); if ($statusCode !== 200) { Util::writeLog("webdav client", 'curl GET ' . curl_getinfo($curl, CURLINFO_EFFECTIVE_URL) . ' returned status code ' . $statusCode, Util::ERROR); + if ($statusCode === 423) { + throw new \OCP\Lock\LockedException($path); + } } curl_close($curl); rewind($fp); @@ -446,10 +449,10 @@ class DAV extends Common { if ($e->getHttpStatus() === 501) { return false; } - $this->convertException($e); + $this->convertException($e, $path); return false; } catch (\Exception $e) { - $this->convertException($e); + $this->convertException($e, $path); return false; } } else { @@ -502,6 +505,9 @@ class DAV extends Common { $statusCode = curl_getinfo($curl, CURLINFO_HTTP_CODE); if ($statusCode !== 200) { Util::writeLog("webdav client", 'curl GET ' . curl_getinfo($curl, CURLINFO_EFFECTIVE_URL) . ' returned status code ' . $statusCode, Util::ERROR); + if ($statusCode === 423) { + throw new \OCP\Lock\LockedException($path); + } } curl_close($curl); fclose($source); @@ -564,9 +570,9 @@ class DAV extends Common { if ($e->getHttpStatus() === 404) { return array(); } - $this->convertException($e); + $this->convertException($e, $path); } catch (\Exception $e) { - $this->convertException($e); + $this->convertException($e, $path); } return array(); } @@ -591,9 +597,9 @@ class DAV extends Common { if ($e->getHttpStatus() === 404) { return false; } - $this->convertException($e); + $this->convertException($e, $path); } catch (\Exception $e) { - $this->convertException($e); + $this->convertException($e, $path); } return false; } @@ -643,9 +649,9 @@ class DAV extends Common { return false; } - $this->convertException($e); + $this->convertException($e, $path); } catch (\Exception $e) { - $this->convertException($e); + $this->convertException($e, $path); } return false; } @@ -767,10 +773,10 @@ class DAV extends Common { } return false; } - $this->convertException($e); + $this->convertException($e, $path); return false; } catch (\Exception $e) { - $this->convertException($e); + $this->convertException($e, $path); return false; } } @@ -782,15 +788,19 @@ class DAV extends Common { * or do nothing. * * @param Exception $e sabre exception + * @param string $path optional path from the operation * * @throws StorageInvalidException if the storage is invalid, for example * when the authentication expired or is invalid * @throws StorageNotAvailableException if the storage is not available, * which might be temporary */ - private function convertException(Exception $e) { + private function convertException(Exception $e, $path = '') { Util::writeLog('files_external', $e->getMessage(), Util::ERROR); if ($e instanceof ClientHttpException) { + if ($e->getHttpStatus() === 423) { + throw new \OCP\Lock\LockedException($path); + } if ($e->getHttpStatus() === 401) { // either password was changed or was invalid all along throw new StorageInvalidException(get_class($e).': '.$e->getMessage()); diff --git a/lib/private/files/storage/wrapper/encryption.php b/lib/private/files/storage/wrapper/encryption.php index 8818b822fa7..61290791faa 100644 --- a/lib/private/files/storage/wrapper/encryption.php +++ b/lib/private/files/storage/wrapper/encryption.php @@ -31,6 +31,7 @@ use OC\Encryption\Util; use OC\Files\Filesystem; use OC\Files\Mount\Manager; use OC\Files\Storage\LocalTempFileTrait; +use OCP\Encryption\Exceptions\GenericEncryptionException; use OCP\Encryption\IFile; use OCP\Encryption\IManager; use OCP\Encryption\Keys\IStorage; @@ -174,9 +175,8 @@ class Encryption extends Wrapper { public function file_get_contents($path) { $encryptionModule = $this->getEncryptionModule($path); - $info = $this->getCache()->get($path); - if ($encryptionModule || $info['encrypted'] === true) { + if ($encryptionModule) { $handle = $this->fopen($path, "r"); if (!$handle) { return false; @@ -338,14 +338,15 @@ class Encryption extends Wrapper { * @param string $path * @param string $mode * @return resource + * @throws GenericEncryptionException + * @throws ModuleDoesNotExistsException */ public function fopen($path, $mode) { $encryptionEnabled = $this->encryptionManager->isEnabled(); $shouldEncrypt = false; $encryptionModule = null; - $rawHeader = $this->getHeader($path); - $header = $this->util->readHeader($rawHeader); + $header = $this->getHeader($path); $fullPath = $this->getFullPath($path); $encryptionModuleId = $this->util->getEncryptionModuleId($header); @@ -380,6 +381,10 @@ class Encryption extends Wrapper { || $mode === 'wb' || $mode === 'wb+' ) { + // don't overwrite encrypted files if encyption is not enabled + if ($targetIsEncrypted && $encryptionEnabled === false) { + throw new GenericEncryptionException('Tried to access encrypted file but encryption is not enabled'); + } if ($encryptionEnabled) { // if $encryptionModuleId is empty, the default module will be used $encryptionModule = $this->encryptionManager->getEncryptionModule($encryptionModuleId); @@ -398,6 +403,7 @@ class Encryption extends Wrapper { // OC_DEFAULT_MODULE to read the file $encryptionModule = $this->encryptionManager->getEncryptionModule('OC_DEFAULT_MODULE'); $shouldEncrypt = true; + $targetIsEncrypted = true; } } } catch (ModuleDoesNotExistsException $e) { @@ -416,7 +422,7 @@ class Encryption extends Wrapper { $source = $this->storage->fopen($path, $mode); $handle = \OC\Files\Stream\Encryption::wrap($source, $path, $fullPath, $header, $this->uid, $encryptionModule, $this->storage, $this, $this->util, $this->fileHelper, $mode, - $size, $unencryptedSize, strlen($rawHeader)); + $size, $unencryptedSize, $this->getHeaderSize($path)); return $handle; } @@ -606,27 +612,101 @@ class Encryption extends Wrapper { } /** + * read first block of encrypted file, typically this will contain the + * encryption header + * + * @param string $path + * @return string + */ + protected function readFirstBlock($path) { + $firstBlock = ''; + if ($this->storage->file_exists($path)) { + $handle = $this->storage->fopen($path, 'r'); + $firstBlock = fread($handle, $this->util->getHeaderSize()); + fclose($handle); + } + return $firstBlock; + } + + /** + * return header size of given file + * + * @param string $path + * @return int + */ + protected function getHeaderSize($path) { + $headerSize = 0; + $realFile = $this->util->stripPartialFileExtension($path); + if ($this->storage->file_exists($realFile)) { + $path = $realFile; + } + $firstBlock = $this->readFirstBlock($path); + + if (substr($firstBlock, 0, strlen(Util::HEADER_START)) === Util::HEADER_START) { + $headerSize = strlen($firstBlock); + } + + return $headerSize; + } + + /** + * parse raw header to array + * + * @param string $rawHeader + * @return array + */ + protected function parseRawHeader($rawHeader) { + $result = array(); + if (substr($rawHeader, 0, strlen(Util::HEADER_START)) === Util::HEADER_START) { + $header = $rawHeader; + $endAt = strpos($header, Util::HEADER_END); + if ($endAt !== false) { + $header = substr($header, 0, $endAt + strlen(Util::HEADER_END)); + + // +1 to not start with an ':' which would result in empty element at the beginning + $exploded = explode(':', substr($header, strlen(Util::HEADER_START)+1)); + + $element = array_shift($exploded); + while ($element !== Util::HEADER_END) { + $result[$element] = array_shift($exploded); + $element = array_shift($exploded); + } + } + } + + return $result; + } + + /** * read header from file * * @param string $path * @return array */ protected function getHeader($path) { - $header = ''; $realFile = $this->util->stripPartialFileExtension($path); if ($this->storage->file_exists($realFile)) { $path = $realFile; } - if ($this->storage->file_exists($path)) { - $handle = $this->storage->fopen($path, 'r'); - $firstBlock = fread($handle, $this->util->getHeaderSize()); - fclose($handle); - if (substr($firstBlock, 0, strlen(Util::HEADER_START)) === Util::HEADER_START) { - $header = $firstBlock; + $firstBlock = $this->readFirstBlock($path); + $result = $this->parseRawHeader($firstBlock); + + // if the header doesn't contain a encryption module we check if it is a + // legacy file. If true, we add the default encryption module + if (!isset($result[Util::HEADER_ENCRYPTION_MODULE_KEY])) { + if (!empty($result)) { + $result[Util::HEADER_ENCRYPTION_MODULE_KEY] = 'OC_DEFAULT_MODULE'; + } else { + // if the header was empty we have to check first if it is a encrypted file at all + $info = $this->getCache()->get($path); + if (isset($info['encrypted']) && $info['encrypted'] === true) { + $result[Util::HEADER_ENCRYPTION_MODULE_KEY] = 'OC_DEFAULT_MODULE'; + } } } - return $header; + + return $result; } /** @@ -639,8 +719,7 @@ class Encryption extends Wrapper { */ protected function getEncryptionModule($path) { $encryptionModule = null; - $rawHeader = $this->getHeader($path); - $header = $this->util->readHeader($rawHeader); + $header = $this->getHeader($path); $encryptionModuleId = $this->util->getEncryptionModuleId($header); if (!empty($encryptionModuleId)) { try { @@ -675,4 +754,5 @@ class Encryption extends Wrapper { return false; } + } diff --git a/lib/private/memcache/factory.php b/lib/private/memcache/factory.php index 5bb7e42c808..fe82558e731 100644 --- a/lib/private/memcache/factory.php +++ b/lib/private/memcache/factory.php @@ -29,6 +29,7 @@ namespace OC\Memcache; use \OCP\ICacheFactory; +use \OCP\ILogger; class Factory implements ICacheFactory { const NULL_CACHE = '\\OC\\Memcache\\NullCache'; @@ -39,6 +40,11 @@ class Factory implements ICacheFactory { private $globalPrefix; /** + * @var ILogger $logger + */ + private $logger; + + /** * @var string $localCacheClass */ private $localCacheClass; @@ -55,13 +61,15 @@ class Factory implements ICacheFactory { /** * @param string $globalPrefix + * @param ILogger $logger * @param string|null $localCacheClass * @param string|null $distributedCacheClass * @param string|null $lockingCacheClass */ - public function __construct($globalPrefix, + public function __construct($globalPrefix, ILogger $logger, $localCacheClass = null, $distributedCacheClass = null, $lockingCacheClass = null) { + $this->logger = $logger; $this->globalPrefix = $globalPrefix; if (!$localCacheClass) { @@ -71,22 +79,43 @@ class Factory implements ICacheFactory { $distributedCacheClass = $localCacheClass; } + $missingCacheMessage = 'Memcache {class} not available for {use} cache'; + $missingCacheHint = 'Is the matching PHP module installed and enabled?'; if (!$localCacheClass::isAvailable()) { - throw new \OC\HintException( - 'Missing memcache class ' . $localCacheClass . ' for local cache', - 'Is the matching PHP module installed and enabled ?' - ); + if (\OC::$CLI && !defined('PHPUNIT_RUN')) { + // CLI should not hard-fail on broken memcache + $this->logger->info($missingCacheMessage, [ + 'class' => $localCacheClass, + 'use' => 'local', + 'app' => 'cli' + ]); + $localCacheClass = self::NULL_CACHE; + } else { + throw new \OC\HintException(strtr($missingCacheMessage, [ + '{class}' => $localCacheClass, '{use}' => 'local' + ]), $missingCacheHint); + } } if (!$distributedCacheClass::isAvailable()) { - throw new \OC\HintException( - 'Missing memcache class ' . $distributedCacheClass . ' for distributed cache', - 'Is the matching PHP module installed and enabled ?' - ); + if (\OC::$CLI && !defined('PHPUNIT_RUN')) { + // CLI should not hard-fail on broken memcache + $this->logger->info($missingCacheMessage, [ + 'class' => $distributedCacheClass, + 'use' => 'distributed', + 'app' => 'cli' + ]); + $distributedCacheClass = self::NULL_CACHE; + } else { + throw new \OC\HintException(strtr($missingCacheMessage, [ + '{class}' => $distributedCacheClass, '{use}' => 'distributed' + ]), $missingCacheHint); + } } if (!($lockingCacheClass && $lockingCacheClass::isAvailable())) { // dont fallback since the fallback might not be suitable for storing lock - $lockingCacheClass = '\OC\Memcache\NullCache'; + $lockingCacheClass = self::NULL_CACHE; } + $this->localCacheClass = $localCacheClass; $this->distributedCacheClass = $distributedCacheClass; $this->lockingCacheClass = $lockingCacheClass; diff --git a/lib/private/server.php b/lib/private/server.php index 84141fe28c1..53949b53df7 100644 --- a/lib/private/server.php +++ b/lib/private/server.php @@ -50,7 +50,6 @@ use OC\Http\Client\ClientService; use OC\Lock\MemcacheLockingProvider; use OC\Lock\NoopLockingProvider; use OC\Mail\Mailer; -use OC\Memcache\ArrayCache; use OC\Memcache\NullCache; use OC\Security\CertificateManager; use OC\Security\Crypto; @@ -234,17 +233,17 @@ class Server extends SimpleContainer implements IServerContainer { $instanceId = \OC_Util::getInstanceId(); $path = \OC::$SERVERROOT; $prefix = md5($instanceId.'-'.$version.'-'.$path); - return new \OC\Memcache\Factory($prefix, + return new \OC\Memcache\Factory($prefix, $c->getLogger(), $config->getSystemValue('memcache.local', null), $config->getSystemValue('memcache.distributed', null), $config->getSystemValue('memcache.locking', null) ); } - return new \OC\Memcache\Factory('', - new ArrayCache(), - new ArrayCache(), - new ArrayCache() + return new \OC\Memcache\Factory('', $c->getLogger(), + '\\OC\\Memcache\\ArrayCache', + '\\OC\\Memcache\\ArrayCache', + '\\OC\\Memcache\\ArrayCache' ); }); $this->registerService('ActivityManager', function (Server $c) { diff --git a/settings/controller/encryptioncontroller.php b/settings/controller/encryptioncontroller.php index 411b9e87cc1..87cbf0a4bf1 100644 --- a/settings/controller/encryptioncontroller.php +++ b/settings/controller/encryptioncontroller.php @@ -102,6 +102,8 @@ class EncryptionController extends Controller { } while (count($users) >= $limit); } + $migration->finalCleanUp(); + } catch (\Exception $e) { return array( 'data' => array( diff --git a/settings/l10n/fr.js b/settings/l10n/fr.js index 3e0abb254ef..67cb61f93a0 100644 --- a/settings/l10n/fr.js +++ b/settings/l10n/fr.js @@ -8,7 +8,7 @@ OC.L10N.register( "Server-side encryption" : "Chiffrement côté serveur", "External Storage" : "Stockage externe", "Cron" : "Cron", - "Email server" : "Serveur mail", + "Email server" : "Serveur e-mail", "Log" : "Log", "Server Status" : "Statut du serveur", "Tips & tricks" : "Trucs et astuces", @@ -38,19 +38,19 @@ OC.L10N.register( "Unable to delete group." : "Impossible de supprimer le groupe.", "log-level out of allowed range" : "niveau de journalisation hors borne", "Saved" : "Sauvegardé", - "test email settings" : "tester les paramètres d'e-mail", + "test email settings" : "tester les paramètres e-mail", "A problem occurred while sending the email. Please revise your settings. (Error: %s)" : "Une erreur est survenue lors de l'envoi de l'e-mail. Veuillez vérifier vos paramètres. (Erreur: %s)", - "Email sent" : "Email envoyé", - "You need to set your user email before being able to send test emails." : "Vous devez définir une adresse email dans vos paramètres personnels avant de pouvoir envoyer des courriels de test.", - "Invalid mail address" : "Adresse email non valide", + "Email sent" : "E-mail envoyé", + "You need to set your user email before being able to send test emails." : "Vous devez définir une adresse e-mail dans vos paramètres personnels avant de pouvoir envoyer des e-mails de test.", + "Invalid mail address" : "Adresse e-mail non valide", "A user with that name already exists." : "Un utilisateur à ce nom existe déjà.", "Unable to create user." : "Impossible de créer l'utilisateur.", "Your %s account was created" : "Votre compte %s a été créé", "Unable to delete user." : "Impossible de supprimer l'utilisateur.", "Forbidden" : "interdit", "Invalid user" : "Utilisateur non valable", - "Unable to change mail address" : "Impossible de modifier l'adresse de courriel", - "Email saved" : "Email sauvegardé", + "Unable to change mail address" : "Impossible de modifier l'adresse e-mail", + "Email saved" : "E-mail sauvegardé", "Are you really sure you want add \"{domain}\" as trusted domain?" : "Êtes-vous vraiment sûr de vouloir ajouter \"{domain}\" comme domaine de confiance ?", "Add trusted domain" : "Ajouter un domaine de confiance", "Migration in progress. Please wait until the migration is finished" : "Migration en cours. Veuillez attendre que celle-ci se termine", @@ -98,7 +98,7 @@ OC.L10N.register( "A valid username must be provided" : "Un nom d'utilisateur valide doit être saisi", "Error creating user" : "Erreur lors de la création de l'utilisateur", "A valid password must be provided" : "Un mot de passe valide doit être saisi", - "A valid email must be provided" : "Vous devez fournir une adresse de courriel valide", + "A valid email must be provided" : "Vous devez fournir une adresse e-mail valide", "__language_name__" : "Français", "Sync clients" : "Clients de synchronisation", "Personal info" : "Informations personnelles", @@ -133,14 +133,14 @@ OC.L10N.register( "Allow users to share via link" : "Autoriser les utilisateurs à partager par lien", "Enforce password protection" : "Imposer la protection par mot de passe", "Allow public uploads" : "Autoriser les téléversements publics", - "Allow users to send mail notification for shared files" : "Autoriser les utilisateurs à envoyer des notifications par courriel concernant les partages", + "Allow users to send mail notification for shared files" : "Autoriser les utilisateurs à envoyer des notifications de partage par e-mail", "Set default expiration date" : "Spécifier une date d'expiration par défaut", "Expire after " : "Expiration après ", "days" : "jours", "Enforce expiration date" : "Imposer la date d'expiration", "Allow resharing" : "Autoriser le repartage", "Restrict users to only share with users in their groups" : "N'autoriser les partages qu'entre membres de mêmes groupes", - "Allow users to send mail notification for shared files to other users" : "Autoriser les utilisateurs à envoyer une notification par courriel concernant les fichiers partagés", + "Allow users to send mail notification for shared files to other users" : "Autoriser les utilisateurs à envoyer des notifications de partage par e-mail", "Exclude groups from sharing" : "Empêcher certains groupes de partager", "These groups will still be able to receive shares, but not to initiate them." : "Ces groupes ne pourront plus initier de partage, mais ils pourront toujours rejoindre les partages faits par d'autres. ", "Last cron job execution: %s." : "Dernière tâche cron exécutée : %s.", @@ -161,7 +161,7 @@ OC.L10N.register( "Send mode" : "Mode d'envoi", "Encryption" : "Chiffrement", "From address" : "Adresse source", - "mail" : "mail", + "mail" : "e-mail", "Authentication method" : "Méthode d'authentification", "Authentication required" : "Authentification requise", "Server address" : "Adresse du serveur", @@ -171,7 +171,7 @@ OC.L10N.register( "SMTP Password" : "Mot de passe SMTP", "Store credentials" : "Enregistrer les identifiants", "Test email settings" : "Tester les paramètres e-mail", - "Send email" : "Envoyer un mail", + "Send email" : "Envoyer un e-mail", "Log level" : "Niveau de log", "Download logfile" : "Télécharger le fichier de journalisation", "More" : "Plus", @@ -227,9 +227,9 @@ OC.L10N.register( "Change password" : "Changer de mot de passe", "Full name" : "Nom complet", "No display name set" : "Aucun nom d'affichage configuré", - "Email" : "Adresse mail", - "Your email address" : "Votre adresse mail", - "Fill in an email address to enable password recovery and receive notifications" : "Saisissez votre adresse mail pour permettre la réinitialisation du mot de passe et la réception des notifications", + "Email" : "Adresse e-mail", + "Your email address" : "Votre adresse e-mail", + "Fill in an email address to enable password recovery and receive notifications" : "Saisissez votre adresse e-mail pour permettre la réinitialisation du mot de passe et la réception des notifications", "No email address set" : "Aucune adresse e-mail configurée", "You are member of the following groups:" : "Vous êtes membre des groupes suivants :", "Profile picture" : "Photo de profil", @@ -251,10 +251,10 @@ OC.L10N.register( "Show storage location" : "Afficher l'emplacement du stockage", "Show last log in" : "Montrer la dernière connexion", "Show user backend" : "Montrer la source de l'identifiant", - "Send email to new user" : "Envoyer un courriel aux utilisateurs créés", - "Show email address" : "Afficher l'adresse email", + "Send email to new user" : "Envoyer un e-mail aux utilisateurs créés", + "Show email address" : "Afficher l'adresse e-mail", "Username" : "Nom d'utilisateur", - "E-Mail" : "Courriel", + "E-Mail" : "E-Mail", "Create" : "Créer", "Admin Recovery Password" : "Mot de passe Administrateur de récupération", "Enter the recovery password in order to recover the users files during password change" : "Entrez le mot de passe de récupération pour récupérer les fichiers utilisateurs pendant le changement de mot de passe", @@ -275,7 +275,7 @@ OC.L10N.register( "Last Login" : "Dernière Connexion", "change full name" : "Modifier le nom complet", "set new password" : "Changer le mot de passe", - "change email address" : "changer l'adresse email", + "change email address" : "changer l'adresse e-mail", "Default" : "Défaut" }, "nplurals=2; plural=(n > 1);"); diff --git a/settings/l10n/fr.json b/settings/l10n/fr.json index c8916074c8e..7ba48772c55 100644 --- a/settings/l10n/fr.json +++ b/settings/l10n/fr.json @@ -6,7 +6,7 @@ "Server-side encryption" : "Chiffrement côté serveur", "External Storage" : "Stockage externe", "Cron" : "Cron", - "Email server" : "Serveur mail", + "Email server" : "Serveur e-mail", "Log" : "Log", "Server Status" : "Statut du serveur", "Tips & tricks" : "Trucs et astuces", @@ -36,19 +36,19 @@ "Unable to delete group." : "Impossible de supprimer le groupe.", "log-level out of allowed range" : "niveau de journalisation hors borne", "Saved" : "Sauvegardé", - "test email settings" : "tester les paramètres d'e-mail", + "test email settings" : "tester les paramètres e-mail", "A problem occurred while sending the email. Please revise your settings. (Error: %s)" : "Une erreur est survenue lors de l'envoi de l'e-mail. Veuillez vérifier vos paramètres. (Erreur: %s)", - "Email sent" : "Email envoyé", - "You need to set your user email before being able to send test emails." : "Vous devez définir une adresse email dans vos paramètres personnels avant de pouvoir envoyer des courriels de test.", - "Invalid mail address" : "Adresse email non valide", + "Email sent" : "E-mail envoyé", + "You need to set your user email before being able to send test emails." : "Vous devez définir une adresse e-mail dans vos paramètres personnels avant de pouvoir envoyer des e-mails de test.", + "Invalid mail address" : "Adresse e-mail non valide", "A user with that name already exists." : "Un utilisateur à ce nom existe déjà.", "Unable to create user." : "Impossible de créer l'utilisateur.", "Your %s account was created" : "Votre compte %s a été créé", "Unable to delete user." : "Impossible de supprimer l'utilisateur.", "Forbidden" : "interdit", "Invalid user" : "Utilisateur non valable", - "Unable to change mail address" : "Impossible de modifier l'adresse de courriel", - "Email saved" : "Email sauvegardé", + "Unable to change mail address" : "Impossible de modifier l'adresse e-mail", + "Email saved" : "E-mail sauvegardé", "Are you really sure you want add \"{domain}\" as trusted domain?" : "Êtes-vous vraiment sûr de vouloir ajouter \"{domain}\" comme domaine de confiance ?", "Add trusted domain" : "Ajouter un domaine de confiance", "Migration in progress. Please wait until the migration is finished" : "Migration en cours. Veuillez attendre que celle-ci se termine", @@ -96,7 +96,7 @@ "A valid username must be provided" : "Un nom d'utilisateur valide doit être saisi", "Error creating user" : "Erreur lors de la création de l'utilisateur", "A valid password must be provided" : "Un mot de passe valide doit être saisi", - "A valid email must be provided" : "Vous devez fournir une adresse de courriel valide", + "A valid email must be provided" : "Vous devez fournir une adresse e-mail valide", "__language_name__" : "Français", "Sync clients" : "Clients de synchronisation", "Personal info" : "Informations personnelles", @@ -131,14 +131,14 @@ "Allow users to share via link" : "Autoriser les utilisateurs à partager par lien", "Enforce password protection" : "Imposer la protection par mot de passe", "Allow public uploads" : "Autoriser les téléversements publics", - "Allow users to send mail notification for shared files" : "Autoriser les utilisateurs à envoyer des notifications par courriel concernant les partages", + "Allow users to send mail notification for shared files" : "Autoriser les utilisateurs à envoyer des notifications de partage par e-mail", "Set default expiration date" : "Spécifier une date d'expiration par défaut", "Expire after " : "Expiration après ", "days" : "jours", "Enforce expiration date" : "Imposer la date d'expiration", "Allow resharing" : "Autoriser le repartage", "Restrict users to only share with users in their groups" : "N'autoriser les partages qu'entre membres de mêmes groupes", - "Allow users to send mail notification for shared files to other users" : "Autoriser les utilisateurs à envoyer une notification par courriel concernant les fichiers partagés", + "Allow users to send mail notification for shared files to other users" : "Autoriser les utilisateurs à envoyer des notifications de partage par e-mail", "Exclude groups from sharing" : "Empêcher certains groupes de partager", "These groups will still be able to receive shares, but not to initiate them." : "Ces groupes ne pourront plus initier de partage, mais ils pourront toujours rejoindre les partages faits par d'autres. ", "Last cron job execution: %s." : "Dernière tâche cron exécutée : %s.", @@ -159,7 +159,7 @@ "Send mode" : "Mode d'envoi", "Encryption" : "Chiffrement", "From address" : "Adresse source", - "mail" : "mail", + "mail" : "e-mail", "Authentication method" : "Méthode d'authentification", "Authentication required" : "Authentification requise", "Server address" : "Adresse du serveur", @@ -169,7 +169,7 @@ "SMTP Password" : "Mot de passe SMTP", "Store credentials" : "Enregistrer les identifiants", "Test email settings" : "Tester les paramètres e-mail", - "Send email" : "Envoyer un mail", + "Send email" : "Envoyer un e-mail", "Log level" : "Niveau de log", "Download logfile" : "Télécharger le fichier de journalisation", "More" : "Plus", @@ -225,9 +225,9 @@ "Change password" : "Changer de mot de passe", "Full name" : "Nom complet", "No display name set" : "Aucun nom d'affichage configuré", - "Email" : "Adresse mail", - "Your email address" : "Votre adresse mail", - "Fill in an email address to enable password recovery and receive notifications" : "Saisissez votre adresse mail pour permettre la réinitialisation du mot de passe et la réception des notifications", + "Email" : "Adresse e-mail", + "Your email address" : "Votre adresse e-mail", + "Fill in an email address to enable password recovery and receive notifications" : "Saisissez votre adresse e-mail pour permettre la réinitialisation du mot de passe et la réception des notifications", "No email address set" : "Aucune adresse e-mail configurée", "You are member of the following groups:" : "Vous êtes membre des groupes suivants :", "Profile picture" : "Photo de profil", @@ -249,10 +249,10 @@ "Show storage location" : "Afficher l'emplacement du stockage", "Show last log in" : "Montrer la dernière connexion", "Show user backend" : "Montrer la source de l'identifiant", - "Send email to new user" : "Envoyer un courriel aux utilisateurs créés", - "Show email address" : "Afficher l'adresse email", + "Send email to new user" : "Envoyer un e-mail aux utilisateurs créés", + "Show email address" : "Afficher l'adresse e-mail", "Username" : "Nom d'utilisateur", - "E-Mail" : "Courriel", + "E-Mail" : "E-Mail", "Create" : "Créer", "Admin Recovery Password" : "Mot de passe Administrateur de récupération", "Enter the recovery password in order to recover the users files during password change" : "Entrez le mot de passe de récupération pour récupérer les fichiers utilisateurs pendant le changement de mot de passe", @@ -273,7 +273,7 @@ "Last Login" : "Dernière Connexion", "change full name" : "Modifier le nom complet", "set new password" : "Changer le mot de passe", - "change email address" : "changer l'adresse email", + "change email address" : "changer l'adresse e-mail", "Default" : "Défaut" },"pluralForm" :"nplurals=2; plural=(n > 1);" }
\ No newline at end of file diff --git a/tests/lib/appframework/http/RequestTest.php b/tests/lib/appframework/http/RequestTest.php index de3430d757c..6e86f3d7041 100644 --- a/tests/lib/appframework/http/RequestTest.php +++ b/tests/lib/appframework/http/RequestTest.php @@ -1112,17 +1112,27 @@ class RequestTest extends \Test\TestCase { $this->assertSame('/test.php', $request->getRequestUri()); } - public function testGetRequestUriWithOverwrite() { + public function providesGetRequestUriWithOverwriteData() { + return [ + ['/scriptname.php/some/PathInfo', '/owncloud/', ''], + ['/scriptname.php/some/PathInfo', '/owncloud/', '123'], + ]; + } + + /** + * @dataProvider providesGetRequestUriWithOverwriteData + */ + public function testGetRequestUriWithOverwrite($expectedUri, $overwriteWebRoot, $overwriteCondAddr) { $this->config ->expects($this->at(0)) ->method('getSystemValue') ->with('overwritewebroot') - ->will($this->returnValue('/owncloud/')); + ->will($this->returnValue($overwriteWebRoot)); $this->config ->expects($this->at(1)) ->method('getSystemValue') ->with('overwritecondaddr') - ->will($this->returnValue('')); + ->will($this->returnValue($overwriteCondAddr)); $request = $this->getMockBuilder('\OC\AppFramework\Http\Request') ->setMethods(['getScriptName']) @@ -1143,6 +1153,7 @@ class RequestTest extends \Test\TestCase { ->method('getScriptName') ->will($this->returnValue('/scriptname.php')); - $this->assertSame('/scriptname.php/some/PathInfo', $request->getRequestUri()); + $this->assertSame($expectedUri, $request->getRequestUri()); } + } diff --git a/tests/lib/encryption/utiltest.php b/tests/lib/encryption/utiltest.php index d5f5ce4c2e9..5aadb4e857f 100644 --- a/tests/lib/encryption/utiltest.php +++ b/tests/lib/encryption/utiltest.php @@ -75,19 +75,6 @@ class UtilTest extends TestCase { /** * @dataProvider providesHeaders */ - public function testReadHeader($header, $expected, $moduleId) { - $expected['oc_encryption_module'] = $moduleId; - $result = $this->util->readHeader($header); - $this->assertSameSize($expected, $result); - foreach ($expected as $key => $value) { - $this->assertArrayHasKey($key, $result); - $this->assertSame($value, $result[$key]); - } - } - - /** - * @dataProvider providesHeaders - */ public function testCreateHeader($expected, $header, $moduleId) { $em = $this->getMock('\OCP\Encryption\IEncryptionModule'); diff --git a/tests/lib/files/storage/wrapper/encryption.php b/tests/lib/files/storage/wrapper/encryption.php index a10e95a3f8b..677bbffc3d2 100644 --- a/tests/lib/files/storage/wrapper/encryption.php +++ b/tests/lib/files/storage/wrapper/encryption.php @@ -2,12 +2,20 @@ namespace Test\Files\Storage\Wrapper; +use OC\Encryption\Util; use OC\Files\Storage\Temporary; use OC\Files\View; class Encryption extends \Test\Files\Storage\Storage { /** + * block size will always be 8192 for a PHP stream + * @see https://bugs.php.net/bug.php?id=21641 + * @var integer + */ + protected $headerSize = 8192; + + /** * @var Temporary */ private $sourceStorage; @@ -407,18 +415,26 @@ class Encryption extends \Test\Files\Storage\Storage { $this->encryptionManager, $util, $this->logger, $this->file, null, $this->keyStore, $this->update, $this->mountManager ] ) + ->setMethods(['readFirstBlock', 'parseRawHeader']) ->getMock(); + $instance->expects($this->once())->method(('parseRawHeader')) + ->willReturn([Util::HEADER_ENCRYPTION_MODULE_KEY => 'OC_DEFAULT_MODULE']); + + if ($strippedPathExists) { + $instance->expects($this->once())->method('readFirstBlock') + ->with($strippedPath)->willReturn(''); + } else { + $instance->expects($this->once())->method('readFirstBlock') + ->with($path)->willReturn(''); + } + $util->expects($this->once())->method('stripPartialFileExtension') ->with($path)->willReturn($strippedPath); - $sourceStorage->expects($this->at(0)) + $sourceStorage->expects($this->once()) ->method('file_exists') ->with($strippedPath) ->willReturn($strippedPathExists); - $sourceStorage->expects($this->at(1)) - ->method('file_exists') - ->with($strippedPathExists ? $strippedPath : $path) - ->willReturn(false); $this->invokePrivate($instance, 'getHeader', [$path]); } @@ -432,4 +448,98 @@ class Encryption extends \Test\Files\Storage\Storage { array('/foo/bar.txt.ocTransferId7437493.part', true, '/foo/bar.txt'), ); } + + /** + * test if getHeader adds the default module correctly to the header for + * legacy files + * + * @dataProvider dataTestGetHeaderAddLegacyModule + */ + public function testGetHeaderAddLegacyModule($header, $isEncrypted, $expected) { + + $sourceStorage = $this->getMockBuilder('\OC\Files\Storage\Storage') + ->disableOriginalConstructor()->getMock(); + + $util = $this->getMockBuilder('\OC\Encryption\Util') + ->setConstructorArgs([new View(), new \OC\User\Manager(), $this->groupManager, $this->config]) + ->getMock(); + + $cache = $this->getMockBuilder('\OC\Files\Cache\Cache') + ->disableOriginalConstructor()->getMock(); + $cache->expects($this->any()) + ->method('get') + ->willReturnCallback(function($path) use ($isEncrypted) {return ['encrypted' => $isEncrypted, 'path' => $path];}); + + $instance = $this->getMockBuilder('\OC\Files\Storage\Wrapper\Encryption') + ->setConstructorArgs( + [ + [ + 'storage' => $sourceStorage, + 'root' => 'foo', + 'mountPoint' => '/', + 'mount' => $this->mount + ], + $this->encryptionManager, $util, $this->logger, $this->file, null, $this->keyStore, $this->update, $this->mountManager + ] + ) + ->setMethods(['readFirstBlock', 'parseRawHeader', 'getCache']) + ->getMock(); + + $instance->expects($this->once())->method(('parseRawHeader'))->willReturn($header); + $instance->expects($this->any())->method('getCache')->willReturn($cache); + + $result = $this->invokePrivate($instance, 'getHeader', ['test.txt']); + $this->assertSameSize($expected, $result); + foreach ($result as $key => $value) { + $this->assertArrayHasKey($key, $expected); + $this->assertSame($expected[$key], $value); + } + } + + public function dataTestGetHeaderAddLegacyModule() { + return [ + [['cipher' => 'AES-128'], true, ['cipher' => 'AES-128', Util::HEADER_ENCRYPTION_MODULE_KEY => 'OC_DEFAULT_MODULE']], + [[], true, [Util::HEADER_ENCRYPTION_MODULE_KEY => 'OC_DEFAULT_MODULE']], + [[], false, []], + ]; + } + + /** + * @dataProvider dataTestParseRawHeader + */ + public function testParseRawHeader($rawHeader, $expected) { + $instance = new \OC\Files\Storage\Wrapper\Encryption( + [ + 'storage' => $this->sourceStorage, + 'root' => 'foo', + 'mountPoint' => '/', + 'mount' => $this->mount + ], + $this->encryptionManager, $this->util, $this->logger, $this->file, null, $this->keyStore, $this->update, $this->mountManager + + ); + + $result = $this->invokePrivate($instance, 'parseRawHeader', [$rawHeader]); + $this->assertSameSize($expected, $result); + foreach ($result as $key => $value) { + $this->assertArrayHasKey($key, $expected); + $this->assertSame($expected[$key], $value); + } + } + + public function dataTestParseRawHeader() { + return [ + [str_pad('HBEGIN:oc_encryption_module:0:HEND', $this->headerSize, '-', STR_PAD_RIGHT) + , [Util::HEADER_ENCRYPTION_MODULE_KEY => '0']], + [str_pad('HBEGIN:oc_encryption_module:0:custom_header:foo:HEND', $this->headerSize, '-', STR_PAD_RIGHT) + , ['custom_header' => 'foo', Util::HEADER_ENCRYPTION_MODULE_KEY => '0']], + [str_pad('HelloWorld', $this->headerSize, '-', STR_PAD_RIGHT), []], + ['', []], + [str_pad('HBEGIN:oc_encryption_module:0', $this->headerSize, '-', STR_PAD_RIGHT) + , []], + [str_pad('oc_encryption_module:0:HEND', $this->headerSize, '-', STR_PAD_RIGHT) + , []], + ]; + } + } diff --git a/tests/lib/memcache/factory.php b/tests/lib/memcache/factory.php index c25e5937c16..33a27a42113 100644 --- a/tests/lib/memcache/factory.php +++ b/tests/lib/memcache/factory.php @@ -114,7 +114,8 @@ class Test_Factory extends \Test\TestCase { */ public function testCacheAvailability($localCache, $distributedCache, $lockingCache, $expectedLocalCache, $expectedDistributedCache, $expectedLockingCache) { - $factory = new \OC\Memcache\Factory('abc', $localCache, $distributedCache, $lockingCache); + $logger = $this->getMockBuilder('\OCP\ILogger')->getMock(); + $factory = new \OC\Memcache\Factory('abc', $logger, $localCache, $distributedCache, $lockingCache); $this->assertTrue(is_a($factory->createLocal(), $expectedLocalCache)); $this->assertTrue(is_a($factory->createDistributed(), $expectedDistributedCache)); $this->assertTrue(is_a($factory->createLocking(), $expectedLockingCache)); @@ -125,6 +126,7 @@ class Test_Factory extends \Test\TestCase { * @expectedException \OC\HintException */ public function testCacheNotAvailableException($localCache, $distributedCache) { - new \OC\Memcache\Factory('abc', $localCache, $distributedCache); + $logger = $this->getMockBuilder('\OCP\ILogger')->getMock(); + new \OC\Memcache\Factory('abc', $logger, $localCache, $distributedCache); } } |