summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--apps/files_external/js/settings.js4
-rw-r--r--apps/files_external/lib/storageconfig.php1
-rw-r--r--apps/files_external/migration/storagemigrator.php6
-rw-r--r--apps/files_external/tests/js/settingsSpec.js2
-rw-r--r--apps/user_ldap/lib/access.php4
-rw-r--r--console.php2
-rw-r--r--lib/private/files/storage/wrapper/encryption.php132
-rw-r--r--tests/lib/files/storage/wrapper/encryption.php158
8 files changed, 299 insertions, 10 deletions
diff --git a/apps/files_external/js/settings.js b/apps/files_external/js/settings.js
index 26df203091e..96b56d221c5 100644
--- a/apps/files_external/js/settings.js
+++ b/apps/files_external/js/settings.js
@@ -23,7 +23,7 @@ var MOUNT_OPTIONS_DROPDOWN_TEMPLATE =
' <label for="mountOptionsPreviews">{{t "files_external" "Enable previews"}}</label>' +
' </div>' +
' <div class="optionRow">' +
- ' <input id="mountOptionsSharing" name="enable_sharing" type="checkbox" value="true" checked="checked"/>' +
+ ' <input id="mountOptionsSharing" name="enable_sharing" type="checkbox" value="true"/>' +
' <label for="mountOptionsSharing">{{t "files_external" "Enable sharing"}}</label>' +
' </div>' +
' <div class="optionRow">' +
@@ -888,7 +888,7 @@ MountConfigListView.prototype = _.extend({
$tr.find('input.mountOptions').val(JSON.stringify({
'encrypt': true,
'previews': true,
- 'enable_sharing': true,
+ 'enable_sharing': false,
'filesystem_check_changes': 1
}));
}
diff --git a/apps/files_external/lib/storageconfig.php b/apps/files_external/lib/storageconfig.php
index 6f44b25a2e6..42c515f4671 100644
--- a/apps/files_external/lib/storageconfig.php
+++ b/apps/files_external/lib/storageconfig.php
@@ -126,6 +126,7 @@ class StorageConfig implements \JsonSerializable {
*/
public function __construct($id = null) {
$this->id = $id;
+ $this->mountOptions['enable_sharing'] = false;
}
/**
diff --git a/apps/files_external/migration/storagemigrator.php b/apps/files_external/migration/storagemigrator.php
index ba81810a4fd..832807feb93 100644
--- a/apps/files_external/migration/storagemigrator.php
+++ b/apps/files_external/migration/storagemigrator.php
@@ -100,6 +100,12 @@ class StorageMigrator {
$this->connection->beginTransaction();
try {
foreach ($existingStorage as $storage) {
+ $mountOptions = $storage->getMountOptions();
+ if (!empty($mountOptions) && !isset($mountOptions['enable_sharing'])) {
+ // existing mounts must have sharing enabled by default to avoid surprises
+ $mountOptions['enable_sharing'] = true;
+ $storage->setMountOptions($mountOptions);
+ }
$storageService->addStorage($storage);
}
$this->connection->commit();
diff --git a/apps/files_external/tests/js/settingsSpec.js b/apps/files_external/tests/js/settingsSpec.js
index 6f5bb2a8e3e..2a7afd6c2fa 100644
--- a/apps/files_external/tests/js/settingsSpec.js
+++ b/apps/files_external/tests/js/settingsSpec.js
@@ -363,7 +363,7 @@ describe('OCA.External.Settings tests', function() {
expect(JSON.parse($tr.find('input.mountOptions').val())).toEqual({
encrypt: true,
previews: true,
- enable_sharing: true,
+ enable_sharing: false,
filesystem_check_changes: 0
});
});
diff --git a/apps/user_ldap/lib/access.php b/apps/user_ldap/lib/access.php
index 16b942084c4..8b83b7d917f 100644
--- a/apps/user_ldap/lib/access.php
+++ b/apps/user_ldap/lib/access.php
@@ -546,8 +546,8 @@ class Access extends LDAPUtility implements user\IUserTools {
if(is_null($nameByLDAP)) {
continue;
}
- $sndName = isset($ldapObject[$sndAttribute])
- ? $ldapObject[$sndAttribute] : '';
+ $sndName = isset($ldapObject[$sndAttribute][0])
+ ? $ldapObject[$sndAttribute][0] : '';
$this->cacheUserDisplayName($ocName, $nameByLDAP, $sndName);
}
}
diff --git a/console.php b/console.php
index eb6c84c3cf8..3c25be34fe4 100644
--- a/console.php
+++ b/console.php
@@ -77,7 +77,7 @@ try {
exit(1);
}
- if (!function_exists('pcntl_signal')) {
+ if (!function_exists('pcntl_signal') && !in_array('--no-warnings', $argv)) {
echo "The process control (PCNTL) extensions are required in case you want to interrupt long running commands - see http://php.net/manual/en/book.pcntl.php" . PHP_EOL;
}
diff --git a/lib/private/files/storage/wrapper/encryption.php b/lib/private/files/storage/wrapper/encryption.php
index 11c6084d00c..1b0f39428a4 100644
--- a/lib/private/files/storage/wrapper/encryption.php
+++ b/lib/private/files/storage/wrapper/encryption.php
@@ -61,7 +61,7 @@ class Encryption extends Wrapper {
private $uid;
/** @var array */
- private $unencryptedSize;
+ protected $unencryptedSize;
/** @var \OCP\Encryption\IFile */
private $fileHelper;
@@ -78,6 +78,9 @@ class Encryption extends Wrapper {
/** @var Manager */
private $mountManager;
+ /** @var array remember for which path we execute the repair step to avoid recursions */
+ private $fixUnencryptedSizeOf = array();
+
/**
* @param array $parameters
* @param IManager $encryptionManager
@@ -147,8 +150,9 @@ class Encryption extends Wrapper {
}
if (isset($info['fileid']) && $info['encrypted']) {
- return $info['size'];
+ return $this->verifyUnencryptedSize($path, $info['size']);
}
+
return $this->storage->filesize($path);
}
@@ -169,8 +173,8 @@ class Encryption extends Wrapper {
} else {
$info = $this->getCache()->get($path);
if (isset($info['fileid']) && $info['encrypted']) {
+ $data['size'] = $this->verifyUnencryptedSize($path, $info['size']);
$data['encrypted'] = true;
- $data['size'] = $info['size'];
}
}
@@ -441,6 +445,128 @@ class Encryption extends Wrapper {
return $this->storage->fopen($path, $mode);
}
+
+ /**
+ * perform some plausibility checks if the the unencrypted size is correct.
+ * If not, we calculate the correct unencrypted size and return it
+ *
+ * @param string $path internal path relative to the storage root
+ * @param int $unencryptedSize size of the unencrypted file
+ *
+ * @return int unencrypted size
+ */
+ protected function verifyUnencryptedSize($path, $unencryptedSize) {
+
+ $size = $this->storage->filesize($path);
+ $result = $unencryptedSize;
+
+ if ($unencryptedSize < 0 ||
+ ($size > 0 && $unencryptedSize === $size)
+ ) {
+ // check if we already calculate the unencrypted size for the
+ // given path to avoid recursions
+ if (isset($this->fixUnencryptedSizeOf[$this->getFullPath($path)]) === false) {
+ $this->fixUnencryptedSizeOf[$this->getFullPath($path)] = true;
+ try {
+ $result = $this->fixUnencryptedSize($path, $size, $unencryptedSize);
+ } catch (\Exception $e) {
+ $this->logger->error('Couldn\'t re-calculate unencrypted size for '. $path);
+ $this->logger->logException($e);
+ }
+ unset($this->fixUnencryptedSizeOf[$this->getFullPath($path)]);
+ }
+ }
+
+ return $result;
+ }
+
+ /**
+ * calculate the unencrypted size
+ *
+ * @param string $path internal path relative to the storage root
+ * @param int $size size of the physical file
+ * @param int $unencryptedSize size of the unencrypted file
+ *
+ * @return int calculated unencrypted size
+ */
+ protected function fixUnencryptedSize($path, $size, $unencryptedSize) {
+
+ $headerSize = $this->getHeaderSize($path);
+ $header = $this->getHeader($path);
+ $encryptionModule = $this->getEncryptionModule($path);
+
+ $stream = $this->storage->fopen($path, 'r');
+
+ // if we couldn't open the file we return the old unencrypted size
+ if (!is_resource($stream)) {
+ $this->logger->error('Could not open ' . $path . '. Recalculation of unencrypted size aborted.');
+ return $unencryptedSize;
+ }
+
+ $newUnencryptedSize = 0;
+ $size -= $headerSize;
+ $blockSize = $this->util->getBlockSize();
+
+ // if a header exists we skip it
+ if ($headerSize > 0) {
+ fread($stream, $headerSize);
+ }
+
+ // fast path, else the calculation for $lastChunkNr is bogus
+ if ($size === 0) {
+ return 0;
+ }
+
+ $signed = (isset($header['signed']) && $header['signed'] === 'true') ? true : false;
+ $unencryptedBlockSize = $encryptionModule->getUnencryptedBlockSize($signed);
+
+ // calculate last chunk nr
+ // next highest is end of chunks, one subtracted is last one
+ // we have to read the last chunk, we can't just calculate it (because of padding etc)
+
+ $lastChunkNr = ceil($size/ $blockSize)-1;
+ // calculate last chunk position
+ $lastChunkPos = ($lastChunkNr * $blockSize);
+ // try to fseek to the last chunk, if it fails we have to read the whole file
+ if (@fseek($stream, $lastChunkPos, SEEK_CUR) === 0) {
+ $newUnencryptedSize += $lastChunkNr * $unencryptedBlockSize;
+ }
+
+ $lastChunkContentEncrypted='';
+ $count = $blockSize;
+
+ while ($count > 0) {
+ $data=fread($stream, $blockSize);
+ $count=strlen($data);
+ $lastChunkContentEncrypted .= $data;
+ if(strlen($lastChunkContentEncrypted) > $blockSize) {
+ $newUnencryptedSize += $unencryptedBlockSize;
+ $lastChunkContentEncrypted=substr($lastChunkContentEncrypted, $blockSize);
+ }
+ }
+
+ fclose($stream);
+
+ // we have to decrypt the last chunk to get it actual size
+ $encryptionModule->begin($this->getFullPath($path), $this->uid, 'r', $header, []);
+ $decryptedLastChunk = $encryptionModule->decrypt($lastChunkContentEncrypted, $lastChunkNr . 'end');
+ $decryptedLastChunk .= $encryptionModule->end($this->getFullPath($path), $lastChunkNr . 'end');
+
+ // calc the real file size with the size of the last chunk
+ $newUnencryptedSize += strlen($decryptedLastChunk);
+
+ $this->updateUnencryptedSize($this->getFullPath($path), $newUnencryptedSize);
+
+ // write to cache if applicable
+ $cache = $this->storage->getCache();
+ if ($cache) {
+ $entry = $cache->get($path);
+ $cache->update($entry['fileid'], ['size' => $newUnencryptedSize]);
+ }
+
+ return $newUnencryptedSize;
+ }
+
/**
* @param Storage $sourceStorage
* @param string $sourceInternalPath
diff --git a/tests/lib/files/storage/wrapper/encryption.php b/tests/lib/files/storage/wrapper/encryption.php
index 2b93aa86db0..c18e518fe6d 100644
--- a/tests/lib/files/storage/wrapper/encryption.php
+++ b/tests/lib/files/storage/wrapper/encryption.php
@@ -5,8 +5,9 @@ namespace Test\Files\Storage\Wrapper;
use OC\Encryption\Util;
use OC\Files\Storage\Temporary;
use OC\Files\View;
+use Test\Files\Storage\Storage;
-class Encryption extends \Test\Files\Storage\Storage {
+class Encryption extends Storage {
/**
* block size will always be 8192 for a PHP stream
@@ -211,6 +212,161 @@ class Encryption extends \Test\Files\Storage\Storage {
}
/**
+ * @dataProvider dataTestGetMetaData
+ *
+ * @param string $path
+ * @param array $metaData
+ * @param bool $encrypted
+ * @param bool $unencryptedSizeSet
+ * @param int $storedUnencryptedSize
+ * @param array $expected
+ */
+ public function testGetMetaData($path, $metaData, $encrypted, $unencryptedSizeSet, $storedUnencryptedSize, $expected) {
+
+ $sourceStorage = $this->getMockBuilder('\OC\Files\Storage\Storage')
+ ->disableOriginalConstructor()->getMock();
+
+ $cache = $this->getMockBuilder('\OC\Files\Cache\Cache')
+ ->disableOriginalConstructor()->getMock();
+ $cache->expects($this->any())
+ ->method('get')
+ ->willReturnCallback(
+ function($path) use ($encrypted) {
+ return ['encrypted' => $encrypted, 'path' => $path, 'size' => 0, 'fileid' => 1];
+ }
+ );
+
+ $this->instance = $this->getMockBuilder('\OC\Files\Storage\Wrapper\Encryption')
+ ->setConstructorArgs(
+ [
+ [
+ 'storage' => $sourceStorage,
+ 'root' => 'foo',
+ 'mountPoint' => '/',
+ 'mount' => $this->mount
+ ],
+ $this->encryptionManager, $this->util, $this->logger, $this->file, null, $this->keyStore, $this->update, $this->mountManager
+ ]
+ )
+ ->setMethods(['getCache', 'verifyUnencryptedSize'])
+ ->getMock();
+
+ if($unencryptedSizeSet) {
+ $this->invokePrivate($this->instance, 'unencryptedSize', [[$path => $storedUnencryptedSize]]);
+ }
+
+
+ $sourceStorage->expects($this->once())->method('getMetaData')->with($path)
+ ->willReturn($metaData);
+
+ $this->instance->expects($this->any())->method('getCache')->willReturn($cache);
+ $this->instance->expects($this->any())->method('verifyUnencryptedSize')
+ ->with($path, 0)->willReturn($expected['size']);
+
+ $result = $this->instance->getMetaData($path);
+ $this->assertSame($expected['encrypted'], $result['encrypted']);
+ $this->assertSame($expected['size'], $result['size']);
+ }
+
+ public function dataTestGetMetaData() {
+ return [
+ ['/test.txt', ['size' => 42, 'encrypted' => false], true, true, 12, ['size' => 12, 'encrypted' => true]],
+ ['/test.txt', null, true, true, 12, null],
+ ['/test.txt', ['size' => 42, 'encrypted' => false], false, false, 12, ['size' => 42, 'encrypted' => false]],
+ ['/test.txt', ['size' => 42, 'encrypted' => false], true, false, 12, ['size' => 12, 'encrypted' => true]]
+ ];
+ }
+
+ public function testFilesize() {
+ $cache = $this->getMockBuilder('\OC\Files\Cache\Cache')
+ ->disableOriginalConstructor()->getMock();
+ $cache->expects($this->any())
+ ->method('get')
+ ->willReturn(['encrypted' => true, 'path' => '/test.txt', 'size' => 0, 'fileid' => 1]);
+
+ $this->instance = $this->getMockBuilder('\OC\Files\Storage\Wrapper\Encryption')
+ ->setConstructorArgs(
+ [
+ [
+ 'storage' => $this->sourceStorage,
+ 'root' => 'foo',
+ 'mountPoint' => '/',
+ 'mount' => $this->mount
+ ],
+ $this->encryptionManager, $this->util, $this->logger, $this->file, null, $this->keyStore, $this->update, $this->mountManager
+ ]
+ )
+ ->setMethods(['getCache', 'verifyUnencryptedSize'])
+ ->getMock();
+
+ $this->instance->expects($this->any())->method('getCache')->willReturn($cache);
+ $this->instance->expects($this->any())->method('verifyUnencryptedSize')
+ ->willReturn(42);
+
+
+ $this->assertSame(42,
+ $this->instance->filesize('/test.txt')
+ );
+
+ }
+
+ /**
+ * @dataProvider dataTestVerifyUnencryptedSize
+ *
+ * @param int $encryptedSize
+ * @param int $unencryptedSize
+ * @param bool $failure
+ * @param int $expected
+ */
+ public function testVerifyUnencryptedSize($encryptedSize, $unencryptedSize, $failure, $expected) {
+ $sourceStorage = $this->getMockBuilder('\OC\Files\Storage\Storage')
+ ->disableOriginalConstructor()->getMock();
+
+ $this->instance = $this->getMockBuilder('\OC\Files\Storage\Wrapper\Encryption')
+ ->setConstructorArgs(
+ [
+ [
+ 'storage' => $sourceStorage,
+ 'root' => 'foo',
+ 'mountPoint' => '/',
+ 'mount' => $this->mount
+ ],
+ $this->encryptionManager, $this->util, $this->logger, $this->file, null, $this->keyStore, $this->update, $this->mountManager
+ ]
+ )
+ ->setMethods(['fixUnencryptedSize'])
+ ->getMock();
+
+ $sourceStorage->expects($this->once())->method('filesize')->willReturn($encryptedSize);
+
+ $this->instance->expects($this->any())->method('fixUnencryptedSize')
+ ->with('/test.txt', $encryptedSize, $unencryptedSize)
+ ->willReturnCallback(
+ function() use ($failure, $expected) {
+ if ($failure) {
+ throw new \Exception();
+ } else {
+ return $expected;
+ }
+ }
+ );
+
+ $this->assertSame(
+ $expected,
+ $this->invokePrivate($this->instance, 'verifyUnencryptedSize', ['/test.txt', $unencryptedSize])
+ );
+ }
+
+ public function dataTestVerifyUnencryptedSize() {
+ return [
+ [120, 80, false, 80],
+ [120, 120, false, 80],
+ [120, -1, false, 80],
+ [120, -1, true, -1]
+ ];
+ }
+
+ /**
* @dataProvider dataTestCopyAndRename
*
* @param string $source