aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMorris Jobke <hey@morrisjobke.de>2016-06-09 15:01:57 +0200
committerMorris Jobke <hey@morrisjobke.de>2016-06-09 15:01:57 +0200
commitfc3ad7d5f7acb018329b22d5120c03cd07feecd6 (patch)
treeefa1958da450eb72ae93bb54dd3fe23a21aba682
parentb928305bb30ade5635a203408090f4ae376278ba (diff)
parentbb67acff9137a96f8ed002da6c8edf7aae8a7ec8 (diff)
downloadnextcloud-server-fc3ad7d5f7acb018329b22d5120c03cd07feecd6.tar.gz
nextcloud-server-fc3ad7d5f7acb018329b22d5120c03cd07feecd6.zip
Merge pull request #25 from nextcloud/downstream-160608
Downstream 2016-06-08
-rw-r--r--apps/dav/lib/Connector/Sabre/Directory.php2
-rw-r--r--apps/dav/lib/Connector/Sabre/FilesPlugin.php9
-rw-r--r--apps/dav/lib/Connector/Sabre/Node.php4
-rw-r--r--apps/dav/lib/Connector/Sabre/ObjectTree.php2
-rw-r--r--apps/dav/tests/unit/Connector/Sabre/FilesPluginTest.php60
-rw-r--r--apps/dav/tests/unit/Connector/Sabre/RequestTest/DeleteTest.php59
-rw-r--r--apps/federatedfilesharing/l10n/ar.js6
-rw-r--r--apps/federatedfilesharing/l10n/ar.json4
-rw-r--r--apps/files/js/app.js3
-rw-r--r--apps/files/l10n/da.js6
-rw-r--r--apps/files/l10n/da.json6
-rw-r--r--apps/files/lib/Controller/ViewController.php61
-rw-r--r--apps/files/templates/index.php1
-rw-r--r--apps/files/tests/Controller/ViewControllerTest.php13
-rw-r--r--apps/files_external/l10n/pt_PT.js14
-rw-r--r--apps/files_external/l10n/pt_PT.json14
-rw-r--r--apps/files_external/lib/Lib/Storage/Google.php16
-rw-r--r--apps/files_external/tests/Storage/GoogleTest.php9
-rw-r--r--apps/files_sharing/lib/Cache.php2
-rw-r--r--apps/files_sharing/lib/sharedstorage.php18
-rw-r--r--apps/systemtags/l10n/fr.js22
-rw-r--r--apps/systemtags/l10n/fr.json22
-rw-r--r--build/integration/features/bootstrap/BasicStructure.php11
-rw-r--r--build/integration/features/bootstrap/FederationContext.php2
-rw-r--r--build/integration/federation_features/federated.feature61
-rw-r--r--core/Application.php3
-rw-r--r--core/Command/Encryption/DecryptAll.php14
-rw-r--r--core/Controller/TokenController.php36
-rw-r--r--core/Controller/TwoFactorChallengeController.php9
-rw-r--r--core/Middleware/TwoFactorMiddleware.php5
-rw-r--r--core/css/styles.css4
-rw-r--r--core/templates/twofactorselectchallenge.php3
-rw-r--r--core/templates/twofactorshowchallenge.php1
-rw-r--r--lib/l10n/ar.js39
-rw-r--r--lib/l10n/ar.json39
-rw-r--r--lib/l10n/ro.js5
-rw-r--r--lib/l10n/ro.json5
-rw-r--r--lib/private/AllConfig.php9
-rw-r--r--lib/private/AppConfig.php2
-rw-r--r--lib/private/Authentication/Token/DefaultTokenCleanupJob.php1
-rw-r--r--lib/private/Encryption/DecryptAll.php4
-rw-r--r--lib/private/Files/Cache/Scanner.php7
-rw-r--r--lib/private/Files/Cache/Updater.php5
-rw-r--r--lib/private/Files/Storage/Common.php3
-rw-r--r--lib/private/Files/Storage/Local.php36
-rw-r--r--lib/private/Files/View.php45
-rw-r--r--lib/private/legacy/helper.php4
-rw-r--r--lib/public/IAppConfig.php2
-rw-r--r--lib/public/IConfig.php3
-rw-r--r--settings/js/users/users.js2
-rw-r--r--tests/Core/Controller/TokenControllerTest.php40
-rw-r--r--tests/Core/Controller/TwoFactorChallengeControllerTest.php21
-rw-r--r--tests/lib/AllConfigTest.php19
-rw-r--r--tests/lib/Encryption/DecryptAllTest.php35
-rw-r--r--tests/lib/Files/Storage/LocalTest.php31
-rw-r--r--tests/lib/Files/ViewTest.php51
-rw-r--r--version.php4
57 files changed, 743 insertions, 171 deletions
diff --git a/apps/dav/lib/Connector/Sabre/Directory.php b/apps/dav/lib/Connector/Sabre/Directory.php
index e7226b58196..ddab34605f3 100644
--- a/apps/dav/lib/Connector/Sabre/Directory.php
+++ b/apps/dav/lib/Connector/Sabre/Directory.php
@@ -196,6 +196,8 @@ class Directory extends \OCA\DAV\Connector\Sabre\Node
throw new \Sabre\DAV\Exception\ServiceUnavailable($e->getMessage());
} catch (\OCP\Files\InvalidPathException $ex) {
throw new InvalidPath($ex->getMessage());
+ } catch (ForbiddenException $e) {
+ throw new \Sabre\DAV\Exception\Forbidden();
}
}
diff --git a/apps/dav/lib/Connector/Sabre/FilesPlugin.php b/apps/dav/lib/Connector/Sabre/FilesPlugin.php
index a4e13709559..dc47416cca8 100644
--- a/apps/dav/lib/Connector/Sabre/FilesPlugin.php
+++ b/apps/dav/lib/Connector/Sabre/FilesPlugin.php
@@ -168,20 +168,19 @@ class FilesPlugin extends ServerPlugin {
*/
function checkMove($source, $destination) {
$sourceNode = $this->tree->getNodeForPath($source);
- if ($sourceNode instanceof FutureFile) {
+ if (!$sourceNode instanceof Node) {
return;
}
list($sourceDir,) = \Sabre\HTTP\URLUtil::splitPath($source);
list($destinationDir,) = \Sabre\HTTP\URLUtil::splitPath($destination);
if ($sourceDir !== $destinationDir) {
- $sourceFileInfo = $this->fileView->getFileInfo($source);
-
- if ($sourceFileInfo === false) {
+ $sourceNodeFileInfo = $sourceNode->getFileInfo();
+ if (is_null($sourceNodeFileInfo)) {
throw new NotFound($source . ' does not exist');
}
- if (!$sourceFileInfo->isDeletable()) {
+ if (!$sourceNodeFileInfo->isDeletable()) {
throw new Forbidden($source . " cannot be deleted");
}
}
diff --git a/apps/dav/lib/Connector/Sabre/Node.php b/apps/dav/lib/Connector/Sabre/Node.php
index 04640bbde4f..c41b5137fde 100644
--- a/apps/dav/lib/Connector/Sabre/Node.php
+++ b/apps/dav/lib/Connector/Sabre/Node.php
@@ -347,4 +347,8 @@ abstract class Node implements \Sabre\DAV\INode {
public function changeLock($type) {
$this->fileView->changeLock($this->path, $type);
}
+
+ public function getFileInfo() {
+ return $this->info;
+ }
}
diff --git a/apps/dav/lib/Connector/Sabre/ObjectTree.php b/apps/dav/lib/Connector/Sabre/ObjectTree.php
index 599f3fdfd0e..9e7d876187d 100644
--- a/apps/dav/lib/Connector/Sabre/ObjectTree.php
+++ b/apps/dav/lib/Connector/Sabre/ObjectTree.php
@@ -161,6 +161,8 @@ class ObjectTree extends \Sabre\DAV\Tree {
throw new \Sabre\DAV\Exception\NotFound('Storage ' . $path . ' is invalid');
} catch (LockedException $e) {
throw new \Sabre\DAV\Exception\Locked();
+ } catch (ForbiddenException $e) {
+ throw new \Sabre\DAV\Exception\Forbidden();
}
}
diff --git a/apps/dav/tests/unit/Connector/Sabre/FilesPluginTest.php b/apps/dav/tests/unit/Connector/Sabre/FilesPluginTest.php
index 87fec17bf6b..80f284e470e 100644
--- a/apps/dav/tests/unit/Connector/Sabre/FilesPluginTest.php
+++ b/apps/dav/tests/unit/Connector/Sabre/FilesPluginTest.php
@@ -24,6 +24,7 @@
*/
namespace OCA\DAV\Tests\unit\Connector\Sabre;
+use OCA\DAV\Connector\Sabre\FilesPlugin;
use OCP\Files\StorageNotAvailableException;
use Sabre\DAV\PropFind;
use Sabre\DAV\PropPatch;
@@ -36,16 +37,16 @@ use Test\TestCase;
* See the COPYING-README file.
*/
class FilesPluginTest extends TestCase {
- const GETETAG_PROPERTYNAME = \OCA\DAV\Connector\Sabre\FilesPlugin::GETETAG_PROPERTYNAME;
- const FILEID_PROPERTYNAME = \OCA\DAV\Connector\Sabre\FilesPlugin::FILEID_PROPERTYNAME;
- const INTERNAL_FILEID_PROPERTYNAME = \OCA\DAV\Connector\Sabre\FilesPlugin::INTERNAL_FILEID_PROPERTYNAME;
- const SIZE_PROPERTYNAME = \OCA\DAV\Connector\Sabre\FilesPlugin::SIZE_PROPERTYNAME;
- const PERMISSIONS_PROPERTYNAME = \OCA\DAV\Connector\Sabre\FilesPlugin::PERMISSIONS_PROPERTYNAME;
- const LASTMODIFIED_PROPERTYNAME = \OCA\DAV\Connector\Sabre\FilesPlugin::LASTMODIFIED_PROPERTYNAME;
- const DOWNLOADURL_PROPERTYNAME = \OCA\DAV\Connector\Sabre\FilesPlugin::DOWNLOADURL_PROPERTYNAME;
- const OWNER_ID_PROPERTYNAME = \OCA\DAV\Connector\Sabre\FilesPlugin::OWNER_ID_PROPERTYNAME;
- const OWNER_DISPLAY_NAME_PROPERTYNAME = \OCA\DAV\Connector\Sabre\FilesPlugin::OWNER_DISPLAY_NAME_PROPERTYNAME;
- const DATA_FINGERPRINT_PROPERTYNAME = \OCA\DAV\Connector\Sabre\FilesPlugin::DATA_FINGERPRINT_PROPERTYNAME;
+ const GETETAG_PROPERTYNAME = FilesPlugin::GETETAG_PROPERTYNAME;
+ const FILEID_PROPERTYNAME = FilesPlugin::FILEID_PROPERTYNAME;
+ const INTERNAL_FILEID_PROPERTYNAME = FilesPlugin::INTERNAL_FILEID_PROPERTYNAME;
+ const SIZE_PROPERTYNAME = FilesPlugin::SIZE_PROPERTYNAME;
+ const PERMISSIONS_PROPERTYNAME = FilesPlugin::PERMISSIONS_PROPERTYNAME;
+ const LASTMODIFIED_PROPERTYNAME = FilesPlugin::LASTMODIFIED_PROPERTYNAME;
+ const DOWNLOADURL_PROPERTYNAME = FilesPlugin::DOWNLOADURL_PROPERTYNAME;
+ const OWNER_ID_PROPERTYNAME = FilesPlugin::OWNER_ID_PROPERTYNAME;
+ const OWNER_DISPLAY_NAME_PROPERTYNAME = FilesPlugin::OWNER_DISPLAY_NAME_PROPERTYNAME;
+ const DATA_FINGERPRINT_PROPERTYNAME = FilesPlugin::DATA_FINGERPRINT_PROPERTYNAME;
/**
* @var \Sabre\DAV\Server | \PHPUnit_Framework_MockObject_MockObject
@@ -58,7 +59,7 @@ class FilesPluginTest extends TestCase {
private $tree;
/**
- * @var \OCA\DAV\Connector\Sabre\FilesPlugin
+ * @var FilesPlugin
*/
private $plugin;
@@ -84,11 +85,11 @@ class FilesPluginTest extends TestCase {
->disableOriginalConstructor()
->getMock();
$this->config = $this->getMock('\OCP\IConfig');
- $this->config->method('getSystemValue')
+ $this->config->expects($this->any())->method('getSystemValue')
->with($this->equalTo('data-fingerprint'), $this->equalTo(''))
->willReturn('my_fingerprint');
- $this->plugin = new \OCA\DAV\Connector\Sabre\FilesPlugin(
+ $this->plugin = new FilesPlugin(
$this->tree,
$this->view,
$this->config
@@ -263,7 +264,7 @@ class FilesPluginTest extends TestCase {
}
public function testGetPublicPermissions() {
- $this->plugin = new \OCA\DAV\Connector\Sabre\FilesPlugin(
+ $this->plugin = new FilesPlugin(
$this->tree,
$this->view,
$this->config,
@@ -331,7 +332,7 @@ class FilesPluginTest extends TestCase {
$node = $this->getMockBuilder('\OCA\DAV\Connector\Sabre\Directory')
->disableOriginalConstructor()
->getMock();
- $node->method('getPath')->willReturn('/');
+ $node->expects($this->any())->method('getPath')->willReturn('/');
$propFind = new PropFind(
'/',
@@ -432,11 +433,16 @@ class FilesPluginTest extends TestCase {
->method('isDeletable')
->willReturn(false);
- $this->view->expects($this->once())
+ $node = $this->getMockBuilder('\OCA\DAV\Connector\Sabre\Node')
+ ->disableOriginalConstructor()
+ ->getMock();
+ $node->expects($this->once())
->method('getFileInfo')
- ->with('FolderA/test.txt')
->willReturn($fileInfoFolderATestTXT);
+ $this->tree->expects($this->once())->method('getNodeForPath')
+ ->willReturn($node);
+
$this->plugin->checkMove('FolderA/test.txt', 'test.txt');
}
@@ -448,11 +454,16 @@ class FilesPluginTest extends TestCase {
->method('isDeletable')
->willReturn(true);
- $this->view->expects($this->once())
+ $node = $this->getMockBuilder('\OCA\DAV\Connector\Sabre\Node')
+ ->disableOriginalConstructor()
+ ->getMock();
+ $node->expects($this->once())
->method('getFileInfo')
- ->with('FolderA/test.txt')
->willReturn($fileInfoFolderATestTXT);
+ $this->tree->expects($this->once())->method('getNodeForPath')
+ ->willReturn($node);
+
$this->plugin->checkMove('FolderA/test.txt', 'test.txt');
}
@@ -461,10 +472,15 @@ class FilesPluginTest extends TestCase {
* @expectedExceptionMessage FolderA/test.txt does not exist
*/
public function testMoveSrcNotExist() {
- $this->view->expects($this->once())
+ $node = $this->getMockBuilder('\OCA\DAV\Connector\Sabre\Node')
+ ->disableOriginalConstructor()
+ ->getMock();
+ $node->expects($this->once())
->method('getFileInfo')
- ->with('FolderA/test.txt')
- ->willReturn(false);
+ ->willReturn(null);
+
+ $this->tree->expects($this->once())->method('getNodeForPath')
+ ->willReturn($node);
$this->plugin->checkMove('FolderA/test.txt', 'test.txt');
}
diff --git a/apps/dav/tests/unit/Connector/Sabre/RequestTest/DeleteTest.php b/apps/dav/tests/unit/Connector/Sabre/RequestTest/DeleteTest.php
new file mode 100644
index 00000000000..4f481cba557
--- /dev/null
+++ b/apps/dav/tests/unit/Connector/Sabre/RequestTest/DeleteTest.php
@@ -0,0 +1,59 @@
+<?php
+/**
+ * @author Joas Schilling <nickvergessen@owncloud.com>
+ * @author Robin Appelman <icewind@owncloud.com>
+ * @author Thomas Müller <thomas.mueller@tmit.eu>
+ *
+ * @copyright Copyright (c) 2016, ownCloud, Inc.
+ * @license AGPL-3.0
+ *
+ * This code is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License, version 3,
+ * as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License, version 3,
+ * along with this program. If not, see <http://www.gnu.org/licenses/>
+ *
+ */
+
+namespace OCA\DAV\Tests\unit\Connector\Sabre\RequestTest;
+
+use OC\Connector\Sabre\Exception\FileLocked;
+use OCP\AppFramework\Http;
+use OCP\Lock\ILockingProvider;
+
+/**
+ * Class DeleteTest
+ *
+ * @group DB
+ *
+ * @package OCA\DAV\Tests\unit\Connector\Sabre\RequestTest
+ */
+class DeleteTest extends RequestTest {
+ public function testBasicUpload() {
+ $user = $this->getUniqueID();
+ $view = $this->setupUser($user, 'pass');
+
+ $view->file_put_contents('foo.txt', 'asd');
+ $mount = $view->getMount('foo.txt');
+ $internalPath = $view->getAbsolutePath();
+
+ // create a ghost file
+ $mount->getStorage()->unlink($mount->getInternalPath($internalPath));
+
+ // cache entry still exists
+ $this->assertInstanceOf('\OCP\Files\FileInfo', $view->getFileInfo('foo.txt'));
+
+ $response = $this->request($view, $user, 'pass', 'DELETE', '/foo.txt');
+
+ $this->assertEquals(Http::STATUS_NO_CONTENT, $response->getStatus());
+
+ // no longer in the cache
+ $this->assertFalse($view->getFileInfo('foo.txt'));
+ }
+}
diff --git a/apps/federatedfilesharing/l10n/ar.js b/apps/federatedfilesharing/l10n/ar.js
new file mode 100644
index 00000000000..451663a7b6b
--- /dev/null
+++ b/apps/federatedfilesharing/l10n/ar.js
@@ -0,0 +1,6 @@
+OC.L10N.register(
+ "federatedfilesharing",
+ {
+ "Invalid Federated Cloud ID" : "معرّف السحابة المتحدة غير صالح"
+},
+"nplurals=6; plural=n==0 ? 0 : n==1 ? 1 : n==2 ? 2 : n%100>=3 && n%100<=10 ? 3 : n%100>=11 && n%100<=99 ? 4 : 5;");
diff --git a/apps/federatedfilesharing/l10n/ar.json b/apps/federatedfilesharing/l10n/ar.json
new file mode 100644
index 00000000000..630ab95f64b
--- /dev/null
+++ b/apps/federatedfilesharing/l10n/ar.json
@@ -0,0 +1,4 @@
+{ "translations": {
+ "Invalid Federated Cloud ID" : "معرّف السحابة المتحدة غير صالح"
+},"pluralForm" :"nplurals=6; plural=n==0 ? 0 : n==1 ? 1 : n==2 ? 2 : n%100>=3 && n%100<=10 ? 3 : n%100>=11 && n%100<=99 ? 4 : 5;"
+} \ No newline at end of file
diff --git a/apps/files/js/app.js b/apps/files/js/app.js
index 7a3d78f9663..fbfa510e07e 100644
--- a/apps/files/js/app.js
+++ b/apps/files/js/app.js
@@ -53,6 +53,9 @@
this.$showHiddenFiles = $('input#showhiddenfilesToggle');
var showHidden = $('#showHiddenFiles').val() === "1";
this.$showHiddenFiles.prop('checked', showHidden);
+ if ($('#fileNotFound').val() === "1") {
+ OC.Notification.showTemporary(t('files', 'File could not be found'));
+ }
this._filesConfig = new OC.Backbone.Model({
showhidden: showHidden
diff --git a/apps/files/l10n/da.js b/apps/files/l10n/da.js
index e1013bcafbb..e585ccacc1c 100644
--- a/apps/files/l10n/da.js
+++ b/apps/files/l10n/da.js
@@ -32,6 +32,8 @@ OC.L10N.register(
"Could not get result from server." : "Kunne ikke hente resultat fra server.",
"Uploading..." : "Uploader...",
"..." : "...",
+ "Any moment now..." : "Når som helst...",
+ "Soon..." : "Snart...",
"File upload is in progress. Leaving the page now will cancel the upload." : "Fil upload kører. Hvis du forlader siden nu, vil uploadet blive annuleret.",
"Actions" : "Handlinger",
"Download" : "Download",
@@ -47,6 +49,8 @@ OC.L10N.register(
"This directory is unavailable, please check the logs or contact the administrator" : "Denne mappe er utilgængelig, tjek venligst loggene eller kontakt administratoren",
"Could not move \"{file}\", target exists" : "Kunne ikke flytte \"{file}\" - der findes allerede en fil med dette navn",
"Could not move \"{file}\"" : "Kunne ikke flytte \"{file}\"",
+ "{newName} already exists" : "{newName} eksistere allerede",
+ "Error deleting file \"{fileName}\"." : "Fejl under sletning af filen \"{fileName}\"",
"No entries in this folder match '{filter}'" : "Der er ingen poster i denne mappe, der matcher '{filter}'",
"Name" : "Navn",
"Size" : "Størrelse",
@@ -68,6 +72,7 @@ OC.L10N.register(
"_%n byte_::_%n bytes_" : ["%n byte","%n bytes"],
"Favorited" : "Gjort til foretrukken",
"Favorite" : "Foretrukken",
+ "Local link" : "Lokalt link",
"Folder" : "Mappe",
"New folder" : "Ny Mappe",
"{newname} already exists" : "{newname} eksistere allerede",
@@ -97,6 +102,7 @@ OC.L10N.register(
"Save" : "Gem",
"Missing permissions to edit from here." : "Rettighed mangler til at redigere på dette sted",
"Settings" : "Indstillinger",
+ "Show hidden files" : "Vis skjulte filer",
"WebDAV" : "WebDAV",
"No files in here" : "Her er ingen filer",
"Upload some content or sync with your devices!" : "Overfør indhold eller synkronisér med dine enheder!",
diff --git a/apps/files/l10n/da.json b/apps/files/l10n/da.json
index 3162770cc40..499533d77ba 100644
--- a/apps/files/l10n/da.json
+++ b/apps/files/l10n/da.json
@@ -30,6 +30,8 @@
"Could not get result from server." : "Kunne ikke hente resultat fra server.",
"Uploading..." : "Uploader...",
"..." : "...",
+ "Any moment now..." : "Når som helst...",
+ "Soon..." : "Snart...",
"File upload is in progress. Leaving the page now will cancel the upload." : "Fil upload kører. Hvis du forlader siden nu, vil uploadet blive annuleret.",
"Actions" : "Handlinger",
"Download" : "Download",
@@ -45,6 +47,8 @@
"This directory is unavailable, please check the logs or contact the administrator" : "Denne mappe er utilgængelig, tjek venligst loggene eller kontakt administratoren",
"Could not move \"{file}\", target exists" : "Kunne ikke flytte \"{file}\" - der findes allerede en fil med dette navn",
"Could not move \"{file}\"" : "Kunne ikke flytte \"{file}\"",
+ "{newName} already exists" : "{newName} eksistere allerede",
+ "Error deleting file \"{fileName}\"." : "Fejl under sletning af filen \"{fileName}\"",
"No entries in this folder match '{filter}'" : "Der er ingen poster i denne mappe, der matcher '{filter}'",
"Name" : "Navn",
"Size" : "Størrelse",
@@ -66,6 +70,7 @@
"_%n byte_::_%n bytes_" : ["%n byte","%n bytes"],
"Favorited" : "Gjort til foretrukken",
"Favorite" : "Foretrukken",
+ "Local link" : "Lokalt link",
"Folder" : "Mappe",
"New folder" : "Ny Mappe",
"{newname} already exists" : "{newname} eksistere allerede",
@@ -95,6 +100,7 @@
"Save" : "Gem",
"Missing permissions to edit from here." : "Rettighed mangler til at redigere på dette sted",
"Settings" : "Indstillinger",
+ "Show hidden files" : "Vis skjulte filer",
"WebDAV" : "WebDAV",
"No files in here" : "Her er ingen filer",
"Upload some content or sync with your devices!" : "Overfør indhold eller synkronisér med dine enheder!",
diff --git a/apps/files/lib/Controller/ViewController.php b/apps/files/lib/Controller/ViewController.php
index 1b0903b41d3..18b6cf719c5 100644
--- a/apps/files/lib/Controller/ViewController.php
+++ b/apps/files/lib/Controller/ViewController.php
@@ -27,9 +27,9 @@ namespace OCA\Files\Controller;
use OC\AppFramework\Http\Request;
use OCP\AppFramework\Controller;
use OCP\AppFramework\Http\ContentSecurityPolicy;
-use OCP\AppFramework\Http\Response;
use OCP\AppFramework\Http\RedirectResponse;
use OCP\AppFramework\Http\TemplateResponse;
+use OCP\Files\NotFoundException;
use OCP\IConfig;
use OCP\IL10N;
use OCP\INavigationManager;
@@ -37,7 +37,6 @@ use OCP\IRequest;
use OCP\IURLGenerator;
use OCP\IUserSession;
use Symfony\Component\EventDispatcher\EventDispatcherInterface;
-use OCP\AppFramework\Http\NotFoundResponse;
use OCP\Files\Folder;
use OCP\App\IAppManager;
@@ -142,11 +141,15 @@ class ViewController extends Controller {
* @param string $view
* @param string $fileid
* @return TemplateResponse
- * @throws \OCP\Files\NotFoundException
*/
public function index($dir = '', $view = '', $fileid = null) {
+ $fileNotFound = false;
if ($fileid !== null) {
- return $this->showFile($fileid);
+ try {
+ return $this->showFile($fileid);
+ } catch (NotFoundException $e) {
+ $fileNotFound = true;
+ }
}
$nav = new \OCP\Template('files', 'appnavigation', '');
@@ -245,6 +248,7 @@ class ViewController extends Controller {
$params['defaultFileSortingDirection'] = $this->config->getUserValue($user, 'files', 'file_sorting_direction', 'asc');
$showHidden = (bool) $this->config->getUserValue($this->userSession->getUser()->getUID(), 'files', 'show_hidden', false);
$params['showHiddenFiles'] = $showHidden ? 1 : 0;
+ $params['fileNotFound'] = $fileNotFound ? 1 : 0;
$params['appNavigation'] = $nav;
$params['appContents'] = $contentItems;
$this->navigationManager->setActiveEntry('files_index');
@@ -265,40 +269,37 @@ class ViewController extends Controller {
* Redirects to the file list and highlight the given file id
*
* @param string $fileId file id to show
- * @return Response redirect response or not found response
+ * @return RedirectResponse redirect response or not found response
+ * @throws \OCP\Files\NotFoundException
*
* @NoCSRFRequired
* @NoAdminRequired
*/
public function showFile($fileId) {
- try {
- $uid = $this->userSession->getUser()->getUID();
- $baseFolder = $this->rootFolder->get($uid . '/files/');
- $files = $baseFolder->getById($fileId);
- $params = [];
+ $uid = $this->userSession->getUser()->getUID();
+ $baseFolder = $this->rootFolder->get($uid . '/files/');
+ $files = $baseFolder->getById($fileId);
+ $params = [];
- if (empty($files) && $this->appManager->isEnabledForUser('files_trashbin')) {
- $baseFolder = $this->rootFolder->get($uid . '/files_trashbin/files/');
- $files = $baseFolder->getById($fileId);
- $params['view'] = 'trashbin';
- }
+ if (empty($files) && $this->appManager->isEnabledForUser('files_trashbin')) {
+ $baseFolder = $this->rootFolder->get($uid . '/files_trashbin/files/');
+ $files = $baseFolder->getById($fileId);
+ $params['view'] = 'trashbin';
+ }
- if (!empty($files)) {
- $file = current($files);
- if ($file instanceof Folder) {
- // set the full path to enter the folder
- $params['dir'] = $baseFolder->getRelativePath($file->getPath());
- } else {
- // set parent path as dir
- $params['dir'] = $baseFolder->getRelativePath($file->getParent()->getPath());
- // and scroll to the entry
- $params['scrollto'] = $file->getName();
- }
- return new RedirectResponse($this->urlGenerator->linkToRoute('files.view.index', $params));
+ if (!empty($files)) {
+ $file = current($files);
+ if ($file instanceof Folder) {
+ // set the full path to enter the folder
+ $params['dir'] = $baseFolder->getRelativePath($file->getPath());
+ } else {
+ // set parent path as dir
+ $params['dir'] = $baseFolder->getRelativePath($file->getParent()->getPath());
+ // and scroll to the entry
+ $params['scrollto'] = $file->getName();
}
- } catch (\OCP\Files\NotFoundException $e) {
- return new NotFoundResponse();
+ return new RedirectResponse($this->urlGenerator->linkToRoute('files.view.index', $params));
}
- return new NotFoundResponse();
+ throw new \OCP\Files\NotFoundException();
}
}
diff --git a/apps/files/templates/index.php b/apps/files/templates/index.php
index 7281edd3aec..42ce941a4a5 100644
--- a/apps/files/templates/index.php
+++ b/apps/files/templates/index.php
@@ -14,6 +14,7 @@
<input type="hidden" name="usedSpacePercent" id="usedSpacePercent" value="<?php p($_['usedSpacePercent']); ?>" />
<input type="hidden" name="owner" id="owner" value="<?php p($_['owner']); ?>" />
<input type="hidden" name="ownerDisplayName" id="ownerDisplayName" value="<?php p($_['ownerDisplayName']); ?>" />
+<input type="hidden" name="fileNotFound" id="fileNotFound" value="<?php p($_['fileNotFound']); ?>"" />
<?php if (!$_['isPublic']) :?>
<input type="hidden" name="mailNotificationEnabled" id="mailNotificationEnabled" value="<?php p($_['mailNotificationEnabled']) ?>" />
<input type="hidden" name="mailPublicNotificationEnabled" id="mailPublicNotificationEnabled" value="<?php p($_['mailPublicNotificationEnabled']) ?>" />
diff --git a/apps/files/tests/Controller/ViewControllerTest.php b/apps/files/tests/Controller/ViewControllerTest.php
index 049f44fc0af..34c40ecea5c 100644
--- a/apps/files/tests/Controller/ViewControllerTest.php
+++ b/apps/files/tests/Controller/ViewControllerTest.php
@@ -26,6 +26,7 @@ namespace OCA\Files\Tests\Controller;
use OCA\Files\Controller\ViewController;
use OCP\AppFramework\Http;
+use OCP\Files\NotFoundException;
use OCP\IUser;
use OCP\Template;
use Test\TestCase;
@@ -259,7 +260,8 @@ class ViewControllerTest extends TestCase {
'isPublic' => false,
'defaultFileSorting' => 'name',
'defaultFileSortingDirection' => 'asc',
- 'showHiddenFiles' => false,
+ 'showHiddenFiles' => 0,
+ 'fileNotFound' => 0,
'mailNotificationEnabled' => 'no',
'mailPublicNotificationEnabled' => 'no',
'allowShareWithLink' => 'yes',
@@ -410,11 +412,14 @@ class ViewControllerTest extends TestCase {
->with(123)
->will($this->returnValue([]));
- $expected = new Http\NotFoundResponse();
if ($useShowFile) {
- $this->assertEquals($expected, $this->viewController->showFile(123));
+ $this->setExpectedException('OCP\Files\NotFoundException');
+ $this->viewController->showFile(123);
} else {
- $this->assertEquals($expected, $this->viewController->index('/whatever', '', '123'));
+ $response = $this->viewController->index('MyDir', 'MyView', '123');
+ $this->assertInstanceOf('OCP\AppFramework\Http\TemplateResponse', $response);
+ $params = $response->getParams();
+ $this->assertEquals(1, $params['fileNotFound']);
}
}
diff --git a/apps/files_external/l10n/pt_PT.js b/apps/files_external/l10n/pt_PT.js
index 9d3998d903b..68f183a36b3 100644
--- a/apps/files_external/l10n/pt_PT.js
+++ b/apps/files_external/l10n/pt_PT.js
@@ -7,13 +7,13 @@ OC.L10N.register(
"Step 1 failed. Exception: %s" : "Passo 1 falhou. Exceção: %s",
"Step 2 failed. Exception: %s" : "Passo 2 falhou. Exceção: %s",
"External storage" : "Armazenamento Externo",
- "Dropbox App Configuration" : "Configuração da app Dropbox",
- "Google Drive App Configuration" : "Configuração da app Google Drive",
+ "Dropbox App Configuration" : "Configuração da aplicação Dropbox",
+ "Google Drive App Configuration" : "Configuração da aplicação Google Drive",
"Personal" : "Pessoal",
"System" : "Sistema",
"Grant access" : "Conceder acesso",
- "Error configuring OAuth1" : "Erro de configuração OAuth1",
- "Error configuring OAuth2" : "Erro de configuração OAuth2",
+ "Error configuring OAuth1" : "Erro ao configurar OAuth1",
+ "Error configuring OAuth2" : "Erro ao configurar OAuth2",
"Generate keys" : "Gerar chaves",
"Error generating key pair" : "Erro ao gerar chave par",
"All users. Type to select user or group." : "Todos os utilizadores. Digite para selecionar o utilizador ou grupo.",
@@ -27,14 +27,14 @@ OC.L10N.register(
"Couldn't get the list of external mount points: {type}" : "Não foi possível conseguir a lista de pontos de montagem externos: {type}",
"There was an error with message: " : "Houve um erro com a mensagem:",
"External mount error" : "Erro de montagem externa",
- "external-storage" : "Armazenamento Externo",
+ "external-storage" : "armazenamento externo",
"Couldn't get the list of Windows network drive mount points: empty response from the server" : "Não foi possível conseguir a lista de pontos de montagem Windows na rede: resposta vazia do servidor",
"Some of the configured external mount points are not connected. Please click on the red row(s) for more information" : "Alguns dos pontos de montagem externos configurados não estão conectados. Por favor, clique na fila vermelha para mais informação",
"Please enter the credentials for the {mount} mount" : "Por favor, introduza as credenciais para {mount}",
"Username" : "Nome de utilizador",
"Password" : "Palavra-passe",
"Credentials saved" : "Credenciais guardadas",
- "Credentials saving failed" : "Falha ao salvar credenciais",
+ "Credentials saving failed" : "Falha ao guardar as credenciais",
"Credentials required" : "Credenciais necessárias",
"Save" : "Guardar",
"Storage with id \"%i\" not found" : "Não foi encontrado o armazenamento com a id. \"%i\"",
@@ -49,7 +49,7 @@ OC.L10N.register(
"Insufficient data: %s" : "Dados insuficientes: %s",
"%s" : "%s",
"Storage with id \"%i\" is not user editable" : "Armazenamento com id \"%i\" não é editável pelo utilizador",
- "Access key" : "Código de acesso",
+ "Access key" : "Chave de acesso",
"Secret key" : "Código secreto",
"Builtin" : "Integrado",
"None" : "Nenhum",
diff --git a/apps/files_external/l10n/pt_PT.json b/apps/files_external/l10n/pt_PT.json
index fab0f05a830..93922c67762 100644
--- a/apps/files_external/l10n/pt_PT.json
+++ b/apps/files_external/l10n/pt_PT.json
@@ -5,13 +5,13 @@
"Step 1 failed. Exception: %s" : "Passo 1 falhou. Exceção: %s",
"Step 2 failed. Exception: %s" : "Passo 2 falhou. Exceção: %s",
"External storage" : "Armazenamento Externo",
- "Dropbox App Configuration" : "Configuração da app Dropbox",
- "Google Drive App Configuration" : "Configuração da app Google Drive",
+ "Dropbox App Configuration" : "Configuração da aplicação Dropbox",
+ "Google Drive App Configuration" : "Configuração da aplicação Google Drive",
"Personal" : "Pessoal",
"System" : "Sistema",
"Grant access" : "Conceder acesso",
- "Error configuring OAuth1" : "Erro de configuração OAuth1",
- "Error configuring OAuth2" : "Erro de configuração OAuth2",
+ "Error configuring OAuth1" : "Erro ao configurar OAuth1",
+ "Error configuring OAuth2" : "Erro ao configurar OAuth2",
"Generate keys" : "Gerar chaves",
"Error generating key pair" : "Erro ao gerar chave par",
"All users. Type to select user or group." : "Todos os utilizadores. Digite para selecionar o utilizador ou grupo.",
@@ -25,14 +25,14 @@
"Couldn't get the list of external mount points: {type}" : "Não foi possível conseguir a lista de pontos de montagem externos: {type}",
"There was an error with message: " : "Houve um erro com a mensagem:",
"External mount error" : "Erro de montagem externa",
- "external-storage" : "Armazenamento Externo",
+ "external-storage" : "armazenamento externo",
"Couldn't get the list of Windows network drive mount points: empty response from the server" : "Não foi possível conseguir a lista de pontos de montagem Windows na rede: resposta vazia do servidor",
"Some of the configured external mount points are not connected. Please click on the red row(s) for more information" : "Alguns dos pontos de montagem externos configurados não estão conectados. Por favor, clique na fila vermelha para mais informação",
"Please enter the credentials for the {mount} mount" : "Por favor, introduza as credenciais para {mount}",
"Username" : "Nome de utilizador",
"Password" : "Palavra-passe",
"Credentials saved" : "Credenciais guardadas",
- "Credentials saving failed" : "Falha ao salvar credenciais",
+ "Credentials saving failed" : "Falha ao guardar as credenciais",
"Credentials required" : "Credenciais necessárias",
"Save" : "Guardar",
"Storage with id \"%i\" not found" : "Não foi encontrado o armazenamento com a id. \"%i\"",
@@ -47,7 +47,7 @@
"Insufficient data: %s" : "Dados insuficientes: %s",
"%s" : "%s",
"Storage with id \"%i\" is not user editable" : "Armazenamento com id \"%i\" não é editável pelo utilizador",
- "Access key" : "Código de acesso",
+ "Access key" : "Chave de acesso",
"Secret key" : "Código secreto",
"Builtin" : "Integrado",
"None" : "Nenhum",
diff --git a/apps/files_external/lib/Lib/Storage/Google.php b/apps/files_external/lib/Lib/Storage/Google.php
index 2a1ff768e2c..49fde7d066f 100644
--- a/apps/files_external/lib/Lib/Storage/Google.php
+++ b/apps/files_external/lib/Lib/Storage/Google.php
@@ -97,6 +97,9 @@ class Google extends \OC\Files\Storage\Common {
private function getDriveFile($path) {
// Remove leading and trailing slashes
$path = trim($path, '/');
+ if ($path === '.') {
+ $path = '';
+ }
if (isset($this->driveFiles[$path])) {
return $this->driveFiles[$path];
} else if ($path === '') {
@@ -138,7 +141,7 @@ class Google extends \OC\Files\Storage\Common {
if ($pos !== false) {
$pathWithoutExt = substr($path, 0, $pos);
$file = $this->getDriveFile($pathWithoutExt);
- if ($file) {
+ if ($file && $this->isGoogleDocFile($file)) {
// Switch cached Google_Service_Drive_DriveFile to the correct index
unset($this->driveFiles[$pathWithoutExt]);
$this->driveFiles[$path] = $file;
@@ -208,6 +211,17 @@ class Google extends \OC\Files\Storage\Common {
}
}
+ /**
+ * Returns whether the given drive file is a Google Doc file
+ *
+ * @param \Google_Service_Drive_DriveFile
+ *
+ * @return true if the file is a Google Doc file, false otherwise
+ */
+ private function isGoogleDocFile($file) {
+ return $this->getGoogleDocExtension($file->getMimeType()) !== '';
+ }
+
public function mkdir($path) {
if (!$this->is_dir($path)) {
$parentFolder = $this->getDriveFile(dirname($path));
diff --git a/apps/files_external/tests/Storage/GoogleTest.php b/apps/files_external/tests/Storage/GoogleTest.php
index 7684fec8429..eb19cc463b1 100644
--- a/apps/files_external/tests/Storage/GoogleTest.php
+++ b/apps/files_external/tests/Storage/GoogleTest.php
@@ -60,4 +60,13 @@ class GoogleTest extends \Test\Files\Storage\Storage {
parent::tearDown();
}
+
+ public function testSameNameAsFolderWithExtension() {
+ $this->assertTrue($this->instance->mkdir('testsamename'));
+ $this->assertEquals(13, $this->instance->file_put_contents('testsamename.txt', 'some contents'));
+ $this->assertEquals('some contents', $this->instance->file_get_contents('testsamename.txt'));
+ $this->assertTrue($this->instance->is_dir('testsamename'));
+ $this->assertTrue($this->instance->unlink('testsamename.txt'));
+ $this->assertTrue($this->instance->rmdir('testsamename'));
+ }
}
diff --git a/apps/files_sharing/lib/Cache.php b/apps/files_sharing/lib/Cache.php
index 82d885a8ef3..a0519cadbaa 100644
--- a/apps/files_sharing/lib/Cache.php
+++ b/apps/files_sharing/lib/Cache.php
@@ -81,7 +81,7 @@ class Cache extends CacheJail {
}
protected function formatCacheEntry($entry) {
- $path = $entry['path'];
+ $path = isset($entry['path']) ? $entry['path'] : '';
$entry = parent::formatCacheEntry($entry);
$sharePermissions = $this->storage->getPermissions($path);
if (isset($entry['permissions'])) {
diff --git a/apps/files_sharing/lib/sharedstorage.php b/apps/files_sharing/lib/sharedstorage.php
index 965c4d36cad..b5b5e416884 100644
--- a/apps/files_sharing/lib/sharedstorage.php
+++ b/apps/files_sharing/lib/sharedstorage.php
@@ -415,4 +415,22 @@ class Shared extends \OC\Files\Storage\Wrapper\Jail implements ISharedStorage {
return $this->sourceStorage;
}
+ public function file_get_contents($path) {
+ $info = [
+ 'target' => $this->getMountPoint() . '/' . $path,
+ 'source' => $this->getSourcePath($path),
+ ];
+ \OCP\Util::emitHook('\OC\Files\Storage\Shared', 'file_get_contents', $info);
+ return parent::file_get_contents($path);
+ }
+
+ public function file_put_contents($path, $data) {
+ $info = [
+ 'target' => $this->getMountPoint() . '/' . $path,
+ 'source' => $this->getSourcePath($path),
+ ];
+ \OCP\Util::emitHook('\OC\Files\Storage\Shared', 'file_put_contents', $info);
+ return parent::file_put_contents($path, $data);
+ }
+
}
diff --git a/apps/systemtags/l10n/fr.js b/apps/systemtags/l10n/fr.js
index 93c7d364b24..387bc45643c 100644
--- a/apps/systemtags/l10n/fr.js
+++ b/apps/systemtags/l10n/fr.js
@@ -7,20 +7,20 @@ OC.L10N.register(
"Please select tags to filter by" : "Veuillez sélectionner les étiquettes par lesquelles filtrer",
"No files found for the selected tags" : "Aucun fichier pour les étiquettes sélectionnées",
"<strong>System tags</strong> for a file have been modified" : "<strong>Les étiquettes systèmes</strong> pour un fichier ont été modifiées",
- "You assigned system tag %3$s" : "Vous avez attribué l'étiquette système %3$s",
+ "You assigned system tag %3$s" : "Vous avez attribué l'étiquette collaborative %3$s",
"%1$s assigned system tag %3$s" : "%1$s a attribué l'étiquette système %3$s",
- "You unassigned system tag %3$s" : "Vous avez retiré l'étiquette système %3$s",
+ "You unassigned system tag %3$s" : "Vous avez retiré l'étiquette collaborative %3$s",
"%1$s unassigned system tag %3$s" : "%1$s a retiré l'étiquette système %3$s",
- "You created system tag %2$s" : "Vous avez créé l'étiquette système %2$s",
+ "You created system tag %2$s" : "Vous avez créé l'étiquette collaborative %2$s",
"%1$s created system tag %2$s" : "%1$s a créé l'étiquette système %2$s",
- "You deleted system tag %2$s" : "Vous avez supprimé l'étiquette système %2$s",
- "%1$s deleted system tag %2$s" : "%1$s a supprimé l'étiquette système %2$s",
- "You updated system tag %3$s to %2$s" : "Vous avez renommé l'étiquette système %3$s en %2$s",
- "%1$s updated system tag %3$s to %2$s" : "%1$s a renommé l'étiquette système %3$s en %2$s",
- "You assigned system tag %3$s to %2$s" : "Vous avez attribué l'étiquette système %3$s à %2$s",
- "%1$s assigned system tag %3$s to %2$s" : "%1$s a attribué l'étiquette système %3$s à %2$s",
- "You unassigned system tag %3$s from %2$s" : "Vous avez retiré l'étiquette système %3$s de %2$s",
- "%1$s unassigned system tag %3$s from %2$s" : "%1$s a retiré l'étiquette système %3$s à %2$s",
+ "You deleted system tag %2$s" : "Vous avez supprimé l'étiquette collaborative %2$s",
+ "%1$s deleted system tag %2$s" : "%1$s a supprimé l'étiquette collaborative %2$s",
+ "You updated system tag %3$s to %2$s" : "Vous avez renommé l'étiquette collaborative %3$s en %2$s",
+ "%1$s updated system tag %3$s to %2$s" : "%1$s a renommé l'étiquette collaborative %3$s en %2$s",
+ "You assigned system tag %3$s to %2$s" : "Vous avez attribué l'étiquette collaborative %3$s à %2$s",
+ "%1$s assigned system tag %3$s to %2$s" : "%1$s a attribué l'étiquette collaborative %3$s à %2$s",
+ "You unassigned system tag %3$s from %2$s" : "Vous avez retiré l'étiquette collaborative %3$s à %2$s",
+ "%1$s unassigned system tag %3$s from %2$s" : "%1$s a retiré l'étiquette collaborative %3$s à %2$s",
"%s (restricted)" : "%s (restreint)",
"%s (invisible)" : "%s (invisible)",
"No files in here" : "Aucun fichier",
diff --git a/apps/systemtags/l10n/fr.json b/apps/systemtags/l10n/fr.json
index 94254c93ab0..66b4d246dac 100644
--- a/apps/systemtags/l10n/fr.json
+++ b/apps/systemtags/l10n/fr.json
@@ -5,20 +5,20 @@
"Please select tags to filter by" : "Veuillez sélectionner les étiquettes par lesquelles filtrer",
"No files found for the selected tags" : "Aucun fichier pour les étiquettes sélectionnées",
"<strong>System tags</strong> for a file have been modified" : "<strong>Les étiquettes systèmes</strong> pour un fichier ont été modifiées",
- "You assigned system tag %3$s" : "Vous avez attribué l'étiquette système %3$s",
+ "You assigned system tag %3$s" : "Vous avez attribué l'étiquette collaborative %3$s",
"%1$s assigned system tag %3$s" : "%1$s a attribué l'étiquette système %3$s",
- "You unassigned system tag %3$s" : "Vous avez retiré l'étiquette système %3$s",
+ "You unassigned system tag %3$s" : "Vous avez retiré l'étiquette collaborative %3$s",
"%1$s unassigned system tag %3$s" : "%1$s a retiré l'étiquette système %3$s",
- "You created system tag %2$s" : "Vous avez créé l'étiquette système %2$s",
+ "You created system tag %2$s" : "Vous avez créé l'étiquette collaborative %2$s",
"%1$s created system tag %2$s" : "%1$s a créé l'étiquette système %2$s",
- "You deleted system tag %2$s" : "Vous avez supprimé l'étiquette système %2$s",
- "%1$s deleted system tag %2$s" : "%1$s a supprimé l'étiquette système %2$s",
- "You updated system tag %3$s to %2$s" : "Vous avez renommé l'étiquette système %3$s en %2$s",
- "%1$s updated system tag %3$s to %2$s" : "%1$s a renommé l'étiquette système %3$s en %2$s",
- "You assigned system tag %3$s to %2$s" : "Vous avez attribué l'étiquette système %3$s à %2$s",
- "%1$s assigned system tag %3$s to %2$s" : "%1$s a attribué l'étiquette système %3$s à %2$s",
- "You unassigned system tag %3$s from %2$s" : "Vous avez retiré l'étiquette système %3$s de %2$s",
- "%1$s unassigned system tag %3$s from %2$s" : "%1$s a retiré l'étiquette système %3$s à %2$s",
+ "You deleted system tag %2$s" : "Vous avez supprimé l'étiquette collaborative %2$s",
+ "%1$s deleted system tag %2$s" : "%1$s a supprimé l'étiquette collaborative %2$s",
+ "You updated system tag %3$s to %2$s" : "Vous avez renommé l'étiquette collaborative %3$s en %2$s",
+ "%1$s updated system tag %3$s to %2$s" : "%1$s a renommé l'étiquette collaborative %3$s en %2$s",
+ "You assigned system tag %3$s to %2$s" : "Vous avez attribué l'étiquette collaborative %3$s à %2$s",
+ "%1$s assigned system tag %3$s to %2$s" : "%1$s a attribué l'étiquette collaborative %3$s à %2$s",
+ "You unassigned system tag %3$s from %2$s" : "Vous avez retiré l'étiquette collaborative %3$s à %2$s",
+ "%1$s unassigned system tag %3$s from %2$s" : "%1$s a retiré l'étiquette collaborative %3$s à %2$s",
"%s (restricted)" : "%s (restreint)",
"%s (invisible)" : "%s (invisible)",
"No files in here" : "Aucun fichier",
diff --git a/build/integration/features/bootstrap/BasicStructure.php b/build/integration/features/bootstrap/BasicStructure.php
index b8fb516fada..9248b2cd252 100644
--- a/build/integration/features/bootstrap/BasicStructure.php
+++ b/build/integration/features/bootstrap/BasicStructure.php
@@ -261,6 +261,17 @@ trait BasicStructure {
}
/**
+ * @Given User :user modifies text of :filename with text :text
+ * @param string $user
+ * @param string $filename
+ * @param string $text
+ */
+ public function modifyTextOfFile($user, $filename, $text) {
+ self::removeFile("../../data/$user/files", "$filename");
+ file_put_contents("../../data/$user/files" . "$filename", "$text");
+ }
+
+ /**
* @BeforeSuite
*/
public static function addFilesToSkeleton(){
diff --git a/build/integration/features/bootstrap/FederationContext.php b/build/integration/features/bootstrap/FederationContext.php
index 2809c6974fa..55f3a55da0d 100644
--- a/build/integration/features/bootstrap/FederationContext.php
+++ b/build/integration/features/bootstrap/FederationContext.php
@@ -12,7 +12,7 @@ require __DIR__ . '/../../vendor/autoload.php';
*/
class FederationContext implements Context, SnippetAcceptingContext {
- use Sharing;
+ use WebDav;
/**
* @Given /^User "([^"]*)" from server "(LOCAL|REMOTE)" shares "([^"]*)" with user "([^"]*)" from server "(LOCAL|REMOTE)"$/
diff --git a/build/integration/federation_features/federated.feature b/build/integration/federation_features/federated.feature
index acd1f91e908..8bf8e921b0f 100644
--- a/build/integration/federation_features/federated.feature
+++ b/build/integration/federation_features/federated.feature
@@ -120,6 +120,67 @@ Feature: federated
| share_with | user2 |
| share_with_displayname | user2 |
+ Scenario: Overwrite a federated shared file as recipient
+ Given Using server "REMOTE"
+ And user "user1" exists
+ And user "user2" exists
+ And Using server "LOCAL"
+ And user "user0" exists
+ And User "user0" from server "LOCAL" shares "/textfile0.txt" with user "user1" from server "REMOTE"
+ And User "user1" from server "REMOTE" accepts last pending share
+ And Using server "REMOTE"
+ And As an "user1"
+ And User "user1" modifies text of "/textfile0.txt" with text "BLABLABLA"
+ When User "user1" uploads file "../../data/user1/files/textfile0.txt" to "/textfile0 (2).txt"
+ And Downloading file "/textfile0 (2).txt" with range "bytes=0-8"
+ Then Downloaded content should be "BLABLABLA"
+
+ Scenario: Overwrite a federated shared folder as recipient
+ Given Using server "REMOTE"
+ And user "user1" exists
+ And user "user2" exists
+ And Using server "LOCAL"
+ And user "user0" exists
+ And User "user0" from server "LOCAL" shares "/PARENT" with user "user1" from server "REMOTE"
+ And User "user1" from server "REMOTE" accepts last pending share
+ And Using server "REMOTE"
+ And As an "user1"
+ And User "user1" modifies text of "/textfile0.txt" with text "BLABLABLA"
+ When User "user1" uploads file "../../data/user1/files/textfile0.txt" to "/PARENT (2)/textfile0.txt"
+ And Downloading file "/PARENT (2)/textfile0.txt" with range "bytes=0-8"
+ Then Downloaded content should be "BLABLABLA"
+
+ Scenario: Overwrite a federated shared file as recipient using old chunking
+ Given Using server "REMOTE"
+ And user "user1" exists
+ And user "user2" exists
+ And Using server "LOCAL"
+ And user "user0" exists
+ And User "user0" from server "LOCAL" shares "/textfile0.txt" with user "user1" from server "REMOTE"
+ And User "user1" from server "REMOTE" accepts last pending share
+ And Using server "REMOTE"
+ And As an "user1"
+ And user "user1" uploads chunk file "1" of "3" with "AAAAA" to "/textfile0 (2).txt"
+ And user "user1" uploads chunk file "2" of "3" with "BBBBB" to "/textfile0 (2).txt"
+ And user "user1" uploads chunk file "3" of "3" with "CCCCC" to "/textfile0 (2).txt"
+ When Downloading file "/textfile0 (2).txt" with range "bytes=0-4"
+ Then Downloaded content should be "AAAAA"
+
+ Scenario: Overwrite a federated shared folder as recipient using old chunking
+ Given Using server "REMOTE"
+ And user "user1" exists
+ And user "user2" exists
+ And Using server "LOCAL"
+ And user "user0" exists
+ And User "user0" from server "LOCAL" shares "/PARENT" with user "user1" from server "REMOTE"
+ And User "user1" from server "REMOTE" accepts last pending share
+ And Using server "REMOTE"
+ And As an "user1"
+ And user "user1" uploads chunk file "1" of "3" with "AAAAA" to "/PARENT (2)/textfile0.txt"
+ And user "user1" uploads chunk file "2" of "3" with "BBBBB" to "/PARENT (2)/textfile0.txt"
+ And user "user1" uploads chunk file "3" of "3" with "CCCCC" to "/PARENT (2)/textfile0.txt"
+ When Downloading file "/PARENT (2)/textfile0.txt" with range "bytes=3-13"
+ Then Downloaded content should be "AABBBBBCCCC"
diff --git a/core/Application.php b/core/Application.php
index 25e2fa76273..a87917b626a 100644
--- a/core/Application.php
+++ b/core/Application.php
@@ -120,7 +120,8 @@ class Application extends App {
$c->query('AppName'),
$c->query('Request'),
$c->query('UserManager'),
- $c->query('OC\Authentication\Token\DefaultTokenProvider'),
+ $c->query('ServerContainer')->query('OC\Authentication\Token\IProvider'),
+ $c->query('TwoFactorAuthManager'),
$c->query('SecureRandom')
);
});
diff --git a/core/Command/Encryption/DecryptAll.php b/core/Command/Encryption/DecryptAll.php
index 8d7d26f3d23..d060918a506 100644
--- a/core/Command/Encryption/DecryptAll.php
+++ b/core/Command/Encryption/DecryptAll.php
@@ -111,7 +111,8 @@ class DecryptAll extends Command {
$this->addArgument(
'user',
InputArgument::OPTIONAL,
- 'user for which you want to decrypt all files (optional)'
+ 'user for which you want to decrypt all files (optional)',
+ ''
);
}
@@ -127,8 +128,16 @@ class DecryptAll extends Command {
return;
}
+ $uid = $input->getArgument('user');
+ //FIXME WHEN https://github.com/owncloud/core/issues/24994 is fixed
+ if ($uid === null) {
+ $message = 'your ownCloud';
+ } else {
+ $message = "$uid's account";
+ }
+
$output->writeln("\n");
- $output->writeln('You are about to start to decrypt all files stored in your ownCloud.');
+ $output->writeln("You are about to start to decrypt all files stored in $message.");
$output->writeln('It will depend on the encryption module and your setup if this is possible.');
$output->writeln('Depending on the number and size of your files this can take some time');
$output->writeln('Please make sure that no user access his files during this process!');
@@ -140,6 +149,7 @@ class DecryptAll extends Command {
$result = $this->decryptAll->decryptAll($input, $output, $user);
if ($result === false) {
$output->writeln(' aborted.');
+ $output->writeln('Server side encryption remains enabled');
$this->config->setAppValue('core', 'encryption_enabled', 'yes');
}
$this->resetSingleUserAndTrashbin();
diff --git a/core/Controller/TokenController.php b/core/Controller/TokenController.php
index 42cc29bad10..13b1db9044a 100644
--- a/core/Controller/TokenController.php
+++ b/core/Controller/TokenController.php
@@ -1,4 +1,5 @@
<?php
+
/**
* @author Christoph Wurst <christoph@owncloud.com>
*
@@ -23,22 +24,27 @@ namespace OC\Core\Controller;
use OC\AppFramework\Http;
use OC\Authentication\Token\DefaultTokenProvider;
+use OC\Authentication\Token\IProvider;
use OC\Authentication\Token\IToken;
-use OC\User\Manager;
+use OC\Authentication\TwoFactorAuth\Manager as TwoFactorAuthManager;
+use OC\User\Manager as UserManager;
+use OCA\User_LDAP\User\Manager;
use OCP\AppFramework\Controller;
use OCP\AppFramework\Http\JSONResponse;
-use OCP\AppFramework\Http\Response;
use OCP\IRequest;
use OCP\Security\ISecureRandom;
class TokenController extends Controller {
- /** @var Manager */
+ /** @var UserManager */
private $userManager;
- /** @var DefaultTokenProvider */
+ /** @var IProvider */
private $tokenProvider;
+ /** @var TwoFactorAuthManager */
+ private $twoFactorAuthManager;
+
/** @var ISecureRandom */
private $secureRandom;
@@ -49,12 +55,12 @@ class TokenController extends Controller {
* @param DefaultTokenProvider $tokenProvider
* @param ISecureRandom $secureRandom
*/
- public function __construct($appName, IRequest $request, Manager $userManager, DefaultTokenProvider $tokenProvider,
- ISecureRandom $secureRandom) {
+ public function __construct($appName, IRequest $request, UserManager $userManager, IProvider $tokenProvider, TwoFactorAuthManager $twoFactorAuthManager, ISecureRandom $secureRandom) {
parent::__construct($appName, $request);
$this->userManager = $userManager;
$this->tokenProvider = $tokenProvider;
$this->secureRandom = $secureRandom;
+ $this->twoFactorAuthManager = $twoFactorAuthManager;
}
/**
@@ -70,18 +76,26 @@ class TokenController extends Controller {
*/
public function generateToken($user, $password, $name = 'unknown client') {
if (is_null($user) || is_null($password)) {
- $response = new Response();
+ $response = new JSONResponse();
$response->setStatus(Http::STATUS_UNPROCESSABLE_ENTITY);
return $response;
}
- $loginResult = $this->userManager->checkPassword($user, $password);
- if ($loginResult === false) {
- $response = new Response();
+ $loginName = $user;
+ $user = $this->userManager->checkPassword($loginName, $password);
+ if ($user === false) {
+ $response = new JSONResponse();
$response->setStatus(Http::STATUS_UNAUTHORIZED);
return $response;
}
+
+ if ($this->twoFactorAuthManager->isTwoFactorAuthenticated($user)) {
+ $resp = new JSONResponse();
+ $resp->setStatus(Http::STATUS_UNAUTHORIZED);
+ return $resp;
+ }
+
$token = $this->secureRandom->generate(128);
- $this->tokenProvider->generateToken($token, $loginResult->getUID(), $user, $password, $name, IToken::PERMANENT_TOKEN);
+ $this->tokenProvider->generateToken($token, $user->getUID(), $loginName, $password, $name, IToken::PERMANENT_TOKEN);
return [
'token' => $token,
];
diff --git a/core/Controller/TwoFactorChallengeController.php b/core/Controller/TwoFactorChallengeController.php
index 499898de3bc..edaf3378cd8 100644
--- a/core/Controller/TwoFactorChallengeController.php
+++ b/core/Controller/TwoFactorChallengeController.php
@@ -62,6 +62,13 @@ class TwoFactorChallengeController extends Controller {
}
/**
+ * @return string
+ */
+ protected function getLogoutAttribute() {
+ return \OC_User::getLogoutAttribute();
+ }
+
+ /**
* @NoAdminRequired
* @NoCSRFRequired
*
@@ -75,6 +82,7 @@ class TwoFactorChallengeController extends Controller {
$data = [
'providers' => $providers,
'redirect_url' => $redirect_url,
+ 'logout_attribute' => $this->getLogoutAttribute(),
];
return new TemplateResponse($this->appName, 'twofactorselectchallenge', $data, 'guest');
}
@@ -106,6 +114,7 @@ class TwoFactorChallengeController extends Controller {
$data = [
'error' => $error,
'provider' => $provider,
+ 'logout_attribute' => $this->getLogoutAttribute(),
'template' => $tmpl->fetchPage(),
];
return new TemplateResponse($this->appName, 'twofactorshowchallenge', $data, 'guest');
diff --git a/core/Middleware/TwoFactorMiddleware.php b/core/Middleware/TwoFactorMiddleware.php
index aa82897ad46..0bad8a2c40f 100644
--- a/core/Middleware/TwoFactorMiddleware.php
+++ b/core/Middleware/TwoFactorMiddleware.php
@@ -82,6 +82,11 @@ class TwoFactorMiddleware extends Middleware {
return;
}
+ if ($controller instanceof \OC\Core\Controller\LoginController && $methodName === 'logout') {
+ // Don't block the logout page, to allow canceling the 2FA
+ return;
+ }
+
if ($this->userSession->isLoggedIn()) {
$user = $this->userSession->getUser();
diff --git a/core/css/styles.css b/core/css/styles.css
index df9509baa19..475c4fa3fb3 100644
--- a/core/css/styles.css
+++ b/core/css/styles.css
@@ -38,6 +38,10 @@ body {
display: inline-block;
}
+a.two-factor-cancel {
+ color: #fff;
+}
+
.float-spinner {
height: 32px;
display: none;
diff --git a/core/templates/twofactorselectchallenge.php b/core/templates/twofactorselectchallenge.php
index 14d599aab3e..4209beac4e6 100644
--- a/core/templates/twofactorselectchallenge.php
+++ b/core/templates/twofactorselectchallenge.php
@@ -18,4 +18,5 @@
</li>
<?php endforeach; ?>
</ul>
-</fieldset> \ No newline at end of file
+</fieldset>
+<a class="two-factor-cancel" <?php print_unescaped($_['logout_attribute']); ?>><?php p($l->t('Cancel login')) ?></a>
diff --git a/core/templates/twofactorshowchallenge.php b/core/templates/twofactorshowchallenge.php
index 66f5ed312ec..c5ee9aca4b4 100644
--- a/core/templates/twofactorshowchallenge.php
+++ b/core/templates/twofactorshowchallenge.php
@@ -17,3 +17,4 @@ $template = $_['template'];
<span class="warning"><?php p($l->t('An error occured while verifying the token')); ?></span>
<?php endif; ?>
<?php print_unescaped($template); ?>
+<a class="two-factor-cancel" <?php print_unescaped($_['logout_attribute']); ?>><?php p($l->t('Cancel login')) ?></a>
diff --git a/lib/l10n/ar.js b/lib/l10n/ar.js
index 21a67883ac3..a7a3e4e4c31 100644
--- a/lib/l10n/ar.js
+++ b/lib/l10n/ar.js
@@ -1,18 +1,46 @@
OC.L10N.register(
"lib",
{
+ "Cannot write into \"config\" directory!" : "الكتابة في مجلد \"config\" غير ممكنة!",
+ "This can usually be fixed by giving the webserver write access to the config directory" : "يمكن حل هذا عادة بإعطاء خادم الوب صلاحية الكتابة في مجلد config",
+ "See %s" : "أنظر %s",
+ "Sample configuration detected" : "تم اكتشاف إعدادات عيّنة",
+ "PHP %s or higher is required." : "إصدار PHP %s أو أحدث منه مطلوب.",
+ "PHP with a version lower than %s is required." : "PHP الإصدار %s أو أقل مطلوب.",
+ "%sbit or higher PHP required." : "مكتبات PHP ذات %s بت أو أعلى مطلوبة.",
+ "Following databases are supported: %s" : "قواعد البيانات التالية مدعومة: %s",
+ "The command line tool %s could not be found" : "لم يتم العثور على أداة سطر الأوامر %s",
+ "The library %s is not available." : "مكتبة %s غير متوفرة.",
"Unknown filetype" : "نوع الملف غير معروف",
"Invalid image" : "الصورة غير صالحة",
"today" : "اليوم",
"yesterday" : "يوم أمس",
+ "_%n day ago_::_%n days ago_" : ["قبل ساعات","قبل يوم","قبل يومين","قبل %n يوماً","قبل %n يوماً","قبل %n يوماً"],
"last month" : "الشهر الماضي",
+ "_%n month ago_::_%n months ago_" : ["قبل عدة أيام","قبل شهر","قبل شهرين","قبل %n شهراً","قبل %n شهراً","قبل %n شهراً"],
"last year" : "السنةالماضية",
"seconds ago" : "منذ ثواني",
+ "Empty filename is not allowed" : "لا يسمح بأسماء فارغة للملفات",
+ "4-byte characters are not supported in file names" : "المحارف ذات 4 بايت غير مسموح بها في أسماء الملفات",
+ "File name is a reserved word" : "اسم الملف كلمة محجوزة",
+ "File name contains at least one invalid character" : "اسم الملف به ، على الأقل ، حرف غير صالح",
+ "File name is too long" : "اسم الملف طويل جداً",
"App directory already exists" : "مجلد التطبيق موجود مسبقا",
"Can't create app folder. Please fix permissions. %s" : "لا يمكن إنشاء مجلد التطبيق. يرجى تعديل الصلاحيات. %s",
+ "Archive does not contain a directory named %s" : "الأرشيف لا يحتوي مجلداً اسمه %s",
"No source specified when installing app" : "لم يتم تحديد المصدر عن تثبيت البرنامج",
+ "No href specified when installing app from http" : "لم يتم تحديد href عند تثبيت التطبيق من http",
+ "No path specified when installing app from local file" : "لم يتم تحديد مسار عند تثبيت التطبيق من ملف محلّي",
"Archives of type %s are not supported" : "الأرشيفات من نوع %s غير مدعومة",
+ "Failed to open archive when installing app" : "فشل فتح الأرشيف أثناء تثبيت التطبيق",
"App does not provide an info.xml file" : "التطبيق لا يتوفر على ملف info.xml",
+ "App cannot be installed because appinfo file cannot be read." : "لا يمكن تثبيت التطبيق لأن ملف appinfo غير ممكنة قراءته.",
+ "Signature could not get checked. Please contact the app developer and check your admin screen." : "لم يتم التحقق من التوقيع. فضلاً اتصل بمطوّر التطبيق و تحقق من شاشة الإدارة في حسابك.",
+ "App can't be installed because of not allowed code in the App" : "لم يتم تثبيت التطبيق لوجود شفرة غير مسموح بها في التطبيق",
+ "App can't be installed because it is not compatible with this version of ownCloud" : "لم يتم تثبيت التطبيق لأنه غير متوافق مع هذا الإصدار من ownCloud",
+ "App can't be installed because it contains the <shipped>true</shipped> tag which is not allowed for non shipped apps" : "لم يتم تثبيت التطبيق لأن به علامة <shipped>true</shipped> التي لايسمح بها في التطبيقات غير المشحونة",
+ "App can't be installed because the version in info.xml is not the same as the version reported from the app store" : "لم يتم تثبيت التطبيق لأن الإصدار في info.xml مختلف عن الإصدار المذكور في متجر التطبيقات",
+ "%s enter the database username and name." : "%s أدخِل اسم قاعدة البيانات واسم مستخدمها.",
"%s enter the database username." : "%s ادخل اسم المستخدم الخاص بقاعدة البيانات.",
"%s enter the database name." : "%s ادخل اسم فاعدة البيانات",
"%s you may not use dots in the database name" : "%s لا يسمح لك باستخدام نقطه (.) في اسم قاعدة البيانات",
@@ -23,9 +51,20 @@ OC.L10N.register(
"You need to enter either an existing account or the administrator." : "انت بحاجة لكتابة اسم مستخدم موجود أو حساب المدير.",
"Offending command was: \"%s\", name: %s, password: %s" : "الأمر المخالف كان : \"%s\", اسم المستخدم : %s, كلمة المرور: %s",
"PostgreSQL username and/or password not valid" : "اسم المستخدم / أو كلمة المرور الخاصة بـPostgreSQL غير صحيحة",
+ "Mac OS X is not supported and %s will not work properly on this platform. Use it at your own risk! " : "نظام ماك الإصدار X غير مدعوم و %s لن يعمل بشكل صحيح على هذه المنصة. استخدمه على مسؤوليتك!",
+ "For the best results, please consider using a GNU/Linux server instead." : "فضلاً ضع في الاعتبار استخدام نظام GNU/Linux بدل الأنظمة الأخرى للحصول على أفضل النتائج.",
+ "Please remove the open_basedir setting within your php.ini or switch to 64-bit PHP." : "فضلاً إحذف إعداد open_basedir من ملف php.ini لديك أو حوّل إلى PHP إصدار 64 بت.",
"Set an admin username." : "اعداد اسم مستخدم للمدير",
"Set an admin password." : "اعداد كلمة مرور للمدير",
+ "Can't create or write into the data directory %s" : "لا يمكن الإنشاء أو الكتابة في مجلد البيانات %s",
+ "Invalid Federated Cloud ID" : "معرّف سحابة الاتحاد غير صالح",
"%s shared »%s« with you" : "%s شارك »%s« معك",
+ "%s via %s" : "%s عبر %s",
+ "Sharing %s failed, because the file does not exist" : "فشلت مشاركة %s فالملف غير موجود",
+ "You are not allowed to share %s" : "أنت غير مسموح لك أن تشارك %s",
+ "Sharing %s failed, because you can not share with yourself" : "فشلت مشاركة %s لأنك لايمكنك المشاركة مع نفسك",
+ "Sharing %s failed, because the user %s does not exist" : "فشلت مشاركة %s لأن المستخدم %s غير موجود",
+ "Share type %s is not valid for %s" : "مشاركة النوع %s غير صالحة لـ %s",
"Could not find category \"%s\"" : "تعذر العثور على المجلد \"%s\"",
"Apps" : "التطبيقات",
"A valid username must be provided" : "يجب ادخال اسم مستخدم صحيح",
diff --git a/lib/l10n/ar.json b/lib/l10n/ar.json
index eb91606bb52..86284a03538 100644
--- a/lib/l10n/ar.json
+++ b/lib/l10n/ar.json
@@ -1,16 +1,44 @@
{ "translations": {
+ "Cannot write into \"config\" directory!" : "الكتابة في مجلد \"config\" غير ممكنة!",
+ "This can usually be fixed by giving the webserver write access to the config directory" : "يمكن حل هذا عادة بإعطاء خادم الوب صلاحية الكتابة في مجلد config",
+ "See %s" : "أنظر %s",
+ "Sample configuration detected" : "تم اكتشاف إعدادات عيّنة",
+ "PHP %s or higher is required." : "إصدار PHP %s أو أحدث منه مطلوب.",
+ "PHP with a version lower than %s is required." : "PHP الإصدار %s أو أقل مطلوب.",
+ "%sbit or higher PHP required." : "مكتبات PHP ذات %s بت أو أعلى مطلوبة.",
+ "Following databases are supported: %s" : "قواعد البيانات التالية مدعومة: %s",
+ "The command line tool %s could not be found" : "لم يتم العثور على أداة سطر الأوامر %s",
+ "The library %s is not available." : "مكتبة %s غير متوفرة.",
"Unknown filetype" : "نوع الملف غير معروف",
"Invalid image" : "الصورة غير صالحة",
"today" : "اليوم",
"yesterday" : "يوم أمس",
+ "_%n day ago_::_%n days ago_" : ["قبل ساعات","قبل يوم","قبل يومين","قبل %n يوماً","قبل %n يوماً","قبل %n يوماً"],
"last month" : "الشهر الماضي",
+ "_%n month ago_::_%n months ago_" : ["قبل عدة أيام","قبل شهر","قبل شهرين","قبل %n شهراً","قبل %n شهراً","قبل %n شهراً"],
"last year" : "السنةالماضية",
"seconds ago" : "منذ ثواني",
+ "Empty filename is not allowed" : "لا يسمح بأسماء فارغة للملفات",
+ "4-byte characters are not supported in file names" : "المحارف ذات 4 بايت غير مسموح بها في أسماء الملفات",
+ "File name is a reserved word" : "اسم الملف كلمة محجوزة",
+ "File name contains at least one invalid character" : "اسم الملف به ، على الأقل ، حرف غير صالح",
+ "File name is too long" : "اسم الملف طويل جداً",
"App directory already exists" : "مجلد التطبيق موجود مسبقا",
"Can't create app folder. Please fix permissions. %s" : "لا يمكن إنشاء مجلد التطبيق. يرجى تعديل الصلاحيات. %s",
+ "Archive does not contain a directory named %s" : "الأرشيف لا يحتوي مجلداً اسمه %s",
"No source specified when installing app" : "لم يتم تحديد المصدر عن تثبيت البرنامج",
+ "No href specified when installing app from http" : "لم يتم تحديد href عند تثبيت التطبيق من http",
+ "No path specified when installing app from local file" : "لم يتم تحديد مسار عند تثبيت التطبيق من ملف محلّي",
"Archives of type %s are not supported" : "الأرشيفات من نوع %s غير مدعومة",
+ "Failed to open archive when installing app" : "فشل فتح الأرشيف أثناء تثبيت التطبيق",
"App does not provide an info.xml file" : "التطبيق لا يتوفر على ملف info.xml",
+ "App cannot be installed because appinfo file cannot be read." : "لا يمكن تثبيت التطبيق لأن ملف appinfo غير ممكنة قراءته.",
+ "Signature could not get checked. Please contact the app developer and check your admin screen." : "لم يتم التحقق من التوقيع. فضلاً اتصل بمطوّر التطبيق و تحقق من شاشة الإدارة في حسابك.",
+ "App can't be installed because of not allowed code in the App" : "لم يتم تثبيت التطبيق لوجود شفرة غير مسموح بها في التطبيق",
+ "App can't be installed because it is not compatible with this version of ownCloud" : "لم يتم تثبيت التطبيق لأنه غير متوافق مع هذا الإصدار من ownCloud",
+ "App can't be installed because it contains the <shipped>true</shipped> tag which is not allowed for non shipped apps" : "لم يتم تثبيت التطبيق لأن به علامة <shipped>true</shipped> التي لايسمح بها في التطبيقات غير المشحونة",
+ "App can't be installed because the version in info.xml is not the same as the version reported from the app store" : "لم يتم تثبيت التطبيق لأن الإصدار في info.xml مختلف عن الإصدار المذكور في متجر التطبيقات",
+ "%s enter the database username and name." : "%s أدخِل اسم قاعدة البيانات واسم مستخدمها.",
"%s enter the database username." : "%s ادخل اسم المستخدم الخاص بقاعدة البيانات.",
"%s enter the database name." : "%s ادخل اسم فاعدة البيانات",
"%s you may not use dots in the database name" : "%s لا يسمح لك باستخدام نقطه (.) في اسم قاعدة البيانات",
@@ -21,9 +49,20 @@
"You need to enter either an existing account or the administrator." : "انت بحاجة لكتابة اسم مستخدم موجود أو حساب المدير.",
"Offending command was: \"%s\", name: %s, password: %s" : "الأمر المخالف كان : \"%s\", اسم المستخدم : %s, كلمة المرور: %s",
"PostgreSQL username and/or password not valid" : "اسم المستخدم / أو كلمة المرور الخاصة بـPostgreSQL غير صحيحة",
+ "Mac OS X is not supported and %s will not work properly on this platform. Use it at your own risk! " : "نظام ماك الإصدار X غير مدعوم و %s لن يعمل بشكل صحيح على هذه المنصة. استخدمه على مسؤوليتك!",
+ "For the best results, please consider using a GNU/Linux server instead." : "فضلاً ضع في الاعتبار استخدام نظام GNU/Linux بدل الأنظمة الأخرى للحصول على أفضل النتائج.",
+ "Please remove the open_basedir setting within your php.ini or switch to 64-bit PHP." : "فضلاً إحذف إعداد open_basedir من ملف php.ini لديك أو حوّل إلى PHP إصدار 64 بت.",
"Set an admin username." : "اعداد اسم مستخدم للمدير",
"Set an admin password." : "اعداد كلمة مرور للمدير",
+ "Can't create or write into the data directory %s" : "لا يمكن الإنشاء أو الكتابة في مجلد البيانات %s",
+ "Invalid Federated Cloud ID" : "معرّف سحابة الاتحاد غير صالح",
"%s shared »%s« with you" : "%s شارك »%s« معك",
+ "%s via %s" : "%s عبر %s",
+ "Sharing %s failed, because the file does not exist" : "فشلت مشاركة %s فالملف غير موجود",
+ "You are not allowed to share %s" : "أنت غير مسموح لك أن تشارك %s",
+ "Sharing %s failed, because you can not share with yourself" : "فشلت مشاركة %s لأنك لايمكنك المشاركة مع نفسك",
+ "Sharing %s failed, because the user %s does not exist" : "فشلت مشاركة %s لأن المستخدم %s غير موجود",
+ "Share type %s is not valid for %s" : "مشاركة النوع %s غير صالحة لـ %s",
"Could not find category \"%s\"" : "تعذر العثور على المجلد \"%s\"",
"Apps" : "التطبيقات",
"A valid username must be provided" : "يجب ادخال اسم مستخدم صحيح",
diff --git a/lib/l10n/ro.js b/lib/l10n/ro.js
index 5c5b7844cf7..de13cd796d6 100644
--- a/lib/l10n/ro.js
+++ b/lib/l10n/ro.js
@@ -29,6 +29,7 @@ OC.L10N.register(
"Module with id: %s does not exist. Please enable it in your apps settings or contact your administrator." : "Modulul cu id-ul %s nu există. Activează-l în setările tale de aplicație sau contactează-ți administratorul.",
"Empty filename is not allowed" : "Nu este permis fișier fără nume",
"Dot files are not allowed" : "Fișierele care încep cu caracterul punct nu sunt permise",
+ "4-byte characters are not supported in file names" : "Caracterele stocate în 4 octeți nu sunt suportate în denumirile fișierelor",
"File name is a reserved word" : "Numele fișierului este un cuvânt rezervat",
"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",
@@ -36,6 +37,8 @@ OC.L10N.register(
"Can't create app folder. Please fix permissions. %s" : "Nu se poate crea directorul de aplicație. Repară permisiunile. %s",
"Archive does not contain a directory named %s" : "Arhiva nu conține vreun director cu numele %s",
"No source specified when installing app" : "Nu a fost specificată vreo sursă la instalarea aplicației",
+ "No href specified when installing app from http" : "Nu s-a specificat adresa la instalarea aplicației dintr-o sursă de pe Internet",
+ "No path specified when installing app from local file" : "Nu s-a specificat vreo cale la instalarea aplicației de pe un fișier local",
"Archives of type %s are not supported" : "Arhivele de tip %s nu sunt suportate",
"Failed to open archive when installing app" : "Deschiderea arhivei a eșuat în timpul instalării aplicației",
"App does not provide an info.xml file" : "Aplicația nu furnizează un fișier info.xml",
@@ -51,10 +54,12 @@ OC.L10N.register(
"DB Error: \"%s\"" : "Eroare Bază de Date: \"%s\"",
"Offending command was: \"%s\"" : "Comanda cauză a fost: \"%s\"",
"PostgreSQL username and/or password not valid" : "Nume utilizator și/sau parolă PostgreSQL greșită",
+ "For the best results, please consider using a GNU/Linux server instead." : "Pentru cele mai bune rezultate, ia în calcul folosirea unui server care rulează un sistem de operare GNU/Linux.",
"Set an admin username." : "Setează un nume de administrator.",
"Set an admin password." : "Setează o parolă de administrator.",
"Invalid Federated Cloud ID" : "ID invalid cloud federalizat",
"%s shared »%s« with you" : "%s Partajat »%s« cu tine de",
+ "%s via %s" : "%s via %s",
"You are not allowed to share %s" : "Nu există permisiunea de partajare %s",
"Sharing %s failed, because this item is already shared with %s" : "Partajarea %s a eșuat deoarece acest element este deja partajat cu %s",
"Not allowed to create a federated share with the same user" : "Nu este permisă crearea unei partajări federalizate cu acelaşi utilizator",
diff --git a/lib/l10n/ro.json b/lib/l10n/ro.json
index 566f6d01123..9149650a015 100644
--- a/lib/l10n/ro.json
+++ b/lib/l10n/ro.json
@@ -27,6 +27,7 @@
"Module with id: %s does not exist. Please enable it in your apps settings or contact your administrator." : "Modulul cu id-ul %s nu există. Activează-l în setările tale de aplicație sau contactează-ți administratorul.",
"Empty filename is not allowed" : "Nu este permis fișier fără nume",
"Dot files are not allowed" : "Fișierele care încep cu caracterul punct nu sunt permise",
+ "4-byte characters are not supported in file names" : "Caracterele stocate în 4 octeți nu sunt suportate în denumirile fișierelor",
"File name is a reserved word" : "Numele fișierului este un cuvânt rezervat",
"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",
@@ -34,6 +35,8 @@
"Can't create app folder. Please fix permissions. %s" : "Nu se poate crea directorul de aplicație. Repară permisiunile. %s",
"Archive does not contain a directory named %s" : "Arhiva nu conține vreun director cu numele %s",
"No source specified when installing app" : "Nu a fost specificată vreo sursă la instalarea aplicației",
+ "No href specified when installing app from http" : "Nu s-a specificat adresa la instalarea aplicației dintr-o sursă de pe Internet",
+ "No path specified when installing app from local file" : "Nu s-a specificat vreo cale la instalarea aplicației de pe un fișier local",
"Archives of type %s are not supported" : "Arhivele de tip %s nu sunt suportate",
"Failed to open archive when installing app" : "Deschiderea arhivei a eșuat în timpul instalării aplicației",
"App does not provide an info.xml file" : "Aplicația nu furnizează un fișier info.xml",
@@ -49,10 +52,12 @@
"DB Error: \"%s\"" : "Eroare Bază de Date: \"%s\"",
"Offending command was: \"%s\"" : "Comanda cauză a fost: \"%s\"",
"PostgreSQL username and/or password not valid" : "Nume utilizator și/sau parolă PostgreSQL greșită",
+ "For the best results, please consider using a GNU/Linux server instead." : "Pentru cele mai bune rezultate, ia în calcul folosirea unui server care rulează un sistem de operare GNU/Linux.",
"Set an admin username." : "Setează un nume de administrator.",
"Set an admin password." : "Setează o parolă de administrator.",
"Invalid Federated Cloud ID" : "ID invalid cloud federalizat",
"%s shared »%s« with you" : "%s Partajat »%s« cu tine de",
+ "%s via %s" : "%s via %s",
"You are not allowed to share %s" : "Nu există permisiunea de partajare %s",
"Sharing %s failed, because this item is already shared with %s" : "Partajarea %s a eșuat deoarece acest element este deja partajat cu %s",
"Not allowed to create a federated share with the same user" : "Nu este permisă crearea unei partajări federalizate cu acelaşi utilizator",
diff --git a/lib/private/AllConfig.php b/lib/private/AllConfig.php
index 6e99e1ac268..e082cea3305 100644
--- a/lib/private/AllConfig.php
+++ b/lib/private/AllConfig.php
@@ -154,7 +154,7 @@ class AllConfig implements \OCP\IConfig {
*
* @param string $appName the appName that we want to store the value under
* @param string $key the key of the value, under which will be saved
- * @param string $value the value that should be stored
+ * @param string|float|int $value the value that should be stored
*/
public function setAppValue($appName, $key, $value) {
\OC::$server->getAppConfig()->setValue($appName, $key, $value);
@@ -198,11 +198,16 @@ class AllConfig implements \OCP\IConfig {
* @param string $userId the userId of the user that we want to store the value under
* @param string $appName the appName that we want to store the value under
* @param string $key the key under which the value is being stored
- * @param string $value the value that you want to store
+ * @param string|float|int $value the value that you want to store
* @param string $preCondition only update if the config value was previously the value passed as $preCondition
* @throws \OCP\PreConditionNotMetException if a precondition is specified and is not met
+ * @throws \UnexpectedValueException when trying to store an unexpected value
*/
public function setUserValue($userId, $appName, $key, $value, $preCondition = null) {
+ if (!is_int($value) && !is_float($value) && !is_string($value)) {
+ throw new \UnexpectedValueException('Only integers, floats and strings are allowed as value');
+ }
+
// TODO - FIXME
$this->fixDIInit();
diff --git a/lib/private/AppConfig.php b/lib/private/AppConfig.php
index 24542152ffc..f84c8a41f17 100644
--- a/lib/private/AppConfig.php
+++ b/lib/private/AppConfig.php
@@ -143,7 +143,7 @@ class AppConfig implements IAppConfig {
*
* @param string $app app
* @param string $key key
- * @param string $value value
+ * @param string|float|int $value value
* @return bool True if the value was inserted or updated, false if the value was the same
*/
public function setValue($app, $key, $value) {
diff --git a/lib/private/Authentication/Token/DefaultTokenCleanupJob.php b/lib/private/Authentication/Token/DefaultTokenCleanupJob.php
index 04b98c6c5a0..7746d6be915 100644
--- a/lib/private/Authentication/Token/DefaultTokenCleanupJob.php
+++ b/lib/private/Authentication/Token/DefaultTokenCleanupJob.php
@@ -28,6 +28,7 @@ class DefaultTokenCleanupJob extends Job {
protected function run($argument) {
/* @var $provider DefaultTokenProvider */
+ // TODO: add OC\Authentication\Token\IProvider::invalidateOldTokens and query interface
$provider = OC::$server->query('OC\Authentication\Token\DefaultTokenProvider');
$provider->invalidateOldTokens();
}
diff --git a/lib/private/Encryption/DecryptAll.php b/lib/private/Encryption/DecryptAll.php
index 8676bc09575..34a3e1bff91 100644
--- a/lib/private/Encryption/DecryptAll.php
+++ b/lib/private/Encryption/DecryptAll.php
@@ -80,7 +80,7 @@ class DecryptAll {
$this->input = $input;
$this->output = $output;
- if (!empty($user) && $this->userManager->userExists($user) === false) {
+ if ($user !== '' && $this->userManager->userExists($user) === false) {
$this->output->writeln('User "' . $user . '" does not exist. Please check the username and try again');
return false;
}
@@ -141,7 +141,7 @@ class DecryptAll {
$this->output->writeln("\n");
$userList = [];
- if (empty($user)) {
+ if ($user === '') {
$fetchUsersProgress = new ProgressBar($this->output);
$fetchUsersProgress->setFormat(" %message% \n [%bar%]");
diff --git a/lib/private/Files/Cache/Scanner.php b/lib/private/Files/Cache/Scanner.php
index e6bd118d5a5..c17f9bfd51b 100644
--- a/lib/private/Files/Cache/Scanner.php
+++ b/lib/private/Files/Cache/Scanner.php
@@ -38,6 +38,7 @@ use OC\Files\Filesystem;
use OC\Hooks\BasicEmitter;
use OCP\Config;
use OCP\Files\Cache\IScanner;
+use OCP\Files\ForbiddenException;
use OCP\Files\Storage\ILockingStorage;
use OCP\Lock\ILockingProvider;
@@ -140,7 +141,11 @@ class Scanner extends BasicEmitter implements IScanner {
}
}
- $data = $this->getData($file);
+ try {
+ $data = $this->getData($file);
+ } catch (ForbiddenException $e) {
+ return null;
+ }
if ($data) {
diff --git a/lib/private/Files/Cache/Updater.php b/lib/private/Files/Cache/Updater.php
index 820941abae1..4e17c4d778d 100644
--- a/lib/private/Files/Cache/Updater.php
+++ b/lib/private/Files/Cache/Updater.php
@@ -231,7 +231,10 @@ class Updater implements IUpdater {
$parentId = $this->cache->getParentId($internalPath);
$parent = dirname($internalPath);
if ($parentId != -1) {
- $this->cache->update($parentId, array('storage_mtime' => $this->storage->filemtime($parent)));
+ $mtime = $this->storage->filemtime($parent);
+ if ($mtime !== false) {
+ $this->cache->update($parentId, array('storage_mtime' => $mtime));
+ }
}
}
}
diff --git a/lib/private/Files/Storage/Common.php b/lib/private/Files/Storage/Common.php
index cec6a42a2c0..0c1b69108d4 100644
--- a/lib/private/Files/Storage/Common.php
+++ b/lib/private/Files/Storage/Common.php
@@ -643,6 +643,9 @@ abstract class Common implements Storage, ILockingStorage {
$data = [];
$data['mimetype'] = $this->getMimeType($path);
$data['mtime'] = $this->filemtime($path);
+ if ($data['mtime'] === false) {
+ $data['mtime'] = time();
+ }
if ($data['mimetype'] == 'httpd/unix-directory') {
$data['size'] = -1; //unknown
} else {
diff --git a/lib/private/Files/Storage/Local.php b/lib/private/Files/Storage/Local.php
index b07e26a3358..005b5f9ab91 100644
--- a/lib/private/Files/Storage/Local.php
+++ b/lib/private/Files/Storage/Local.php
@@ -33,20 +33,31 @@
*/
namespace OC\Files\Storage;
+
+use OCP\Files\ForbiddenException;
+
/**
* for local filestore, we only have to map the paths
*/
class Local extends \OC\Files\Storage\Common {
protected $datadir;
+ protected $dataDirLength;
+
+ protected $allowSymlinks = false;
+
+ protected $realDataDir;
+
public function __construct($arguments) {
if (!isset($arguments['datadir']) || !is_string($arguments['datadir'])) {
throw new \InvalidArgumentException('No data directory set for local storage');
}
$this->datadir = $arguments['datadir'];
+ $this->realDataDir = rtrim(realpath($this->datadir), '/') . '/';
if (substr($this->datadir, -1) !== '/') {
$this->datadir .= '/';
}
+ $this->dataDirLength = strlen($this->realDataDir);
}
public function __destruct() {
@@ -157,7 +168,7 @@ class Local extends \OC\Files\Storage\Common {
public function filemtime($path) {
clearstatcache($this->getSourcePath($path));
- return filemtime($this->getSourcePath($path));
+ return $this->file_exists($path) ? filemtime($this->getSourcePath($path)) : false;
}
public function touch($path, $mtime = null) {
@@ -188,7 +199,7 @@ class Local extends \OC\Files\Storage\Common {
return '';
}
- $handle = fopen($fileName,'rb');
+ $handle = fopen($fileName, 'rb');
$content = fread($handle, $fileSize);
fclose($handle);
return $content;
@@ -337,10 +348,27 @@ class Local extends \OC\Files\Storage\Common {
*
* @param string $path
* @return string
+ * @throws ForbiddenException
*/
public function getSourcePath($path) {
$fullPath = $this->datadir . $path;
- return $fullPath;
+ if ($this->allowSymlinks || $path === '') {
+ return $fullPath;
+ }
+ $pathToResolve = $fullPath;
+ $realPath = realpath($pathToResolve);
+ while ($realPath === false) { // for non existing files check the parent directory
+ $pathToResolve = dirname($pathToResolve);
+ $realPath = realpath($pathToResolve);
+ }
+ if ($realPath) {
+ $realPath = $realPath . '/';
+ }
+ if (substr($realPath, 0, $this->dataDirLength) === $this->realDataDir) {
+ return $fullPath;
+ } else {
+ throw new ForbiddenException("Following symlinks is not allowed ('$fullPath' -> '$realPath' not inside '{$this->realDataDir}')", false);
+ }
}
/**
@@ -377,7 +405,7 @@ class Local extends \OC\Files\Storage\Common {
* @return bool
*/
public function copyFromStorage(\OCP\Files\Storage $sourceStorage, $sourceInternalPath, $targetInternalPath) {
- if($sourceStorage->instanceOfStorage('\OC\Files\Storage\Local')){
+ if ($sourceStorage->instanceOfStorage('\OC\Files\Storage\Local')) {
/**
* @var \OC\Files\Storage\Local $sourceStorage
*/
diff --git a/lib/private/Files/View.php b/lib/private/Files/View.php
index f738542ea8c..e9daa123470 100644
--- a/lib/private/Files/View.php
+++ b/lib/private/Files/View.php
@@ -337,10 +337,17 @@ class View {
return $this->removeMount($mount, $absolutePath);
}
if ($this->is_dir($path)) {
- return $this->basicOperation('rmdir', $path, array('delete'));
+ $result = $this->basicOperation('rmdir', $path, array('delete'));
} else {
- return false;
+ $result = false;
+ }
+
+ if (!$result && !$this->file_exists($path)) { //clear ghost files from the cache on delete
+ $storage = $mount->getStorage();
+ $internalPath = $mount->getInternalPath($absolutePath);
+ $storage->getUpdater()->remove($internalPath);
}
+ return $result;
}
/**
@@ -429,7 +436,7 @@ class View {
/**
* @param string $path
- * @param int $from
+ * @param int $from
* @param int $to
* @return bool|mixed
* @throws \OCP\Files\InvalidPathException
@@ -441,18 +448,18 @@ class View {
$handle = $this->fopen($path, 'rb');
if ($handle) {
if (fseek($handle, $from) === 0) {
- $chunkSize = 8192; // 8 kB chunks
- $end = $to + 1;
- while (!feof($handle) && ftell($handle) < $end) {
- $len = $end-ftell($handle);
- if ($len > $chunkSize) {
- $len = $chunkSize;
+ $chunkSize = 8192; // 8 kB chunks
+ $end = $to + 1;
+ while (!feof($handle) && ftell($handle) < $end) {
+ $len = $end - ftell($handle);
+ if ($len > $chunkSize) {
+ $len = $chunkSize;
+ }
+ echo fread($handle, $len);
+ flush();
}
- echo fread($handle, $len);
- flush();
- }
- $size = ftell($handle) - $from;
- return $size;
+ $size = ftell($handle) - $from;
+ return $size;
}
throw new \OCP\Files\UnseekableException('fseek error');
@@ -679,7 +686,15 @@ class View {
if ($mount and $mount->getInternalPath($absolutePath) === '') {
return $this->removeMount($mount, $absolutePath);
}
- return $this->basicOperation('unlink', $path, array('delete'));
+ $result = $this->basicOperation('unlink', $path, array('delete'));
+ if (!$result && !$this->file_exists($path)) { //clear ghost files from the cache on delete
+ $storage = $mount->getStorage();
+ $internalPath = $mount->getInternalPath($absolutePath);
+ $storage->getUpdater()->remove($internalPath);
+ return true;
+ } else {
+ return $result;
+ }
}
/**
diff --git a/lib/private/legacy/helper.php b/lib/private/legacy/helper.php
index f107d47faf7..21fb3cbc5ab 100644
--- a/lib/private/legacy/helper.php
+++ b/lib/private/legacy/helper.php
@@ -206,7 +206,9 @@ class OC_Helper {
foreach ($files as $fileInfo) {
/** @var SplFileInfo $fileInfo */
- if ($fileInfo->isDir()) {
+ if ($fileInfo->isLink()) {
+ unlink($fileInfo->getPathname());
+ } else if ($fileInfo->isDir()) {
rmdir($fileInfo->getRealPath());
} else {
unlink($fileInfo->getRealPath());
diff --git a/lib/public/IAppConfig.php b/lib/public/IAppConfig.php
index 1406e8a56d6..22fcdbbb205 100644
--- a/lib/public/IAppConfig.php
+++ b/lib/public/IAppConfig.php
@@ -88,7 +88,7 @@ interface IAppConfig {
* sets a value in the appconfig
* @param string $app app
* @param string $key key
- * @param string $value value
+ * @param string|float|int $value value
* @deprecated 8.0.0 use method setAppValue of \OCP\IConfig
*
* Sets a value. If the key did not exist before it will be created.
diff --git a/lib/public/IConfig.php b/lib/public/IConfig.php
index 9e5024545b3..05158e9063e 100644
--- a/lib/public/IConfig.php
+++ b/lib/public/IConfig.php
@@ -104,7 +104,7 @@ interface IConfig {
* Writes a new app wide value
*
* @param string $appName the appName that we want to store the value under
- * @param string $key the key of the value, under which will be saved
+ * @param string|float|int $key the key of the value, under which will be saved
* @param string $value the value that should be stored
* @return void
* @since 6.0.0
@@ -149,6 +149,7 @@ interface IConfig {
* @param string $value the value that you want to store
* @param string $preCondition only update if the config value was previously the value passed as $preCondition
* @throws \OCP\PreConditionNotMetException if a precondition is specified and is not met
+ * @throws \UnexpectedValueException when trying to store an unexpected value
* @since 6.0.0 - parameter $precondition was added in 8.0.0
*/
public function setUserValue($userId, $appName, $key, $value, $preCondition = null);
diff --git a/settings/js/users/users.js b/settings/js/users/users.js
index 03f471bc3b4..e0fccbd9539 100644
--- a/settings/js/users/users.js
+++ b/settings/js/users/users.js
@@ -14,7 +14,7 @@ var UserList = {
availableGroups: [],
offset: 0,
usersToLoad: 10, //So many users will be loaded when user scrolls down
- initialUsersToLoad: 250, //initial number of users to load
+ initialUsersToLoad: 50, //initial number of users to load
currentGid: '',
filter: '',
diff --git a/tests/Core/Controller/TokenControllerTest.php b/tests/Core/Controller/TokenControllerTest.php
index 386140a8a4f..b6b54b14fad 100644
--- a/tests/Core/Controller/TokenControllerTest.php
+++ b/tests/Core/Controller/TokenControllerTest.php
@@ -23,8 +23,9 @@
namespace Tests\Core\Controller;
use OC\AppFramework\Http;
+use OC\Authentication\Token\IToken;
use OC\Core\Controller\TokenController;
-use OCP\AppFramework\Http\Response;
+use OCP\AppFramework\Http\JSONResponse;
use Test\TestCase;
class TokenControllerTest extends TestCase {
@@ -34,6 +35,7 @@ class TokenControllerTest extends TestCase {
private $request;
private $userManager;
private $tokenProvider;
+ private $twoFactorAuthManager;
private $secureRandom;
protected function setUp() {
@@ -43,17 +45,17 @@ class TokenControllerTest extends TestCase {
$this->userManager = $this->getMockBuilder('\OC\User\Manager')
->disableOriginalConstructor()
->getMock();
- $this->tokenProvider = $this->getMockBuilder('\OC\Authentication\Token\DefaultTokenProvider')
+ $this->tokenProvider = $this->getMock('\OC\Authentication\Token\IProvider');
+ $this->twoFactorAuthManager = $this->getMockBuilder('\OC\Authentication\TwoFactorAuth\Manager')
->disableOriginalConstructor()
->getMock();
$this->secureRandom = $this->getMock('\OCP\Security\ISecureRandom');
- $this->tokenController = new TokenController('core', $this->request, $this->userManager, $this->tokenProvider,
- $this->secureRandom);
+ $this->tokenController = new TokenController('core', $this->request, $this->userManager, $this->tokenProvider, $this->twoFactorAuthManager, $this->secureRandom);
}
public function testWithoutCredentials() {
- $expected = new Response();
+ $expected = new JSONResponse();
$expected->setStatus(Http::STATUS_UNPROCESSABLE_ENTITY);
$actual = $this->tokenController->generateToken(null, null);
@@ -66,7 +68,7 @@ class TokenControllerTest extends TestCase {
->method('checkPassword')
->with('john', 'passme')
->will($this->returnValue(false));
- $expected = new Response();
+ $expected = new JSONResponse();
$expected->setStatus(Http::STATUS_UNAUTHORIZED);
$actual = $this->tokenController->generateToken('john', 'passme');
@@ -83,13 +85,17 @@ class TokenControllerTest extends TestCase {
$user->expects($this->once())
->method('getUID')
->will($this->returnValue('john'));
+ $this->twoFactorAuthManager->expects($this->once())
+ ->method('isTwoFactorAuthenticated')
+ ->with($user)
+ ->will($this->returnValue(false));
$this->secureRandom->expects($this->once())
->method('generate')
->with(128)
->will($this->returnValue('verysecurerandomtoken'));
$this->tokenProvider->expects($this->once())
->method('generateToken')
- ->with('verysecurerandomtoken', 'john', 'john', '123456', 'unknown client', \OC\Authentication\Token\IToken::PERMANENT_TOKEN);
+ ->with('verysecurerandomtoken', 'john', 'john', '123456', 'unknown client', IToken::PERMANENT_TOKEN);
$expected = [
'token' => 'verysecurerandomtoken'
];
@@ -99,4 +105,24 @@ class TokenControllerTest extends TestCase {
$this->assertEquals($expected, $actual);
}
+ public function testWithValidCredentialsBut2faEnabled() {
+ $user = $this->getMock('\OCP\IUser');
+ $this->userManager->expects($this->once())
+ ->method('checkPassword')
+ ->with('john', '123456')
+ ->will($this->returnValue($user));
+ $this->twoFactorAuthManager->expects($this->once())
+ ->method('isTwoFactorAuthenticated')
+ ->with($user)
+ ->will($this->returnValue(true));
+ $this->secureRandom->expects($this->never())
+ ->method('generate');
+ $expected = new JSONResponse();
+ $expected->setStatus(Http::STATUS_UNAUTHORIZED);
+
+ $actual = $this->tokenController->generateToken('john', '123456');
+
+ $this->assertEquals($expected, $actual);
+ }
+
}
diff --git a/tests/Core/Controller/TwoFactorChallengeControllerTest.php b/tests/Core/Controller/TwoFactorChallengeControllerTest.php
index 2da6dcd52ac..08d8dd1452c 100644
--- a/tests/Core/Controller/TwoFactorChallengeControllerTest.php
+++ b/tests/Core/Controller/TwoFactorChallengeControllerTest.php
@@ -33,7 +33,7 @@ class TwoFactorChallengeControllerTest extends TestCase {
private $session;
private $urlGenerator;
- /** TwoFactorChallengeController */
+ /** @var TwoFactorChallengeController|\PHPUnit_Framework_MockObject_MockObject */
private $controller;
protected function setUp() {
@@ -47,9 +47,20 @@ class TwoFactorChallengeControllerTest extends TestCase {
$this->session = $this->getMock('\OCP\ISession');
$this->urlGenerator = $this->getMock('\OCP\IURLGenerator');
- $this->controller = new TwoFactorChallengeController(
- 'core', $this->request, $this->twoFactorManager, $this->userSession, $this->session, $this->urlGenerator
- );
+ $this->controller = $this->getMockBuilder('OC\Core\Controller\TwoFactorChallengeController')
+ ->setConstructorArgs([
+ 'core',
+ $this->request,
+ $this->twoFactorManager,
+ $this->userSession,
+ $this->session,
+ $this->urlGenerator,
+ ])
+ ->setMethods(['getLogoutAttribute'])
+ ->getMock();
+ $this->controller->expects($this->any())
+ ->method('getLogoutAttribute')
+ ->willReturn('logoutAttribute');
}
public function testSelectChallenge() {
@@ -70,6 +81,7 @@ class TwoFactorChallengeControllerTest extends TestCase {
$expected = new \OCP\AppFramework\Http\TemplateResponse('core', 'twofactorselectchallenge', [
'providers' => $providers,
'redirect_url' => '/some/url',
+ 'logout_attribute' => 'logoutAttribute',
], 'guest');
$this->assertEquals($expected, $this->controller->selectChallenge('/some/url'));
@@ -110,6 +122,7 @@ class TwoFactorChallengeControllerTest extends TestCase {
$expected = new \OCP\AppFramework\Http\TemplateResponse('core', 'twofactorshowchallenge', [
'error' => true,
'provider' => $provider,
+ 'logout_attribute' => 'logoutAttribute',
'template' => '<html/>',
], 'guest');
diff --git a/tests/lib/AllConfigTest.php b/tests/lib/AllConfigTest.php
index 4f8b0658b80..3d0a9cb0827 100644
--- a/tests/lib/AllConfigTest.php
+++ b/tests/lib/AllConfigTest.php
@@ -123,6 +123,25 @@ class AllConfigTest extends \Test\TestCase {
$config->deleteUserValue('userPreCond', 'appPreCond', 'keyPreCond');
}
+ public function dataSetUserValueUnexpectedValue() {
+ return [
+ [true],
+ [false],
+ [null],
+ [new \stdClass()],
+ ];
+ }
+
+ /**
+ * @dataProvider dataSetUserValueUnexpectedValue
+ * @param mixed $value
+ * @expectedException \UnexpectedValueException
+ */
+ public function testSetUserValueUnexpectedValue($value) {
+ $config = $this->getConfig();
+ $config->setUserValue('userSetBool', 'appSetBool', 'keySetBool', $value);
+ }
+
/**
* @expectedException \OCP\PreConditionNotMetException
*/
diff --git a/tests/lib/Encryption/DecryptAllTest.php b/tests/lib/Encryption/DecryptAllTest.php
index ffcbbc74a99..d7cf2fb7baf 100644
--- a/tests/lib/Encryption/DecryptAllTest.php
+++ b/tests/lib/Encryption/DecryptAllTest.php
@@ -86,13 +86,25 @@ class DecryptAllTest extends TestCase {
$this->invokePrivate($this->instance, 'output', [$this->outputInterface]);
}
+ public function dataDecryptAll() {
+ return [
+ [true, 'user1', true],
+ [false, 'user1', true],
+ [true, '0', true],
+ [false, '0', true],
+ [true, '', false],
+ ];
+ }
+
/**
- * @dataProvider dataTrueFalse
+ * @dataProvider dataDecryptAll
* @param bool $prepareResult
+ * @param string $user
+ * @param bool $userExistsChecked
*/
- public function testDecryptAll($prepareResult, $user) {
+ public function testDecryptAll($prepareResult, $user, $userExistsChecked) {
- if (!empty($user)) {
+ if ($userExistsChecked) {
$this->userManager->expects($this->once())->method('userExists')->willReturn(true);
} else {
$this->userManager->expects($this->never())->method('userExists');
@@ -125,15 +137,6 @@ class DecryptAllTest extends TestCase {
$instance->decryptAll($this->inputInterface, $this->outputInterface, $user);
}
- public function dataTrueFalse() {
- return [
- [true, 'user1'],
- [false, 'user1'],
- [true, ''],
- [true, null]
- ];
- }
-
/**
* test decrypt all call with a user who doesn't exists
*/
@@ -147,8 +150,16 @@ class DecryptAllTest extends TestCase {
);
}
+ public function dataTrueFalse() {
+ return [
+ [true],
+ [false],
+ ];
+ }
+
/**
* @dataProvider dataTrueFalse
+ * @param bool $success
*/
public function testPrepareEncryptionModules($success) {
diff --git a/tests/lib/Files/Storage/LocalTest.php b/tests/lib/Files/Storage/LocalTest.php
index 7b8ae6a24b2..cca4d6a6676 100644
--- a/tests/lib/Files/Storage/LocalTest.php
+++ b/tests/lib/Files/Storage/LocalTest.php
@@ -84,5 +84,36 @@ class LocalTest extends Storage {
public function testInvalidArgumentsNoArray() {
new \OC\Files\Storage\Local(null);
}
+
+ /**
+ * @expectedException \OCP\Files\ForbiddenException
+ */
+ public function testDisallowSymlinksOutsideDatadir() {
+ $subDir1 = $this->tmpDir . 'sub1';
+ $subDir2 = $this->tmpDir . 'sub2';
+ $sym = $this->tmpDir . 'sub1/sym';
+ mkdir($subDir1);
+ mkdir($subDir2);
+
+ symlink($subDir2, $sym);
+
+ $storage = new \OC\Files\Storage\Local(['datadir' => $subDir1]);
+
+ $storage->file_put_contents('sym/foo', 'bar');
+ }
+
+ public function testDisallowSymlinksInsideDatadir() {
+ $subDir1 = $this->tmpDir . 'sub1';
+ $subDir2 = $this->tmpDir . 'sub1/sub2';
+ $sym = $this->tmpDir . 'sub1/sym';
+ mkdir($subDir1);
+ mkdir($subDir2);
+
+ symlink($subDir2, $sym);
+
+ $storage = new \OC\Files\Storage\Local(['datadir' => $subDir1]);
+
+ $storage->file_put_contents('sym/foo', 'bar');
+ }
}
diff --git a/tests/lib/Files/ViewTest.php b/tests/lib/Files/ViewTest.php
index 2c27bb64a70..59b17b83958 100644
--- a/tests/lib/Files/ViewTest.php
+++ b/tests/lib/Files/ViewTest.php
@@ -2417,7 +2417,7 @@ class ViewTest extends \Test\TestCase {
$content = $view->getDirectoryContent('', $filter);
- $files = array_map(function(FileInfo $info) {
+ $files = array_map(function (FileInfo $info) {
return $info->getName();
}, $content);
sort($files);
@@ -2444,4 +2444,53 @@ class ViewTest extends \Test\TestCase {
$data = $view->getFileInfo('.');
$this->assertEquals('', $data->getChecksum());
}
+
+ public function testDeleteGhostFile() {
+ $storage = new Temporary(array());
+ $scanner = $storage->getScanner();
+ $cache = $storage->getCache();
+ $storage->file_put_contents('foo.txt', 'bar');
+ \OC\Files\Filesystem::mount($storage, array(), '/test/');
+ $scanner->scan('');
+
+ $storage->unlink('foo.txt');
+
+ $this->assertTrue($cache->inCache('foo.txt'));
+
+ $view = new \OC\Files\View('/test');
+ $rootInfo = $view->getFileInfo('');
+ $this->assertEquals(3, $rootInfo->getSize());
+ $view->unlink('foo.txt');
+ $newInfo = $view->getFileInfo('');
+
+ $this->assertFalse($cache->inCache('foo.txt'));
+ $this->assertNotEquals($rootInfo->getEtag(), $newInfo->getEtag());
+ $this->assertEquals(0, $newInfo->getSize());
+ }
+
+ public function testDeleteGhostFolder() {
+ $storage = new Temporary(array());
+ $scanner = $storage->getScanner();
+ $cache = $storage->getCache();
+ $storage->mkdir('foo');
+ $storage->file_put_contents('foo/foo.txt', 'bar');
+ \OC\Files\Filesystem::mount($storage, array(), '/test/');
+ $scanner->scan('');
+
+ $storage->rmdir('foo');
+
+ $this->assertTrue($cache->inCache('foo'));
+ $this->assertTrue($cache->inCache('foo/foo.txt'));
+
+ $view = new \OC\Files\View('/test');
+ $rootInfo = $view->getFileInfo('');
+ $this->assertEquals(3, $rootInfo->getSize());
+ $view->rmdir('foo');
+ $newInfo = $view->getFileInfo('');
+
+ $this->assertFalse($cache->inCache('foo'));
+ $this->assertFalse($cache->inCache('foo/foo.txt'));
+ $this->assertNotEquals($rootInfo->getEtag(), $newInfo->getEtag());
+ $this->assertEquals(0, $newInfo->getSize());
+ }
}
diff --git a/version.php b/version.php
index 967f2690276..698636a2196 100644
--- a/version.php
+++ b/version.php
@@ -25,10 +25,10 @@
// We only can count up. The 4. digit is only for the internal patchlevel to trigger DB upgrades
// between betas, final and RCs. This is _not_ the public version number. Reset minor/patchlevel
// when updating major/minor version number.
-$OC_Version = array(9, 1, 0, 7);
+$OC_Version = array(9, 1, 0, 8);
// The human readable string
-$OC_VersionString = '9.1.0 beta 1';
+$OC_VersionString = '9.1.0 beta 2';
$OC_VersionCanBeUpgradedFrom = array(9, 0);