summaryrefslogtreecommitdiffstats
path: root/lib/private
diff options
context:
space:
mode:
authorSandro Lutz <sandro.lutz@temparus.ch>2017-02-07 00:12:45 +0100
committerSandro Lutz <sandro.lutz@temparus.ch>2017-02-07 00:15:30 +0100
commitfa1d607bfa951711a2c358f889db56962c179153 (patch)
tree904b6bd3b7f9d2ed133f64da22b3fb9bbfbf1842 /lib/private
parentff3fa538e43bb38a5ff142b07216b9de79645c01 (diff)
parentb55f5af7eaab6f827989407fa7b8d51cbb877eab (diff)
downloadnextcloud-server-fa1d607bfa951711a2c358f889db56962c179153.tar.gz
nextcloud-server-fa1d607bfa951711a2c358f889db56962c179153.zip
Merge remote-tracking branch 'nextcloud/master'
Signed-off-by: Sandro Lutz <sandro.lutz@temparus.ch>
Diffstat (limited to 'lib/private')
-rw-r--r--lib/private/AllConfig.php13
-rw-r--r--lib/private/App/AppManager.php15
-rw-r--r--lib/private/App/AppStore/Fetcher/AppFetcher.php22
-rw-r--r--lib/private/App/AppStore/Fetcher/CategoryFetcher.php8
-rw-r--r--lib/private/App/AppStore/Fetcher/Fetcher.php56
-rw-r--r--lib/private/AppConfig.php59
-rw-r--r--lib/private/AppFramework/Db/Db.php314
-rw-r--r--lib/private/AppFramework/DependencyInjection/DIContainer.php29
-rw-r--r--lib/private/AppFramework/Middleware/Security/SecurityMiddleware.php14
-rw-r--r--lib/private/AppFramework/Utility/ControllerMethodReflector.php23
-rw-r--r--lib/private/Archive/TAR.php19
-rw-r--r--lib/private/Archive/ZIP.php18
-rw-r--r--lib/private/Authentication/LoginCredentials/Credentials.php72
-rw-r--r--lib/private/Authentication/LoginCredentials/Store.php120
-rw-r--r--lib/private/Authentication/TwoFactorAuth/Manager.php45
-rw-r--r--lib/private/Console/TimestampFormatter.php4
-rw-r--r--lib/private/DB/QueryBuilder/ExpressionBuilder/ExpressionBuilder.php38
-rw-r--r--lib/private/Encryption/Keys/Storage.php35
-rw-r--r--lib/private/Files/Cache/Cache.php7
-rw-r--r--lib/private/Files/Config/UserMountCache.php7
-rw-r--r--lib/private/Files/Mount/LocalHomeMountProvider.php3
-rw-r--r--lib/private/Files/Node/File.php56
-rw-r--r--lib/private/Files/Node/Folder.php55
-rw-r--r--lib/private/Files/Node/Node.php80
-rw-r--r--lib/private/Files/Notify/Change.php65
-rw-r--r--lib/private/Files/Notify/RenameChange.php52
-rw-r--r--lib/private/Files/ObjectStore/ObjectStoreStorage.php21
-rw-r--r--lib/private/Files/Storage/Common.php10
-rw-r--r--lib/private/Files/Storage/DAV.php123
-rw-r--r--lib/private/Files/Storage/Home.php11
-rw-r--r--lib/private/Files/Storage/Local.php13
-rw-r--r--lib/private/Files/Stream/Close.php119
-rw-r--r--lib/private/Files/Stream/Dir.php67
-rw-r--r--lib/private/Files/Stream/Quota.php94
-rw-r--r--lib/private/Files/Stream/StaticStream.php171
-rw-r--r--lib/private/Files/Type/Detection.php4
-rw-r--r--lib/private/Files/Utils/Scanner.php21
-rw-r--r--lib/private/Files/View.php27
-rw-r--r--lib/private/Group/Backend.php15
-rw-r--r--lib/private/Group/Database.php5
-rw-r--r--lib/private/Group/Group.php14
-rw-r--r--lib/private/Group/Manager.php16
-rw-r--r--lib/private/Http/Client/Client.php11
-rw-r--r--lib/private/IntegrityCheck/Checker.php58
-rw-r--r--lib/private/IntegrityCheck/Helpers/FileAccessHelper.php29
-rw-r--r--lib/private/Log.php28
-rw-r--r--lib/private/Log/File.php4
-rw-r--r--lib/private/Log/Rotate.php4
-rw-r--r--lib/private/Mail/Mailer.php2
-rw-r--r--lib/private/Memcache/APC.php136
-rw-r--r--lib/private/Memcache/Factory.php6
-rw-r--r--lib/private/Memcache/Memcached.php27
-rw-r--r--lib/private/NavigationManager.php107
-rw-r--r--lib/private/Preview.php1349
-rw-r--r--lib/private/PreviewManager.php24
-rw-r--r--lib/private/Repair.php6
-rw-r--r--lib/private/Repair/AvatarPermissions.php116
-rw-r--r--lib/private/Repair/Collation.php12
-rw-r--r--lib/private/Repair/MoveUpdaterStepFile.php2
-rw-r--r--lib/private/Repair/NC11/FixMountStorages.php78
-rw-r--r--lib/private/Repair/NC11/MoveAvatarsBackgroundJob.php (renamed from lib/private/Repair/NC11/MoveAvatarBackgroundJob.php)0
-rw-r--r--lib/private/Repair/RepairInvalidShares.php24
-rw-r--r--lib/private/Repair/RepairLegacyStorages.php257
-rw-r--r--lib/private/Security/Bruteforce/Throttler.php13
-rw-r--r--lib/private/Security/CSP/ContentSecurityPolicyNonceManager.php2
-rw-r--r--lib/private/Security/CertificateManager.php20
-rw-r--r--lib/private/Server.php62
-rw-r--r--lib/private/Settings/Admin/Encryption.php8
-rw-r--r--lib/private/Settings/Manager.php199
-rw-r--r--lib/private/Settings/Mapper.php163
-rw-r--r--lib/private/Settings/Section.php21
-rw-r--r--lib/private/Setup.php8
-rw-r--r--lib/private/Setup/MySQL.php4
-rw-r--r--lib/private/Setup/PostgreSQL.php16
-rw-r--r--lib/private/Setup/Sqlite.php2
-rw-r--r--lib/private/Share20/DefaultShareProvider.php3
-rw-r--r--lib/private/Share20/Manager.php9
-rw-r--r--lib/private/SubAdmin.php2
-rw-r--r--lib/private/SystemConfig.php1
-rw-r--r--lib/private/Template/CSSResourceLocator.php52
-rwxr-xr-x[-rw-r--r--]lib/private/Template/ResourceLocator.php4
-rw-r--r--lib/private/Template/SCSSCacher.php190
-rw-r--r--lib/private/TemplateLayout.php51
-rw-r--r--lib/private/Updater.php6
-rw-r--r--lib/private/User/Database.php6
-rw-r--r--lib/private/User/Session.php7
-rw-r--r--lib/private/User/User.php10
-rw-r--r--lib/private/legacy/app.php48
-rw-r--r--lib/private/legacy/files.php1
-rw-r--r--lib/private/legacy/helper.php34
-rw-r--r--lib/private/legacy/user.php11
-rw-r--r--lib/private/legacy/util.php25
92 files changed, 1933 insertions, 3285 deletions
diff --git a/lib/private/AllConfig.php b/lib/private/AllConfig.php
index 4e13d70371b..1f59e677a54 100644
--- a/lib/private/AllConfig.php
+++ b/lib/private/AllConfig.php
@@ -68,7 +68,7 @@ class AllConfig implements \OCP\IConfig {
/**
* @param SystemConfig $systemConfig
*/
- function __construct(SystemConfig $systemConfig) {
+ public function __construct(SystemConfig $systemConfig) {
$this->userCache = new CappedMemoryCache();
$this->systemConfig = $systemConfig;
}
@@ -358,12 +358,17 @@ class AllConfig implements \OCP\IConfig {
* ]
*/
private function getUserValues($userId) {
- // TODO - FIXME
- $this->fixDIInit();
-
if (isset($this->userCache[$userId])) {
return $this->userCache[$userId];
}
+ if ($userId === null || $userId === '') {
+ $this->userCache[$userId]=array();
+ return $this->userCache[$userId];
+ }
+
+ // TODO - FIXME
+ $this->fixDIInit();
+
$data = array();
$query = 'SELECT `appid`, `configkey`, `configvalue` FROM `*PREFIX*preferences` WHERE `userid` = ?';
$result = $this->connection->executeQuery($query, array($userId));
diff --git a/lib/private/App/AppManager.php b/lib/private/App/AppManager.php
index fca5c9b87ac..6b819ef7ac1 100644
--- a/lib/private/App/AppManager.php
+++ b/lib/private/App/AppManager.php
@@ -221,6 +221,21 @@ class AppManager implements IAppManager {
}
/**
+ * Whether a list of types contains a protected app type
+ *
+ * @param string[] $types
+ * @return bool
+ */
+ public function hasProtectedAppType($types) {
+ if (empty($types)) {
+ return false;
+ }
+
+ $protectedTypes = array_intersect($this->protectedAppTypes, $types);
+ return !empty($protectedTypes);
+ }
+
+ /**
* Enable an app only for specific groups
*
* @param string $appId
diff --git a/lib/private/App/AppStore/Fetcher/AppFetcher.php b/lib/private/App/AppStore/Fetcher/AppFetcher.php
index bbe75c723d5..7c5efafc92f 100644
--- a/lib/private/App/AppStore/Fetcher/AppFetcher.php
+++ b/lib/private/App/AppStore/Fetcher/AppFetcher.php
@@ -28,9 +28,6 @@ use OCP\Http\Client\IClientService;
use OCP\IConfig;
class AppFetcher extends Fetcher {
- /** @var IConfig */
- private $config;
-
/**
* @param IAppData $appData
* @param IClientService $clientService
@@ -44,13 +41,13 @@ class AppFetcher extends Fetcher {
parent::__construct(
$appData,
$clientService,
- $timeFactory
+ $timeFactory,
+ $config
);
$this->fileName = 'apps.json';
- $this->config = $config;
- $versionArray = \OC_Util::getVersion();
+ $versionArray = explode('.', $this->config->getSystemValue('version'));
$this->endpointUrl = sprintf(
'https://apps.nextcloud.com/api/v1/platform/%d.%d.%d/apps.json',
$versionArray[0],
@@ -62,15 +59,14 @@ class AppFetcher extends Fetcher {
/**
* Only returns the latest compatible app release in the releases array
*
+ * @param string $ETag
+ * @param string $content
+ *
* @return array
*/
- protected function fetch() {
- $client = $this->clientService->newClient();
- $response = $client->get($this->endpointUrl);
- $responseJson = [];
- $responseJson['data'] = json_decode($response->getBody(), true);
- $responseJson['timestamp'] = $this->timeFactory->getTime();
- $response = $responseJson;
+ protected function fetch($ETag, $content) {
+ /** @var mixed[] $response */
+ $response = parent::fetch($ETag, $content);
$ncVersion = $this->config->getSystemValue('version');
$ncMajorVersion = explode('.', $ncVersion)[0];
diff --git a/lib/private/App/AppStore/Fetcher/CategoryFetcher.php b/lib/private/App/AppStore/Fetcher/CategoryFetcher.php
index 74201ec3737..8b79259a66a 100644
--- a/lib/private/App/AppStore/Fetcher/CategoryFetcher.php
+++ b/lib/private/App/AppStore/Fetcher/CategoryFetcher.php
@@ -24,20 +24,24 @@ namespace OC\App\AppStore\Fetcher;
use OCP\AppFramework\Utility\ITimeFactory;
use OCP\Files\IAppData;
use OCP\Http\Client\IClientService;
+use OCP\IConfig;
class CategoryFetcher extends Fetcher {
/**
* @param IAppData $appData
* @param IClientService $clientService
* @param ITimeFactory $timeFactory
+ * @param IConfig $config
*/
public function __construct(IAppData $appData,
IClientService $clientService,
- ITimeFactory $timeFactory) {
+ ITimeFactory $timeFactory,
+ IConfig $config) {
parent::__construct(
$appData,
$clientService,
- $timeFactory
+ $timeFactory,
+ $config
);
$this->fileName = 'categories.json';
$this->endpointUrl = 'https://apps.nextcloud.com/api/v1/categories.json';
diff --git a/lib/private/App/AppStore/Fetcher/Fetcher.php b/lib/private/App/AppStore/Fetcher/Fetcher.php
index f82e1a253f6..dab79e11821 100644
--- a/lib/private/App/AppStore/Fetcher/Fetcher.php
+++ b/lib/private/App/AppStore/Fetcher/Fetcher.php
@@ -21,10 +21,12 @@
namespace OC\App\AppStore\Fetcher;
+use OCP\AppFramework\Http;
use OCP\AppFramework\Utility\ITimeFactory;
use OCP\Files\IAppData;
use OCP\Files\NotFoundException;
use OCP\Http\Client\IClientService;
+use OCP\IConfig;
abstract class Fetcher {
const INVALIDATE_AFTER_SECONDS = 300;
@@ -35,6 +37,8 @@ abstract class Fetcher {
protected $clientService;
/** @var ITimeFactory */
protected $timeFactory;
+ /** @var IConfig */
+ protected $config;
/** @var string */
protected $fileName;
/** @var string */
@@ -44,26 +48,52 @@ abstract class Fetcher {
* @param IAppData $appData
* @param IClientService $clientService
* @param ITimeFactory $timeFactory
+ * @param IConfig $config
*/
public function __construct(IAppData $appData,
IClientService $clientService,
- ITimeFactory $timeFactory) {
+ ITimeFactory $timeFactory,
+ IConfig $config) {
$this->appData = $appData;
$this->clientService = $clientService;
$this->timeFactory = $timeFactory;
+ $this->config = $config;
}
/**
* Fetches the response from the server
*
+ * @param string $ETag
+ * @param string $content
+ *
* @return array
*/
- protected function fetch() {
+ protected function fetch($ETag, $content) {
+ $options = [];
+
+ if ($ETag !== '') {
+ $options['headers'] = [
+ 'If-None-Match' => $ETag,
+ ];
+ }
+
$client = $this->clientService->newClient();
- $response = $client->get($this->endpointUrl);
+ $response = $client->get($this->endpointUrl, $options);
+
$responseJson = [];
- $responseJson['data'] = json_decode($response->getBody(), true);
+ if ($response->getStatusCode() === Http::STATUS_NOT_MODIFIED) {
+ $responseJson['data'] = json_decode($content, true);
+ } else {
+ $responseJson['data'] = json_decode($response->getBody(), true);
+ $ETag = $response->getHeader('ETag');
+ }
+
$responseJson['timestamp'] = $this->timeFactory->getTime();
+ $responseJson['ncversion'] = $this->config->getSystemValue('version');
+ if ($ETag !== '') {
+ $responseJson['ETag'] = $ETag;
+ }
+
return $responseJson;
}
@@ -75,15 +105,27 @@ abstract class Fetcher {
public function get() {
$rootFolder = $this->appData->getFolder('/');
+ $ETag = '';
+ $content = '';
+
try {
// File does already exists
$file = $rootFolder->getFile($this->fileName);
$jsonBlob = json_decode($file->getContent(), true);
if(is_array($jsonBlob)) {
- // If the timestamp is older than 300 seconds request the files new
- if((int)$jsonBlob['timestamp'] > ($this->timeFactory->getTime() - self::INVALIDATE_AFTER_SECONDS)) {
+ /*
+ * If the timestamp is older than 300 seconds request the files new
+ * If the version changed (update!) also refresh
+ */
+ if((int)$jsonBlob['timestamp'] > ($this->timeFactory->getTime() - self::INVALIDATE_AFTER_SECONDS) &&
+ isset($jsonBlob['ncversion']) && $jsonBlob['ncversion'] === $this->config->getSystemValue('version', '0.0.0')) {
return $jsonBlob['data'];
}
+
+ if (isset($jsonBlob['ETag'])) {
+ $ETag = $jsonBlob['ETag'];
+ $content = json_encode($jsonBlob['data']);
+ }
}
} catch (NotFoundException $e) {
// File does not already exists
@@ -92,7 +134,7 @@ abstract class Fetcher {
// Refresh the file content
try {
- $responseJson = $this->fetch();
+ $responseJson = $this->fetch($ETag, $content);
$file->putContent(json_encode($responseJson));
return json_decode($file->getContent(), true)['data'];
} catch (\Exception $e) {
diff --git a/lib/private/AppConfig.php b/lib/private/AppConfig.php
index d92e8965b5c..4e102522550 100644
--- a/lib/private/AppConfig.php
+++ b/lib/private/AppConfig.php
@@ -1,5 +1,6 @@
<?php
/**
+ * @copyright Copyright (c) 2017, Joas Schilling <coding@schilljs.com>
* @copyright Copyright (c) 2016, ownCloud, Inc.
*
* @author Arthur Schiwon <blizzz@arthur-schiwon.de>
@@ -29,7 +30,9 @@
namespace OC;
+use OC\DB\OracleConnection;
use OCP\IAppConfig;
+use OCP\IConfig;
use OCP\IDBConnection;
/**
@@ -37,12 +40,25 @@ use OCP\IDBConnection;
* database.
*/
class AppConfig implements IAppConfig {
- /**
- * @var \OCP\IDBConnection $conn
- */
+
+ /** @var array[] */
+ protected $sensitiveValues = [
+ 'spreed' => [
+ 'turn_server_secret',
+ ],
+ 'user_ldap' => [
+ 'ldap_agent_password',
+ ],
+ ];
+
+ /** @var \OCP\IDBConnection */
protected $conn;
- private $cache = array();
+ /** @var array[] */
+ private $cache = [];
+
+ /** @var bool */
+ private $configLoaded = false;
/**
* @param IDBConnection $conn
@@ -85,6 +101,7 @@ class AppConfig implements IAppConfig {
*
* @param string $app the app we are looking for
* @return array an array of key names
+ * @deprecated 8.0.0 use method getAppKeys of \OCP\IConfig
*
* This function gets all keys of an app. Please note that the values are
* not returned.
@@ -112,6 +129,7 @@ class AppConfig implements IAppConfig {
* @param string $key key
* @param string $default = null, default value if the key does not exist
* @return string the value or $default
+ * @deprecated 8.0.0 use method getAppValue of \OCP\IConfig
*
* This function gets a value from the appconfig table. If the key does
* not exist the default value will be returned
@@ -146,6 +164,7 @@ class AppConfig implements IAppConfig {
* @param string $key key
* @param string|float|int $value value
* @return bool True if the value was inserted or updated, false if the value was the same
+ * @deprecated 8.0.0 use method setAppValue of \OCP\IConfig
*/
public function setValue($app, $key, $value) {
if (!$this->hasKey($app, $key)) {
@@ -182,7 +201,7 @@ class AppConfig implements IAppConfig {
* http://docs.oracle.com/cd/E11882_01/server.112/e26088/conditions002.htm#i1033286
* > Large objects (LOBs) are not supported in comparison conditions.
*/
- if (!($this->conn instanceof \OC\DB\OracleConnection)) {
+ if (!($this->conn instanceof OracleConnection)) {
// Only update the value when it is not the same
$sql->andWhere($sql->expr()->neq('configvalue', $sql->createParameter('configvalue')))
->setParameter('configvalue', $value);
@@ -200,7 +219,8 @@ class AppConfig implements IAppConfig {
*
* @param string $app app
* @param string $key key
- * @return boolean|null
+ * @return boolean
+ * @deprecated 8.0.0 use method deleteAppValue of \OCP\IConfig
*/
public function deleteKey($app, $key) {
$this->loadConfigValues();
@@ -214,13 +234,15 @@ class AppConfig implements IAppConfig {
$sql->execute();
unset($this->cache[$app][$key]);
+ return false;
}
/**
* Remove app from appconfig
*
* @param string $app app
- * @return boolean|null
+ * @return boolean
+ * @deprecated 8.0.0 use method deleteAppValue of \OCP\IConfig
*
* Removes all keys in appconfig belonging to the app.
*/
@@ -234,6 +256,7 @@ class AppConfig implements IAppConfig {
$sql->execute();
unset($this->cache[$app]);
+ return false;
}
/**
@@ -262,10 +285,30 @@ class AppConfig implements IAppConfig {
}
/**
+ * get all values of the app or and filters out sensitive data
+ *
+ * @param string $app
+ * @return array
+ */
+ public function getFilteredValues($app) {
+ $values = $this->getValues($app, false);
+
+ foreach ($this->sensitiveValues[$app] as $sensitiveKey) {
+ if (isset($values[$sensitiveKey])) {
+ $values[$sensitiveKey] = IConfig::SENSITIVE_VALUE;
+ }
+ }
+
+ return $values;
+ }
+
+ /**
* Load all the app config values
*/
protected function loadConfigValues() {
- if ($this->configLoaded) return;
+ if ($this->configLoaded) {
+ return;
+ }
$this->cache = [];
diff --git a/lib/private/AppFramework/Db/Db.php b/lib/private/AppFramework/Db/Db.php
deleted file mode 100644
index 5aacdc517a2..00000000000
--- a/lib/private/AppFramework/Db/Db.php
+++ /dev/null
@@ -1,314 +0,0 @@
-<?php
-/**
- * @copyright Copyright (c) 2016, ownCloud, Inc.
- *
- * @author Bernhard Posselt <dev@bernhard-posselt.com>
- * @author Joas Schilling <coding@schilljs.com>
- * @author Morris Jobke <hey@morrisjobke.de>
- * @author Robin Appelman <robin@icewind.nl>
- * @author Robin McCorkell <robin@mccorkell.me.uk>
- * @author Thomas Müller <thomas.mueller@tmit.eu>
- *
- * @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 OC\AppFramework\Db;
-
-use OCP\DB\QueryBuilder\IQueryBuilder;
-use OCP\IDb;
-use OCP\IDBConnection;
-use OCP\PreConditionNotMetException;
-use Doctrine\DBAL\Platforms\MySqlPlatform;
-
-/**
- * @deprecated use IDBConnection directly, will be removed in ownCloud 10
- * Small Facade for being able to inject the database connection for tests
- */
-class Db implements IDb {
- /**
- * @var IDBConnection
- */
- protected $connection;
-
- /**
- * @param IDBConnection $connection
- */
- public function __construct(IDBConnection $connection) {
- $this->connection = $connection;
- }
-
- /**
- * Gets the ExpressionBuilder for the connection.
- *
- * @return \OCP\DB\QueryBuilder\IQueryBuilder
- */
- public function getQueryBuilder() {
- return $this->connection->getQueryBuilder();
- }
-
- /**
- * Used to abstract the ownCloud database access away
- *
- * @param string $sql the sql query with ? placeholder for params
- * @param int $limit the maximum number of rows
- * @param int $offset from which row we want to start
- * @deprecated use prepare instead, will be removed in ownCloud 10
- * @return \OC_DB_StatementWrapper prepared SQL query
- */
- public function prepareQuery($sql, $limit = null, $offset = null) {
- $isManipulation = \OC_DB::isManipulation($sql);
- $statement = $this->connection->prepare($sql, $limit, $offset);
- return new \OC_DB_StatementWrapper($statement, $isManipulation);
- }
-
-
- /**
- * Used to get the id of the just inserted element
- *
- * @deprecated use lastInsertId instead, will be removed in ownCloud 10
- * @param string $tableName the name of the table where we inserted the item
- * @return int the id of the inserted element
- */
- public function getInsertId($tableName) {
- return $this->connection->lastInsertId($tableName);
- }
-
- /**
- * Used to abstract the ownCloud database access away
- * @param string $sql the sql query with ? placeholder for params
- * @param int $limit the maximum number of rows
- * @param int $offset from which row we want to start
- * @return \Doctrine\DBAL\Driver\Statement The prepared statement.
- */
- public function prepare($sql, $limit=null, $offset=null) {
- return $this->connection->prepare($sql, $limit, $offset);
- }
-
- /**
- * Executes an, optionally parameterized, SQL query.
- *
- * If the query is parameterized, a prepared statement is used.
- * If an SQLLogger is configured, the execution is logged.
- *
- * @param string $query The SQL query to execute.
- * @param string[] $params The parameters to bind to the query, if any.
- * @param array $types The types the previous parameters are in.
- * @return \Doctrine\DBAL\Driver\Statement The executed statement.
- */
- public function executeQuery($query, array $params = array(), $types = array()) {
- return $this->connection->executeQuery($query, $params, $types);
- }
-
- /**
- * Executes an SQL INSERT/UPDATE/DELETE query with the given parameters
- * and returns the number of affected rows.
- *
- * This method supports PDO binding types as well as DBAL mapping types.
- *
- * @param string $query The SQL query.
- * @param array $params The query parameters.
- * @param array $types The parameter types.
- * @return integer The number of affected rows.
- */
- public function executeUpdate($query, array $params = array(), array $types = array()) {
- return $this->connection->executeUpdate($query, $params, $types);
- }
-
- /**
- * Used to get the id of the just inserted element
- * @param string $table the name of the table where we inserted the item
- * @return int the id of the inserted element
- */
- public function lastInsertId($table = null) {
- return $this->connection->lastInsertId($table);
- }
-
- /**
- * Insert a row if the matching row does not exists.
- *
- * @param string $table The table name (will replace *PREFIX* with the actual prefix)
- * @param array $input data that should be inserted into the table (column name => value)
- * @param array|null $compare List of values that should be checked for "if not exists"
- * If this is null or an empty array, all keys of $input will be compared
- * Please note: text fields (clob) must not be used in the compare array
- * @return int number of inserted rows
- * @throws \Doctrine\DBAL\DBALException
- */
- public function insertIfNotExist($table, $input, array $compare = null) {
- return $this->connection->insertIfNotExist($table, $input, $compare);
- }
-
- /**
- * Insert or update a row value
- *
- * @param string $table
- * @param array $keys (column name => value)
- * @param array $values (column name => value)
- * @param array $updatePreconditionValues ensure values match preconditions (column name => value)
- * @return int number of new rows
- * @throws \Doctrine\DBAL\DBALException
- * @throws PreConditionNotMetException
- */
- public function setValues($table, array $keys, array $values, array $updatePreconditionValues = []) {
- return $this->connection->setValues($table, $keys, $values, $updatePreconditionValues);
- }
-
- /**
- * @inheritdoc
- */
- public function lockTable($tableName) {
- $this->connection->lockTable($tableName);
- }
-
- /**
- * @inheritdoc
- */
- public function unlockTable() {
- $this->connection->unlockTable();
- }
-
- /**
- * Start a transaction
- */
- public function beginTransaction() {
- $this->connection->beginTransaction();
- }
-
- /**
- * Check if a transaction is active
- *
- * @return bool
- */
- public function inTransaction() {
- return $this->connection->inTransaction();
- }
-
- /**
- * Commit the database changes done during a transaction that is in progress
- */
- public function commit() {
- $this->connection->commit();
- }
-
- /**
- * Rollback the database changes done during a transaction that is in progress
- */
- public function rollBack() {
- $this->connection->rollBack();
- }
-
- /**
- * Gets the error code and message as a string for logging
- * @return string
- */
- public function getError() {
- return $this->connection->getError();
- }
-
- /**
- * Fetch the SQLSTATE associated with the last database operation.
- *
- * @return integer The last error code.
- */
- public function errorCode() {
- return $this->connection->errorCode();
- }
-
- /**
- * Fetch extended error information associated with the last database operation.
- *
- * @return array The last error information.
- */
- public function errorInfo() {
- return $this->connection->errorInfo();
- }
-
- /**
- * Establishes the connection with the database.
- *
- * @return bool
- */
- public function connect() {
- return $this->connection->connect();
- }
-
- /**
- * Close the database connection
- */
- public function close() {
- $this->connection->close();
- }
-
- /**
- * Quotes a given input parameter.
- *
- * @param mixed $input Parameter to be quoted.
- * @param int $type Type of the parameter.
- * @return string The quoted parameter.
- */
- public function quote($input, $type = IQueryBuilder::PARAM_STR) {
- return $this->connection->quote($input, $type);
- }
-
- /**
- * Gets the DatabasePlatform instance that provides all the metadata about
- * the platform this driver connects to.
- *
- * @return \Doctrine\DBAL\Platforms\AbstractPlatform The database platform.
- */
- public function getDatabasePlatform() {
- return $this->connection->getDatabasePlatform();
- }
-
- /**
- * Drop a table from the database if it exists
- *
- * @param string $table table name without the prefix
- */
- public function dropTable($table) {
- $this->connection->dropTable($table);
- }
-
- /**
- * Check if a table exists
- *
- * @param string $table table name without the prefix
- * @return bool
- */
- public function tableExists($table) {
- return $this->connection->tableExists($table);
- }
-
- /**
- * Espace a parameter to be used in a LIKE query
- *
- * @param string $param
- * @return string
- */
- public function escapeLikeParameter($param) {
- return $this->connection->escapeLikeParameter($param);
- }
-
- /**
- * Check whether or not the current database support 4byte wide unicode
- *
- * @return bool
- * @since 11.0.0
- */
- public function supports4ByteText() {
- return $this->connection->supports4ByteText();
- }
-}
diff --git a/lib/private/AppFramework/DependencyInjection/DIContainer.php b/lib/private/AppFramework/DependencyInjection/DIContainer.php
index 148a15172ac..0879b3e9330 100644
--- a/lib/private/AppFramework/DependencyInjection/DIContainer.php
+++ b/lib/private/AppFramework/DependencyInjection/DIContainer.php
@@ -43,8 +43,10 @@ use OC\AppFramework\Middleware\OCSMiddleware;
use OC\AppFramework\Middleware\Security\SecurityMiddleware;
use OC\AppFramework\Middleware\SessionMiddleware;
use OC\AppFramework\Utility\SimpleContainer;
+use OC\AppFramework\Utility\TimeFactory;
use OC\Core\Middleware\TwoFactorMiddleware;
use OC\RichObjectStrings\Validator;
+use OC\Security\Bruteforce\Throttler;
use OCP\AppFramework\IApi;
use OCP\AppFramework\IAppContainer;
use OCP\Files\IAppData;
@@ -91,6 +93,10 @@ class DIContainer extends SimpleContainer implements IAppContainer {
return new Output($this->getServer()->getWebRoot());
});
+ $this->registerService(\OCP\Authentication\LoginCredentials\IStore::class, function() {
+ return $this->getServer()->query(\OCP\Authentication\LoginCredentials\IStore::class);
+ });
+
$this->registerService('OCP\\IAvatarManager', function($c) {
return $this->getServer()->getAvatarManager();
});
@@ -134,10 +140,6 @@ class DIContainer extends SimpleContainer implements IAppContainer {
return $this->getServer()->getDateTimeFormatter();
});
- $this->registerService('OCP\\IDb', function($c) {
- return $this->getServer()->getDb();
- });
-
$this->registerService('OCP\\IDBConnection', function($c) {
return $this->getServer()->getDatabaseConnection();
});
@@ -380,20 +382,25 @@ class DIContainer extends SimpleContainer implements IAppContainer {
*/
$app = $this;
$this->registerService('SecurityMiddleware', function($c) use ($app){
+ /** @var \OC\Server $server */
+ $server = $app->getServer();
+
return new SecurityMiddleware(
$c['Request'],
$c['ControllerMethodReflector'],
- $app->getServer()->getNavigationManager(),
- $app->getServer()->getURLGenerator(),
- $app->getServer()->getLogger(),
- $app->getServer()->getSession(),
+ $server->getNavigationManager(),
+ $server->getURLGenerator(),
+ $server->getLogger(),
+ $server->getSession(),
$c['AppName'],
$app->isLoggedIn(),
$app->isAdminUser(),
- $app->getServer()->getContentSecurityPolicyManager(),
- $app->getServer()->getCsrfTokenManager(),
- $app->getServer()->getContentSecurityPolicyNonceManager()
+ $server->getContentSecurityPolicyManager(),
+ $server->getCsrfTokenManager(),
+ $server->getContentSecurityPolicyNonceManager(),
+ $server->getBruteForceThrottler()
);
+
});
$this->registerService('CORSMiddleware', function($c) {
diff --git a/lib/private/AppFramework/Middleware/Security/SecurityMiddleware.php b/lib/private/AppFramework/Middleware/Security/SecurityMiddleware.php
index d60d5749d57..edba6a3e759 100644
--- a/lib/private/AppFramework/Middleware/Security/SecurityMiddleware.php
+++ b/lib/private/AppFramework/Middleware/Security/SecurityMiddleware.php
@@ -36,6 +36,7 @@ use OC\AppFramework\Middleware\Security\Exceptions\NotConfirmedException;
use OC\AppFramework\Middleware\Security\Exceptions\NotLoggedInException;
use OC\AppFramework\Middleware\Security\Exceptions\StrictCookieMissingException;
use OC\AppFramework\Utility\ControllerMethodReflector;
+use OC\Security\Bruteforce\Throttler;
use OC\Security\CSP\ContentSecurityPolicyManager;
use OC\Security\CSP\ContentSecurityPolicyNonceManager;
use OC\Security\CSRF\CsrfTokenManager;
@@ -87,6 +88,8 @@ class SecurityMiddleware extends Middleware {
private $csrfTokenManager;
/** @var ContentSecurityPolicyNonceManager */
private $cspNonceManager;
+ /** @var Throttler */
+ private $throttler;
/**
* @param IRequest $request
@@ -101,6 +104,7 @@ class SecurityMiddleware extends Middleware {
* @param ContentSecurityPolicyManager $contentSecurityPolicyManager
* @param CSRFTokenManager $csrfTokenManager
* @param ContentSecurityPolicyNonceManager $cspNonceManager
+ * @param Throttler $throttler
*/
public function __construct(IRequest $request,
ControllerMethodReflector $reflector,
@@ -113,7 +117,8 @@ class SecurityMiddleware extends Middleware {
$isAdminUser,
ContentSecurityPolicyManager $contentSecurityPolicyManager,
CsrfTokenManager $csrfTokenManager,
- ContentSecurityPolicyNonceManager $cspNonceManager) {
+ ContentSecurityPolicyNonceManager $cspNonceManager,
+ Throttler $throttler) {
$this->navigationManager = $navigationManager;
$this->request = $request;
$this->reflector = $reflector;
@@ -126,6 +131,7 @@ class SecurityMiddleware extends Middleware {
$this->contentSecurityPolicyManager = $contentSecurityPolicyManager;
$this->csrfTokenManager = $csrfTokenManager;
$this->cspNonceManager = $cspNonceManager;
+ $this->throttler = $throttler;
}
@@ -185,6 +191,12 @@ class SecurityMiddleware extends Middleware {
}
}
+ if($this->reflector->hasAnnotation('BruteForceProtection')) {
+ $action = $this->reflector->getAnnotationParameter('BruteForceProtection');
+ $this->throttler->sleepDelay($this->request->getRemoteAddress(), $action);
+ $this->throttler->registerAttempt($action, $this->request->getRemoteAddress());
+ }
+
/**
* FIXME: Use DI once available
* Checks if app is enabled (also includes a check whether user is allowed to access the resource)
diff --git a/lib/private/AppFramework/Utility/ControllerMethodReflector.php b/lib/private/AppFramework/Utility/ControllerMethodReflector.php
index 33a117d8121..034fc3a1759 100644
--- a/lib/private/AppFramework/Utility/ControllerMethodReflector.php
+++ b/lib/private/AppFramework/Utility/ControllerMethodReflector.php
@@ -55,8 +55,10 @@ class ControllerMethodReflector implements IControllerMethodReflector{
$docs = $reflection->getDocComment();
// extract everything prefixed by @ and first letter uppercase
- preg_match_all('/@([A-Z]\w+)/', $docs, $matches);
- $this->annotations = $matches[1];
+ preg_match_all('/^\h+\*\h+@(?P<annotation>[A-Z]\w+)(\h+(?P<parameter>\w+))?$/m', $docs, $matches);
+ foreach($matches['annotation'] as $key => $annontation) {
+ $this->annotations[$annontation] = $matches['parameter'][$key];
+ }
// extract type parameter information
preg_match_all('/@param\h+(?P<type>\w+)\h+\$(?P<var>\w+)/', $docs, $matches);
@@ -112,7 +114,22 @@ class ControllerMethodReflector implements IControllerMethodReflector{
* @return bool true if the annotation is found
*/
public function hasAnnotation($name){
- return in_array($name, $this->annotations);
+ return array_key_exists($name, $this->annotations);
+ }
+
+
+ /**
+ * Get optional annotation parameter
+ * @param string $name the name of the annotation
+ * @return string
+ */
+ public function getAnnotationParameter($name){
+ $parameter = '';
+ if($this->hasAnnotation($name)) {
+ $parameter = $this->annotations[$name];
+ }
+
+ return $parameter;
}
diff --git a/lib/private/Archive/TAR.php b/lib/private/Archive/TAR.php
index bbd24bd05a1..07ccd09f399 100644
--- a/lib/private/Archive/TAR.php
+++ b/lib/private/Archive/TAR.php
@@ -33,6 +33,8 @@
namespace OC\Archive;
+use Icewind\Streams\CallbackWrapper;
+
class TAR extends Archive {
const PLAIN = 0;
const GZIP = 1;
@@ -359,22 +361,19 @@ class TAR extends Archive {
if ($mode == 'r' or $mode == 'rb') {
return fopen($tmpFile, $mode);
} else {
- \OC\Files\Stream\Close::registerCallback($tmpFile, array($this, 'writeBack'));
- self::$tempFiles[$tmpFile] = $path;
- return fopen('close://' . $tmpFile, $mode);
+ $handle = fopen($tmpFile, $mode);
+ return CallbackWrapper::wrap($handle, null, null, function () use ($path, $tmpFile) {
+ $this->writeBack($tmpFile, $path);
+ });
}
}
- private static $tempFiles = array();
-
/**
* write back temporary files
*/
- function writeBack($tmpFile) {
- if (isset(self::$tempFiles[$tmpFile])) {
- $this->addFile(self::$tempFiles[$tmpFile], $tmpFile);
- unlink($tmpFile);
- }
+ function writeBack($tmpFile, $path) {
+ $this->addFile($path, $tmpFile);
+ unlink($tmpFile);
}
/**
diff --git a/lib/private/Archive/ZIP.php b/lib/private/Archive/ZIP.php
index 9e9fe40b2b4..0ed0f48acc4 100644
--- a/lib/private/Archive/ZIP.php
+++ b/lib/private/Archive/ZIP.php
@@ -31,6 +31,8 @@
namespace OC\Archive;
+use Icewind\Streams\CallbackWrapper;
+
class ZIP extends Archive{
/**
* @var \ZipArchive zip
@@ -198,24 +200,22 @@ class ZIP extends Archive{
$ext='';
}
$tmpFile=\OCP\Files::tmpFile($ext);
- \OC\Files\Stream\Close::registerCallback($tmpFile, array($this, 'writeBack'));
if($this->fileExists($path)) {
$this->extractFile($path, $tmpFile);
}
- self::$tempFiles[$tmpFile]=$path;
- return fopen('close://'.$tmpFile, $mode);
+ $handle = fopen($tmpFile, $mode);
+ return CallbackWrapper::wrap($handle, null, null, function () use ($path, $tmpFile) {
+ $this->writeBack($tmpFile, $path);
+ });
}
}
- private static $tempFiles=array();
/**
* write back temporary files
*/
- function writeBack($tmpFile) {
- if(isset(self::$tempFiles[$tmpFile])) {
- $this->addFile(self::$tempFiles[$tmpFile], $tmpFile);
- unlink($tmpFile);
- }
+ function writeBack($tmpFile, $path) {
+ $this->addFile($path, $tmpFile);
+ unlink($tmpFile);
}
/**
diff --git a/lib/private/Authentication/LoginCredentials/Credentials.php b/lib/private/Authentication/LoginCredentials/Credentials.php
new file mode 100644
index 00000000000..9314b7489db
--- /dev/null
+++ b/lib/private/Authentication/LoginCredentials/Credentials.php
@@ -0,0 +1,72 @@
+<?php
+
+/**
+ * @copyright 2016 Christoph Wurst <christoph@winzerhof-wurst.at>
+ *
+ * @author 2016 Christoph Wurst <christoph@winzerhof-wurst.at>
+ *
+ * @license GNU AGPL version 3 or any later version
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+namespace OC\Authentication\LoginCredentials;
+
+use OCP\Authentication\LoginCredentials\ICredentials;
+
+class Credentials implements ICredentials {
+
+ /** @var string */
+ private $uid;
+
+ /** @var string */
+ private $loginName;
+
+ /** @var string */
+ private $password;
+
+ /**
+ * @param string $uid
+ * @param string $loginName
+ * @param string $password
+ */
+ public function __construct($uid, $loginName, $password) {
+ $this->uid = $uid;
+ $this->loginName = $loginName;
+ $this->password = $password;
+ }
+
+ /**
+ * @return string
+ */
+ public function getUID() {
+ return $this->uid;
+ }
+
+ /**
+ * @return string
+ */
+ public function getLoginName() {
+ return $this->loginName;
+ }
+
+ /**
+ * @return string
+ */
+ public function getPassword() {
+ return $this->password;
+ }
+
+}
diff --git a/lib/private/Authentication/LoginCredentials/Store.php b/lib/private/Authentication/LoginCredentials/Store.php
new file mode 100644
index 00000000000..e44c88c7aea
--- /dev/null
+++ b/lib/private/Authentication/LoginCredentials/Store.php
@@ -0,0 +1,120 @@
+<?php
+
+/**
+ * @copyright 2016 Christoph Wurst <christoph@winzerhof-wurst.at>
+ *
+ * @author 2016 Christoph Wurst <christoph@winzerhof-wurst.at>
+ *
+ * @license GNU AGPL version 3 or any later version
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+namespace OC\Authentication\LoginCredentials;
+
+use OC\Authentication\Exceptions\InvalidTokenException;
+use OC\Authentication\Exceptions\PasswordlessTokenException;
+use OC\Authentication\Token\IProvider;
+use OCP\Authentication\Exceptions\CredentialsUnavailableException;
+use OCP\Authentication\LoginCredentials\ICredentials;
+use OCP\Authentication\LoginCredentials\IStore;
+use OCP\ILogger;
+use OCP\ISession;
+use OCP\Session\Exceptions\SessionNotAvailableException;
+use OCP\Util;
+
+class Store implements IStore {
+
+ /** @var ISession */
+ private $session;
+
+ /** @var ILogger */
+ private $logger;
+
+ /** @var IProvider|null */
+ private $tokenProvider;
+
+ /**
+ * @param ISession $session
+ * @param ILogger $logger
+ * @param IProvider $tokenProvider
+ */
+ public function __construct(ISession $session, ILogger $logger, IProvider $tokenProvider = null) {
+ $this->session = $session;
+ $this->logger = $logger;
+ $this->tokenProvider = $tokenProvider;
+
+ Util::connectHook('OC_User', 'post_login', $this, 'authenticate');
+ }
+
+ /**
+ * Hook listener on post login
+ *
+ * @param array $params
+ */
+ public function authenticate(array $params) {
+ $this->session->set('login_credentials', json_encode($params));
+ }
+
+ /**
+ * Replace the session implementation
+ *
+ * @param ISession $session
+ */
+ public function setSession(ISession $session) {
+ $this->session = $session;
+ }
+
+ /**
+ * @since 12
+ *
+ * @return ICredentials the login credentials of the current user
+ * @throws CredentialsUnavailableException
+ */
+ public function getLoginCredentials() {
+ if (is_null($this->tokenProvider)) {
+ throw new CredentialsUnavailableException();
+ }
+
+ $trySession = false;
+ try {
+ $sessionId = $this->session->getId();
+ $token = $this->tokenProvider->getToken($sessionId);
+
+ $uid = $token->getUID();
+ $user = $token->getLoginName();
+ $password = $this->tokenProvider->getPassword($token, $sessionId);
+
+ return new Credentials($uid, $user, $password);
+ } catch (SessionNotAvailableException $ex) {
+ $this->logger->debug('could not get login credentials because session is unavailable', ['app' => 'core']);
+ } catch (InvalidTokenException $ex) {
+ $this->logger->debug('could not get login credentials because the token is invalid', ['app' => 'core']);
+ $trySession = true;
+ } catch (PasswordlessTokenException $ex) {
+ $this->logger->debug('could not get login credentials because the token has no password', ['app' => 'core']);
+ $trySession = true;
+ }
+
+ if ($trySession && $this->session->exists('login_credentials')) {
+ $creds = json_decode($this->session->get('login_credentials'));
+ return new Credentials($creds->uid, $creds->uid, $creds->password);
+ }
+
+ // If we reach this line, an exception was thrown.
+ throw new CredentialsUnavailableException();
+ }
+
+}
diff --git a/lib/private/Authentication/TwoFactorAuth/Manager.php b/lib/private/Authentication/TwoFactorAuth/Manager.php
index 48792aa685b..1d0deada696 100644
--- a/lib/private/Authentication/TwoFactorAuth/Manager.php
+++ b/lib/private/Authentication/TwoFactorAuth/Manager.php
@@ -1,4 +1,5 @@
<?php
+
/**
* @copyright Copyright (c) 2016, ownCloud, Inc.
*
@@ -26,9 +27,11 @@ use Exception;
use OC;
use OC\App\AppManager;
use OC_App;
+use OCP\Activity\IManager;
use OCP\AppFramework\QueryException;
use OCP\Authentication\TwoFactorAuth\IProvider;
use OCP\IConfig;
+use OCP\ILogger;
use OCP\ISession;
use OCP\IUser;
@@ -48,15 +51,26 @@ class Manager {
/** @var IConfig */
private $config;
+ /** @var IManager */
+ private $activityManager;
+
+ /** @var ILogger */
+ private $logger;
+
/**
* @param AppManager $appManager
* @param ISession $session
* @param IConfig $config
+ * @param IManager $activityManager
+ * @param ILogger $logger
*/
- public function __construct(AppManager $appManager, ISession $session, IConfig $config) {
+ public function __construct(AppManager $appManager, ISession $session, IConfig $config, IManager $activityManager,
+ ILogger $logger) {
$this->appManager = $appManager;
$this->session = $session;
$this->config = $config;
+ $this->activityManager = $activityManager;
+ $this->logger = $logger;
}
/**
@@ -184,11 +198,40 @@ class Manager {
}
$this->session->remove(self::SESSION_UID_KEY);
$this->session->remove(self::REMEMBER_LOGIN);
+
+ $this->publishEvent($user, 'twofactor_success', [
+ 'provider' => $provider->getDisplayName(),
+ ]);
+ } else {
+ $this->publishEvent($user, 'twofactor_failed', [
+ 'provider' => $provider->getDisplayName(),
+ ]);
}
return $passed;
}
/**
+ * Push a 2fa event the user's activity stream
+ *
+ * @param IUser $user
+ * @param string $event
+ */
+ private function publishEvent(IUser $user, $event, array $params) {
+ $activity = $this->activityManager->generateEvent();
+ $activity->setApp('twofactor_generic')
+ ->setType('twofactor')
+ ->setAuthor($user->getUID())
+ ->setAffectedUser($user->getUID())
+ ->setSubject($event, $params);
+ try {
+ $this->activityManager->publish($activity);
+ } catch (Exception $e) {
+ $this->logger->warning('could not publish backup code creation activity', ['app' => 'twofactor_backupcodes']);
+ $this->logger->logException($e, ['app' => 'twofactor_backupcodes']);
+ }
+ }
+
+ /**
* Check if the currently logged in user needs to pass 2FA
*
* @param IUser $user the currently logged in user
diff --git a/lib/private/Console/TimestampFormatter.php b/lib/private/Console/TimestampFormatter.php
index 5b292439a4c..66dd38e6ac3 100644
--- a/lib/private/Console/TimestampFormatter.php
+++ b/lib/private/Console/TimestampFormatter.php
@@ -97,11 +97,11 @@ class TimestampFormatter implements OutputFormatterInterface {
*/
public function format($message) {
- $timeZone = $this->config->getSystemValue('logtimezone', null);
+ $timeZone = $this->config->getSystemValue('logtimezone', 'UTC');
$timeZone = $timeZone !== null ? new \DateTimeZone($timeZone) : null;
$time = new \DateTime('now', $timeZone);
- $timestampInfo = $time->format($this->config->getSystemValue('logdateformat', 'c'));
+ $timestampInfo = $time->format($this->config->getSystemValue('logdateformat', \DateTime::ATOM));
return $timestampInfo . ' ' . $this->formatter->format($message);
}
diff --git a/lib/private/DB/QueryBuilder/ExpressionBuilder/ExpressionBuilder.php b/lib/private/DB/QueryBuilder/ExpressionBuilder/ExpressionBuilder.php
index f9170e97a02..f172260df79 100644
--- a/lib/private/DB/QueryBuilder/ExpressionBuilder/ExpressionBuilder.php
+++ b/lib/private/DB/QueryBuilder/ExpressionBuilder/ExpressionBuilder.php
@@ -30,6 +30,8 @@ use OC\DB\QueryBuilder\Literal;
use OC\DB\QueryBuilder\QueryFunction;
use OC\DB\QueryBuilder\QuoteHelper;
use OCP\DB\QueryBuilder\IExpressionBuilder;
+use OCP\DB\QueryBuilder\ILiteral;
+use OCP\DB\QueryBuilder\IQueryFunction;
use OCP\IDBConnection;
class ExpressionBuilder implements IExpressionBuilder {
@@ -39,12 +41,16 @@ class ExpressionBuilder implements IExpressionBuilder {
/** @var QuoteHelper */
protected $helper;
+ /** @var IDBConnection */
+ protected $connection;
+
/**
* Initializes a new <tt>ExpressionBuilder</tt>.
*
* @param \OCP\IDBConnection $connection
*/
public function __construct(IDBConnection $connection) {
+ $this->connection = $connection;
$this->helper = new QuoteHelper();
$this->expressionBuilder = new DoctrineExpressionBuilder($connection);
}
@@ -345,12 +351,42 @@ class ExpressionBuilder implements IExpressionBuilder {
}
/**
+ * Binary AND Operator copies a bit to the result if it exists in both operands.
+ *
+ * @param string|ILiteral $x The field or value to check
+ * @param int $y Bitmap that must be set
+ * @return IQueryFunction
+ * @since 12.0.0
+ */
+ public function bitwiseAnd($x, $y) {
+ return new QueryFunction($this->connection->getDatabasePlatform()->getBitAndComparisonExpression(
+ $this->helper->quoteColumnName($x),
+ $y
+ ));
+ }
+
+ /**
+ * Binary OR Operator copies a bit if it exists in either operand.
+ *
+ * @param string|ILiteral $x The field or value to check
+ * @param int $y Bitmap that must be set
+ * @return IQueryFunction
+ * @since 12.0.0
+ */
+ public function bitwiseOr($x, $y) {
+ return new QueryFunction($this->connection->getDatabasePlatform()->getBitOrComparisonExpression(
+ $this->helper->quoteColumnName($x),
+ $y
+ ));
+ }
+
+ /**
* Quotes a given input parameter.
*
* @param mixed $input The parameter to be quoted.
* @param mixed|null $type One of the IQueryBuilder::PARAM_* constants
*
- * @return Literal
+ * @return ILiteral
*/
public function literal($input, $type = null) {
return new Literal($this->expressionBuilder->literal($input, $type));
diff --git a/lib/private/Encryption/Keys/Storage.php b/lib/private/Encryption/Keys/Storage.php
index 8149ffe9dce..e8d152581fe 100644
--- a/lib/private/Encryption/Keys/Storage.php
+++ b/lib/private/Encryption/Keys/Storage.php
@@ -51,6 +51,9 @@ class Storage implements IStorage {
/** @var string */
private $encryption_base_dir;
+ /** @var string */
+ private $backup_base_dir;
+
/** @var array */
private $keyCache = [];
@@ -64,6 +67,7 @@ class Storage implements IStorage {
$this->encryption_base_dir = '/files_encryption';
$this->keys_base_dir = $this->encryption_base_dir .'/keys';
+ $this->backup_base_dir = $this->encryption_base_dir .'/backup';
$this->root_dir = $this->util->getKeyStorageRoot();
}
@@ -287,6 +291,37 @@ class Storage implements IStorage {
}
/**
+ * backup keys of a given encryption module
+ *
+ * @param string $encryptionModuleId
+ * @param string $purpose
+ * @param string $uid
+ * @return bool
+ * @since 12.0.0
+ */
+ public function backupUserKeys($encryptionModuleId, $purpose, $uid) {
+ $source = $uid . $this->encryption_base_dir . '/' . $encryptionModuleId;
+ $backupDir = $uid . $this->backup_base_dir;
+ if (!$this->view->file_exists($backupDir)) {
+ $this->view->mkdir($backupDir);
+ }
+
+ $backupDir = $backupDir . '/' . $purpose . '.' . $encryptionModuleId . '.' . $this->getTimestamp();
+ $this->view->mkdir($backupDir);
+
+ return $this->view->copy($source, $backupDir);
+ }
+
+ /**
+ * get the current timestamp
+ *
+ * @return int
+ */
+ protected function getTimestamp() {
+ return time();
+ }
+
+ /**
* get system wide path and detect mount points
*
* @param string $path
diff --git a/lib/private/Files/Cache/Cache.php b/lib/private/Files/Cache/Cache.php
index 3a3f51488e6..a966d621c58 100644
--- a/lib/private/Files/Cache/Cache.php
+++ b/lib/private/Files/Cache/Cache.php
@@ -142,7 +142,7 @@ class Cache implements ICache {
}
return $data;
} else {
- return self::cacheEntryFromData($data, $this->storageId, $this->mimetypeLoader);
+ return self::cacheEntryFromData($data, $this->mimetypeLoader);
}
}
@@ -150,11 +150,10 @@ class Cache implements ICache {
* Create a CacheEntry from database row
*
* @param array $data
- * @param string $storageId
* @param IMimeTypeLoader $mimetypeLoader
* @return CacheEntry
*/
- public static function cacheEntryFromData($data, $storageId, IMimeTypeLoader $mimetypeLoader) {
+ public static function cacheEntryFromData($data, IMimeTypeLoader $mimetypeLoader) {
//fix types
$data['fileid'] = (int)$data['fileid'];
$data['parent'] = (int)$data['parent'];
@@ -163,7 +162,7 @@ class Cache implements ICache {
$data['storage_mtime'] = (int)$data['storage_mtime'];
$data['encryptedVersion'] = (int)$data['encrypted'];
$data['encrypted'] = (bool)$data['encrypted'];
- $data['storage'] = $storageId;
+ $data['storage_id'] = $data['storage'];
$data['mimetype'] = $mimetypeLoader->getMimetypeById($data['mimetype']);
$data['mimepart'] = $mimetypeLoader->getMimetypeById($data['mimepart']);
if ($data['storage_mtime'] == 0) {
diff --git a/lib/private/Files/Config/UserMountCache.php b/lib/private/Files/Config/UserMountCache.php
index c9a701d24b6..423eb5c423d 100644
--- a/lib/private/Files/Config/UserMountCache.php
+++ b/lib/private/Files/Config/UserMountCache.php
@@ -141,7 +141,11 @@ class UserMountCache implements IUserMountCache {
foreach ($cachedMounts as $cachedMount) {
if (
$newMount->getRootId() === $cachedMount->getRootId() &&
- ($newMount->getMountPoint() !== $cachedMount->getMountPoint() || $newMount->getMountId() !== $cachedMount->getMountId())
+ (
+ $newMount->getMountPoint() !== $cachedMount->getMountPoint() ||
+ $newMount->getStorageId() !== $cachedMount->getStorageId() ||
+ $newMount->getMountId() !== $cachedMount->getMountId()
+ )
) {
$changed[] = $newMount;
}
@@ -169,6 +173,7 @@ class UserMountCache implements IUserMountCache {
$builder = $this->connection->getQueryBuilder();
$query = $builder->update('mounts')
+ ->set('storage_id', $builder->createNamedParameter($mount->getStorageId()))
->set('mount_point', $builder->createNamedParameter($mount->getMountPoint()))
->set('mount_id', $builder->createNamedParameter($mount->getMountId(), IQueryBuilder::PARAM_INT))
->where($builder->expr()->eq('user_id', $builder->createNamedParameter($mount->getUser()->getUID())))
diff --git a/lib/private/Files/Mount/LocalHomeMountProvider.php b/lib/private/Files/Mount/LocalHomeMountProvider.php
index 23bbfcd5ffa..9057f62995f 100644
--- a/lib/private/Files/Mount/LocalHomeMountProvider.php
+++ b/lib/private/Files/Mount/LocalHomeMountProvider.php
@@ -39,9 +39,6 @@ class LocalHomeMountProvider implements IHomeMountProvider {
*/
public function getHomeMountForUser(IUser $user, IStorageFactory $loader) {
$arguments = ['user' => $user];
- if (\OC\Files\Cache\Storage::exists('local::' . $user->getHome() . '/')) {
- $arguments['legacy'] = true;
- }
return new MountPoint('\OC\Files\Storage\Home', '/' . $user->getUID(), $arguments, $loader);
}
}
diff --git a/lib/private/Files/Node/File.php b/lib/private/Files/Node/File.php
index c4430b9181d..4bfa5d583f7 100644
--- a/lib/private/Files/Node/File.php
+++ b/lib/private/Files/Node/File.php
@@ -30,6 +30,16 @@ use OCP\Files\NotPermittedException;
class File extends Node implements \OCP\Files\File {
/**
+ * Creates a Folder that represents a non-existing path
+ *
+ * @param string $path path
+ * @return string non-existing node class
+ */
+ protected function createNonExistingNode($path) {
+ return new NonExistingFile($this->root, $this->view, $path);
+ }
+
+ /**
* @return string
* @throws \OCP\Files\NotPermittedException
*/
@@ -114,52 +124,6 @@ class File extends Node implements \OCP\Files\File {
}
/**
- * @param string $targetPath
- * @throws \OCP\Files\NotPermittedException
- * @return \OC\Files\Node\Node
- */
- public function copy($targetPath) {
- $targetPath = $this->normalizePath($targetPath);
- $parent = $this->root->get(dirname($targetPath));
- if ($parent instanceof Folder and $this->isValidPath($targetPath) and $parent->isCreatable()) {
- $nonExisting = new NonExistingFile($this->root, $this->view, $targetPath);
- $this->root->emit('\OC\Files', 'preCopy', array($this, $nonExisting));
- $this->root->emit('\OC\Files', 'preWrite', array($nonExisting));
- $this->view->copy($this->path, $targetPath);
- $targetNode = $this->root->get($targetPath);
- $this->root->emit('\OC\Files', 'postCopy', array($this, $targetNode));
- $this->root->emit('\OC\Files', 'postWrite', array($targetNode));
- return $targetNode;
- } else {
- throw new NotPermittedException();
- }
- }
-
- /**
- * @param string $targetPath
- * @throws \OCP\Files\NotPermittedException
- * @return \OC\Files\Node\Node
- */
- public function move($targetPath) {
- $targetPath = $this->normalizePath($targetPath);
- $parent = $this->root->get(dirname($targetPath));
- if ($parent instanceof Folder and $this->isValidPath($targetPath) and $parent->isCreatable()) {
- $nonExisting = new NonExistingFile($this->root, $this->view, $targetPath);
- $this->root->emit('\OC\Files', 'preRename', array($this, $nonExisting));
- $this->root->emit('\OC\Files', 'preWrite', array($nonExisting));
- $this->view->rename($this->path, $targetPath);
- $targetNode = $this->root->get($targetPath);
- $this->root->emit('\OC\Files', 'postRename', array($this, $targetNode));
- $this->root->emit('\OC\Files', 'postWrite', array($targetNode));
- $this->path = $targetPath;
- $this->fileInfo = null;
- return $targetNode;
- } else {
- throw new NotPermittedException();
- }
- }
-
- /**
* @param string $type
* @param bool $raw
* @return string
diff --git a/lib/private/Files/Node/Folder.php b/lib/private/Files/Node/Folder.php
index 288a02ef207..fd907f708f3 100644
--- a/lib/private/Files/Node/Folder.php
+++ b/lib/private/Files/Node/Folder.php
@@ -36,6 +36,16 @@ use OCP\Files\NotPermittedException;
class Folder extends Node implements \OCP\Files\Folder {
/**
+ * Creates a Folder that represents a non-existing path
+ *
+ * @param string $path path
+ * @return string non-existing node class
+ */
+ protected function createNonExistingNode($path) {
+ return new NonExistingFolder($this->root, $this->view, $path);
+ }
+
+ /**
* @param string $path path relative to the folder
* @return string
* @throws \OCP\Files\NotPermittedException
@@ -326,51 +336,6 @@ class Folder extends Node implements \OCP\Files\Folder {
}
/**
- * @param string $targetPath
- * @throws \OCP\Files\NotPermittedException
- * @return \OC\Files\Node\Node
- */
- public function copy($targetPath) {
- $targetPath = $this->normalizePath($targetPath);
- $parent = $this->root->get(dirname($targetPath));
- if ($parent instanceof Folder and $this->isValidPath($targetPath) and $parent->isCreatable()) {
- $nonExisting = new NonExistingFolder($this->root, $this->view, $targetPath);
- $this->root->emit('\OC\Files', 'preCopy', array($this, $nonExisting));
- $this->root->emit('\OC\Files', 'preWrite', array($nonExisting));
- $this->view->copy($this->path, $targetPath);
- $targetNode = $this->root->get($targetPath);
- $this->root->emit('\OC\Files', 'postCopy', array($this, $targetNode));
- $this->root->emit('\OC\Files', 'postWrite', array($targetNode));
- return $targetNode;
- } else {
- throw new NotPermittedException('No permission to copy to path');
- }
- }
-
- /**
- * @param string $targetPath
- * @throws \OCP\Files\NotPermittedException
- * @return \OC\Files\Node\Node
- */
- public function move($targetPath) {
- $targetPath = $this->normalizePath($targetPath);
- $parent = $this->root->get(dirname($targetPath));
- if ($parent instanceof Folder and $this->isValidPath($targetPath) and $parent->isCreatable()) {
- $nonExisting = new NonExistingFolder($this->root, $this->view, $targetPath);
- $this->root->emit('\OC\Files', 'preRename', array($this, $nonExisting));
- $this->root->emit('\OC\Files', 'preWrite', array($nonExisting));
- $this->view->rename($this->path, $targetPath);
- $targetNode = $this->root->get($targetPath);
- $this->root->emit('\OC\Files', 'postRename', array($this, $targetNode));
- $this->root->emit('\OC\Files', 'postWrite', array($targetNode));
- $this->path = $targetPath;
- return $targetNode;
- } else {
- throw new NotPermittedException('No permission to move to path');
- }
- }
-
- /**
* Add a suffix to the name in case the file exists
*
* @param string $name
diff --git a/lib/private/Files/Node/Node.php b/lib/private/Files/Node/Node.php
index 226c182622f..e00debe6903 100644
--- a/lib/private/Files/Node/Node.php
+++ b/lib/private/Files/Node/Node.php
@@ -33,6 +33,7 @@ use OCP\Files\InvalidPathException;
use OCP\Files\NotFoundException;
use OCP\Files\NotPermittedException;
+// FIXME: this class really should be abstract
class Node implements \OCP\Files\Node {
/**
* @var \OC\Files\View $view
@@ -56,7 +57,7 @@ class Node implements \OCP\Files\Node {
/**
* @param \OC\Files\View $view
- * @param \OC\Files\Node\Root $root
+ * @param \OCP\Files\IRootFolder $root
* @param string $path
* @param FileInfo $fileInfo
*/
@@ -68,6 +69,16 @@ class Node implements \OCP\Files\Node {
}
/**
+ * Creates a Node of the same type that represents a non-existing path
+ *
+ * @param string $path path
+ * @return string non-existing node class
+ */
+ protected function createNonExistingNode($path) {
+ throw new \Exception('Must be implemented by subclasses');
+ }
+
+ /**
* Returns the matching file info
*
* @return FileInfo
@@ -106,28 +117,11 @@ class Node implements \OCP\Files\Node {
return ($this->getPermissions() & $permissions) === $permissions;
}
- /**
- * @param string $targetPath
- * @throws \OCP\Files\NotPermittedException
- * @return \OC\Files\Node\Node
- */
- public function move($targetPath) {
- return;
- }
-
public function delete() {
return;
}
/**
- * @param string $targetPath
- * @return \OC\Files\Node\Node
- */
- public function copy($targetPath) {
- return;
- }
-
- /**
* @param int $mtime
* @throws \OCP\Files\NotPermittedException
*/
@@ -381,4 +375,54 @@ class Node implements \OCP\Files\Node {
public function unlock($type) {
$this->view->unlockFile($this->path, $type);
}
+
+ /**
+ * @param string $targetPath
+ * @throws \OCP\Files\NotPermittedException if copy not allowed or failed
+ * @return \OC\Files\Node\Node
+ */
+ public function copy($targetPath) {
+ $targetPath = $this->normalizePath($targetPath);
+ $parent = $this->root->get(dirname($targetPath));
+ if ($parent instanceof Folder and $this->isValidPath($targetPath) and $parent->isCreatable()) {
+ $nonExisting = $this->createNonExistingNode($targetPath);
+ $this->root->emit('\OC\Files', 'preCopy', [$this, $nonExisting]);
+ $this->root->emit('\OC\Files', 'preWrite', [$nonExisting]);
+ if (!$this->view->copy($this->path, $targetPath)) {
+ throw new NotPermittedException('Could not copy ' . $this->path . ' to ' . $targetPath);
+ }
+ $targetNode = $this->root->get($targetPath);
+ $this->root->emit('\OC\Files', 'postCopy', [$this, $targetNode]);
+ $this->root->emit('\OC\Files', 'postWrite', [$targetNode]);
+ return $targetNode;
+ } else {
+ throw new NotPermittedException('No permission to copy to path ' . $targetPath);
+ }
+ }
+
+ /**
+ * @param string $targetPath
+ * @throws \OCP\Files\NotPermittedException if move not allowed or failed
+ * @return \OC\Files\Node\Node
+ */
+ public function move($targetPath) {
+ $targetPath = $this->normalizePath($targetPath);
+ $parent = $this->root->get(dirname($targetPath));
+ if ($parent instanceof Folder and $this->isValidPath($targetPath) and $parent->isCreatable()) {
+ $nonExisting = $this->createNonExistingNode($targetPath);
+ $this->root->emit('\OC\Files', 'preRename', [$this, $nonExisting]);
+ $this->root->emit('\OC\Files', 'preWrite', [$nonExisting]);
+ if (!$this->view->rename($this->path, $targetPath)) {
+ throw new NotPermittedException('Could not move ' . $this->path . ' to ' . $targetPath);
+ }
+ $targetNode = $this->root->get($targetPath);
+ $this->root->emit('\OC\Files', 'postRename', [$this, $targetNode]);
+ $this->root->emit('\OC\Files', 'postWrite', [$targetNode]);
+ $this->path = $targetPath;
+ return $targetNode;
+ } else {
+ throw new NotPermittedException('No permission to move to path ' . $targetPath);
+ }
+ }
+
}
diff --git a/lib/private/Files/Notify/Change.php b/lib/private/Files/Notify/Change.php
new file mode 100644
index 00000000000..78cc007b27d
--- /dev/null
+++ b/lib/private/Files/Notify/Change.php
@@ -0,0 +1,65 @@
+<?php
+/**
+ * @copyright Copyright (c) 2017 Robin Appelman <robin@icewind.nl>
+ *
+ * @author Robin Appelman <robin@icewind.nl>
+ *
+ * @license GNU AGPL version 3 or any later version
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+namespace OC\Files\Notify;
+
+use OCP\Files\Notify\IChange;
+
+class Change implements IChange {
+ /** @var int */
+ private $type;
+
+ /** @var string */
+ private $path;
+
+ /**
+ * Change constructor.
+ *
+ * @param int $type
+ * @param string $path
+ */
+ public function __construct($type, $path) {
+ $this->type = $type;
+ $this->path = $path;
+ }
+
+ /**
+ * Get the type of the change
+ *
+ * @return int IChange::ADDED, IChange::REMOVED, IChange::MODIFIED or IChange::RENAMED
+ */
+ public function getType() {
+ return $this->type;
+ }
+
+ /**
+ * Get the path of the file that was changed relative to the root of the storage
+ *
+ * Note, for rename changes this path is the old path for the file
+ *
+ * @return mixed
+ */
+ public function getPath() {
+ return $this->path;
+ }
+}
diff --git a/lib/private/Files/Notify/RenameChange.php b/lib/private/Files/Notify/RenameChange.php
new file mode 100644
index 00000000000..b83dffa0cb2
--- /dev/null
+++ b/lib/private/Files/Notify/RenameChange.php
@@ -0,0 +1,52 @@
+<?php
+/**
+ * @copyright Copyright (c) 2017 Robin Appelman <robin@icewind.nl>
+ *
+ * @author Robin Appelman <robin@icewind.nl>
+ *
+ * @license GNU AGPL version 3 or any later version
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+namespace OC\Files\Notify;
+
+use OCP\Files\Notify\IRenameChange;
+
+class RenameChange extends Change implements IRenameChange {
+ /** @var string */
+ private $targetPath;
+
+ /**
+ * Change constructor.
+ *
+ * @param int $type
+ * @param string $path
+ * @param string $targetPath
+ */
+ public function __construct($type, $path, $targetPath) {
+ parent::__construct($type, $path);
+ $this->targetPath = $targetPath;
+ }
+
+ /**
+ * Get the new path of the renamed file relative to the storage root
+ *
+ * @return string
+ */
+ public function getTargetPath() {
+ return $this->targetPath;
+ }
+}
diff --git a/lib/private/Files/ObjectStore/ObjectStoreStorage.php b/lib/private/Files/ObjectStore/ObjectStoreStorage.php
index 2dcf830cc1e..ab77c21e6c4 100644
--- a/lib/private/Files/ObjectStore/ObjectStoreStorage.php
+++ b/lib/private/Files/ObjectStore/ObjectStoreStorage.php
@@ -25,16 +25,12 @@
namespace OC\Files\ObjectStore;
+use Icewind\Streams\CallbackWrapper;
use Icewind\Streams\IteratorDirectory;
use OC\Files\Cache\CacheEntry;
use OCP\Files\ObjectStore\IObjectStore;
class ObjectStoreStorage extends \OC\Files\Storage\Common {
-
- /**
- * @var array
- */
- private static $tmpFiles = array();
/**
* @var \OCP\Files\ObjectStore\IObjectStore $objectStore
*/
@@ -291,14 +287,14 @@ class ObjectStoreStorage extends \OC\Files\Storage\Common {
$ext = '';
}
$tmpFile = \OC::$server->getTempManager()->getTemporaryFile($ext);
- \OC\Files\Stream\Close::registerCallback($tmpFile, array($this, 'writeBack'));
if ($this->file_exists($path)) {
$source = $this->fopen($path, 'r');
file_put_contents($tmpFile, $source);
}
- self::$tmpFiles[$tmpFile] = $path;
-
- return fopen('close://' . $tmpFile, $mode);
+ $handle = fopen($tmpFile, $mode);
+ return CallbackWrapper::wrap($handle, null, null, function () use ($path, $tmpFile) {
+ $this->writeBack($tmpFile, $path);
+ });
}
return false;
}
@@ -368,12 +364,7 @@ class ObjectStoreStorage extends \OC\Files\Storage\Common {
return true;
}
- public function writeBack($tmpFile) {
- if (!isset(self::$tmpFiles[$tmpFile])) {
- return;
- }
-
- $path = self::$tmpFiles[$tmpFile];
+ public function writeBack($tmpFile, $path) {
$stat = $this->stat($path);
if (empty($stat)) {
// create new file
diff --git a/lib/private/Files/Storage/Common.php b/lib/private/Files/Storage/Common.php
index 5561f6a889b..f3e3cb9e58c 100644
--- a/lib/private/Files/Storage/Common.php
+++ b/lib/private/Files/Storage/Common.php
@@ -436,10 +436,14 @@ abstract class Common implements Storage, ILockingStorage {
* @return bool
*/
public function test() {
- if ($this->stat('')) {
- return true;
+ try {
+ if ($this->stat('')) {
+ return true;
+ }
+ return false;
+ } catch (\Exception $e) {
+ return false;
}
- return false;
}
/**
diff --git a/lib/private/Files/Storage/DAV.php b/lib/private/Files/Storage/DAV.php
index ea4bbba2748..3b89a66d6a2 100644
--- a/lib/private/Files/Storage/DAV.php
+++ b/lib/private/Files/Storage/DAV.php
@@ -36,6 +36,7 @@ namespace OC\Files\Storage;
use Exception;
use GuzzleHttp\Exception\RequestException;
use GuzzleHttp\Message\ResponseInterface;
+use Icewind\Streams\CallbackWrapper;
use OC\Files\Filesystem;
use OC\Files\Stream\Close;
use Icewind\Streams\IteratorDirectory;
@@ -48,7 +49,6 @@ use OCP\Files\StorageInvalidException;
use OCP\Files\StorageNotAvailableException;
use OCP\Util;
use Sabre\DAV\Client;
-use Sabre\DAV\Exception\NotFound;
use Sabre\DAV\Xml\Property\ResourceType;
use Sabre\HTTP\ClientException;
use Sabre\HTTP\ClientHttpException;
@@ -77,8 +77,6 @@ class DAV extends Common {
private $client;
/** @var ArrayCache */
private $statCache;
- /** @var array */
- private static $tempFiles = [];
/** @var \OCP\Http\Client\IClientService */
private $httpClientService;
@@ -203,13 +201,15 @@ class DAV extends Common {
$this->init();
$path = $this->cleanPath($path);
try {
- $response = $this->client->propfind(
+ $response = $this->client->propFind(
$this->encodePath($path),
- array(),
+ [],
1
);
- $id = md5('webdav' . $this->root . $path);
- $content = array();
+ if ($response === false) {
+ return false;
+ }
+ $content = [];
$files = array_keys($response);
array_shift($files); //the first entry is the current directory
@@ -226,13 +226,6 @@ class DAV extends Common {
$content[] = $file;
}
return IteratorDirectory::wrap($content);
- } catch (ClientHttpException $e) {
- if ($e->getHttpStatus() === 404) {
- $this->statCache->clear($path . '/');
- $this->statCache->set($path, false);
- return false;
- }
- $this->convertException($e, $path);
} catch (\Exception $e) {
$this->convertException($e, $path);
}
@@ -247,22 +240,18 @@ class DAV extends Common {
*
* @param string $path path to propfind
*
- * @return array propfind response
+ * @return array|boolean propfind response or false if the entry was not found
*
- * @throws NotFound
+ * @throws ClientHttpException
*/
protected function propfind($path) {
$path = $this->cleanPath($path);
$cachedResponse = $this->statCache->get($path);
- if ($cachedResponse === false) {
- // we know it didn't exist
- throw new NotFound();
- }
// we either don't know it, or we know it exists but need more details
if (is_null($cachedResponse) || $cachedResponse === true) {
$this->init();
try {
- $response = $this->client->propfind(
+ $response = $this->client->propFind(
$this->encodePath($path),
array(
'{DAV:}getlastmodified',
@@ -275,11 +264,15 @@ class DAV extends Common {
)
);
$this->statCache->set($path, $response);
- } catch (NotFound $e) {
- // remember that this path did not exist
- $this->statCache->clear($path . '/');
- $this->statCache->set($path, false);
- throw $e;
+ } catch (ClientHttpException $e) {
+ if ($e->getHttpStatus() === 404) {
+ $this->statCache->clear($path . '/');
+ $this->statCache->set($path, false);
+ return false;
+ }
+ $this->convertException($e, $path);
+ } catch (\Exception $e) {
+ $this->convertException($e, $path);
}
} else {
$response = $cachedResponse;
@@ -291,17 +284,15 @@ class DAV extends Common {
public function filetype($path) {
try {
$response = $this->propfind($path);
- $responseType = array();
+ if ($response === false) {
+ return false;
+ }
+ $responseType = [];
if (isset($response["{DAV:}resourcetype"])) {
/** @var ResourceType[] $response */
$responseType = $response["{DAV:}resourcetype"]->getValue();
}
return (count($responseType) > 0 and $responseType[0] == "{DAV:}collection") ? 'dir' : 'file';
- } catch (ClientHttpException $e) {
- if ($e->getHttpStatus() === 404) {
- return false;
- }
- $this->convertException($e, $path);
} catch (\Exception $e) {
$this->convertException($e, $path);
}
@@ -320,13 +311,7 @@ class DAV extends Common {
return true;
}
// need to get from server
- $this->propfind($path);
- return true; //no 404 exception
- } catch (ClientHttpException $e) {
- if ($e->getHttpStatus() === 404) {
- return false;
- }
- $this->convertException($e, $path);
+ return ($this->propfind($path) !== false);
} catch (\Exception $e) {
$this->convertException($e, $path);
}
@@ -409,20 +394,19 @@ class DAV extends Common {
}
$tmpFile = $tempManager->getTemporaryFile($ext);
}
- Close::registerCallback($tmpFile, array($this, 'writeBack'));
- self::$tempFiles[$tmpFile] = $path;
- return fopen('close://' . $tmpFile, $mode);
+ $handle = fopen($tmpFile, $mode);
+ return CallbackWrapper::wrap($handle, null, null, function () use ($path, $tmpFile) {
+ $this->writeBack($tmpFile, $path);
+ });
}
}
/**
* @param string $tmpFile
*/
- public function writeBack($tmpFile) {
- if (isset(self::$tempFiles[$tmpFile])) {
- $this->uploadFile($tmpFile, self::$tempFiles[$tmpFile]);
- unlink($tmpFile);
- }
+ public function writeBack($tmpFile, $path) {
+ $this->uploadFile($tmpFile, $path);
+ unlink($tmpFile);
}
/** {@inheritdoc} */
@@ -431,7 +415,10 @@ class DAV extends Common {
$path = $this->cleanPath($path);
try {
// TODO: cacheable ?
- $response = $this->client->propfind($this->encodePath($path), array('{DAV:}quota-available-bytes'));
+ $response = $this->client->propfind($this->encodePath($path), ['{DAV:}quota-available-bytes']);
+ if ($response === false) {
+ return FileInfo::SPACE_UNKNOWN;
+ }
if (isset($response['{DAV:}quota-available-bytes'])) {
return (int)$response['{DAV:}quota-available-bytes'];
} else {
@@ -457,6 +444,9 @@ class DAV extends Common {
$this->client->proppatch($this->encodePath($path), ['{DAV:}lastmodified' => $mtime]);
// non-owncloud clients might not have accepted the property, need to recheck it
$response = $this->client->propfind($this->encodePath($path), ['{DAV:}getlastmodified'], 0);
+ if ($response === false) {
+ return false;
+ }
if (isset($response['{DAV:}getlastmodified'])) {
$remoteMtime = strtotime($response['{DAV:}getlastmodified']);
if ($remoteMtime !== $mtime) {
@@ -579,15 +569,13 @@ class DAV extends Common {
public function stat($path) {
try {
$response = $this->propfind($path);
- return array(
+ if (!$response) {
+ return false;
+ }
+ return [
'mtime' => strtotime($response['{DAV:}getlastmodified']),
'size' => (int)isset($response['{DAV:}getcontentlength']) ? $response['{DAV:}getcontentlength'] : 0,
- );
- } catch (ClientHttpException $e) {
- if ($e->getHttpStatus() === 404) {
- return array();
- }
- $this->convertException($e, $path);
+ ];
} catch (\Exception $e) {
$this->convertException($e, $path);
}
@@ -598,7 +586,10 @@ class DAV extends Common {
public function getMimeType($path) {
try {
$response = $this->propfind($path);
- $responseType = array();
+ if ($response === false) {
+ return false;
+ }
+ $responseType = [];
if (isset($response["{DAV:}resourcetype"])) {
/** @var ResourceType[] $response */
$responseType = $response["{DAV:}resourcetype"]->getValue();
@@ -611,11 +602,6 @@ class DAV extends Common {
} else {
return false;
}
- } catch (ClientHttpException $e) {
- if ($e->getHttpStatus() === 404) {
- return false;
- }
- $this->convertException($e, $path);
} catch (\Exception $e) {
$this->convertException($e, $path);
}
@@ -706,6 +692,9 @@ class DAV extends Common {
$this->init();
$path = $this->cleanPath($path);
$response = $this->propfind($path);
+ if ($response === false) {
+ return 0;
+ }
if (isset($response['{http://owncloud.org/ns}permissions'])) {
return $this->parsePermissions($response['{http://owncloud.org/ns}permissions']);
} else if ($this->is_dir($path)) {
@@ -722,6 +711,9 @@ class DAV extends Common {
$this->init();
$path = $this->cleanPath($path);
$response = $this->propfind($path);
+ if ($response === false) {
+ return null;
+ }
if (isset($response['{DAV:}getetag'])) {
return trim($response['{DAV:}getetag'], '"');
}
@@ -765,6 +757,13 @@ class DAV extends Common {
// force refresh for $path
$this->statCache->remove($path);
$response = $this->propfind($path);
+ if ($response === false) {
+ if ($path === '') {
+ // if root is gone it means the storage is not available
+ throw new StorageNotAvailableException(get_class($e) . ': ' . $e->getMessage());
+ }
+ return false;
+ }
if (isset($response['{DAV:}getetag'])) {
$cachedData = $this->getCache()->get($path);
$etag = null;
@@ -787,7 +786,7 @@ class DAV extends Common {
return $remoteMtime > $time;
}
} catch (ClientHttpException $e) {
- if ($e->getHttpStatus() === 404 || $e->getHttpStatus() === 405) {
+ if ($e->getHttpStatus() === 405) {
if ($path === '') {
// if root is gone it means the storage is not available
throw new StorageNotAvailableException(get_class($e) . ': ' . $e->getMessage());
diff --git a/lib/private/Files/Storage/Home.php b/lib/private/Files/Storage/Home.php
index e5ba0f9dfe4..57b32349324 100644
--- a/lib/private/Files/Storage/Home.php
+++ b/lib/private/Files/Storage/Home.php
@@ -44,19 +44,12 @@ class Home extends Local implements \OCP\Files\IHomeStorage {
/**
* Construct a Home storage instance
* @param array $arguments array with "user" containing the
- * storage owner and "legacy" containing "true" if the storage is
- * a legacy storage with "local::" URL instead of the new "home::" one.
+ * storage owner
*/
public function __construct($arguments) {
$this->user = $arguments['user'];
$datadir = $this->user->getHome();
- if (isset($arguments['legacy']) && $arguments['legacy']) {
- // legacy home id (<= 5.0.12)
- $this->id = 'local::' . $datadir . '/';
- }
- else {
- $this->id = 'home::' . $this->user->getUID();
- }
+ $this->id = 'home::' . $this->user->getUID();
parent::__construct(array('datadir' => $datadir));
}
diff --git a/lib/private/Files/Storage/Local.php b/lib/private/Files/Storage/Local.php
index 4fe7dcafbbf..80d48680be1 100644
--- a/lib/private/Files/Storage/Local.php
+++ b/lib/private/Files/Storage/Local.php
@@ -205,18 +205,7 @@ class Local extends \OC\Files\Storage\Common {
}
public function file_get_contents($path) {
- // file_get_contents() has a memory leak: https://bugs.php.net/bug.php?id=61961
- $fileName = $this->getSourcePath($path);
-
- $fileSize = filesize($fileName);
- if ($fileSize === 0) {
- return '';
- }
-
- $handle = fopen($fileName, 'rb');
- $content = fread($handle, $fileSize);
- fclose($handle);
- return $content;
+ return file_get_contents($this->getSourcePath($path));
}
public function file_put_contents($path, $data) {
diff --git a/lib/private/Files/Stream/Close.php b/lib/private/Files/Stream/Close.php
deleted file mode 100644
index 7cc9903c912..00000000000
--- a/lib/private/Files/Stream/Close.php
+++ /dev/null
@@ -1,119 +0,0 @@
-<?php
-/**
- * @copyright Copyright (c) 2016, ownCloud, Inc.
- *
- * @author Jörn Friedrich Dreyer <jfd@butonic.de>
- * @author Morris Jobke <hey@morrisjobke.de>
- * @author Robin Appelman <robin@icewind.nl>
- *
- * @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 OC\Files\Stream;
-
-/**
- * stream wrapper that provides a callback on stream close
- */
-class Close {
- private static $callBacks = array();
- private $path = '';
- private $source;
- private static $open = array();
-
- public function stream_open($path, $mode, $options, &$opened_path) {
- $path = substr($path, strlen('close://'));
- $this->path = $path;
- $this->source = fopen($path, $mode);
- if (is_resource($this->source)) {
- $this->meta = stream_get_meta_data($this->source);
- }
- self::$open[] = $path;
- return is_resource($this->source);
- }
-
- public function stream_seek($offset, $whence = SEEK_SET) {
- return fseek($this->source, $offset, $whence) === 0;
- }
-
- public function stream_tell() {
- return ftell($this->source);
- }
-
- public function stream_read($count) {
- return fread($this->source, $count);
- }
-
- public function stream_write($data) {
- return fwrite($this->source, $data);
- }
-
- public function stream_set_option($option, $arg1, $arg2) {
- switch ($option) {
- case STREAM_OPTION_BLOCKING:
- stream_set_blocking($this->source, $arg1);
- break;
- case STREAM_OPTION_READ_TIMEOUT:
- stream_set_timeout($this->source, $arg1, $arg2);
- break;
- case STREAM_OPTION_WRITE_BUFFER:
- stream_set_write_buffer($this->source, $arg1, $arg2);
- }
- }
-
- public function stream_stat() {
- return fstat($this->source);
- }
-
- public function stream_lock($mode) {
- flock($this->source, $mode);
- }
-
- public function stream_flush() {
- return fflush($this->source);
- }
-
- public function stream_eof() {
- return feof($this->source);
- }
-
- public function url_stat($path) {
- $path = substr($path, strlen('close://'));
- if (file_exists($path)) {
- return stat($path);
- } else {
- return false;
- }
- }
-
- public function stream_close() {
- fclose($this->source);
- if (isset(self::$callBacks[$this->path])) {
- call_user_func(self::$callBacks[$this->path], $this->path);
- }
- }
-
- public function unlink($path) {
- $path = substr($path, strlen('close://'));
- return unlink($path);
- }
-
- /**
- * @param string $path
- */
- public static function registerCallback($path, $callback) {
- self::$callBacks[$path] = $callback;
- }
-}
diff --git a/lib/private/Files/Stream/Dir.php b/lib/private/Files/Stream/Dir.php
deleted file mode 100644
index f8f6b1fa89a..00000000000
--- a/lib/private/Files/Stream/Dir.php
+++ /dev/null
@@ -1,67 +0,0 @@
-<?php
-/**
- * @copyright Copyright (c) 2016, ownCloud, Inc.
- *
- * @author Jörn Friedrich Dreyer <jfd@butonic.de>
- * @author Morris Jobke <hey@morrisjobke.de>
- * @author Robin Appelman <robin@icewind.nl>
- *
- * @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 OC\Files\Stream;
-
-class Dir {
- private static $dirs = array();
- private $name;
- private $index;
-
- public function dir_opendir($path, $options) {
- $this->name = substr($path, strlen('fakedir://'));
- $this->index = 0;
- if (!isset(self::$dirs[$this->name])) {
- self::$dirs[$this->name] = array();
- }
- return true;
- }
-
- public function dir_readdir() {
- if ($this->index >= count(self::$dirs[$this->name])) {
- return false;
- }
- $filename = self::$dirs[$this->name][$this->index];
- $this->index++;
- return $filename;
- }
-
- public function dir_closedir() {
- $this->name = '';
- return true;
- }
-
- public function dir_rewinddir() {
- $this->index = 0;
- return true;
- }
-
- /**
- * @param string $path
- * @param string[] $content
- */
- public static function register($path, $content) {
- self::$dirs[$path] = $content;
- }
-}
diff --git a/lib/private/Files/Stream/Quota.php b/lib/private/Files/Stream/Quota.php
index f064ca6c011..624a2021b9c 100644
--- a/lib/private/Files/Stream/Quota.php
+++ b/lib/private/Files/Stream/Quota.php
@@ -25,61 +25,44 @@
namespace OC\Files\Stream;
+use Icewind\Streams\Wrapper;
+
/**
* stream wrapper limits the amount of data that can be written to a stream
*
- * usage: void \OC\Files\Stream\Quota::register($id, $stream, $limit)
- * or: resource \OC\Files\Stream\Quota::wrap($stream, $limit)
+ * usage: resource \OC\Files\Stream\Quota::wrap($stream, $limit)
*/
-class Quota {
- private static $streams = array();
-
- /**
- * @var resource $source
- */
- private $source;
-
+class Quota extends Wrapper {
/**
* @var int $limit
*/
private $limit;
/**
- * @param string $id
- * @param resource $stream
- * @param int $limit
- */
- public static function register($id, $stream, $limit) {
- self::$streams[$id] = array($stream, $limit);
- }
-
- /**
- * remove all registered streams
- */
- public static function clear() {
- self::$streams = array();
- }
-
- /**
* @param resource $stream
* @param int $limit
* @return resource
*/
static public function wrap($stream, $limit) {
- $id = uniqid();
- self::register($id, $stream, $limit);
- $meta = stream_get_meta_data($stream);
- return fopen('quota://' . $id, $meta['mode']);
+ $context = stream_context_create(array(
+ 'quota' => array(
+ 'source' => $stream,
+ 'limit' => $limit
+ )
+ ));
+ return Wrapper::wrapSource($stream, $context, 'quota', self::class);
}
public function stream_open($path, $mode, $options, &$opened_path) {
- $id = substr($path, strlen('quota://'));
- if (isset(self::$streams[$id])) {
- list($this->source, $this->limit) = self::$streams[$id];
- return true;
- } else {
- return false;
- }
+ $context = $this->loadContext('quota');
+ $this->source = $context['source'];
+ $this->limit = $context['limit'];
+
+ return true;
+ }
+
+ public function dir_opendir($path, $options) {
+ return false;
}
public function stream_seek($offset, $whence = SEEK_SET) {
@@ -103,10 +86,6 @@ class Quota {
return fseek($this->source, $offset, $whence) === 0;
}
- public function stream_tell() {
- return ftell($this->source);
- }
-
public function stream_read($count) {
$this->limit -= $count;
return fread($this->source, $count);
@@ -121,37 +100,4 @@ class Quota {
$this->limit -= $size;
return fwrite($this->source, $data);
}
-
- public function stream_set_option($option, $arg1, $arg2) {
- switch ($option) {
- case STREAM_OPTION_BLOCKING:
- stream_set_blocking($this->source, $arg1);
- break;
- case STREAM_OPTION_READ_TIMEOUT:
- stream_set_timeout($this->source, $arg1, $arg2);
- break;
- case STREAM_OPTION_WRITE_BUFFER:
- stream_set_write_buffer($this->source, $arg1, $arg2);
- }
- }
-
- public function stream_stat() {
- return fstat($this->source);
- }
-
- public function stream_lock($mode) {
- return flock($this->source, $mode);
- }
-
- public function stream_flush() {
- return fflush($this->source);
- }
-
- public function stream_eof() {
- return feof($this->source);
- }
-
- public function stream_close() {
- fclose($this->source);
- }
}
diff --git a/lib/private/Files/Stream/StaticStream.php b/lib/private/Files/Stream/StaticStream.php
deleted file mode 100644
index 3c91b23fd36..00000000000
--- a/lib/private/Files/Stream/StaticStream.php
+++ /dev/null
@@ -1,171 +0,0 @@
-<?php
-/**
- * @copyright Copyright (c) 2016, ownCloud, Inc.
- *
- * @author Morris Jobke <hey@morrisjobke.de>
- * @author Robin Appelman <robin@icewind.nl>
- *
- * @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 OC\Files\Stream;
-
-class StaticStream {
- const MODE_FILE = 0100000;
-
- public $context;
- protected static $data = array();
-
- protected $path = '';
- protected $pointer = 0;
- protected $writable = false;
-
- public function stream_close() {
- }
-
- public function stream_eof() {
- return $this->pointer >= strlen(self::$data[$this->path]);
- }
-
- public function stream_flush() {
- }
-
- public static function clear() {
- self::$data = array();
- }
-
- public function stream_open($path, $mode, $options, &$opened_path) {
- switch ($mode[0]) {
- case 'r':
- if (!isset(self::$data[$path])) return false;
- $this->path = $path;
- $this->writable = isset($mode[1]) && $mode[1] == '+';
- break;
- case 'w':
- self::$data[$path] = '';
- $this->path = $path;
- $this->writable = true;
- break;
- case 'a':
- if (!isset(self::$data[$path])) self::$data[$path] = '';
- $this->path = $path;
- $this->writable = true;
- $this->pointer = strlen(self::$data[$path]);
- break;
- case 'x':
- if (isset(self::$data[$path])) return false;
- $this->path = $path;
- $this->writable = true;
- break;
- case 'c':
- if (!isset(self::$data[$path])) self::$data[$path] = '';
- $this->path = $path;
- $this->writable = true;
- break;
- default:
- return false;
- }
- $opened_path = $this->path;
- return true;
- }
-
- public function stream_read($count) {
- $bytes = min(strlen(self::$data[$this->path]) - $this->pointer, $count);
- $data = substr(self::$data[$this->path], $this->pointer, $bytes);
- $this->pointer += $bytes;
- return $data;
- }
-
- public function stream_seek($offset, $whence = SEEK_SET) {
- $len = strlen(self::$data[$this->path]);
- switch ($whence) {
- case SEEK_SET:
- if ($offset <= $len) {
- $this->pointer = $offset;
- return true;
- }
- break;
- case SEEK_CUR:
- if ($this->pointer + $offset <= $len) {
- $this->pointer += $offset;
- return true;
- }
- break;
- case SEEK_END:
- if ($len + $offset <= $len) {
- $this->pointer = $len + $offset;
- return true;
- }
- break;
- }
- return false;
- }
-
- public function stream_stat() {
- return $this->url_stat($this->path);
- }
-
- public function stream_tell() {
- return $this->pointer;
- }
-
- public function stream_write($data) {
- if (!$this->writable) return 0;
- $size = strlen($data);
- if ($this->stream_eof()) {
- self::$data[$this->path] .= $data;
- } else {
- self::$data[$this->path] = substr_replace(
- self::$data[$this->path],
- $data,
- $this->pointer
- );
- }
- $this->pointer += $size;
- return $size;
- }
-
- public function unlink($path) {
- if (isset(self::$data[$path])) {
- unset(self::$data[$path]);
- }
- return true;
- }
-
- public function url_stat($path) {
- if (isset(self::$data[$path])) {
- $size = strlen(self::$data[$path]);
- $time = time();
- $data = array(
- 'dev' => 0,
- 'ino' => 0,
- 'mode' => self::MODE_FILE | 0777,
- 'nlink' => 1,
- 'uid' => 0,
- 'gid' => 0,
- 'rdev' => '',
- 'size' => $size,
- 'atime' => $time,
- 'mtime' => $time,
- 'ctime' => $time,
- 'blksize' => -1,
- 'blocks' => -1,
- );
- return array_values($data) + $data;
- }
- return false;
- }
-}
diff --git a/lib/private/Files/Type/Detection.php b/lib/private/Files/Type/Detection.php
index 84d727ebb0e..cd4ddc2f067 100644
--- a/lib/private/Files/Type/Detection.php
+++ b/lib/private/Files/Type/Detection.php
@@ -167,6 +167,10 @@ class Detection implements IMimeTypeDetector {
$this->loadMappings();
$fileName = basename($path);
+
+ // remove leading dot on hidden files with a file extension
+ $fileName = ltrim($fileName, '.');
+
// note: leading dot doesn't qualify as extension
if (strpos($fileName, '.') > 0) {
//try to guess the type by the file extension
diff --git a/lib/private/Files/Utils/Scanner.php b/lib/private/Files/Utils/Scanner.php
index d26c601be1a..98e2c3c8ca2 100644
--- a/lib/private/Files/Utils/Scanner.php
+++ b/lib/private/Files/Utils/Scanner.php
@@ -31,6 +31,7 @@ use OC\Files\Filesystem;
use OC\ForbiddenException;
use OC\Hooks\PublicEmitter;
use OC\Lock\DBLockingProvider;
+use OCA\Files_Sharing\SharedStorage;
use OCP\Files\Storage\IStorage;
use OCP\Files\StorageNotAvailableException;
use OCP\ILogger;
@@ -118,14 +119,19 @@ class Scanner extends PublicEmitter {
public function backgroundScan($dir) {
$mounts = $this->getMounts($dir);
foreach ($mounts as $mount) {
- if (is_null($mount->getStorage())) {
+ $storage = $mount->getStorage();
+ if (is_null($storage)) {
continue;
}
// don't scan the root storage
- if ($mount->getStorage()->instanceOfStorage('\OC\Files\Storage\Local') && $mount->getMountPoint() === '/') {
+ if ($storage->instanceOfStorage('\OC\Files\Storage\Local') && $mount->getMountPoint() === '/') {
+ continue;
+ }
+
+ // don't scan received local shares, these can be scanned when scanning the owner's storage
+ if ($storage->instanceOfStorage(SharedStorage::class)) {
continue;
}
- $storage = $mount->getStorage();
$scanner = $storage->getScanner();
$this->attachListener($mount);
@@ -156,10 +162,10 @@ class Scanner extends PublicEmitter {
}
$mounts = $this->getMounts($dir);
foreach ($mounts as $mount) {
- if (is_null($mount->getStorage())) {
+ $storage = $mount->getStorage();
+ if (is_null($storage)) {
continue;
}
- $storage = $mount->getStorage();
// if the home storage isn't writable then the scanner is run as the wrong user
if ($storage->instanceOfStorage('\OC\Files\Storage\Home') and
(!$storage->isCreatable('') or !$storage->isCreatable('files'))
@@ -171,6 +177,11 @@ class Scanner extends PublicEmitter {
}
}
+
+ // don't scan received local shares, these can be scanned when scanning the owner's storage
+ if ($storage->instanceOfStorage(SharedStorage::class)) {
+ continue;
+ }
$relativePath = $mount->getInternalPath($dir);
$scanner = $storage->getScanner();
$scanner->setUseTransactions(false);
diff --git a/lib/private/Files/View.php b/lib/private/Files/View.php
index 6facc7b9462..db21d400b39 100644
--- a/lib/private/Files/View.php
+++ b/lib/private/Files/View.php
@@ -931,39 +931,36 @@ class View {
/**
* @param string $path
- * @param string $mode
+ * @param string $mode 'r' or 'w'
* @return resource
*/
public function fopen($path, $mode) {
+ $mode = str_replace('b', '', $mode); // the binary flag is a windows only feature which we do not support
$hooks = array();
switch ($mode) {
case 'r':
- case 'rb':
$hooks[] = 'read';
break;
case 'r+':
- case 'rb+':
case 'w+':
- case 'wb+':
case 'x+':
- case 'xb+':
case 'a+':
- case 'ab+':
$hooks[] = 'read';
$hooks[] = 'write';
break;
case 'w':
- case 'wb':
case 'x':
- case 'xb':
case 'a':
- case 'ab':
$hooks[] = 'write';
break;
default:
\OCP\Util::writeLog('core', 'invalid mode (' . $mode . ') for ' . $path, \OCP\Util::ERROR);
}
+ if ($mode !== 'r' && $mode !== 'w') {
+ \OC::$server->getLogger()->info('Trying to open a file with a mode other than "r" or "w" can cause severe performance issues with some backends');
+ }
+
return $this->basicOperation('fopen', $path, $hooks, $mode);
}
@@ -1005,7 +1002,7 @@ class View {
// Create the directories if any
if (!$this->file_exists($filePath)) {
$result = $this->createParentDirectories($filePath);
- if($result === false) {
+ if ($result === false) {
return false;
}
}
@@ -1149,6 +1146,8 @@ class View {
$unlockLater = false;
if ($this->lockingEnabled && $operation === 'fopen' && is_resource($result)) {
$unlockLater = true;
+ // make sure our unlocking callback will still be called if connection is aborted
+ ignore_user_abort(true);
$result = CallbackWrapper::wrap($result, null, null, function () use ($hooks, $path) {
if (in_array('write', $hooks)) {
$this->unlockFile($path, ILockingProvider::LOCK_EXCLUSIVE);
@@ -1357,7 +1356,7 @@ class View {
//add the sizes of other mount points to the folder
$extOnly = ($includeMountPoints === 'ext');
$mounts = Filesystem::getMountManager()->findIn($path);
- $info->setSubMounts(array_filter($mounts, function(IMountPoint $mount) use ($extOnly) {
+ $info->setSubMounts(array_filter($mounts, function (IMountPoint $mount) use ($extOnly) {
$subStorage = $mount->getStorage();
return !($extOnly && $subStorage instanceof \OCA\Files_Sharing\SharedStorage);
}));
@@ -2106,13 +2105,13 @@ class View {
private function createParentDirectories($filePath) {
$directoryParts = explode('/', $filePath);
$directoryParts = array_filter($directoryParts);
- foreach($directoryParts as $key => $part) {
+ foreach ($directoryParts as $key => $part) {
$currentPathElements = array_slice($directoryParts, 0, $key);
$currentPath = '/' . implode('/', $currentPathElements);
- if($this->is_file($currentPath)) {
+ if ($this->is_file($currentPath)) {
return false;
}
- if(!$this->file_exists($currentPath)) {
+ if (!$this->file_exists($currentPath)) {
$this->mkdir($currentPath);
}
}
diff --git a/lib/private/Group/Backend.php b/lib/private/Group/Backend.php
index 14c36d93422..1e8d62f5e42 100644
--- a/lib/private/Group/Backend.php
+++ b/lib/private/Group/Backend.php
@@ -31,23 +31,14 @@ abstract class Backend implements \OCP\GroupInterface {
*/
const NOT_IMPLEMENTED = -501;
- /**
- * actions that user backends can define
- */
- const CREATE_GROUP = 0x00000001;
- const DELETE_GROUP = 0x00000010;
- const ADD_TO_GROUP = 0x00000100;
- const REMOVE_FROM_GOUP = 0x00001000;
- //OBSOLETE const GET_DISPLAYNAME = 0x00010000;
- const COUNT_USERS = 0x00100000;
-
- protected $possibleActions = array(
+ protected $possibleActions = [
self::CREATE_GROUP => 'createGroup',
self::DELETE_GROUP => 'deleteGroup',
self::ADD_TO_GROUP => 'addToGroup',
self::REMOVE_FROM_GOUP => 'removeFromGroup',
self::COUNT_USERS => 'countUsersInGroup',
- );
+ self::GROUP_DETAILS => 'getGroupDetails',
+ ];
/**
* Get all supported actions
diff --git a/lib/private/Group/Database.php b/lib/private/Group/Database.php
index e4144fdbe20..8be24fc50de 100644
--- a/lib/private/Group/Database.php
+++ b/lib/private/Group/Database.php
@@ -202,6 +202,11 @@ class Database extends \OC\Group\Backend {
* if the user exists at all.
*/
public function getUserGroups( $uid ) {
+ //guests has empty or null $uid
+ if ($uid === null || $uid === '') {
+ return [];
+ }
+
$this->fixDI();
// No magic!
diff --git a/lib/private/Group/Group.php b/lib/private/Group/Group.php
index 9379d604c48..69dce215694 100644
--- a/lib/private/Group/Group.php
+++ b/lib/private/Group/Group.php
@@ -31,6 +31,9 @@ namespace OC\Group;
use OCP\IGroup;
class Group implements IGroup {
+ /** @var null|string */
+ protected $displayName;
+
/**
* @var string $id
*/
@@ -66,18 +69,27 @@ class Group implements IGroup {
* @param \OC\Group\Backend[] $backends
* @param \OC\User\Manager $userManager
* @param \OC\Hooks\PublicEmitter $emitter
+ * @param string $displayName
*/
- public function __construct($gid, $backends, $userManager, $emitter = null) {
+ public function __construct($gid, $backends, $userManager, $emitter = null, $displayName = null) {
$this->gid = $gid;
$this->backends = $backends;
$this->userManager = $userManager;
$this->emitter = $emitter;
+ $this->displayName = $displayName;
}
public function getGID() {
return $this->gid;
}
+ public function getDisplayName() {
+ if (is_null($this->displayName)) {
+ return $this->gid;
+ }
+ return $this->displayName;
+ }
+
/**
* get all users in the group
*
diff --git a/lib/private/Group/Manager.php b/lib/private/Group/Manager.php
index 31911138985..944598a8296 100644
--- a/lib/private/Group/Manager.php
+++ b/lib/private/Group/Manager.php
@@ -155,19 +155,29 @@ class Manager extends PublicEmitter implements IGroupManager {
/**
* @param string $gid
+ * @param string $displayName
* @return \OCP\IGroup
*/
- protected function getGroupObject($gid) {
+ protected function getGroupObject($gid, $displayName = null) {
$backends = array();
foreach ($this->backends as $backend) {
- if ($backend->groupExists($gid)) {
+ if ($backend->implementsActions(\OC\Group\Backend::GROUP_DETAILS)) {
+ $groupData = $backend->getGroupDetails($gid);
+ if (is_array($groupData)) {
+ // take the display name from the first backend that has a non-null one
+ if (is_null($displayName) && isset($groupData['displayName'])) {
+ $displayName = $groupData['displayName'];
+ }
+ $backends[] = $backend;
+ }
+ } else if ($backend->groupExists($gid)) {
$backends[] = $backend;
}
}
if (count($backends) === 0) {
return null;
}
- $this->cachedGroups[$gid] = new Group($gid, $backends, $this->userManager, $this);
+ $this->cachedGroups[$gid] = new Group($gid, $backends, $this->userManager, $this, $displayName);
return $this->cachedGroups[$gid];
}
diff --git a/lib/private/Http/Client/Client.php b/lib/private/Http/Client/Client.php
index 8e182111303..4697f2e038c 100644
--- a/lib/private/Http/Client/Client.php
+++ b/lib/private/Http/Client/Client.php
@@ -77,9 +77,10 @@ class Client implements IClient {
}
}
- $this->client->setDefaultOption('headers/User-Agent', 'ownCloud Server Crawler');
- if ($this->getProxyUri() !== '') {
- $this->client->setDefaultOption('proxy', $this->getProxyUri());
+ $this->client->setDefaultOption('headers/User-Agent', 'Nextcloud Server Crawler');
+ $proxyUri = $this->getProxyUri();
+ if ($proxyUri !== '') {
+ $this->client->setDefaultOption('proxy', $proxyUri);
}
}
@@ -93,10 +94,10 @@ class Client implements IClient {
$proxyUserPwd = $this->config->getSystemValue('proxyuserpwd', null);
$proxyUri = '';
- if (!is_null($proxyUserPwd)) {
+ if ($proxyUserPwd !== null) {
$proxyUri .= $proxyUserPwd . '@';
}
- if (!is_null($proxyHost)) {
+ if ($proxyHost !== null) {
$proxyUri .= $proxyHost;
}
diff --git a/lib/private/IntegrityCheck/Checker.php b/lib/private/IntegrityCheck/Checker.php
index 102fe42a99d..419f989fa0f 100644
--- a/lib/private/IntegrityCheck/Checker.php
+++ b/lib/private/IntegrityCheck/Checker.php
@@ -96,12 +96,8 @@ class Checker {
* @return bool
*/
public function isCodeCheckEnforced() {
- $signedChannels = [
- 'daily',
- 'testing',
- 'stable',
- ];
- if(!in_array($this->environmentHelper->getChannel(), $signedChannels, true)) {
+ $notSignedChannels = [ '', 'git'];
+ if (in_array($this->environmentHelper->getChannel(), $notSignedChannels, true)) {
return false;
}
@@ -115,7 +111,7 @@ class Checker {
} else {
$isIntegrityCheckDisabled = false;
}
- if($isIntegrityCheckDisabled === true) {
+ if ($isIntegrityCheckDisabled === true) {
return false;
}
@@ -271,16 +267,23 @@ class Checker {
public function writeAppSignature($path,
X509 $certificate,
RSA $privateKey) {
- if(!is_dir($path)) {
- throw new \Exception('Directory does not exist.');
- }
- $iterator = $this->getFolderIterator($path);
- $hashes = $this->generateHashes($iterator, $path);
- $signature = $this->createSignatureData($hashes, $certificate, $privateKey);
- $this->fileAccessHelper->file_put_contents(
- $path . '/appinfo/signature.json',
+ $appInfoDir = $path . '/appinfo';
+ try {
+ $this->fileAccessHelper->assertDirectoryExists($appInfoDir);
+
+ $iterator = $this->getFolderIterator($path);
+ $hashes = $this->generateHashes($iterator, $path);
+ $signature = $this->createSignatureData($hashes, $certificate, $privateKey);
+ $this->fileAccessHelper->file_put_contents(
+ $appInfoDir . '/signature.json',
json_encode($signature, JSON_PRETTY_PRINT)
- );
+ );
+ } catch (\Exception $e){
+ if (!$this->fileAccessHelper->is_writable($appInfoDir)) {
+ throw new \Exception($appInfoDir . ' is not writable');
+ }
+ throw $e;
+ }
}
/**
@@ -289,17 +292,28 @@ class Checker {
* @param X509 $certificate
* @param RSA $rsa
* @param string $path
+ * @throws \Exception
*/
public function writeCoreSignature(X509 $certificate,
RSA $rsa,
$path) {
- $iterator = $this->getFolderIterator($path, $path);
- $hashes = $this->generateHashes($iterator, $path);
- $signatureData = $this->createSignatureData($hashes, $certificate, $rsa);
- $this->fileAccessHelper->file_put_contents(
- $path . '/core/signature.json',
+ $coreDir = $path . '/core';
+ try {
+
+ $this->fileAccessHelper->assertDirectoryExists($coreDir);
+ $iterator = $this->getFolderIterator($path, $path);
+ $hashes = $this->generateHashes($iterator, $path);
+ $signatureData = $this->createSignatureData($hashes, $certificate, $rsa);
+ $this->fileAccessHelper->file_put_contents(
+ $coreDir . '/signature.json',
json_encode($signatureData, JSON_PRETTY_PRINT)
- );
+ );
+ } catch (\Exception $e){
+ if (!$this->fileAccessHelper->is_writable($coreDir)) {
+ throw new \Exception($coreDir . ' is not writable');
+ }
+ throw $e;
+ }
}
/**
diff --git a/lib/private/IntegrityCheck/Helpers/FileAccessHelper.php b/lib/private/IntegrityCheck/Helpers/FileAccessHelper.php
index 9e2b76ce11a..a7e378c165e 100644
--- a/lib/private/IntegrityCheck/Helpers/FileAccessHelper.php
+++ b/lib/private/IntegrityCheck/Helpers/FileAccessHelper.php
@@ -53,10 +53,33 @@ class FileAccessHelper {
* Wrapper around file_put_contents($filename, $data)
*
* @param string $filename
- * @param $data
- * @return int|false
+ * @param string $data
+ * @return int
+ * @throws \Exception
*/
public function file_put_contents($filename, $data) {
- return file_put_contents($filename, $data);
+ $bytesWritten = @file_put_contents($filename, $data);
+ if ($bytesWritten === false || $bytesWritten !== strlen($data)){
+ throw new \Exception('Failed to write into ' . $filename);
+ }
+ return $bytesWritten;
+ }
+
+ /**
+ * @param string $path
+ * @return bool
+ */
+ public function is_writable($path) {
+ return is_writable($path);
+ }
+
+ /**
+ * @param string $path
+ * @throws \Exception
+ */
+ public function assertDirectoryExists($path) {
+ if (!is_dir($path)) {
+ throw new \Exception('Directory ' . $path . ' does not exist.');
+ }
}
}
diff --git a/lib/private/Log.php b/lib/private/Log.php
index ef1b70d3cb9..fddd3593127 100644
--- a/lib/private/Log.php
+++ b/lib/private/Log.php
@@ -106,12 +106,8 @@ class Log implements ILogger {
// FIXME: Add this for backwards compatibility, should be fixed at some point probably
if($logger === null) {
- // TODO: Drop backwards compatibility for config in the future
$logType = $this->config->getValue('log_type', 'file');
- if($logType==='owncloud') {
- $logType = 'file';
- }
- $this->logger = 'OC\\Log\\'.ucfirst($logType);
+ $this->logger = static::getLogClass($logType);
call_user_func(array($this->logger, 'init'));
} else {
$this->logger = $logger;
@@ -327,4 +323,26 @@ class Log implements ILogger {
$msg .= ': ' . json_encode($exception);
$this->error($msg, $context);
}
+
+ /**
+ * @param string $logType
+ * @return string
+ * @internal
+ */
+ public static function getLogClass($logType) {
+ switch (strtolower($logType)) {
+ case 'errorlog':
+ return \OC\Log\Errorlog::class;
+ case 'syslog':
+ return \OC\Log\Syslog::class;
+ case 'file':
+ return \OC\Log\File::class;
+
+ // Backwards compatibility for old and fallback for unknown log types
+ case 'owncloud':
+ case 'nextcloud':
+ default:
+ return \OC\Log\File::class;
+ }
+ }
}
diff --git a/lib/private/Log/File.php b/lib/private/Log/File.php
index 904aa1d93f1..be8b72b3a3f 100644
--- a/lib/private/Log/File.php
+++ b/lib/private/Log/File.php
@@ -75,8 +75,8 @@ class File {
$config = \OC::$server->getSystemConfig();
// default to ISO8601
- $format = $config->getValue('logdateformat', 'c');
- $logTimeZone = $config->getValue( "logtimezone", 'UTC' );
+ $format = $config->getValue('logdateformat', \DateTime::ATOM);
+ $logTimeZone = $config->getValue('logtimezone', 'UTC');
try {
$timezone = new \DateTimeZone($logTimeZone);
} catch (\Exception $e) {
diff --git a/lib/private/Log/Rotate.php b/lib/private/Log/Rotate.php
index 6c87c167378..866068433ff 100644
--- a/lib/private/Log/Rotate.php
+++ b/lib/private/Log/Rotate.php
@@ -32,7 +32,9 @@ namespace OC\Log;
*/
class Rotate extends \OC\BackgroundJob\Job {
private $max_log_size;
- public function run($logFile) {
+ public function run($dummy) {
+ $systemConfig = \OC::$server->getSystemConfig();
+ $logFile = $systemConfig->getValue('logfile', $systemConfig->getValue('datadirectory', \OC::$SERVERROOT . '/data') . '/nextcloud.log');
$this->max_log_size = \OC::$server->getConfig()->getSystemValue('log_rotate_size', false);
if ($this->max_log_size) {
$filesize = @filesize($logFile);
diff --git a/lib/private/Mail/Mailer.php b/lib/private/Mail/Mailer.php
index df8fa80f2c2..e704e8e3490 100644
--- a/lib/private/Mail/Mailer.php
+++ b/lib/private/Mail/Mailer.php
@@ -200,7 +200,7 @@ class Mailer implements IMailer {
* @return \Swift_SendmailTransport
*/
protected function getSendMailInstance() {
- switch ($this->config->getSystemValue('mail_smtpmode', 'sendmail')) {
+ switch ($this->config->getSystemValue('mail_smtpmode', 'php')) {
case 'qmail':
$binaryPath = '/var/qmail/bin/sendmail';
break;
diff --git a/lib/private/Memcache/APC.php b/lib/private/Memcache/APC.php
deleted file mode 100644
index 7db6d64b085..00000000000
--- a/lib/private/Memcache/APC.php
+++ /dev/null
@@ -1,136 +0,0 @@
-<?php
-/**
- * @copyright Copyright (c) 2016, ownCloud, Inc.
- *
- * @author Andreas Fischer <bantu@owncloud.com>
- * @author Clark Tomlinson <fallen013@gmail.com>
- * @author Morris Jobke <hey@morrisjobke.de>
- * @author Otto Sabart <ottosabart@seberm.com>
- * @author Robin Appelman <robin@icewind.nl>
- *
- * @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 OC\Memcache;
-
-use OCP\IMemcache;
-
-class APC extends Cache implements IMemcache {
- use CASTrait {
- cas as casEmulated;
- }
-
- use CADTrait;
-
- public function get($key) {
- $result = apc_fetch($this->getPrefix() . $key, $success);
- if (!$success) {
- return null;
- }
- return $result;
- }
-
- public function set($key, $value, $ttl = 0) {
- return apc_store($this->getPrefix() . $key, $value, $ttl);
- }
-
- public function hasKey($key) {
- return apc_exists($this->getPrefix() . $key);
- }
-
- public function remove($key) {
- return apc_delete($this->getPrefix() . $key);
- }
-
- public function clear($prefix = '') {
- $ns = $this->getPrefix() . $prefix;
- $ns = preg_quote($ns, '/');
- $iter = new \APCIterator('user', '/^' . $ns . '/', APC_ITER_KEY);
- return apc_delete($iter);
- }
-
- /**
- * Set a value in the cache if it's not already stored
- *
- * @param string $key
- * @param mixed $value
- * @param int $ttl Time To Live in seconds. Defaults to 60*60*24
- * @return bool
- */
- public function add($key, $value, $ttl = 0) {
- return apc_add($this->getPrefix() . $key, $value, $ttl);
- }
-
- /**
- * Increase a stored number
- *
- * @param string $key
- * @param int $step
- * @return int | bool
- */
- public function inc($key, $step = 1) {
- $this->add($key, 0);
- return apc_inc($this->getPrefix() . $key, $step);
- }
-
- /**
- * Decrease a stored number
- *
- * @param string $key
- * @param int $step
- * @return int | bool
- */
- public function dec($key, $step = 1) {
- return apc_dec($this->getPrefix() . $key, $step);
- }
-
- /**
- * Compare and set
- *
- * @param string $key
- * @param mixed $old
- * @param mixed $new
- * @return bool
- */
- public function cas($key, $old, $new) {
- // apc only does cas for ints
- if (is_int($old) and is_int($new)) {
- return apc_cas($this->getPrefix() . $key, $old, $new);
- } else {
- return $this->casEmulated($key, $old, $new);
- }
- }
-
- static public function isAvailable() {
- if (!extension_loaded('apc')) {
- return false;
- } elseif (!\OC::$server->getIniWrapper()->getBool('apc.enabled')) {
- return false;
- } elseif (!\OC::$server->getIniWrapper()->getBool('apc.enable_cli') && \OC::$CLI) {
- return false;
- } else {
- return true;
- }
- }
-}
-
-if (!function_exists('apc_exists')) {
- function apc_exists($keys) {
- $result = false;
- apc_fetch($keys, $result);
- return $result;
- }
-}
diff --git a/lib/private/Memcache/Factory.php b/lib/private/Memcache/Factory.php
index f3841d31679..8e62e020faa 100644
--- a/lib/private/Memcache/Factory.php
+++ b/lib/private/Memcache/Factory.php
@@ -83,7 +83,7 @@ class Factory implements ICacheFactory {
$missingCacheMessage = 'Memcache {class} not available for {use} cache';
$missingCacheHint = 'Is the matching PHP module installed and enabled?';
- if (!$localCacheClass::isAvailable()) {
+ if (!class_exists($localCacheClass) || !$localCacheClass::isAvailable()) {
if (\OC::$CLI && !defined('PHPUNIT_RUN')) {
// CLI should not hard-fail on broken memcache
$this->logger->info($missingCacheMessage, [
@@ -98,7 +98,7 @@ class Factory implements ICacheFactory {
]), $missingCacheHint);
}
}
- if (!$distributedCacheClass::isAvailable()) {
+ if (!class_exists($distributedCacheClass) || !$distributedCacheClass::isAvailable()) {
if (\OC::$CLI && !defined('PHPUNIT_RUN')) {
// CLI should not hard-fail on broken memcache
$this->logger->info($missingCacheMessage, [
@@ -113,7 +113,7 @@ class Factory implements ICacheFactory {
]), $missingCacheHint);
}
}
- if (!($lockingCacheClass && $lockingCacheClass::isAvailable())) {
+ if (!($lockingCacheClass && class_exists($distributedCacheClass) && $lockingCacheClass::isAvailable())) {
// don't fallback since the fallback might not be suitable for storing lock
$lockingCacheClass = self::NULL_CACHE;
}
diff --git a/lib/private/Memcache/Memcached.php b/lib/private/Memcache/Memcached.php
index 12f45739374..bf07fd0e6e7 100644
--- a/lib/private/Memcache/Memcached.php
+++ b/lib/private/Memcache/Memcached.php
@@ -46,16 +46,6 @@ class Memcached extends Cache implements IMemcache {
parent::__construct($prefix);
if (is_null(self::$cache)) {
self::$cache = new \Memcached();
- $servers = \OC::$server->getSystemConfig()->getValue('memcached_servers');
- if (!$servers) {
- $server = \OC::$server->getSystemConfig()->getValue('memcached_server');
- if ($server) {
- $servers = array($server);
- } else {
- $servers = array(array('localhost', 11211));
- }
- }
- self::$cache->addServers($servers);
$defaultOptions = [
\Memcached::OPT_CONNECT_TIMEOUT => 50,
@@ -71,7 +61,7 @@ class Memcached extends Cache implements IMemcache {
\Memcached::OPT_LIBKETAMA_COMPATIBLE => true,
// Enable Binary Protocol
- \Memcached::OPT_BINARY_PROTOCOL => true,
+ //\Memcached::OPT_BINARY_PROTOCOL => true,
];
// by default enable igbinary serializer if available
if (\Memcached::HAVE_IGBINARY) {
@@ -85,6 +75,17 @@ class Memcached extends Cache implements IMemcache {
} else {
throw new HintException("Expected 'memcached_options' config to be an array, got $options");
}
+
+ $servers = \OC::$server->getSystemConfig()->getValue('memcached_servers');
+ if (!$servers) {
+ $server = \OC::$server->getSystemConfig()->getValue('memcached_server');
+ if ($server) {
+ $servers = [$server];
+ } else {
+ $servers = [['localhost', 11211]];
+ }
+ }
+ self::$cache->addServers($servers);
}
}
@@ -110,7 +111,9 @@ class Memcached extends Cache implements IMemcache {
} else {
$result = self::$cache->set($this->getNamespace() . $key, $value);
}
- $this->verifyReturnCode();
+ if ($result !== true) {
+ $this->verifyReturnCode();
+ }
return $result;
}
diff --git a/lib/private/NavigationManager.php b/lib/private/NavigationManager.php
index 3e9ddfc6466..f7bc02943a3 100644
--- a/lib/private/NavigationManager.php
+++ b/lib/private/NavigationManager.php
@@ -1,6 +1,6 @@
<?php
/**
- * @copyright Copyright (c) 2016, ownCloud, Inc.
+ * @copyright Copyright (c) 2016, ownCloud GmbH
*
* @author Bart Visscher <bartv@thisnet.nl>
* @author Joas Schilling <coding@schilljs.com>
@@ -26,13 +26,46 @@
namespace OC;
+use OC\App\AppManager;
+use OCP\App\IAppManager;
+use OCP\IGroupManager;
+use OCP\INavigationManager;
+use OCP\IURLGenerator;
+use OCP\IUserSession;
+use OCP\L10N\IFactory;
+
/**
* Manages the ownCloud navigation
*/
-class NavigationManager implements \OCP\INavigationManager {
- protected $entries = array();
- protected $closureEntries = array();
+
+class NavigationManager implements INavigationManager {
+ protected $entries = [];
+ protected $closureEntries = [];
protected $activeEntry;
+ /** @var bool */
+ protected $init = false;
+ /** @var IAppManager|AppManager */
+ protected $appManager;
+ /** @var IURLGenerator */
+ private $urlGenerator;
+ /** @var IFactory */
+ private $l10nFac;
+ /** @var IUserSession */
+ private $userSession;
+ /** @var IGroupManager */
+ private $groupManager;
+
+ public function __construct(IAppManager $appManager = null,
+ IURLGenerator $urlGenerator = null,
+ IFactory $l10nFac = null,
+ IUserSession $userSession = null,
+ IGroupManager$groupManager = null) {
+ $this->appManager = $appManager;
+ $this->urlGenerator = $urlGenerator;
+ $this->l10nFac = $l10nFac;
+ $this->userSession = $userSession;
+ $this->groupManager = $groupManager;
+ }
/**
* Creates a new navigation entry
@@ -60,6 +93,7 @@ class NavigationManager implements \OCP\INavigationManager {
* @return array an array of the added entries
*/
public function getAll() {
+ $this->init();
foreach ($this->closureEntries as $c) {
$this->add($c());
}
@@ -71,8 +105,9 @@ class NavigationManager implements \OCP\INavigationManager {
* removes all the entries
*/
public function clear() {
- $this->entries = array();
- $this->closureEntries = array();
+ $this->entries = [];
+ $this->closureEntries = [];
+ $this->init = false;
}
/**
@@ -93,4 +128,64 @@ class NavigationManager implements \OCP\INavigationManager {
public function getActiveEntry() {
return $this->activeEntry;
}
+
+ private function init() {
+ if ($this->init) {
+ return;
+ }
+ $this->init = true;
+ if (is_null($this->appManager)) {
+ return;
+ }
+ foreach ($this->appManager->getInstalledApps() as $app) {
+ // load plugins and collections from info.xml
+ $info = $this->appManager->getAppInfo($app);
+ if (!isset($info['navigation'])) {
+ continue;
+ }
+ $nav = $info['navigation'];
+ if (!isset($nav['name'])) {
+ continue;
+ }
+ if (!isset($nav['route'])) {
+ continue;
+ }
+ $role = isset($nav['@attributes']['role']) ? $nav['@attributes']['role'] : 'all';
+ if ($role === 'admin' && !$this->isAdmin()) {
+ continue;
+ }
+ $l = $this->l10nFac->get($app);
+ $order = isset($nav['order']) ? $nav['order'] : 100;
+ $route = $this->urlGenerator->linkToRoute($nav['route']);
+ $icon = isset($nav['icon']) ? $nav['icon'] : 'app.svg';
+ foreach ([$icon, "$app.svg"] as $i) {
+ try {
+ $icon = $this->urlGenerator->imagePath($app, $i);
+ break;
+ } catch (\RuntimeException $ex) {
+ // no icon? - ignore it then
+ }
+ }
+ if (is_null($icon)) {
+ $icon = $this->urlGenerator->imagePath('core', 'default-app-icon');
+ }
+
+ $this->add([
+ 'id' => $app,
+ 'order' => $order,
+ 'href' => $route,
+ 'icon' => $icon,
+ 'name' => $l->t($nav['name']),
+ ]);
+ }
+ }
+
+ private function isAdmin() {
+ $user = $this->userSession->getUser();
+ if ($user !== null) {
+ return $this->groupManager->isAdmin($user->getUID());
+ }
+ return false;
+ }
+
}
diff --git a/lib/private/Preview.php b/lib/private/Preview.php
deleted file mode 100644
index caa1e89bacc..00000000000
--- a/lib/private/Preview.php
+++ /dev/null
@@ -1,1349 +0,0 @@
-<?php
-/**
- * @copyright Copyright (c) 2016, ownCloud, Inc.
- *
- * @author Björn Schießle <bjoern@schiessle.org>
- * @author Frank Karlitschek <frank@karlitschek.de>
- * @author Georg Ehrke <georg@owncloud.com>
- * @author Joas Schilling <coding@schilljs.com>
- * @author Jörn Friedrich Dreyer <jfd@butonic.de>
- * @author Lukas Reschke <lukas@statuscode.ch>
- * @author Morris Jobke <hey@morrisjobke.de>
- * @author Olivier Paroz <github@oparoz.com>
- * @author Robin Appelman <robin@icewind.nl>
- * @author Roeland Jago Douma <roeland@famdouma.nl>
- * @author Thomas Müller <thomas.mueller@tmit.eu>
- * @author Tobias Kaminsky <tobias@kaminsky.me>
- *
- * @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 OC;
-
-use OC\Preview\Provider;
-use OCP\Files\FileInfo;
-use OCP\Files\NotFoundException;
-
-class Preview {
- //the thumbnail folder
- const THUMBNAILS_FOLDER = 'thumbnails';
-
- const MODE_FILL = 'fill';
- const MODE_COVER = 'cover';
-
- //config
- private $maxScaleFactor;
- /** @var int maximum width allowed for a preview */
- private $configMaxWidth;
- /** @var int maximum height allowed for a preview */
- private $configMaxHeight;
-
- //fileview object
- private $fileView = null;
- private $userView = null;
-
- //vars
- private $file;
- private $maxX;
- private $maxY;
- private $scalingUp;
- private $mimeType;
- private $keepAspect = false;
- private $mode = self::MODE_FILL;
-
- //used to calculate the size of the preview to generate
- /** @var int $maxPreviewWidth max width a preview can have */
- private $maxPreviewWidth;
- /** @var int $maxPreviewHeight max height a preview can have */
- private $maxPreviewHeight;
- /** @var int $previewWidth calculated width of the preview we're looking for */
- private $previewWidth;
- /** @var int $previewHeight calculated height of the preview we're looking for */
- private $previewHeight;
-
- //filemapper used for deleting previews
- // index is path, value is fileinfo
- static public $deleteFileMapper = array();
- static public $deleteChildrenMapper = array();
-
- /**
- * preview images object
- *
- * @var \OCP\IImage
- */
- private $preview;
-
- /**
- * @var \OCP\Files\FileInfo
- */
- protected $info;
-
- /**
- * check if thumbnail or bigger version of thumbnail of file is cached
- *
- * @param string $user userid - if no user is given, OC_User::getUser will be used
- * @param string $root path of root
- * @param string $file The path to the file where you want a thumbnail from
- * @param int $maxX The maximum X size of the thumbnail. It can be smaller depending on the
- * shape of the image
- * @param int $maxY The maximum Y size of the thumbnail. It can be smaller depending on the
- * shape of the image
- * @param bool $scalingUp Disable/Enable upscaling of previews
- *
- * @throws \Exception
- * @return mixed (bool / string)
- * false if thumbnail does not exist
- * path to thumbnail if thumbnail exists
- */
- public function __construct(
- $user = '',
- $root = '/',
- $file = '', $maxX = 1,
- $maxY = 1,
- $scalingUp = true
- ) {
- //init fileviews
- if ($user === '') {
- $user = \OC_User::getUser();
- }
- $this->fileView = new \OC\Files\View('/' . $user . '/' . $root);
- $this->userView = new \OC\Files\View('/' . $user);
-
- //set config
- $sysConfig = \OC::$server->getConfig();
- $this->configMaxWidth = $sysConfig->getSystemValue('preview_max_x', 2048);
- $this->configMaxHeight = $sysConfig->getSystemValue('preview_max_y', 2048);
- $this->maxScaleFactor = $sysConfig->getSystemValue('preview_max_scale_factor', 1);
-
- //save parameters
- $this->setFile($file);
- $this->setMaxX((int)$maxX);
- $this->setMaxY((int)$maxY);
- $this->setScalingup($scalingUp);
-
- $this->preview = null;
-
- //check if there are preview backends
- if (!\OC::$server->getPreviewManager()
- ->hasProviders()
- && \OC::$server->getConfig()
- ->getSystemValue('enable_previews', true)
- ) {
- \OCP\Util::writeLog('core', 'No preview providers exist', \OCP\Util::ERROR);
- throw new \Exception('No preview providers');
- }
- }
-
- /**
- * returns the path of the file you want a thumbnail from
- *
- * @return string
- */
- public function getFile() {
- return $this->file;
- }
-
- /**
- * returns the max width of the preview
- *
- * @return integer
- */
- public function getMaxX() {
- return $this->maxX;
- }
-
- /**
- * returns the max height of the preview
- *
- * @return integer
- */
- public function getMaxY() {
- return $this->maxY;
- }
-
- /**
- * returns whether or not scalingup is enabled
- *
- * @return bool
- */
- public function getScalingUp() {
- return $this->scalingUp;
- }
-
- /**
- * returns the name of the thumbnailfolder
- *
- * @return string
- */
- public function getThumbnailsFolder() {
- return self::THUMBNAILS_FOLDER;
- }
-
- /**
- * returns the max scale factor
- *
- * @return string
- */
- public function getMaxScaleFactor() {
- return $this->maxScaleFactor;
- }
-
- /**
- * returns the max width set in ownCloud's config
- *
- * @return integer
- */
- public function getConfigMaxX() {
- return $this->configMaxWidth;
- }
-
- /**
- * returns the max height set in ownCloud's config
- *
- * @return integer
- */
- public function getConfigMaxY() {
- return $this->configMaxHeight;
- }
-
- /**
- * Returns the FileInfo object associated with the file to preview
- *
- * @return false|Files\FileInfo|\OCP\Files\FileInfo
- */
- protected function getFileInfo() {
- $absPath = $this->fileView->getAbsolutePath($this->file);
- $absPath = Files\Filesystem::normalizePath($absPath);
- if (array_key_exists($absPath, self::$deleteFileMapper)) {
- $this->info = self::$deleteFileMapper[$absPath];
- } else if (!$this->info) {
- $this->info = $this->fileView->getFileInfo($this->file);
- }
-
- return $this->info;
- }
-
-
- /**
- * @return array|null
- */
- private function getChildren() {
- $absPath = $this->fileView->getAbsolutePath($this->file);
- $absPath = Files\Filesystem::normalizePath($absPath);
-
- if (array_key_exists($absPath, self::$deleteChildrenMapper)) {
- return self::$deleteChildrenMapper[$absPath];
- }
-
- return null;
- }
-
- /**
- * Sets the path of the file you want a preview of
- *
- * @param string $file
- * @param \OCP\Files\FileInfo|null $info
- *
- * @return \OC\Preview
- */
- public function setFile($file, $info = null) {
- $this->file = $file;
- $this->info = $info;
-
- if ($file !== '') {
- $this->getFileInfo();
- if ($this->info instanceof \OCP\Files\FileInfo) {
- $this->mimeType = $this->info->getMimetype();
- }
- }
-
- return $this;
- }
-
- /**
- * Forces the use of a specific media type
- *
- * @param string $mimeType
- */
- public function setMimetype($mimeType) {
- $this->mimeType = $mimeType;
- }
-
- /**
- * Sets the max width of the preview. It's capped by the maximum allowed size set in the
- * configuration
- *
- * @param int $maxX
- *
- * @throws \Exception
- * @return \OC\Preview
- */
- public function setMaxX($maxX = 1) {
- if ($maxX <= 0) {
- throw new \Exception('Cannot set width of 0 or smaller!');
- }
- $configMaxX = $this->getConfigMaxX();
- $maxX = $this->limitMaxDim($maxX, $configMaxX, 'maxX');
- $this->maxX = $maxX;
-
- return $this;
- }
-
- /**
- * Sets the max height of the preview. It's capped by the maximum allowed size set in the
- * configuration
- *
- * @param int $maxY
- *
- * @throws \Exception
- * @return \OC\Preview
- */
- public function setMaxY($maxY = 1) {
- if ($maxY <= 0) {
- throw new \Exception('Cannot set height of 0 or smaller!');
- }
- $configMaxY = $this->getConfigMaxY();
- $maxY = $this->limitMaxDim($maxY, $configMaxY, 'maxY');
- $this->maxY = $maxY;
-
- return $this;
- }
-
- /**
- * Sets whether we're allowed to scale up when generating a preview. It's capped by the maximum
- * allowed scale factor set in the configuration
- *
- * @param bool $scalingUp
- *
- * @return \OC\Preview
- */
- public function setScalingup($scalingUp) {
- if ($this->getMaxScaleFactor() === 1) {
- $scalingUp = false;
- }
- $this->scalingUp = $scalingUp;
-
- return $this;
- }
-
- /**
- * Set whether to cover or fill the specified dimensions
- *
- * @param string $mode
- *
- * @return \OC\Preview
- */
- public function setMode($mode) {
- $this->mode = $mode;
-
- return $this;
- }
-
- /**
- * Sets whether we need to generate a preview which keeps the aspect ratio of the original file
- *
- * @param bool $keepAspect
- *
- * @return \OC\Preview
- */
- public function setKeepAspect($keepAspect) {
- $this->keepAspect = $keepAspect;
-
- return $this;
- }
-
- /**
- * Makes sure we were given a file to preview and that it exists in the filesystem
- *
- * @return bool
- */
- public function isFileValid() {
- $file = $this->getFile();
- if ($file === '') {
- \OCP\Util::writeLog('core', 'No filename passed', \OCP\Util::DEBUG);
-
- return false;
- }
-
- if (!$this->getFileInfo() instanceof FileInfo) {
- \OCP\Util::writeLog('core', 'File:"' . $file . '" not found', \OCP\Util::DEBUG);
-
- return false;
- }
-
- return true;
- }
-
- /**
- * Deletes the preview of a file with specific width and height
- *
- * This should never delete the max preview, use deleteAllPreviews() instead
- *
- * @return bool
- */
- public function deletePreview() {
- $fileInfo = $this->getFileInfo();
- if ($fileInfo !== null && $fileInfo !== false) {
- $fileId = $fileInfo->getId();
-
- $previewPath = $this->buildCachePath($fileId);
- if (!strpos($previewPath, 'max')) {
- return $this->userView->unlink($previewPath);
- }
- }
-
- return false;
- }
-
- /**
- * Deletes all previews of a file
- */
- public function deleteAllPreviews() {
- $thumbnailMount = $this->userView->getMount($this->getThumbnailsFolder());
- $propagator = $thumbnailMount->getStorage()->getPropagator();
- $propagator->beginBatch();
-
- $toDelete = $this->getChildren();
- $toDelete[] = $this->getFileInfo();
-
- foreach ($toDelete as $delete) {
- if ($delete instanceof FileInfo) {
- /** @var \OCP\Files\FileInfo $delete */
- $fileId = $delete->getId();
-
- // getId() might return null, e.g. when the file is a
- // .ocTransferId*.part file from chunked file upload.
- if (!empty($fileId)) {
- $previewPath = $this->getPreviewPath($fileId);
- $this->userView->rmdir($previewPath);
- }
- }
- }
-
- $propagator->commitBatch();
- }
-
- /**
- * Checks if a preview matching the asked dimensions or a bigger version is already cached
- *
- * * We first retrieve the size of the max preview since this is what we be used to create
- * all our preview. If it doesn't exist we return false, so that it can be generated
- * * Using the dimensions of the max preview, we calculate what the size of the new
- * thumbnail should be
- * * And finally, we look for a suitable candidate in the cache
- *
- * @param int $fileId fileId of the original file we need a preview of
- *
- * @return string|false path to the cached preview if it exists or false
- */
- public function isCached($fileId) {
- if (is_null($fileId)) {
- return false;
- }
-
- /**
- * Phase 1: Looking for the max preview
- */
- $previewPath = $this->getPreviewPath($fileId);
- // We currently can't look for a single file due to bugs related to #16478
- $allThumbnails = $this->userView->getDirectoryContent($previewPath);
- list($maxPreviewWidth, $maxPreviewHeight) = $this->getMaxPreviewSize($allThumbnails);
-
- // Only use the cache if we have a max preview
- if (!is_null($maxPreviewWidth) && !is_null($maxPreviewHeight)) {
-
- /**
- * Phase 2: Calculating the size of the preview we need to send back
- */
- $this->maxPreviewWidth = $maxPreviewWidth;
- $this->maxPreviewHeight = $maxPreviewHeight;
-
- list($previewWidth, $previewHeight) = $this->simulatePreviewDimensions();
- if (empty($previewWidth) || empty($previewHeight)) {
- return false;
- }
-
- $this->previewWidth = $previewWidth;
- $this->previewHeight = $previewHeight;
-
- /**
- * Phase 3: We look for a preview of the exact size
- */
- // This gives us a calculated path to a preview of asked dimensions
- // thumbnailFolder/fileId/<maxX>-<maxY>(-max|-with-aspect).png
- $preview = $this->buildCachePath($fileId, $previewWidth, $previewHeight);
-
- // This checks if we have a preview of those exact dimensions in the cache
- if ($this->thumbnailSizeExists($allThumbnails, basename($preview))) {
- return $preview;
- }
-
- /**
- * Phase 4: We look for a larger preview, matching the aspect ratio
- */
- if (($this->getMaxX() >= $maxPreviewWidth)
- && ($this->getMaxY() >= $maxPreviewHeight)
- ) {
- // The preview we-re looking for is the exact size or larger than the max preview,
- // so return that
- return $this->buildCachePath($fileId, $maxPreviewWidth, $maxPreviewHeight);
- } else {
- // The last resort is to look for something bigger than what we've calculated,
- // but still smaller than the max preview
- return $this->isCachedBigger($fileId, $allThumbnails);
- }
- }
-
- return false;
- }
-
- /**
- * Returns the dimensions of the max preview
- *
- * @param FileInfo[] $allThumbnails the list of all our cached thumbnails
- *
- * @return int[]
- */
- private function getMaxPreviewSize($allThumbnails) {
- $maxPreviewX = null;
- $maxPreviewY = null;
-
- foreach ($allThumbnails as $thumbnail) {
- $name = $thumbnail['name'];
- if (strpos($name, 'max')) {
- list($maxPreviewX, $maxPreviewY) = $this->getDimensionsFromFilename($name);
- break;
- }
- }
-
- return [$maxPreviewX, $maxPreviewY];
- }
-
- /**
- * Check if a specific thumbnail size is cached
- *
- * @param FileInfo[] $allThumbnails the list of all our cached thumbnails
- * @param string $name
- * @return bool
- */
- private function thumbnailSizeExists(array $allThumbnails, $name) {
-
- foreach ($allThumbnails as $thumbnail) {
- if ($name === $thumbnail->getName()) {
- return true;
- }
- }
-
- return false;
- }
-
- /**
- * Determines the size of the preview we should be looking for in the cache
- *
- * @return integer[]
- */
- private function simulatePreviewDimensions() {
- $askedWidth = $this->getMaxX();
- $askedHeight = $this->getMaxY();
-
- if ($this->keepAspect) {
- list($newPreviewWidth, $newPreviewHeight) =
- $this->applyAspectRatio($askedWidth, $askedHeight);
- } else {
- list($newPreviewWidth, $newPreviewHeight) = $this->fixSize($askedWidth, $askedHeight);
- }
-
- return [(int)$newPreviewWidth, (int)$newPreviewHeight];
- }
-
- /**
- * Resizes the boundaries to match the aspect ratio
- *
- * @param int $askedWidth
- * @param int $askedHeight
- *
- * @param int $originalWidth
- * @param int $originalHeight
- * @return integer[]
- */
- private function applyAspectRatio($askedWidth, $askedHeight, $originalWidth = 0, $originalHeight = 0) {
- if (!$originalWidth) {
- $originalWidth = $this->maxPreviewWidth;
- }
- if (!$originalHeight) {
- $originalHeight = $this->maxPreviewHeight;
- }
- $originalRatio = $originalWidth / $originalHeight;
- // Defines the box in which the preview has to fit
- $scaleFactor = $this->scalingUp ? $this->maxScaleFactor : 1;
- $askedWidth = min($askedWidth, $originalWidth * $scaleFactor);
- $askedHeight = min($askedHeight, $originalHeight * $scaleFactor);
-
- if ($askedWidth / $originalRatio < $askedHeight) {
- // width restricted
- $askedHeight = round($askedWidth / $originalRatio);
- } else {
- $askedWidth = round($askedHeight * $originalRatio);
- }
-
- return [(int)$askedWidth, (int)$askedHeight];
- }
-
- /**
- * Resizes the boundaries to cover the area
- *
- * @param int $askedWidth
- * @param int $askedHeight
- * @param int $previewWidth
- * @param int $previewHeight
- * @return integer[]
- */
- private function applyCover($askedWidth, $askedHeight, $previewWidth, $previewHeight) {
- $originalRatio = $previewWidth / $previewHeight;
- // Defines the box in which the preview has to fit
- $scaleFactor = $this->scalingUp ? $this->maxScaleFactor : 1;
- $askedWidth = min($askedWidth, $previewWidth * $scaleFactor);
- $askedHeight = min($askedHeight, $previewHeight * $scaleFactor);
-
- if ($askedWidth / $originalRatio > $askedHeight) {
- // height restricted
- $askedHeight = round($askedWidth / $originalRatio);
- } else {
- $askedWidth = round($askedHeight * $originalRatio);
- }
-
- return [(int)$askedWidth, (int)$askedHeight];
- }
-
- /**
- * Makes sure an upscaled preview doesn't end up larger than the max dimensions defined in the
- * config
- *
- * @param int $askedWidth
- * @param int $askedHeight
- *
- * @return integer[]
- */
- private function fixSize($askedWidth, $askedHeight) {
- if ($this->scalingUp) {
- $askedWidth = min($this->configMaxWidth, $askedWidth);
- $askedHeight = min($this->configMaxHeight, $askedHeight);
- }
-
- return [(int)$askedWidth, (int)$askedHeight];
- }
-
- /**
- * Checks if a bigger version of a file preview is cached and if not
- * return the preview of max allowed dimensions
- *
- * @param int $fileId fileId of the original image
- * @param FileInfo[] $allThumbnails the list of all our cached thumbnails
- *
- * @return string path to bigger thumbnail
- */
- private function isCachedBigger($fileId, $allThumbnails) {
- // This is used to eliminate any thumbnail narrower than what we need
- $maxX = $this->getMaxX();
-
- //array for usable cached thumbnails
- $possibleThumbnails = $this->getPossibleThumbnails($allThumbnails);
-
- foreach ($possibleThumbnails as $width => $path) {
- if ($width < $maxX) {
- continue;
- } else {
- return $path;
- }
- }
-
- // At this stage, we didn't find a preview, so we return the max preview
- return $this->buildCachePath($fileId, $this->maxPreviewWidth, $this->maxPreviewHeight);
- }
-
- /**
- * Get possible bigger thumbnails of the given image with the proper aspect ratio
- *
- * @param FileInfo[] $allThumbnails the list of all our cached thumbnails
- *
- * @return string[] an array of paths to bigger thumbnails
- */
- private function getPossibleThumbnails($allThumbnails) {
- if ($this->keepAspect) {
- $wantedAspectRatio = (float)($this->maxPreviewWidth / $this->maxPreviewHeight);
- } else {
- $wantedAspectRatio = (float)($this->getMaxX() / $this->getMaxY());
- }
-
- //array for usable cached thumbnails
- $possibleThumbnails = array();
- foreach ($allThumbnails as $thumbnail) {
- $name = rtrim($thumbnail['name'], '.png');
- list($x, $y, $aspectRatio) = $this->getDimensionsFromFilename($name);
- if (abs($aspectRatio - $wantedAspectRatio) >= 0.000001
- || $this->unscalable($x, $y)
- ) {
- continue;
- }
- $possibleThumbnails[$x] = $thumbnail['path'];
- }
-
- ksort($possibleThumbnails);
-
- return $possibleThumbnails;
- }
-
- /**
- * Looks at the preview filename from the cache and extracts the size of the preview
- *
- * @param string $name
- *
- * @return array<int,int,float>
- */
- private function getDimensionsFromFilename($name) {
- $size = explode('-', $name);
- $x = (int)$size[0];
- $y = (int)$size[1];
- $aspectRatio = (float)($x / $y);
-
- return array($x, $y, $aspectRatio);
- }
-
- /**
- * @param int $x
- * @param int $y
- *
- * @return bool
- */
- private function unscalable($x, $y) {
-
- $maxX = $this->getMaxX();
- $maxY = $this->getMaxY();
- $scalingUp = $this->getScalingUp();
- $maxScaleFactor = $this->getMaxScaleFactor();
-
- if ($x < $maxX || $y < $maxY) {
- if ($scalingUp) {
- $scaleFactor = $maxX / $x;
- if ($scaleFactor > $maxScaleFactor) {
- return true;
- }
- } else {
- return true;
- }
- }
-
- return false;
- }
-
- /**
- * Returns a preview of a file
- *
- * The cache is searched first and if nothing usable was found then a preview is
- * generated by one of the providers
- *
- * @return \OCP\IImage
- */
- public function getPreview() {
- if (!is_null($this->preview) && $this->preview->valid()) {
- return $this->preview;
- }
-
- $this->preview = null;
- $fileInfo = $this->getFileInfo();
- if ($fileInfo === null || $fileInfo === false || !$fileInfo->isReadable()) {
- return new \OC_Image();
- }
-
- $fileId = $fileInfo->getId();
- $cached = $this->isCached($fileId);
- if ($cached) {
- $this->getCachedPreview($fileId, $cached);
- }
-
- if (is_null($this->preview)) {
- $this->generatePreview($fileId);
- }
-
- // We still don't have a preview, so we send back an empty object
- if (is_null($this->preview)) {
- $this->preview = new \OC_Image();
- }
-
- return $this->preview;
- }
-
- /**
- * Sends the preview, including the headers to client which requested it
- *
- * @param null|string $mimeTypeForHeaders the media type to use when sending back the reply
- *
- * @throws NotFoundException
- * @throws PreviewNotAvailableException
- */
- public function showPreview($mimeTypeForHeaders = null) {
- // Check if file is valid
- if ($this->isFileValid() === false) {
- throw new NotFoundException('File not found.');
- }
-
- if (is_null($this->preview)) {
- $this->getPreview();
- }
- if ($this->preview instanceof \OCP\IImage) {
- if ($this->preview->valid()) {
- \OCP\Response::enableCaching(3600 * 24); // 24 hours
- } else {
- $this->getMimeIcon();
- }
- $this->preview->show($mimeTypeForHeaders);
- }
- }
-
- /**
- * Retrieves the preview from the cache and resizes it if necessary
- *
- * @param int $fileId fileId of the original image
- * @param string $cached the path to the cached preview
- */
- private function getCachedPreview($fileId, $cached) {
- $stream = $this->userView->fopen($cached, 'r');
- $this->preview = null;
- if ($stream) {
- $image = new \OC_Image();
- $image->loadFromFileHandle($stream);
-
- $this->preview = $image->valid() ? $image : null;
-
- if (!is_null($this->preview)) {
- // Size of the preview we calculated
- $maxX = $this->previewWidth;
- $maxY = $this->previewHeight;
- // Size of the preview we retrieved from the cache
- $previewX = (int)$this->preview->width();
- $previewY = (int)$this->preview->height();
-
- // We don't have an exact match
- if ($previewX !== $maxX || $previewY !== $maxY) {
- $this->resizeAndStore($fileId);
- }
- }
-
- fclose($stream);
- }
- }
-
- /**
- * Resizes, crops, fixes orientation and stores in the cache
- *
- * @param int $fileId fileId of the original image
- */
- private function resizeAndStore($fileId) {
- $image = $this->preview;
- if (!($image instanceof \OCP\IImage)) {
- \OCP\Util::writeLog(
- 'core', '$this->preview is not an instance of \OCP\IImage', \OCP\Util::DEBUG
- );
-
- return;
- }
- $previewWidth = (int)$image->width();
- $previewHeight = (int)$image->height();
- $askedWidth = $this->getMaxX();
- $askedHeight = $this->getMaxY();
-
- if ($this->mode === self::MODE_COVER) {
- list($askedWidth, $askedHeight) =
- $this->applyCover($askedWidth, $askedHeight, $previewWidth, $previewHeight);
- }
-
- /**
- * Phase 1: If required, adjust boundaries to keep aspect ratio
- */
- if ($this->keepAspect) {
- list($askedWidth, $askedHeight) =
- $this->applyAspectRatio($askedWidth, $askedHeight, $previewWidth, $previewHeight);
- }
-
- /**
- * Phase 2: Resizes preview to try and match requirements.
- * Takes the scaling ratio into consideration
- */
- list($newPreviewWidth, $newPreviewHeight) = $this->scale(
- $image, $askedWidth, $askedHeight, $previewWidth, $previewHeight
- );
-
- // The preview has been resized and should now have the asked dimensions
- if ($newPreviewWidth === $askedWidth && $newPreviewHeight === $askedHeight) {
- $this->storePreview($fileId, $newPreviewWidth, $newPreviewHeight);
-
- return;
- }
-
- /**
- * Phase 3: We're still not there yet, so we're clipping and filling
- * to match the asked dimensions
- */
- // It turns out the scaled preview is now too big, so we crop the image
- if ($newPreviewWidth >= $askedWidth && $newPreviewHeight >= $askedHeight) {
- $this->crop($image, $askedWidth, $askedHeight, $newPreviewWidth, $newPreviewHeight);
- $this->storePreview($fileId, $askedWidth, $askedHeight);
-
- return;
- }
-
- // At least one dimension of the scaled preview is too small,
- // so we fill the space with a transparent background
- if (($newPreviewWidth < $askedWidth || $newPreviewHeight < $askedHeight)) {
- $this->cropAndFill(
- $image, $askedWidth, $askedHeight, $newPreviewWidth, $newPreviewHeight
- );
- $this->storePreview($fileId, $askedWidth, $askedHeight);
-
- return;
- }
-
- // The preview is smaller, but we can't touch it
- $this->storePreview($fileId, $newPreviewWidth, $newPreviewHeight);
- }
-
- /**
- * Calculates the new dimensions of the preview
- *
- * The new dimensions can be larger or smaller than the ones of the preview we have to resize
- *
- * @param \OCP\IImage $image
- * @param int $askedWidth
- * @param int $askedHeight
- * @param int $previewWidth
- * @param int $previewHeight
- *
- * @return int[]
- */
- private function scale($image, $askedWidth, $askedHeight, $previewWidth, $previewHeight) {
- $scalingUp = $this->getScalingUp();
- $maxScaleFactor = $this->getMaxScaleFactor();
-
- $factorX = $askedWidth / $previewWidth;
- $factorY = $askedHeight / $previewHeight;
-
- if ($factorX >= $factorY) {
- $factor = $factorX;
- } else {
- $factor = $factorY;
- }
-
- if ($scalingUp === false) {
- if ($factor > 1) {
- $factor = 1;
- }
- }
-
- // We cap when upscaling
- if (!is_null($maxScaleFactor)) {
- if ($factor > $maxScaleFactor) {
- \OCP\Util::writeLog(
- 'core', 'scale factor reduced from ' . $factor . ' to ' . $maxScaleFactor,
- \OCP\Util::DEBUG
- );
- $factor = $maxScaleFactor;
- }
- }
-
- $newPreviewWidth = round($previewWidth * $factor);
- $newPreviewHeight = round($previewHeight * $factor);
-
- $image->preciseResize($newPreviewWidth, $newPreviewHeight);
- $this->preview = $image;
-
- return [$newPreviewWidth, $newPreviewHeight];
- }
-
- /**
- * Crops a preview which is larger than the dimensions we've received
- *
- * @param \OCP\IImage $image
- * @param int $askedWidth
- * @param int $askedHeight
- * @param int $previewWidth
- * @param int $previewHeight
- */
- private function crop($image, $askedWidth, $askedHeight, $previewWidth, $previewHeight = null) {
- $cropX = floor(abs($askedWidth - $previewWidth) * 0.5);
- //don't crop previews on the Y axis, this sucks if it's a document.
- //$cropY = floor(abs($y - $newPreviewHeight) * 0.5);
- $cropY = 0;
- $image->crop($cropX, $cropY, $askedWidth, $askedHeight);
- $this->preview = $image;
- }
-
- /**
- * Crops an image if it's larger than the dimensions we've received and fills the empty space
- * with a transparent background
- *
- * @param \OCP\IImage $image
- * @param int $askedWidth
- * @param int $askedHeight
- * @param int $previewWidth
- * @param int $previewHeight
- */
- private function cropAndFill($image, $askedWidth, $askedHeight, $previewWidth, $previewHeight) {
- if ($previewWidth > $askedWidth) {
- $cropX = floor(($previewWidth - $askedWidth) * 0.5);
- $image->crop($cropX, 0, $askedWidth, $previewHeight);
- $previewWidth = $askedWidth;
- }
-
- if ($previewHeight > $askedHeight) {
- $cropY = floor(($previewHeight - $askedHeight) * 0.5);
- $image->crop(0, $cropY, $previewWidth, $askedHeight);
- $previewHeight = $askedHeight;
- }
-
- // Creates a transparent background
- $backgroundLayer = imagecreatetruecolor($askedWidth, $askedHeight);
- imagealphablending($backgroundLayer, false);
- $transparency = imagecolorallocatealpha($backgroundLayer, 0, 0, 0, 127);
- imagefill($backgroundLayer, 0, 0, $transparency);
- imagesavealpha($backgroundLayer, true);
-
- $image = $image->resource();
-
- $mergeX = floor(abs($askedWidth - $previewWidth) * 0.5);
- $mergeY = floor(abs($askedHeight - $previewHeight) * 0.5);
-
- // Pastes the preview on top of the background
- imagecopy(
- $backgroundLayer, $image, $mergeX, $mergeY, 0, 0, $previewWidth,
- $previewHeight
- );
-
- $image = new \OC_Image($backgroundLayer);
-
- $this->preview = $image;
- }
-
- /**
- * Saves a preview in the cache to speed up future calls
- *
- * Do not nullify the preview as it might send the whole process in a loop
- *
- * @param int $fileId fileId of the original image
- * @param int $previewWidth
- * @param int $previewHeight
- */
- private function storePreview($fileId, $previewWidth, $previewHeight) {
- if (empty($previewWidth) || empty($previewHeight)) {
- \OCP\Util::writeLog(
- 'core', 'Cannot save preview of dimension ' . $previewWidth . 'x' . $previewHeight,
- \OCP\Util::DEBUG
- );
-
- } else {
- $cachePath = $this->buildCachePath($fileId, $previewWidth, $previewHeight);
- $this->userView->file_put_contents($cachePath, $this->preview->data());
- }
- }
-
- /**
- * Returns the path to a preview based on its dimensions and aspect
- *
- * @param int $fileId
- * @param int|null $maxX
- * @param int|null $maxY
- *
- * @return string
- */
- private function buildCachePath($fileId, $maxX = null, $maxY = null) {
- if (is_null($maxX)) {
- $maxX = $this->getMaxX();
- }
- if (is_null($maxY)) {
- $maxY = $this->getMaxY();
- }
-
- $previewPath = $this->getPreviewPath($fileId);
- $previewPath = $previewPath . strval($maxX) . '-' . strval($maxY);
- $isMaxPreview =
- ($maxX === $this->maxPreviewWidth && $maxY === $this->maxPreviewHeight) ? true : false;
- if ($isMaxPreview) {
- $previewPath .= '-max';
- }
- if ($this->keepAspect && !$isMaxPreview) {
- $previewPath .= '-with-aspect';
- }
- if ($this->mode === self::MODE_COVER) {
- $previewPath .= '-cover';
- }
- $previewPath .= '.png';
-
- return $previewPath;
- }
-
- /**
- * Returns the path to the folder where the previews are stored, identified by the fileId
- *
- * @param int $fileId
- *
- * @return string
- */
- private function getPreviewPath($fileId) {
- return $this->getThumbnailsFolder() . '/' . $fileId . '/';
- }
-
- /**
- * Asks the provider to send a preview of the file which respects the maximum dimensions
- * defined in the configuration and after saving it in the cache, it is then resized to the
- * asked dimensions
- *
- * This is only called once in order to generate a large PNG of dimensions defined in the
- * configuration file. We'll be able to quickly resize it later on.
- * We never upscale the original conversion as this will be done later by the resizing
- * operation
- *
- * @param int $fileId fileId of the original image
- */
- private function generatePreview($fileId) {
- $file = $this->getFile();
- $preview = null;
-
- $previewProviders = \OC::$server->getPreviewManager()
- ->getProviders();
- foreach ($previewProviders as $supportedMimeType => $providers) {
- if (!preg_match($supportedMimeType, $this->mimeType)) {
- continue;
- }
-
- foreach ($providers as $closure) {
- $provider = $closure();
- if (!($provider instanceof \OCP\Preview\IProvider)) {
- continue;
- }
-
- \OCP\Util::writeLog(
- 'core', 'Generating preview for "' . $file . '" with "' . get_class($provider)
- . '"', \OCP\Util::DEBUG
- );
-
- /** @var $provider Provider */
- $preview = $provider->getThumbnail(
- $file, $this->configMaxWidth, $this->configMaxHeight, $scalingUp = false,
- $this->fileView
- );
-
- if (!($preview instanceof \OCP\IImage)) {
- continue;
- }
-
- $this->preview = $preview;
- $previewPath = $this->getPreviewPath($fileId);
-
- if ($this->userView->is_dir($this->getThumbnailsFolder() . '/') === false) {
- $this->userView->mkdir($this->getThumbnailsFolder() . '/');
- }
-
- if ($this->userView->is_dir($previewPath) === false) {
- $this->userView->mkdir($previewPath);
- }
-
- // This stores our large preview so that it can be used in subsequent resizing requests
- $this->storeMaxPreview($previewPath);
-
- break 2;
- }
- }
-
- // The providers have been kind enough to give us a preview
- if ($preview) {
- $this->resizeAndStore($fileId);
- }
- }
-
- /**
- * Defines the media icon, for the media type of the original file, as the preview
- * @throws PreviewNotAvailableException
- */
- private function getMimeIcon() {
- $image = new \OC_Image();
- $mimeIconWebPath = \OC::$server->getMimeTypeDetector()->mimeTypeIcon($this->mimeType);
- if (empty(\OC::$WEBROOT)) {
- $mimeIconServerPath = \OC::$SERVERROOT . $mimeIconWebPath;
- } else {
- $mimeIconServerPath = str_replace(\OC::$WEBROOT, \OC::$SERVERROOT, $mimeIconWebPath);
- }
- // we can't load SVGs into an image
- if (substr($mimeIconWebPath, -4) === '.svg') {
- throw new PreviewNotAvailableException('SVG mimetype cannot be rendered');
- }
- $image->loadFromFile($mimeIconServerPath);
-
- $this->preview = $image;
- }
-
- /**
- * Stores the max preview in the cache
- *
- * @param string $previewPath path to the preview
- */
- private function storeMaxPreview($previewPath) {
- $maxPreviewExists = false;
- $preview = $this->preview;
-
- $allThumbnails = $this->userView->getDirectoryContent($previewPath);
- // This is so that the cache doesn't need emptying when upgrading
- // Can be replaced by an upgrade script...
- foreach ($allThumbnails as $thumbnail) {
- $name = rtrim($thumbnail['name'], '.png');
- if (strpos($name, 'max')) {
- $maxPreviewExists = true;
- break;
- }
- }
- // We haven't found the max preview, so we create it
- if (!$maxPreviewExists) {
- $previewWidth = $preview->width();
- $previewHeight = $preview->height();
- $previewPath = $previewPath . strval($previewWidth) . '-' . strval($previewHeight);
- $previewPath .= '-max.png';
- $this->userView->file_put_contents($previewPath, $preview->data());
- $this->maxPreviewWidth = $previewWidth;
- $this->maxPreviewHeight = $previewHeight;
- }
- }
-
- /**
- * Limits a dimension to the maximum dimension provided as argument
- *
- * @param int $dim
- * @param int $maxDim
- * @param string $dimName
- *
- * @return integer
- */
- private function limitMaxDim($dim, $maxDim, $dimName) {
- if (!is_null($maxDim)) {
- if ($dim > $maxDim) {
- \OCP\Util::writeLog(
- 'core', $dimName . ' reduced from ' . $dim . ' to ' . $maxDim, \OCP\Util::DEBUG
- );
- $dim = $maxDim;
- }
- }
-
- return $dim;
- }
-
- /**
- * @param array $args
- */
- public static function post_write($args) {
- self::post_delete($args, 'files/');
- }
-
- /**
- * @param array $args
- */
- public static function prepare_delete_files($args) {
- self::prepare_delete($args, 'files/');
- }
-
- /**
- * @param array $args
- * @param string $prefix
- */
- public static function prepare_delete(array $args, $prefix = '') {
- $path = $args['path'];
- if (substr($path, 0, 1) === '/') {
- $path = substr($path, 1);
- }
-
- $view = new \OC\Files\View('/' . \OC_User::getUser() . '/' . $prefix);
-
- $absPath = Files\Filesystem::normalizePath($view->getAbsolutePath($path));
- $fileInfo = $view->getFileInfo($path);
- if ($fileInfo === false) {
- return;
- }
- self::addPathToDeleteFileMapper($absPath, $fileInfo);
- if ($view->is_dir($path)) {
- $children = self::getAllChildren($view, $path);
- self::$deleteChildrenMapper[$absPath] = $children;
- }
- }
-
- /**
- * @param string $absolutePath
- * @param \OCP\Files\FileInfo $info
- */
- private static function addPathToDeleteFileMapper($absolutePath, $info) {
- self::$deleteFileMapper[$absolutePath] = $info;
- }
-
- /**
- * @param \OC\Files\View $view
- * @param string $path
- *
- * @return array
- */
- private static function getAllChildren($view, $path) {
- $children = $view->getDirectoryContent($path);
- $childrensFiles = array();
-
- $fakeRootLength = strlen($view->getRoot());
-
- for ($i = 0; $i < count($children); $i++) {
- $child = $children[$i];
-
- $childsPath = substr($child->getPath(), $fakeRootLength);
-
- if ($view->is_dir($childsPath)) {
- $children = array_merge(
- $children,
- $view->getDirectoryContent($childsPath)
- );
- } else {
- $childrensFiles[] = $child;
- }
- }
-
- return $childrensFiles;
- }
-
- /**
- * @param array $args
- */
- public static function post_delete_files($args) {
- self::post_delete($args, 'files/');
- }
-
- /**
- * @param array $args
- */
- public static function post_delete_versions($args) {
- self::post_delete($args, 'files/');
- }
-
- /**
- * @param array $args
- * @param string $prefix
- */
- public static function post_delete($args, $prefix = '') {
- $path = Files\Filesystem::normalizePath($args['path']);
-
- $preview = new Preview(\OC_User::getUser(), $prefix, $path);
- $preview->deleteAllPreviews();
- }
-
-}
diff --git a/lib/private/PreviewManager.php b/lib/private/PreviewManager.php
index 36b3730a720..8c5a7ad29f1 100644
--- a/lib/private/PreviewManager.php
+++ b/lib/private/PreviewManager.php
@@ -68,6 +68,9 @@ class PreviewManager implements IPreview {
/** @var array */
protected $defaultProviders;
+ /** @var string */
+ protected $userId;
+
/**
* PreviewManager constructor.
*
@@ -75,15 +78,18 @@ class PreviewManager implements IPreview {
* @param IRootFolder $rootFolder
* @param IAppData $appData
* @param EventDispatcherInterface $eventDispatcher
+ * @param string $userId
*/
public function __construct(IConfig $config,
IRootFolder $rootFolder,
IAppData $appData,
- EventDispatcherInterface $eventDispatcher) {
+ EventDispatcherInterface $eventDispatcher,
+ $userId) {
$this->config = $config;
$this->rootFolder = $rootFolder;
$this->appData = $appData;
$this->eventDispatcher = $eventDispatcher;
+ $this->userId = $userId;
}
/**
@@ -144,10 +150,22 @@ class PreviewManager implements IPreview {
* @param int $maxY The maximum Y size of the thumbnail. It can be smaller depending on the shape of the image
* @param boolean $scaleUp Scale smaller images up to the thumbnail size or not. Might look ugly
* @return \OCP\IImage
+ * @deprecated 11 Use getPreview
*/
public function createPreview($file, $maxX = 100, $maxY = 75, $scaleUp = false) {
- $preview = new \OC\Preview('', '/', $file, $maxX, $maxY, $scaleUp);
- return $preview->getPreview();
+ try {
+ $userRoot = $this->rootFolder->getUserFolder($this->userId)->getParent();
+ $node = $userRoot->get($file);
+ if (!($file instanceof File)) {
+ throw new NotFoundException();
+ }
+
+ $preview = $this->getPreview($node, $maxX, $maxY);
+ } catch (\Exception $e) {
+ return new \OC_Image();
+ }
+
+ return new \OC_Image($preview->getContent());
}
/**
diff --git a/lib/private/Repair.php b/lib/private/Repair.php
index c212ea90744..e8d466cd844 100644
--- a/lib/private/Repair.php
+++ b/lib/private/Repair.php
@@ -31,12 +31,12 @@
namespace OC;
use OC\Repair\AssetCache;
-use OC\Repair\AvatarPermissions;
use OC\Repair\CleanTags;
use OC\Repair\Collation;
use OC\Repair\DropOldJobs;
use OC\Repair\MoveUpdaterStepFile;
use OC\Repair\NC11\CleanPreviews;
+use OC\Repair\NC11\FixMountStorages;
use OC\Repair\NC11\MoveAvatars;
use OC\Repair\OldGroupMembershipShares;
use OC\Repair\RemoveGetETagEntries;
@@ -47,7 +47,6 @@ use OC\Repair\SqliteAutoincrement;
use OC\Repair\DropOldTables;
use OC\Repair\FillETags;
use OC\Repair\InnoDB;
-use OC\Repair\RepairLegacyStorages;
use OC\Repair\RepairMimeTypes;
use OC\Repair\SearchLuceneTables;
use OC\Repair\UpdateOutdatedOcsIds;
@@ -132,7 +131,6 @@ class Repair implements IOutput{
return [
new Collation(\OC::$server->getConfig(), \OC::$server->getLogger(), \OC::$server->getDatabaseConnection(), false),
new RepairMimeTypes(\OC::$server->getConfig()),
- new RepairLegacyStorages(\OC::$server->getConfig(), \OC::$server->getDatabaseConnection()),
new AssetCache(),
new FillETags(\OC::$server->getDatabaseConnection()),
new CleanTags(\OC::$server->getDatabaseConnection(), \OC::$server->getUserManager()),
@@ -143,7 +141,6 @@ class Repair implements IOutput{
new RepairInvalidShares(\OC::$server->getConfig(), \OC::$server->getDatabaseConnection()),
new SharePropagation(\OC::$server->getConfig()),
new RemoveOldShares(\OC::$server->getDatabaseConnection()),
- new AvatarPermissions(\OC::$server->getDatabaseConnection()),
new RemoveRootShares(\OC::$server->getDatabaseConnection(), \OC::$server->getUserManager(), \OC::$server->getLazyRootFolder()),
new RepairUnmergedShares(
\OC::$server->getConfig(),
@@ -161,6 +158,7 @@ class Repair implements IOutput{
\OC::$server->getUserManager(),
\OC::$server->getConfig()
),
+ new FixMountStorages(\OC::$server->getDatabaseConnection()),
];
}
diff --git a/lib/private/Repair/AvatarPermissions.php b/lib/private/Repair/AvatarPermissions.php
deleted file mode 100644
index 9d75062358c..00000000000
--- a/lib/private/Repair/AvatarPermissions.php
+++ /dev/null
@@ -1,116 +0,0 @@
-<?php
-/**
- * @copyright Copyright (c) 2016, ownCloud, Inc.
- *
- * @author Roeland Jago Douma <roeland@famdouma.nl>
- *
- * @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 OC\Repair;
-
-use Doctrine\DBAL\Platforms\OraclePlatform;
-use OCP\IDBConnection;
-use OCP\Migration\IOutput;
-use OCP\Migration\IRepairStep;
-
-/**
- * Class AvatarPermissions
- *
- * @package OC\Repair
- */
-class AvatarPermissions implements IRepairStep {
- /** @var IDBConnection */
- private $connection;
-
- /**
- * AvatarPermissions constructor.
- *
- * @param IDBConnection $connection
- */
- public function __construct(IDBConnection $connection) {
- $this->connection = $connection;
- }
-
- /**
- * @return string
- */
- public function getName() {
- return 'Fix permissions so avatars can be stored again';
- }
-
- /**
- * @param IOutput $output
- */
- public function run(IOutput $output) {
- $output->startProgress(2);
- $this->fixUserRootPermissions();
- $output->advance();
- $this->fixAvatarPermissions();
- $output->finishProgress();
- }
-
- /**
- * Make sure all user roots have permissions 23 (all but share)
- */
- protected function fixUserRootPermissions() {
- $qb = $this->connection->getQueryBuilder();
- $qb2 = $this->connection->getQueryBuilder();
-
- $qb->select('numeric_id')
- ->from('storages')
- ->where($qb->expr()->like('id', $qb2->createParameter('like')));
-
- if ($this->connection->getDatabasePlatform() instanceof OraclePlatform) {
- // '' is null on oracle
- $path = $qb2->expr()->isNull('path');
- } else {
- $path = $qb2->expr()->eq('path', $qb2->createNamedParameter(''));
- }
-
- $qb2->update('filecache')
- ->set('permissions', $qb2->createNamedParameter(23))
- ->where($path)
- ->andWhere($qb2->expr()->in('storage', $qb2->createFunction($qb->getSQL())))
- ->andWhere($qb2->expr()->neq('permissions', $qb2->createNamedParameter(23)))
- ->setParameter('like', 'home::%');
-
-
- $qb2->execute();
- }
-
- /**
- * Make sure all avatar files in the user roots have permission 27
- */
- protected function fixAvatarPermissions() {
- $qb = $this->connection->getQueryBuilder();
- $qb2 = $this->connection->getQueryBuilder();
-
- $qb->select('numeric_id')
- ->from('storages')
- ->where($qb->expr()->like('id', $qb2->createParameter('like')));
-
- $qb2->update('filecache')
- ->set('permissions', $qb2->createNamedParameter(27))
- ->where($qb2->expr()->like('path', $qb2->createNamedParameter('avatar.%')))
- ->andWhere($qb2->expr()->in('storage', $qb2->createFunction($qb->getSQL())))
- ->andWhere($qb2->expr()->neq('permissions', $qb2->createNamedParameter(27)))
- ->setParameter('like', 'home::%');
-
- $qb2->execute();
- }
-
-}
-
diff --git a/lib/private/Repair/Collation.php b/lib/private/Repair/Collation.php
index 54de1a719bd..a3535fb33a2 100644
--- a/lib/private/Repair/Collation.php
+++ b/lib/private/Repair/Collation.php
@@ -75,6 +75,18 @@ class Collation implements IRepairStep {
$tables = $this->getAllNonUTF8BinTables($this->connection);
foreach ($tables as $table) {
+ $output->info("Change row format for $table ...");
+ $query = $this->connection->prepare('ALTER TABLE `' . $table . '` ROW_FORMAT = DYNAMIC;');
+ try {
+ $query->execute();
+ } catch (DriverException $e) {
+ // Just log this
+ $this->logger->logException($e);
+ if (!$this->ignoreFailures) {
+ throw $e;
+ }
+ }
+
$output->info("Change collation for $table ...");
$query = $this->connection->prepare('ALTER TABLE `' . $table . '` CONVERT TO CHARACTER SET ' . $characterSet . ' COLLATE ' . $characterSet . '_bin;');
try {
diff --git a/lib/private/Repair/MoveUpdaterStepFile.php b/lib/private/Repair/MoveUpdaterStepFile.php
index fabaff40d24..feb8a291282 100644
--- a/lib/private/Repair/MoveUpdaterStepFile.php
+++ b/lib/private/Repair/MoveUpdaterStepFile.php
@@ -44,7 +44,7 @@ class MoveUpdaterStepFile implements IRepairStep {
public function run(IOutput $output) {
- $dataDir = $this->config->getSystemValue('datadirectory', \OC::$SERVERROOT);
+ $dataDir = $this->config->getSystemValue('datadirectory', \OC::$SERVERROOT . '/data');
$instanceId = $this->config->getSystemValue('instanceid', null);
if(!is_string($instanceId) || empty($instanceId)) {
diff --git a/lib/private/Repair/NC11/FixMountStorages.php b/lib/private/Repair/NC11/FixMountStorages.php
new file mode 100644
index 00000000000..d57a356dff9
--- /dev/null
+++ b/lib/private/Repair/NC11/FixMountStorages.php
@@ -0,0 +1,78 @@
+<?php
+/**
+ * @copyright 2016 Joas Schilling <coding@schilljs.com>
+ *
+ * @author Joas Schilling <coding@schilljs.com>
+ *
+ * @license GNU AGPL version 3 or any later version
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+namespace OC\Repair\NC11;
+
+use OCP\DB\QueryBuilder\IQueryBuilder;
+use OCP\IDBConnection;
+use OCP\Migration\IOutput;
+use OCP\Migration\IRepairStep;
+
+class FixMountStorages implements IRepairStep {
+
+ /** @var IDBConnection */
+ private $db;
+
+ /**
+ * @param IDBConnection $db
+ */
+ public function __construct(IDBConnection $db) {
+ $this->db = $db;
+ }
+
+ /**
+ * @return string
+ */
+ public function getName() {
+ return 'Fix potential broken mount points';
+ }
+
+ public function run(IOutput $output) {
+ $query = $this->db->getQueryBuilder();
+ $query->select('m.id', 'f.storage')
+ ->from('mounts', 'm')
+ ->leftJoin('m', 'filecache', 'f', $query->expr()->eq('m.root_id', 'f.fileid'))
+ ->where($query->expr()->neq('m.storage_id', 'f.storage'));
+
+ $update = $this->db->getQueryBuilder();
+ $update->update('mounts')
+ ->set('storage_id', $update->createParameter('storage'))
+ ->where($query->expr()->eq('id', $update->createParameter('mount')));
+
+ $result = $query->execute();
+ $entriesUpdated = 0;
+ while ($row = $result->fetch()) {
+ $update->setParameter('storage', $row['storage'], IQueryBuilder::PARAM_INT)
+ ->setParameter('mount', $row['id'], IQueryBuilder::PARAM_INT);
+ $update->execute();
+ $entriesUpdated++;
+ }
+ $result->closeCursor();
+
+ if ($entriesUpdated > 0) {
+ $output->info($entriesUpdated . ' mounts updated');
+ return;
+ }
+
+ $output->info('No mounts updated');
+ }
+}
diff --git a/lib/private/Repair/NC11/MoveAvatarBackgroundJob.php b/lib/private/Repair/NC11/MoveAvatarsBackgroundJob.php
index f8c0d9b3abf..f8c0d9b3abf 100644
--- a/lib/private/Repair/NC11/MoveAvatarBackgroundJob.php
+++ b/lib/private/Repair/NC11/MoveAvatarsBackgroundJob.php
diff --git a/lib/private/Repair/RepairInvalidShares.php b/lib/private/Repair/RepairInvalidShares.php
index 6cb690057bb..04624c910dd 100644
--- a/lib/private/Repair/RepairInvalidShares.php
+++ b/lib/private/Repair/RepairInvalidShares.php
@@ -27,6 +27,7 @@ namespace OC\Repair;
use OCP\Migration\IOutput;
use OCP\Migration\IRepairStep;
+use Doctrine\DBAL\Platforms\OraclePlatform;
/**
* Repairs shares with invalid data
@@ -92,6 +93,26 @@ class RepairInvalidShares implements IRepairStep {
}
/**
+ * Adjust file share permissions
+ */
+ private function adjustFileSharePermissions(IOutput $out) {
+ $mask = \OCP\Constants::PERMISSION_READ | \OCP\Constants::PERMISSION_UPDATE | \OCP\Constants::PERMISSION_SHARE;
+ $builder = $this->connection->getQueryBuilder();
+
+ $permsFunc = $builder->expr()->bitwiseAnd('permissions', $mask);
+ $builder
+ ->update('share')
+ ->set('permissions', $permsFunc)
+ ->where($builder->expr()->eq('item_type', $builder->expr()->literal('file')))
+ ->andWhere($builder->expr()->neq('permissions', $permsFunc));
+
+ $updatedEntries = $builder->execute();
+ if ($updatedEntries > 0) {
+ $out->info('Fixed file share permissions for ' . $updatedEntries . ' shares');
+ }
+ }
+
+ /**
* Remove shares where the parent share does not exist anymore
*/
private function removeSharesNonExistingParent(IOutput $out) {
@@ -137,6 +158,9 @@ class RepairInvalidShares implements IRepairStep {
// this situation was only possible before 9.1
$this->addShareLinkDeletePermission($out);
}
+ if (version_compare($ocVersionFromBeforeUpdate, '12.0.0.11', '<')) {
+ $this->adjustFileSharePermissions($out);
+ }
$this->removeSharesNonExistingParent($out);
}
diff --git a/lib/private/Repair/RepairLegacyStorages.php b/lib/private/Repair/RepairLegacyStorages.php
deleted file mode 100644
index 228bdb67fe8..00000000000
--- a/lib/private/Repair/RepairLegacyStorages.php
+++ /dev/null
@@ -1,257 +0,0 @@
-<?php
-/**
- * @copyright Copyright (c) 2016, ownCloud, Inc.
- *
- * @author Aaron Wood <aaronjwood@gmail.com>
- * @author Joas Schilling <coding@schilljs.com>
- * @author Morris Jobke <hey@morrisjobke.de>
- * @author Thomas Müller <thomas.mueller@tmit.eu>
- * @author Vincent Petry <pvince81@owncloud.com>
- *
- * @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 OC\Repair;
-
-use OC\Files\Cache\Storage;
-use OC\RepairException;
-use OCP\Migration\IOutput;
-use OCP\Migration\IRepairStep;
-
-class RepairLegacyStorages implements IRepairStep{
- /**
- * @var \OCP\IConfig
- */
- protected $config;
-
- /**
- * @var \OCP\IDBConnection
- */
- protected $connection;
-
- protected $findStorageInCacheStatement;
- protected $renameStorageStatement;
-
- /**
- * @param \OCP\IConfig $config
- * @param \OCP\IDBConnection $connection
- */
- public function __construct($config, $connection) {
- $this->connection = $connection;
- $this->config = $config;
-
- $this->findStorageInCacheStatement = $this->connection->prepare(
- 'SELECT DISTINCT `storage` FROM `*PREFIX*filecache`'
- . ' WHERE `storage` in (?, ?)'
- );
- $this->renameStorageStatement = $this->connection->prepare(
- 'UPDATE `*PREFIX*storages`'
- . ' SET `id` = ?'
- . ' WHERE `id` = ?'
- );
- }
-
- public function getName() {
- return 'Repair legacy storages';
- }
-
- /**
- * Extracts the user id from a legacy storage id
- *
- * @param string $storageId legacy storage id in the
- * format "local::/path/to/datadir/userid"
- * @return string user id extracted from the storage id
- */
- private function extractUserId($storageId) {
- $storageId = rtrim($storageId, '/');
- $pos = strrpos($storageId, '/');
- return substr($storageId, $pos + 1);
- }
-
- /**
- * Fix the given legacy storage by renaming the old id
- * to the new id. If the new id already exists, whichever
- * storage that has data in the file cache will be used.
- * If both have data, nothing will be done and false is
- * returned.
- *
- * @param string $oldId old storage id
- * @param int $oldNumericId old storage numeric id
- * @param string $userId
- * @return bool true if fixed, false otherwise
- * @throws RepairException
- */
- private function fixLegacyStorage($oldId, $oldNumericId, $userId = null) {
- // check whether the new storage already exists
- if (is_null($userId)) {
- $userId = $this->extractUserId($oldId);
- }
- $newId = 'home::' . $userId;
-
- // check if target id already exists
- $newNumericId = Storage::getNumericStorageId($newId);
- if (!is_null($newNumericId)) {
- $newNumericId = (int)$newNumericId;
- // try and resolve the conflict
- // check which one of "local::" or "home::" needs to be kept
- $this->findStorageInCacheStatement->execute(array($oldNumericId, $newNumericId));
- $row1 = $this->findStorageInCacheStatement->fetch();
- $row2 = $this->findStorageInCacheStatement->fetch();
- $this->findStorageInCacheStatement->closeCursor();
- if ($row2 !== false) {
- // two results means both storages have data, not auto-fixable
- throw new RepairException(
- 'Could not automatically fix legacy storage '
- . '"' . $oldId . '" => "' . $newId . '"'
- . ' because they both have data.'
- );
- }
- if ($row1 === false || (int)$row1['storage'] === $oldNumericId) {
- // old storage has data, then delete the empty new id
- $toDelete = $newId;
- } else if ((int)$row1['storage'] === $newNumericId) {
- // new storage has data, then delete the empty old id
- $toDelete = $oldId;
- } else {
- // unknown case, do not continue
- return false;
- }
-
- // delete storage including file cache
- Storage::remove($toDelete);
-
- // if we deleted the old id, the new id will be used
- // automatically
- if ($toDelete === $oldId) {
- // nothing more to do
- return true;
- }
- }
-
- // rename old id to new id
- $newId = Storage::adjustStorageId($newId);
- $oldId = Storage::adjustStorageId($oldId);
- $rowCount = $this->renameStorageStatement->execute(array($newId, $oldId));
- $this->renameStorageStatement->closeCursor();
- return ($rowCount === 1);
- }
-
- /**
- * Converts legacy home storage ids in the format
- * "local::/data/dir/path/userid/" to the new format "home::userid"
- */
- public function run(IOutput $out) {
- // only run once
- if ($this->config->getAppValue('core', 'repairlegacystoragesdone') === 'yes') {
- return;
- }
-
- $dataDir = $this->config->getSystemValue('datadirectory', \OC::$SERVERROOT . '/data/');
- $dataDir = rtrim($dataDir, '/') . '/';
- $dataDirId = 'local::' . $dataDir;
-
- $count = 0;
- $hasWarnings = false;
-
- $this->connection->beginTransaction();
-
- // note: not doing a direct UPDATE with the REPLACE function
- // because regexp search/extract is needed and it is not guaranteed
- // to work on all database types
- $sql = 'SELECT `id`, `numeric_id` FROM `*PREFIX*storages`'
- . ' WHERE `id` LIKE ?'
- . ' ORDER BY `id`';
- $result = $this->connection->executeQuery($sql, array($this->connection->escapeLikeParameter($dataDirId) . '%'));
-
- while ($row = $result->fetch()) {
- $currentId = $row['id'];
- // one entry is the datadir itself
- if ($currentId === $dataDirId) {
- continue;
- }
-
- try {
- if ($this->fixLegacyStorage($currentId, (int)$row['numeric_id'])) {
- $count++;
- }
- }
- catch (RepairException $e) {
- $hasWarnings = true;
- $out->warning('Could not repair legacy storage ' . $currentId . ' automatically.');
- }
- }
-
- // check for md5 ids, not in the format "prefix::"
- $sql = 'SELECT COUNT(*) AS "c" FROM `*PREFIX*storages`'
- . ' WHERE `id` NOT LIKE \'%::%\'';
- $result = $this->connection->executeQuery($sql);
- $row = $result->fetch();
-
- // find at least one to make sure it's worth
- // querying the user list
- if ((int)$row['c'] > 0) {
- $userManager = \OC::$server->getUserManager();
-
- // use chunks to avoid caching too many users in memory
- $limit = 30;
- $offset = 0;
-
- do {
- // query the next page of users
- $results = $userManager->search('', $limit, $offset);
- $storageIds = array();
- foreach ($results as $uid => $userObject) {
- $storageId = $dataDirId . $uid . '/';
- if (strlen($storageId) <= 64) {
- // skip short storage ids as they were handled in the previous section
- continue;
- }
- $storageIds[$uid] = $storageId;
- }
-
- if (count($storageIds) > 0) {
- // update the storages of these users
- foreach ($storageIds as $uid => $storageId) {
- $numericId = Storage::getNumericStorageId($storageId);
- try {
- if (!is_null($numericId) && $this->fixLegacyStorage($storageId, (int)$numericId)) {
- $count++;
- }
- }
- catch (RepairException $e) {
- $hasWarnings = true;
- $out->warning('Could not repair legacy storage ' . $storageId . ' automatically.');
- }
- }
- }
- $offset += $limit;
- } while (count($results) >= $limit);
- }
-
- $out->info('Updated ' . $count . ' legacy home storage ids');
-
- $this->connection->commit();
-
- Storage::getGlobalCache()->clearCache();
-
- if ($hasWarnings) {
- $out->warning('Some legacy storages could not be repaired. Please manually fix them then re-run ./occ maintenance:repair');
- } else {
- // if all were done, no need to redo the repair during next upgrade
- $this->config->setAppValue('core', 'repairlegacystoragesdone', 'yes');
- }
- }
-}
diff --git a/lib/private/Security/Bruteforce/Throttler.php b/lib/private/Security/Bruteforce/Throttler.php
index 031c5ffd411..765f109fdb3 100644
--- a/lib/private/Security/Bruteforce/Throttler.php
+++ b/lib/private/Security/Bruteforce/Throttler.php
@@ -189,9 +189,10 @@ class Throttler {
* Get the throttling delay (in milliseconds)
*
* @param string $ip
+ * @param string $action optionally filter by action
* @return int
*/
- public function getDelay($ip) {
+ public function getDelay($ip, $action = '') {
$cutoffTime = (new \DateTime())
->sub($this->getCutoff(43200))
->getTimestamp();
@@ -201,6 +202,11 @@ class Throttler {
->from('bruteforce_attempts')
->where($qb->expr()->gt('occurred', $qb->createNamedParameter($cutoffTime)))
->andWhere($qb->expr()->eq('subnet', $qb->createNamedParameter($this->getSubnet($ip))));
+
+ if ($action !== '') {
+ $qb->andWhere($qb->expr()->eq('action', $qb->createNamedParameter($action)));
+ }
+
$attempts = count($qb->execute()->fetchAll());
if ($attempts === 0) {
@@ -225,10 +231,11 @@ class Throttler {
* Will sleep for the defined amount of time
*
* @param string $ip
+ * @param string $action optionally filter by action
* @return int the time spent sleeping
*/
- public function sleepDelay($ip) {
- $delay = $this->getDelay($ip);
+ public function sleepDelay($ip, $action = '') {
+ $delay = $this->getDelay($ip, $action);
usleep($delay * 1000);
return $delay;
}
diff --git a/lib/private/Security/CSP/ContentSecurityPolicyNonceManager.php b/lib/private/Security/CSP/ContentSecurityPolicyNonceManager.php
index 284700566d6..85ae127f5f1 100644
--- a/lib/private/Security/CSP/ContentSecurityPolicyNonceManager.php
+++ b/lib/private/Security/CSP/ContentSecurityPolicyNonceManager.php
@@ -69,8 +69,6 @@ class ContentSecurityPolicyNonceManager {
Request::USER_AGENT_CHROME,
// Firefox 45+
'/^Mozilla\/5\.0 \([^)]+\) Gecko\/[0-9.]+ Firefox\/(4[5-9]|[5-9][0-9])\.[0-9.]+$/',
- // Safari 10+
- '/^Mozilla\/5\.0 \([^)]+\) AppleWebKit\/[0-9.]+ \(KHTML, like Gecko\) Version\/1[0-9.]+ Safari\/[0-9.A-Z]+$/',
];
if($this->request->isUserAgent($browserWhitelist)) {
diff --git a/lib/private/Security/CertificateManager.php b/lib/private/Security/CertificateManager.php
index f7bf0df58c5..461ef9457a7 100644
--- a/lib/private/Security/CertificateManager.php
+++ b/lib/private/Security/CertificateManager.php
@@ -30,6 +30,7 @@ namespace OC\Security;
use OC\Files\Filesystem;
use OCP\ICertificateManager;
use OCP\IConfig;
+use OCP\ILogger;
/**
* Manage trusted certificates for users
@@ -51,14 +52,21 @@ class CertificateManager implements ICertificateManager {
protected $config;
/**
+ * @var ILogger
+ */
+ protected $logger;
+
+ /**
* @param string $uid
* @param \OC\Files\View $view relative to data/
* @param IConfig $config
+ * @param ILogger $logger
*/
- public function __construct($uid, \OC\Files\View $view, IConfig $config) {
+ public function __construct($uid, \OC\Files\View $view, IConfig $config, ILogger $logger) {
$this->uid = $uid;
$this->view = $view;
$this->config = $config;
+ $this->logger = $logger;
}
/**
@@ -104,6 +112,13 @@ class CertificateManager implements ICertificateManager {
$this->view->mkdir($path);
}
+ $defaultCertificates = file_get_contents(\OC::$SERVERROOT . '/resources/config/ca-bundle.crt');
+ if (strlen($defaultCertificates) < 1024) { // sanity check to verify that we have some content for our bundle
+ // log as exception so we have a stacktrace
+ $this->logger->logException(new \Exception('Shipped ca-bundle is empty, refusing to create certificate bundle'));
+ return;
+ }
+
$fhCerts = $this->view->fopen($path . '/rootcerts.crt', 'w');
// Write user certificates
@@ -117,7 +132,6 @@ class CertificateManager implements ICertificateManager {
}
// Append the default certificates
- $defaultCertificates = file_get_contents(\OC::$SERVERROOT . '/resources/config/ca-bundle.crt');
fwrite($fhCerts, $defaultCertificates);
// Append the system certificate bundle
@@ -203,7 +217,7 @@ class CertificateManager implements ICertificateManager {
}
if ($this->needsRebundling($uid)) {
if (is_null($uid)) {
- $manager = new CertificateManager(null, $this->view, $this->config);
+ $manager = new CertificateManager(null, $this->view, $this->config, $this->logger);
$manager->createCertificateBundle();
} else {
$this->createCertificateBundle();
diff --git a/lib/private/Server.php b/lib/private/Server.php
index 2c0aac9b43c..3c716ae6ce6 100644
--- a/lib/private/Server.php
+++ b/lib/private/Server.php
@@ -45,8 +45,8 @@ use bantu\IniGetWrapper\IniGetWrapper;
use OC\App\AppStore\Fetcher\AppFetcher;
use OC\App\AppStore\Fetcher\CategoryFetcher;
use OC\AppFramework\Http\Request;
-use OC\AppFramework\Db\Db;
use OC\AppFramework\Utility\TimeFactory;
+use OC\Authentication\LoginCredentials\Store;
use OC\Command\AsyncBus;
use OC\Diagnostics\EventLogger;
use OC\Diagnostics\NullEventLogger;
@@ -90,6 +90,7 @@ use OC\Security\TrustedDomainHelper;
use OC\Session\CryptoWrapper;
use OC\Tagging\TagMapper;
use OCA\Theming\ThemingDefaults;
+use OCP\Authentication\LoginCredentials\IStore;
use OCP\IL10N;
use OCP\IServerContainer;
use OCP\RichObjectStrings\IValidator;
@@ -125,7 +126,8 @@ class Server extends ServerContainer implements IServerContainer {
$c->getConfig(),
$c->getRootFolder(),
$c->getAppDataDir('preview'),
- $c->getEventDispatcher()
+ $c->getEventDispatcher(),
+ $c->getSession()->get('user_id')
);
});
@@ -246,6 +248,17 @@ class Server extends ServerContainer implements IServerContainer {
});
return $groupManager;
});
+ $this->registerService(Store::class, function(Server $c) {
+ $session = $c->getSession();
+ if (\OC::$server->getSystemConfig()->getValue('installed', false)) {
+ $tokenProvider = $c->query('OC\Authentication\Token\IProvider');
+ } else {
+ $tokenProvider = null;
+ }
+ $logger = $c->getLogger();
+ return new Store($session, $logger, $tokenProvider);
+ });
+ $this->registerAlias(IStore::class, Store::class);
$this->registerService('OC\Authentication\Token\DefaultTokenMapper', function (Server $c) {
$dbConnection = $c->getDatabaseConnection();
return new Authentication\Token\DefaultTokenMapper($dbConnection);
@@ -313,11 +326,15 @@ class Server extends ServerContainer implements IServerContainer {
});
$this->registerService(\OC\Authentication\TwoFactorAuth\Manager::class, function (Server $c) {
- return new \OC\Authentication\TwoFactorAuth\Manager($c->getAppManager(), $c->getSession(), $c->getConfig());
+ return new \OC\Authentication\TwoFactorAuth\Manager($c->getAppManager(), $c->getSession(), $c->getConfig(), $c->getActivityManager(), $c->getLogger());
});
- $this->registerService('NavigationManager', function ($c) {
- return new \OC\NavigationManager();
+ $this->registerService('NavigationManager', function (Server $c) {
+ return new \OC\NavigationManager($c->getAppManager(),
+ $c->getURLGenerator(),
+ $c->getL10NFactory(),
+ $c->getUserSession(),
+ $c->getGroupManager());
});
$this->registerService('AllConfig', function (Server $c) {
return new \OC\AllConfig(
@@ -361,7 +378,8 @@ class Server extends ServerContainer implements IServerContainer {
return new CategoryFetcher(
$this->getAppDataDir('appstore'),
$this->getHTTPClientService(),
- $this->query(TimeFactory::class)
+ $this->query(TimeFactory::class),
+ $this->getConfig()
);
});
$this->registerService('UserCache', function ($c) {
@@ -376,7 +394,7 @@ class Server extends ServerContainer implements IServerContainer {
$version = implode(',', $v);
$instanceId = \OC_Util::getInstanceId();
$path = \OC::$SERVERROOT;
- $prefix = md5($instanceId . '-' . $version . '-' . $path);
+ $prefix = md5($instanceId . '-' . $version . '-' . $path . '-' . \OC::$WEBROOT);
return new \OC\Memcache\Factory($prefix, $c->getLogger(),
$config->getSystemValue('memcache.local', null),
$config->getSystemValue('memcache.distributed', null),
@@ -418,9 +436,8 @@ class Server extends ServerContainer implements IServerContainer {
);
});
$this->registerService('Logger', function (Server $c) {
- $logClass = $c->query('AllConfig')->getSystemValue('log_type', 'file');
- // TODO: Drop backwards compatibility for config in the future
- $logger = 'OC\\Log\\' . ucfirst($logClass=='owncloud' ? 'file' : $logClass);
+ $logType = $c->query('AllConfig')->getSystemValue('log_type', 'file');
+ $logger = Log::getLogClass($logType);
call_user_func(array($logger, 'init'));
return new Log($logger);
@@ -470,9 +487,6 @@ class Server extends ServerContainer implements IServerContainer {
$connection->getConfiguration()->setSQLLogger($c->getQueryLogger());
return $connection;
});
- $this->registerService('Db', function (Server $c) {
- return new Db($c->getDatabaseConnection());
- });
$this->registerService('HTTPHelper', function (Server $c) {
$config = $c->getConfig();
return new HTTPHelper(
@@ -485,7 +499,7 @@ class Server extends ServerContainer implements IServerContainer {
$uid = $user ? $user : null;
return new ClientService(
$c->getConfig(),
- new \OC\Security\CertificateManager($uid, new View(), $c->getConfig())
+ new \OC\Security\CertificateManager($uid, new View(), $c->getConfig(), $c->getLogger())
);
});
$this->registerService('EventLogger', function (Server $c) {
@@ -794,7 +808,9 @@ class Server extends ServerContainer implements IServerContainer {
$c->getConfig(),
$c->getEncryptionManager(),
$c->getUserManager(),
- $c->getLockingProvider()
+ $c->getLockingProvider(),
+ new \OC\Settings\Mapper($c->getDatabaseConnection()),
+ $c->getURLGenerator()
);
return $manager;
});
@@ -900,7 +916,6 @@ class Server extends ServerContainer implements IServerContainer {
return $this->query('SystemTagObjectMapper');
}
-
/**
* Returns the avatar manager, used for avatar functionality
*
@@ -998,7 +1013,8 @@ class Server extends ServerContainer implements IServerContainer {
*/
public function setSession(\OCP\ISession $session) {
$this->query(SessionStorage::class)->setSession($session);
- return $this->query('UserSession')->setSession($session);
+ $this->query('UserSession')->setSession($session);
+ $this->query(Store::class)->setSession($session);
}
/**
@@ -1199,16 +1215,6 @@ class Server extends ServerContainer implements IServerContainer {
}
/**
- * Returns an instance of the db facade
- *
- * @deprecated use getDatabaseConnection, will be removed in ownCloud 10
- * @return \OCP\IDb
- */
- public function getDb() {
- return $this->query('Db');
- }
-
- /**
* Returns an instance of the HTTP helper class
*
* @deprecated Use getHTTPClientService()
@@ -1233,7 +1239,7 @@ class Server extends ServerContainer implements IServerContainer {
}
$userId = $user->getUID();
}
- return new CertificateManager($userId, new View(), $this->getConfig());
+ return new CertificateManager($userId, new View(), $this->getConfig(), $this->getLogger());
}
/**
diff --git a/lib/private/Settings/Admin/Encryption.php b/lib/private/Settings/Admin/Encryption.php
index 6e93407f1a3..63020c6bce7 100644
--- a/lib/private/Settings/Admin/Encryption.php
+++ b/lib/private/Settings/Admin/Encryption.php
@@ -23,23 +23,23 @@
namespace OC\Settings\Admin;
-use OC\Encryption\Manager;
use OCP\AppFramework\Http\TemplateResponse;
+use OCP\Encryption\IManager;
use OCP\IUserManager;
use OCP\Settings\ISettings;
class Encryption implements ISettings {
- /** @var Manager */
+ /** @var IManager */
private $manager;
/** @var IUserManager */
private $userManager;
/**
- * @param Manager $manager
+ * @param IManager $manager
* @param IUserManager $userManager
*/
- public function __construct(Manager $manager, IUserManager $userManager) {
+ public function __construct(IManager $manager, IUserManager $userManager) {
$this->manager = $manager;
$this->userManager = $userManager;
}
diff --git a/lib/private/Settings/Manager.php b/lib/private/Settings/Manager.php
index 990750848d3..7a339b94199 100644
--- a/lib/private/Settings/Manager.php
+++ b/lib/private/Settings/Manager.php
@@ -29,6 +29,7 @@ use OCP\IConfig;
use OCP\IDBConnection;
use OCP\IL10N;
use OCP\ILogger;
+use OCP\IURLGenerator;
use OCP\IUserManager;
use OCP\Lock\ILockingProvider;
use OCP\Settings\ISettings;
@@ -43,6 +44,8 @@ class Manager implements IManager {
private $log;
/** @var IDBConnection */
private $dbc;
+ /** @var Mapper */
+ private $mapper;
/** @var IL10N */
private $l;
/** @var IConfig */
@@ -53,6 +56,8 @@ class Manager implements IManager {
private $userManager;
/** @var ILockingProvider */
private $lockingProvider;
+ /** @var IURLGenerator */
+ private $url;
/**
* @param ILogger $log
@@ -62,6 +67,8 @@ class Manager implements IManager {
* @param EncryptionManager $encryptionManager
* @param IUserManager $userManager
* @param ILockingProvider $lockingProvider
+ * @param Mapper $mapper
+ * @param IURLGenerator $url
*/
public function __construct(
ILogger $log,
@@ -70,25 +77,29 @@ class Manager implements IManager {
IConfig $config,
EncryptionManager $encryptionManager,
IUserManager $userManager,
- ILockingProvider $lockingProvider
+ ILockingProvider $lockingProvider,
+ Mapper $mapper,
+ IURLGenerator $url
) {
$this->log = $log;
$this->dbc = $dbc;
+ $this->mapper = $mapper;
$this->l = $l;
$this->config = $config;
$this->encryptionManager = $encryptionManager;
$this->userManager = $userManager;
$this->lockingProvider = $lockingProvider;
+ $this->url = $url;
}
/**
* @inheritdoc
*/
public function setupSettings(array $settings) {
- if(isset($settings[IManager::KEY_ADMIN_SECTION])) {
+ if (isset($settings[IManager::KEY_ADMIN_SECTION])) {
$this->setupAdminSection($settings[IManager::KEY_ADMIN_SECTION]);
}
- if(isset($settings[IManager::KEY_ADMIN_SETTINGS])) {
+ if (isset($settings[IManager::KEY_ADMIN_SETTINGS])) {
$this->setupAdminSettings($settings[IManager::KEY_ADMIN_SETTINGS]);
}
}
@@ -104,50 +115,33 @@ class Manager implements IManager {
public function onAppDisabled($appId) {
$appInfo = \OC_App::getAppInfo($appId); // hello static legacy
- if(isset($appInfo['settings'][IManager::KEY_ADMIN_SECTION])) {
- $this->remove(self::TABLE_ADMIN_SECTIONS, trim($appInfo['settings'][IManager::KEY_ADMIN_SECTION], '\\'));
+ if (isset($appInfo['settings'][IManager::KEY_ADMIN_SECTION])) {
+ $this->mapper->remove(self::TABLE_ADMIN_SECTIONS, trim($appInfo['settings'][IManager::KEY_ADMIN_SECTION], '\\'));
}
- if(isset($appInfo['settings'][IManager::KEY_ADMIN_SETTINGS])) {
- $this->remove(self::TABLE_ADMIN_SETTINGS, trim($appInfo['settings'][IManager::KEY_ADMIN_SETTINGS], '\\'));
+ if (isset($appInfo['settings'][IManager::KEY_ADMIN_SETTINGS])) {
+ $this->mapper->remove(self::TABLE_ADMIN_SETTINGS, trim($appInfo['settings'][IManager::KEY_ADMIN_SETTINGS], '\\'));
}
}
public function checkForOrphanedClassNames() {
- $tables = [ self::TABLE_ADMIN_SECTIONS, self::TABLE_ADMIN_SETTINGS ];
+ $tables = [self::TABLE_ADMIN_SECTIONS, self::TABLE_ADMIN_SETTINGS];
foreach ($tables as $table) {
- $classes = $this->getClasses($table);
- foreach($classes as $className) {
+ $classes = $this->mapper->getClasses($table);
+ foreach ($classes as $className) {
try {
\OC::$server->query($className);
} catch (QueryException $e) {
- $this->remove($table, $className);
+ $this->mapper->remove($table, $className);
}
}
}
}
/**
- * returns the registerd classes in the given table
- *
- * @param $table
- * @return string[]
- */
- private function getClasses($table) {
- $q = $this->dbc->getQueryBuilder();
- $resultStatement = $q->select('class')
- ->from($table)
- ->execute();
- $data = $resultStatement->fetchAll();
- $resultStatement->closeCursor();
-
- return array_map(function($row) { return $row['class']; }, $data);
- }
-
- /**
* @param string $sectionClassName
*/
private function setupAdminSection($sectionClassName) {
- if(!class_exists($sectionClassName)) {
+ if (!class_exists($sectionClassName)) {
$this->log->debug('Could not find admin section class ' . $sectionClassName);
return;
}
@@ -158,14 +152,14 @@ class Manager implements IManager {
return;
}
- if(!$section instanceof ISection) {
+ if (!$section instanceof ISection) {
$this->log->error(
'Admin section instance must implement \OCP\ISection. Invalid class: {class}',
['class' => $sectionClassName]
);
return;
}
- if(!$this->hasAdminSection(get_class($section))) {
+ if (!$this->hasAdminSection(get_class($section))) {
$this->addAdminSection($section);
} else {
$this->updateAdminSection($section);
@@ -173,7 +167,7 @@ class Manager implements IManager {
}
private function addAdminSection(ISection $section) {
- $this->add(self::TABLE_ADMIN_SECTIONS, [
+ $this->mapper->add(self::TABLE_ADMIN_SECTIONS, [
'id' => $section->getID(),
'class' => get_class($section),
'priority' => $section->getPriority(),
@@ -181,28 +175,15 @@ class Manager implements IManager {
}
private function addAdminSettings(ISettings $settings) {
- $this->add(self::TABLE_ADMIN_SETTINGS, [
+ $this->mapper->add(self::TABLE_ADMIN_SETTINGS, [
'class' => get_class($settings),
'section' => $settings->getSection(),
'priority' => $settings->getPriority(),
]);
}
- /**
- * @param string $table
- * @param array $values
- */
- private function add($table, array $values) {
- $query = $this->dbc->getQueryBuilder();
- $values = array_map(function($value) use ($query) {
- return $query->createNamedParameter($value);
- }, $values);
- $query->insert($table)->values($values);
- $query->execute();
- }
-
private function updateAdminSettings(ISettings $settings) {
- $this->update(
+ $this->mapper->update(
self::TABLE_ADMIN_SETTINGS,
'class',
get_class($settings),
@@ -214,35 +195,23 @@ class Manager implements IManager {
}
private function updateAdminSection(ISection $section) {
- $this->update(
+ $this->mapper->update(
self::TABLE_ADMIN_SECTIONS,
'class',
get_class($section),
[
- 'id' => $section->getID(),
+ 'id' => $section->getID(),
'priority' => $section->getPriority(),
]
);
}
- private function update($table, $idCol, $id, $values) {
- $query = $this->dbc->getQueryBuilder();
- $query->update($table);
- foreach($values as $key => $value) {
- $query->set($key, $query->createNamedParameter($value));
- }
- $query
- ->where($query->expr()->eq($idCol, $query->createParameter($idCol)))
- ->setParameter($idCol, $id)
- ->execute();
- }
-
/**
* @param string $className
* @return bool
*/
private function hasAdminSection($className) {
- return $this->has(self::TABLE_ADMIN_SECTIONS, $className);
+ return $this->mapper->has(self::TABLE_ADMIN_SECTIONS, $className);
}
/**
@@ -250,44 +219,11 @@ class Manager implements IManager {
* @return bool
*/
private function hasAdminSettings($className) {
- return $this->has(self::TABLE_ADMIN_SETTINGS, $className);
- }
-
- /**
- * @param string $table
- * @param string $className
- * @return bool
- */
- private function has($table, $className) {
- $query = $this->dbc->getQueryBuilder();
- $query->select('class')
- ->from($table)
- ->where($query->expr()->eq('class', $query->createNamedParameter($className)))
- ->setMaxResults(1);
-
- $result = $query->execute();
- $row = $result->fetch();
- $result->closeCursor();
-
- return (bool) $row;
- }
-
- /**
- * deletes an settings or admin entry from the given table
- *
- * @param $table
- * @param $className
- */
- private function remove($table, $className) {
- $query = $this->dbc->getQueryBuilder();
- $query->delete($table)
- ->where($query->expr()->eq('class', $query->createNamedParameter($className)));
-
- $query->execute();
+ return $this->mapper->has(self::TABLE_ADMIN_SETTINGS, $className);
}
private function setupAdminSettings($settingsClassName) {
- if(!class_exists($settingsClassName)) {
+ if (!class_exists($settingsClassName)) {
$this->log->debug('Could not find admin section class ' . $settingsClassName);
return;
}
@@ -300,14 +236,14 @@ class Manager implements IManager {
return;
}
- if(!$settings instanceof ISettings) {
+ if (!$settings instanceof ISettings) {
$this->log->error(
'Admin section instance must implement \OCP\Settings\ISection. Invalid class: {class}',
['class' => $settingsClassName]
);
return;
}
- if(!$this->hasAdminSettings(get_class($settings))) {
+ if (!$this->hasAdminSettings(get_class($settings))) {
$this->addAdminSettings($settings);
} else {
$this->updateAdminSettings($settings);
@@ -329,24 +265,17 @@ class Manager implements IManager {
public function getAdminSections() {
// built-in sections
$sections = [
- 0 => [new Section('server', $this->l->t('Server settings'), 0)],
- 5 => [new Section('sharing', $this->l->t('Sharing'), 0)],
- 45 => [new Section('encryption', $this->l->t('Encryption'), 0)],
- 98 => [new Section('additional', $this->l->t('Additional settings'), 0)],
- 99 => [new Section('tips-tricks', $this->l->t('Tips & tricks'), 0)],
+ 0 => [new Section('server', $this->l->t('Server settings'), 0, $this->url->imagePath('settings', 'admin.svg'))],
+ 5 => [new Section('sharing', $this->l->t('Sharing'), 0, $this->url->imagePath('core', 'actions/share.svg'))],
+ 45 => [new Section('encryption', $this->l->t('Encryption'), 0, $this->url->imagePath('core', 'actions/password.svg'))],
+ 98 => [new Section('additional', $this->l->t('Additional settings'), 0, $this->url->imagePath('core', 'actions/settings-dark.svg'))],
+ 99 => [new Section('tips-tricks', $this->l->t('Tips & tricks'), 0, $this->url->imagePath('settings', 'help.svg'))],
];
- $query = $this->dbc->getQueryBuilder();
- $query->selectDistinct('s.class')
- ->addSelect('s.priority')
- ->from(self::TABLE_ADMIN_SECTIONS, 's')
- ->from(self::TABLE_ADMIN_SETTINGS, 'f')
- ->where($query->expr()->eq('s.id', 'f.section'))
- ;
- $result = $query->execute();
+ $rows = $this->mapper->getAdminSectionsFromDB();
- while($row = $result->fetch()) {
- if(!isset($sections[$row['priority']])) {
+ foreach ($rows as $row) {
+ if (!isset($sections[$row['priority']])) {
$sections[$row['priority']] = [];
}
try {
@@ -355,38 +284,42 @@ class Manager implements IManager {
// skip
}
}
- $result->closeCursor();
ksort($sections);
+
return $sections;
}
+ /**
+ * @param string $section
+ * @return ISection[]
+ */
private function getBuiltInAdminSettings($section) {
$forms = [];
try {
- if($section === 'server') {
+ if ($section === 'server') {
/** @var ISettings $form */
$form = new Admin\Server($this->dbc, $this->config, $this->lockingProvider, $this->l);
$forms[$form->getPriority()] = [$form];
$form = new Admin\ServerDevNotice();
$forms[$form->getPriority()] = [$form];
}
- if($section === 'encryption') {
+ if ($section === 'encryption') {
/** @var ISettings $form */
$form = new Admin\Encryption($this->encryptionManager, $this->userManager);
$forms[$form->getPriority()] = [$form];
}
- if($section === 'sharing') {
+ if ($section === 'sharing') {
/** @var ISettings $form */
$form = new Admin\Sharing($this->config);
$forms[$form->getPriority()] = [$form];
}
- if($section === 'additional') {
+ if ($section === 'additional') {
/** @var ISettings $form */
$form = new Admin\Additional($this->config);
$forms[$form->getPriority()] = [$form];
}
- if($section === 'tips-tricks') {
+ if ($section === 'tips-tricks') {
/** @var ISettings $form */
$form = new Admin\TipsTricks($this->config);
$forms[$form->getPriority()] = [$form];
@@ -397,16 +330,15 @@ class Manager implements IManager {
return $forms;
}
- private function getAdminSettingsFromDB($section, &$settings) {
- $query = $this->dbc->getQueryBuilder();
- $query->select(['class', 'priority'])
- ->from(self::TABLE_ADMIN_SETTINGS)
- ->where($query->expr()->eq('section', $this->dbc->getQueryBuilder()->createParameter('section')))
- ->setParameter('section', $section);
+ /**
+ * @inheritdoc
+ */
+ public function getAdminSettings($section) {
+ $settings = $this->getBuiltInAdminSettings($section);
+ $dbRows = $this->mapper->getAdminSettingsFromDB($section);
- $result = $query->execute();
- while($row = $result->fetch()) {
- if(!isset($settings[$row['priority']])) {
+ foreach ($dbRows as $row) {
+ if (!isset($settings[$row['priority']])) {
$settings[$row['priority']] = [];
}
try {
@@ -415,17 +347,8 @@ class Manager implements IManager {
// skip
}
}
- $result->closeCursor();
ksort($settings);
- }
-
- /**
- * @inheritdoc
- */
- public function getAdminSettings($section) {
- $settings = $this->getBuiltInAdminSettings($section);
- $this->getAdminSettingsFromDB($section, $settings);
return $settings;
}
}
diff --git a/lib/private/Settings/Mapper.php b/lib/private/Settings/Mapper.php
new file mode 100644
index 00000000000..2525f2c9854
--- /dev/null
+++ b/lib/private/Settings/Mapper.php
@@ -0,0 +1,163 @@
+<?php
+/**
+ * @copyright Copyright (c) 2016 Robin Appelman <robin@icewind.nl>
+ *
+ * @author Robin Appelman <robin@icewind.nl>
+ *
+ * @license GNU AGPL version 3 or any later version
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+namespace OC\Settings;
+
+use OCP\IDBConnection;
+
+class Mapper {
+ const TABLE_ADMIN_SETTINGS = 'admin_settings';
+ const TABLE_ADMIN_SECTIONS = 'admin_sections';
+
+ /** @var IDBConnection */
+ private $dbc;
+
+ /**
+ * @param IDBConnection $dbc
+ */
+ public function __construct(IDBConnection $dbc) {
+ $this->dbc = $dbc;
+ }
+
+ /**
+ * Get the configured admin settings from the database for the provided section
+ *
+ * @param string $section
+ * @return array[] [['class' => string, 'priority' => int], ...]
+ */
+ public function getAdminSettingsFromDB($section) {
+ $query = $this->dbc->getQueryBuilder();
+ $query->select(['class', 'priority'])
+ ->from(self::TABLE_ADMIN_SETTINGS)
+ ->where($query->expr()->eq('section', $this->dbc->getQueryBuilder()->createParameter('section')))
+ ->setParameter('section', $section);
+
+ $result = $query->execute();
+ return $result->fetchAll();
+ }
+
+ /**
+ * Get the configured admin sections from the database
+ *
+ * @return array[] [['class' => string, 'priority' => int], ...]
+ */
+ public function getAdminSectionsFromDB() {
+ $query = $this->dbc->getQueryBuilder();
+ $query->selectDistinct('s.class')
+ ->addSelect('s.priority')
+ ->from(self::TABLE_ADMIN_SECTIONS, 's')
+ ->from(self::TABLE_ADMIN_SETTINGS, 'f')
+ ->where($query->expr()->eq('s.id', 'f.section'));
+ $result = $query->execute();
+ return array_map(function ($row) {
+ $row['priority'] = (int)$row['priority'];
+ return $row;
+ }, $result->fetchAll());
+ }
+
+ /**
+ * @param string $table Mapper::TABLE_ADMIN_SECTIONS or Mapper::TABLE_ADMIN_SETTINGS
+ * @param array $values
+ */
+ public function add($table, array $values) {
+ $query = $this->dbc->getQueryBuilder();
+ $values = array_map(function ($value) use ($query) {
+ return $query->createNamedParameter($value);
+ }, $values);
+ $query->insert($table)->values($values);
+ $query->execute();
+ }
+
+ /**
+ * returns the registered classes in the given table
+ *
+ * @param $table Mapper::TABLE_ADMIN_SECTIONS or Mapper::TABLE_ADMIN_SETTINGS
+ * @return string[]
+ */
+ public function getClasses($table) {
+ $q = $this->dbc->getQueryBuilder();
+ $resultStatement = $q->select('class')
+ ->from($table)
+ ->execute();
+ $data = $resultStatement->fetchAll();
+ $resultStatement->closeCursor();
+
+ return array_map(function ($row) {
+ return $row['class'];
+ }, $data);
+ }
+
+ /**
+ * Check if a class is configured in the database
+ *
+ * @param string $table Mapper::TABLE_ADMIN_SECTIONS or Mapper::TABLE_ADMIN_SETTINGS
+ * @param string $className
+ * @return bool
+ */
+ public function has($table, $className) {
+ $query = $this->dbc->getQueryBuilder();
+ $query->select('class')
+ ->from($table)
+ ->where($query->expr()->eq('class', $query->createNamedParameter($className)))
+ ->setMaxResults(1);
+
+ $result = $query->execute();
+ $row = $result->fetch();
+ $result->closeCursor();
+
+ return (bool)$row;
+ }
+
+ /**
+ * deletes an settings or admin entry from the given table
+ *
+ * @param $table Mapper::TABLE_ADMIN_SECTIONS or Mapper::TABLE_ADMIN_SETTINGS
+ * @param $className
+ */
+ public function remove($table, $className) {
+ $query = $this->dbc->getQueryBuilder();
+ $query->delete($table)
+ ->where($query->expr()->eq('class', $query->createNamedParameter($className)));
+
+ $query->execute();
+ }
+
+ /**
+ * @param $table Mapper::TABLE_ADMIN_SECTIONS or Mapper::TABLE_ADMIN_SETTINGS
+ * @param $idCol
+ * @param $id
+ * @param $values
+ */
+ public function update($table, $idCol, $id, $values) {
+ $query = $this->dbc->getQueryBuilder();
+ $query->update($table);
+ foreach ($values as $key => $value) {
+ $query->set($key, $query->createNamedParameter($value));
+ }
+ $query
+ ->where($query->expr()->eq($idCol, $query->createParameter($idCol)))
+ ->setParameter($idCol, $id)
+ ->execute();
+ }
+
+}
diff --git a/lib/private/Settings/Section.php b/lib/private/Settings/Section.php
index b3cf242279f..c89a3999c4e 100644
--- a/lib/private/Settings/Section.php
+++ b/lib/private/Settings/Section.php
@@ -23,25 +23,29 @@
namespace OC\Settings;
-use OCP\Settings\ISection;
+use OCP\Settings\IIconSection;
-class Section implements ISection {
+class Section implements IIconSection {
/** @var string */
private $id;
/** @var string */
private $name;
/** @var int */
private $priority;
+ /** @var string */
+ private $icon;
/**
* @param string $id
* @param string $name
* @param int $priority
+ * @param string $icon
*/
- public function __construct($id, $name, $priority) {
+ public function __construct($id, $name, $priority, $icon = '') {
$this->id = $id;
$this->name = $name;
$this->priority = $priority;
+ $this->icon = $icon;
}
/**
@@ -74,4 +78,15 @@ class Section implements ISection {
public function getPriority() {
return $this->priority;
}
+
+ /**
+ * returns the relative path to an 16*16 icon describing the section.
+ * e.g. '/core/img/places/files.svg'
+ *
+ * @returns string
+ * @since 12
+ */
+ public function getIcon() {
+ return $this->icon;
+ }
}
diff --git a/lib/private/Setup.php b/lib/private/Setup.php
index 81a5343fe21..d9997767684 100644
--- a/lib/private/Setup.php
+++ b/lib/private/Setup.php
@@ -125,8 +125,8 @@ class Setup {
public function getSupportedDatabases($allowAllDatabases = false) {
$availableDatabases = array(
'sqlite' => array(
- 'type' => 'class',
- 'call' => 'SQLite3',
+ 'type' => 'pdo',
+ 'call' => 'sqlite',
'name' => 'SQLite'
),
'mysql' => array(
@@ -163,9 +163,7 @@ class Setup {
$type = $availableDatabases[$database]['type'];
$call = $availableDatabases[$database]['call'];
- if($type === 'class') {
- $working = $this->class_exists($call);
- } elseif ($type === 'function') {
+ if ($type === 'function') {
$working = $this->is_callable($call);
} elseif($type === 'pdo') {
$working = in_array($call, $this->getAvailableDbDriversForPdo(), TRUE);
diff --git a/lib/private/Setup/MySQL.php b/lib/private/Setup/MySQL.php
index d1399c8821c..bafb3866b76 100644
--- a/lib/private/Setup/MySQL.php
+++ b/lib/private/Setup/MySQL.php
@@ -35,7 +35,7 @@ class MySQL extends AbstractDatabase {
public function setupDatabase($username) {
//check if the database user has admin right
- $connection = $this->connect();
+ $connection = $this->connect(['dbname' => null]);
$this->createSpecificUser($username, $connection);
@@ -152,7 +152,7 @@ class MySQL extends AbstractDatabase {
};
}
} catch (\Exception $ex) {
- $this->logger->error('Specific user creation failed: {error}', [
+ $this->logger->info('Can not create a new MySQL user, will continue with the provided user: {error}', [
'app' => 'mysql.setup',
'error' => $ex->getMessage()
]);
diff --git a/lib/private/Setup/PostgreSQL.php b/lib/private/Setup/PostgreSQL.php
index 085e8609ab2..c01e5bc0332 100644
--- a/lib/private/Setup/PostgreSQL.php
+++ b/lib/private/Setup/PostgreSQL.php
@@ -150,14 +150,16 @@ class PostgreSQL extends AbstractDatabase {
}
private function createDBUser(IDBConnection $connection) {
+ $dbUser = $this->dbUser;
try {
- if ($this->userExists($connection)) {
- // change the password
- $query = $connection->prepare("ALTER ROLE " . addslashes($this->dbUser) . " WITH CREATEDB PASSWORD '" . addslashes($this->dbPassword) . "'");
- } else {
- // create the user
- $query = $connection->prepare("CREATE USER " . addslashes($this->dbUser) . " CREATEDB PASSWORD '" . addslashes($this->dbPassword) . "'");
- }
+ $i = 1;
+ while ($this->userExists($connection)) {
+ $i++;
+ $this->dbUser = $dbUser . $i;
+ };
+
+ // create the user
+ $query = $connection->prepare("CREATE USER " . addslashes($this->dbUser) . " CREATEDB PASSWORD '" . addslashes($this->dbPassword) . "'");
$query->execute();
} catch (DatabaseException $e) {
$this->logger->error('Error while trying to create database user');
diff --git a/lib/private/Setup/Sqlite.php b/lib/private/Setup/Sqlite.php
index 63b970be42e..4d860103b60 100644
--- a/lib/private/Setup/Sqlite.php
+++ b/lib/private/Setup/Sqlite.php
@@ -33,7 +33,7 @@ class Sqlite extends AbstractDatabase {
}
public function setupDatabase($username) {
- $datadir = \OC::$server->getSystemConfig()->getValue('datadirectory');
+ $datadir = \OC::$server->getSystemConfig()->getValue('datadirectory', \OC::$SERVERROOT . '/data');
//delete the old sqlite database first, might cause infinte loops otherwise
if(file_exists("$datadir/owncloud.db")) {
diff --git a/lib/private/Share20/DefaultShareProvider.php b/lib/private/Share20/DefaultShareProvider.php
index 7a602950171..23e8db34d7f 100644
--- a/lib/private/Share20/DefaultShareProvider.php
+++ b/lib/private/Share20/DefaultShareProvider.php
@@ -162,7 +162,6 @@ class DefaultShareProvider implements IShareProvider {
$this->dbConn->beginTransaction();
$qb->execute();
$id = $this->dbConn->lastInsertId('*PREFIX*share');
- $this->dbConn->commit();
// Now fetch the inserted share and create a complete share object
$qb = $this->dbConn->getQueryBuilder();
@@ -172,6 +171,7 @@ class DefaultShareProvider implements IShareProvider {
$cursor = $qb->execute();
$data = $cursor->fetch();
+ $this->dbConn->commit();
$cursor->closeCursor();
if ($data === false) {
@@ -853,7 +853,6 @@ class DefaultShareProvider implements IShareProvider {
$entryData['permissions'] = $entryData['f_permissions'];
$entryData['parent'] = $entryData['f_parent'];;
$share->setNodeCacheEntry(Cache::cacheEntryFromData($entryData,
- $entryData['storage_string_id'],
\OC::$server->getMimeTypeLoader()));
}
diff --git a/lib/private/Share20/Manager.php b/lib/private/Share20/Manager.php
index cd1d52c3bbf..acc142f62be 100644
--- a/lib/private/Share20/Manager.php
+++ b/lib/private/Share20/Manager.php
@@ -587,7 +587,6 @@ class Manager implements IManager {
$share->setPassword($this->hasher->hash($share->getPassword()));
}
} else if ($share->getShareType() === \OCP\Share::SHARE_TYPE_EMAIL) {
- $this->linkCreateChecks($share);
$share->setToken(
$this->secureRandom->generate(
\OC\Share\Constants::TOKEN_LENGTH,
@@ -1055,8 +1054,10 @@ class Manager implements IManager {
public function getShareByToken($token) {
$share = null;
try {
- $provider = $this->factory->getProviderForType(\OCP\Share::SHARE_TYPE_LINK);
- $share = $provider->getShareByToken($token);
+ if($this->shareApiAllowLinks()) {
+ $provider = $this->factory->getProviderForType(\OCP\Share::SHARE_TYPE_LINK);
+ $share = $provider->getShareByToken($token);
+ }
} catch (ProviderException $e) {
} catch (ShareNotFound $e) {
}
@@ -1072,7 +1073,7 @@ class Manager implements IManager {
}
}
- // If it is not a link share try to fetch a federated share by token
+ // If it is not a link share try to fetch a mail share by token
if ($share === null && $this->shareProviderExists(\OCP\Share::SHARE_TYPE_EMAIL)) {
try {
$provider = $this->factory->getProviderForType(\OCP\Share::SHARE_TYPE_EMAIL);
diff --git a/lib/private/SubAdmin.php b/lib/private/SubAdmin.php
index 9a2beed09fe..0650c7c6c01 100644
--- a/lib/private/SubAdmin.php
+++ b/lib/private/SubAdmin.php
@@ -188,7 +188,7 @@ class SubAdmin extends PublicEmitter {
* @param IGroup $group
* @return bool
*/
- public function isSubAdminofGroup(IUser $user, IGroup $group) {
+ public function isSubAdminOfGroup(IUser $user, IGroup $group) {
$qb = $this->dbConn->getQueryBuilder();
/*
diff --git a/lib/private/SystemConfig.php b/lib/private/SystemConfig.php
index 1029a6619ff..e5f1adaf004 100644
--- a/lib/private/SystemConfig.php
+++ b/lib/private/SystemConfig.php
@@ -44,7 +44,6 @@ class SystemConfig {
'passwordsalt' => true,
'secret' => true,
'updater.secret' => true,
- 'ldap_agent_password' => true,
'proxyuserpwd' => true,
'log.condition' => [
'shared_secret' => true,
diff --git a/lib/private/Template/CSSResourceLocator.php b/lib/private/Template/CSSResourceLocator.php
index ffeaf765ff5..3a474a1ecfd 100644
--- a/lib/private/Template/CSSResourceLocator.php
+++ b/lib/private/Template/CSSResourceLocator.php
@@ -25,23 +25,46 @@
namespace OC\Template;
+use OCP\ILogger;
+
class CSSResourceLocator extends ResourceLocator {
+
+ /** @var SCSSCacher */
+ protected $scssCacher;
+
+ /**
+ * @param ILogger $logger
+ * @param string $theme
+ * @param array $core_map
+ * @param array $party_map
+ * @param SCSSCacher $scssCacher
+ */
+ public function __construct(ILogger $logger, $theme, $core_map, $party_map, $scssCacher) {
+ $this->scssCacher = $scssCacher;
+
+ parent::__construct($logger, $theme, $core_map, $party_map);
+ }
+
/**
* @param string $style
*/
public function doFind($style) {
+ $app = substr($style, 0, strpos($style, '/'));
if (strpos($style, '3rdparty') === 0
&& $this->appendIfExist($this->thirdpartyroot, $style.'.css')
+ || $this->cacheAndAppendScssIfExist($this->serverroot, $style.'.scss', $app)
+ || $this->cacheAndAppendScssIfExist($this->serverroot, 'core/'.$style.'.scss')
|| $this->appendIfExist($this->serverroot, $style.'.css')
|| $this->appendIfExist($this->serverroot, 'core/'.$style.'.css')
) {
return;
}
- $app = substr($style, 0, strpos($style, '/'));
$style = substr($style, strpos($style, '/')+1);
$app_path = \OC_App::getAppPath($app);
$app_url = \OC_App::getAppWebPath($app);
- $this->append($app_path, $style.'.css', $app_url);
+ if(!$this->cacheAndAppendScssIfExist($app_path, $style.'.scss', $app)) {
+ $this->append($app_path, $style.'.css', $app_url);
+ }
}
/**
@@ -53,4 +76,29 @@ class CSSResourceLocator extends ResourceLocator {
|| $this->appendIfExist($this->serverroot, $theme_dir.$style.'.css')
|| $this->appendIfExist($this->serverroot, $theme_dir.'core/'.$style.'.css');
}
+
+ /**
+ * cache and append the scss $file if exist at $root
+ *
+ * @param string $root path to check
+ * @param string $file the filename
+ * @return bool True if the resource was found and cached, false otherwise
+ */
+ protected function cacheAndAppendScssIfExist($root, $file, $app = 'core') {
+ if (is_file($root.'/'.$file)) {
+ if($this->scssCacher !== null) {
+ if($this->scssCacher->process($root, $file, $app)) {
+ $this->append($root, $this->scssCacher->getCachedSCSS($app, $file), false);
+ return true;
+ } else {
+ $this->logger->warning('Failed to compile and/or save '.$root.'/'.$file, ['app' => 'core']);
+ return false;
+ }
+ } else {
+ $this->logger->debug('Scss is disabled for '.$root.'/'.$file.', ignoring', ['app' => 'core']);
+ return true;
+ }
+ }
+ return false;
+ }
}
diff --git a/lib/private/Template/ResourceLocator.php b/lib/private/Template/ResourceLocator.php
index 420317d27ac..e22ebdcab7d 100644..100755
--- a/lib/private/Template/ResourceLocator.php
+++ b/lib/private/Template/ResourceLocator.php
@@ -75,7 +75,7 @@ abstract class ResourceLocator {
$this->doFind($resource);
} catch (ResourceNotFoundException $e) {
$resourceApp = substr($resource, 0, strpos($resource, '/'));
- $this->logger->error('Could not find resource file "' . $e->getResourcePath() . '"', ['app' => $resourceApp]);
+ $this->logger->debug('Could not find resource file "' . $e->getResourcePath() . '"', ['app' => $resourceApp]);
}
}
if (!empty($this->theme)) {
@@ -84,7 +84,7 @@ abstract class ResourceLocator {
$this->doFindTheme($resource);
} catch (ResourceNotFoundException $e) {
$resourceApp = substr($resource, 0, strpos($resource, '/'));
- $this->logger->error('Could not find resource file "' . $e->getResourcePath() . '"', ['app' => $resourceApp]);
+ $this->logger->debug('Could not find resource file "' . $e->getResourcePath() . '"', ['app' => $resourceApp]);
}
}
}
diff --git a/lib/private/Template/SCSSCacher.php b/lib/private/Template/SCSSCacher.php
new file mode 100644
index 00000000000..d6f5a2c6fd3
--- /dev/null
+++ b/lib/private/Template/SCSSCacher.php
@@ -0,0 +1,190 @@
+<?php
+/**
+ * @copyright Copyright (c) 2016, John Molakvoæ (skjnldsv@protonmail.com)
+ *
+ * @license GNU AGPL version 3 or any later version
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+namespace OC\Template;
+
+use Leafo\ScssPhp\Compiler;
+use Leafo\ScssPhp\Exception\ParserException;
+use Leafo\ScssPhp\Formatter\Crunched;
+use Leafo\ScssPhp\Formatter\Expanded;
+use OC\SystemConfig;
+use OCP\Files\IAppData;
+use OCP\Files\NotFoundException;
+use OCP\Files\SimpleFS\ISimpleFolder;
+use OCP\ILogger;
+use OCP\IURLGenerator;
+
+class SCSSCacher {
+
+ /** @var ILogger */
+ protected $logger;
+
+ /** @var IAppData */
+ protected $appData;
+
+ /** @var IURLGenerator */
+ protected $urlGenerator;
+
+ /** @var SystemConfig */
+ protected $systemConfig;
+
+ /**
+ * @param ILogger $logger
+ * @param IAppData $appData
+ * @param IURLGenerator $urlGenerator
+ * @param SystemConfig $systemConfig
+ */
+ public function __construct(ILogger $logger, IAppData $appData, IURLGenerator $urlGenerator, SystemConfig $systemConfig) {
+ $this->logger = $logger;
+ $this->appData = $appData;
+ $this->urlGenerator = $urlGenerator;
+ $this->systemConfig = $systemConfig;
+ }
+
+ /**
+ * Process the caching process if needed
+ * @param string $root Root path to the nextcloud installation
+ * @param string $file
+ * @param string $app The app name
+ * @return boolean
+ */
+ public function process($root, $file, $app) {
+ $path = explode('/', $root . '/' . $file);
+
+ $fileNameSCSS = array_pop($path);
+ $fileNameCSS = str_replace('.scss', '.css', $fileNameSCSS);
+
+ $path = implode('/', $path);
+
+ $webDir = explode('/', $file);
+ array_pop($webDir);
+ $webDir = implode('/', $webDir);
+
+ try {
+ $folder = $this->appData->getFolder($app);
+ } catch(NotFoundException $e) {
+ // creating css appdata folder
+ $folder = $this->appData->newFolder($app);
+ }
+
+ if($this->isCached($fileNameCSS, $fileNameSCSS, $folder, $path)) {
+ return true;
+ } else {
+ return $this->cache($path, $fileNameCSS, $fileNameSCSS, $folder, $webDir);
+ }
+ }
+
+ /**
+ * Check if the file is cached or not
+ * @param string $fileNameCSS
+ * @param string $fileNameSCSS
+ * @param ISimpleFolder $folder
+ * @param string $path
+ * @return boolean
+ */
+ private function isCached($fileNameCSS, $fileNameSCSS, ISimpleFolder $folder, $path) {
+ try{
+ $cachedFile = $folder->getFile($fileNameCSS);
+ if( $cachedFile->getMTime() > filemtime($path.'/'.$fileNameSCSS)
+ && $cachedFile->getSize() > 0 ) {
+ return true;
+ }
+ } catch(NotFoundException $e) {
+ return false;
+ }
+ return false;
+ }
+
+ /**
+ * Cache the file with AppData
+ * @param string $path
+ * @param string $fileNameCSS
+ * @param string $fileNameSCSS
+ * @param ISimpleFolder $folder
+ * @param string $webDir
+ * @return boolean
+ */
+ private function cache($path, $fileNameCSS, $fileNameSCSS, ISimpleFolder $folder, $webDir) {
+ $scss = new Compiler();
+ $scss->setImportPaths($path);
+ if($this->systemConfig->getValue('debug')) {
+ // Debug mode
+ $scss->setFormatter(Expanded::class);
+ $scss->setLineNumberStyle(Compiler::LINE_COMMENTS);
+ } else {
+ // Compression
+ $scss->setFormatter(Crunched::class);
+ }
+
+ try {
+ $cachedfile = $folder->getFile($fileNameCSS);
+ } catch(NotFoundException $e) {
+ $cachedfile = $folder->newFile($fileNameCSS);
+ }
+
+ // Compile
+ try {
+ $compiledScss = $scss->compile('@import "'.$fileNameSCSS.'";');
+ } catch(ParserException $e) {
+ $this->logger->error($e, ['app' => 'core']);
+ return false;
+ }
+
+ try {
+ $cachedfile->putContent($this->rebaseUrls($compiledScss, $webDir));
+ $this->logger->debug($webDir.'/'.$fileNameSCSS.' compiled and successfully cached', ['app' => 'core']);
+ return true;
+ } catch(NotFoundException $e) {
+ return false;
+ }
+ }
+
+ /**
+ * Add the correct uri prefix to make uri valid again
+ * @param string $css
+ * @param string $webDir
+ * @return string
+ */
+ private function rebaseUrls($css, $webDir) {
+ $re = '/url\([\'"]([\.\w?=\/-]*)[\'"]\)/x';
+ // OC\Route\Router:75
+ if(($this->systemConfig->getValue('htaccess.IgnoreFrontController', false) === true || getenv('front_controller_active') === 'true')) {
+ $subst = 'url(\'../../'.$webDir.'/$1\')';
+ } else {
+ $subst = 'url(\'../../../'.$webDir.'/$1\')';
+ }
+ return preg_replace($re, $subst, $css);
+ }
+
+ /**
+ * Return the cached css file uri
+ * @param string $appName the app name
+ * @param string $fileName
+ * @return string
+ */
+ public function getCachedSCSS($appName, $fileName) {
+ $tmpfileLoc = explode('/', $fileName);
+ $fileName = array_pop($tmpfileLoc);
+ $fileName = str_replace('.scss', '.css', $fileName);
+
+ return substr($this->urlGenerator->linkToRoute('core.Css.getCss', array('fileName' => $fileName, 'appName' => $appName)), strlen(\OC::$WEBROOT) + 1);
+ }
+}
diff --git a/lib/private/TemplateLayout.php b/lib/private/TemplateLayout.php
index 8919f14216e..7ded109f76b 100644
--- a/lib/private/TemplateLayout.php
+++ b/lib/private/TemplateLayout.php
@@ -35,15 +35,8 @@
*/
namespace OC;
-use Assetic\Asset\AssetCollection;
-use Assetic\Asset\FileAsset;
-use Assetic\AssetWriter;
-use Assetic\Filter\CssImportFilter;
-use Assetic\Filter\CssMinFilter;
-use Assetic\Filter\CssRewriteFilter;
-use Assetic\Filter\JSqueezeFilter;
-use Assetic\Filter\SeparatorFilter;
use OC\Template\JSConfigHelper;
+use OC\Template\SCSSCacher;
class TemplateLayout extends \OC_Template {
@@ -164,11 +157,18 @@ class TemplateLayout extends \OC_Template {
foreach($jsFiles as $info) {
$web = $info[1];
$file = $info[2];
- $this->append( 'jsfiles', $web.'/'.$file . '?v=' . self::$versionHash);
+ $this->append( 'jsfiles', $web.'/'.$file . $this->getVersionHashSuffix() );
}
- // Add the css files
- $cssFiles = self::findStylesheetFiles(\OC_Util::$styles);
+ // Do not initialise scss appdata until we have a fully installed instance
+ // Do not load scss for update, errors, installation or login page
+ if(\OC::$server->getSystemConfig()->getValue('installed', false)
+ && !\OCP\Util::needUpgrade()
+ && strpos(\OC::$server->getRequest()->getRequestUri(), \OC::$server->getURLGenerator()->linkToRoute('core.login.tryLogin')) !== 0) {
+ $cssFiles = self::findStylesheetFiles(\OC_Util::$styles);
+ } else {
+ $cssFiles = self::findStylesheetFiles(\OC_Util::$styles, false);
+ }
$this->assign('cssfiles', array());
$this->assign('printcssfiles', []);
$this->assign('versionHash', self::$versionHash);
@@ -177,26 +177,47 @@ class TemplateLayout extends \OC_Template {
$file = $info[2];
if (substr($file, -strlen('print.css')) === 'print.css') {
- $this->append( 'printcssfiles', $web.'/'.$file . '?v=' . self::$versionHash);
+ $this->append( 'printcssfiles', $web.'/'.$file . $this->getVersionHashSuffix() );
} else {
- $this->append( 'cssfiles', $web.'/'.$file . '?v=' . self::$versionHash);
+ $this->append( 'cssfiles', $web.'/'.$file . $this->getVersionHashSuffix() );
}
}
}
+ protected function getVersionHashSuffix() {
+ if(\OC::$server->getConfig()->getSystemValue('debug', false)) {
+ // allows chrome workspace mapping in debug mode
+ return "";
+ }
+
+ return '?v=' . self::$versionHash;
+ }
+
/**
* @param array $styles
* @return array
*/
- static public function findStylesheetFiles($styles) {
+ static public function findStylesheetFiles($styles, $compileScss = true) {
// Read the selected theme from the config file
$theme = \OC_Util::getTheme();
+ if($compileScss) {
+ $SCSSCacher = new SCSSCacher(
+ \OC::$server->getLogger(),
+ \OC::$server->getAppDataDir('css'),
+ \OC::$server->getURLGenerator(),
+ \OC::$server->getSystemConfig()
+ );
+ } else {
+ $SCSSCacher = null;
+ }
+
$locator = new \OC\Template\CSSResourceLocator(
\OC::$server->getLogger(),
$theme,
array( \OC::$SERVERROOT => \OC::$WEBROOT ),
- array( \OC::$SERVERROOT => \OC::$WEBROOT ));
+ array( \OC::$SERVERROOT => \OC::$WEBROOT ),
+ $SCSSCacher);
$locator->find($styles);
return $locator->getResources();
}
diff --git a/lib/private/Updater.php b/lib/private/Updater.php
index e30777227cc..a66d49941cd 100644
--- a/lib/private/Updater.php
+++ b/lib/private/Updater.php
@@ -82,6 +82,12 @@ class Updater extends BasicEmitter {
$this->log = $log;
$this->config = $config;
$this->checker = $checker;
+
+ // If at least PHP 7.0.0 is used we don't need to disable apps as we catch
+ // fatal errors and exceptions and disable the app just instead.
+ if(version_compare(phpversion(), '7.0.0', '>=')) {
+ $this->skip3rdPartyAppsDisable = true;
+ }
}
/**
diff --git a/lib/private/User/Database.php b/lib/private/User/Database.php
index 28cb3302858..a281572ad55 100644
--- a/lib/private/User/Database.php
+++ b/lib/private/User/Database.php
@@ -238,6 +238,12 @@ class Database extends Backend implements IUserBackend {
*/
private function loadUser($uid) {
if (!isset($this->cache[$uid])) {
+ //guests $uid could be NULL or ''
+ if ($uid === null || $uid === '') {
+ $this->cache[$uid]=false;
+ return true;
+ }
+
$query = \OC_DB::prepare('SELECT `uid`, `displayname` FROM `*PREFIX*users` WHERE LOWER(`uid`) = LOWER(?)');
$result = $query->execute(array($uid));
diff --git a/lib/private/User/Session.php b/lib/private/User/Session.php
index 36bd45521cf..2ebe895a592 100644
--- a/lib/private/User/Session.php
+++ b/lib/private/User/Session.php
@@ -317,7 +317,7 @@ class Session implements IUserSession, Emitter {
$password,
IRequest $request,
OC\Security\Bruteforce\Throttler $throttler) {
- $currentDelay = $throttler->sleepDelay($request->getRemoteAddress());
+ $currentDelay = $throttler->sleepDelay($request->getRemoteAddress(), 'login');
if ($this->manager instanceof PublicEmitter) {
$this->manager->emit('\OC\User', 'preLogin', array($user, $password));
@@ -338,7 +338,7 @@ class Session implements IUserSession, Emitter {
$throttler->registerAttempt('login', $request->getRemoteAddress(), ['uid' => $user]);
if($currentDelay === 0) {
- $throttler->sleepDelay($request->getRemoteAddress());
+ $throttler->sleepDelay($request->getRemoteAddress(), 'login');
}
return false;
}
@@ -744,6 +744,7 @@ class Session implements IUserSession, Emitter {
//login
$this->setUser($user);
+ $this->setLoginName($this->tokenProvider->getToken($sessionId)->getLoginName());
$user->updateLastLoginTimestamp();
$this->manager->emit('\OC\User', 'postRememberedLogin', [$user]);
return true;
@@ -768,7 +769,7 @@ class Session implements IUserSession, Emitter {
try {
$this->tokenProvider->invalidateToken($this->session->getId());
} catch (SessionNotAvailableException $ex) {
-
+
}
}
$this->setUser(null);
diff --git a/lib/private/User/User.php b/lib/private/User/User.php
index 3cc6dc3b7ed..c37bb59028e 100644
--- a/lib/private/User/User.php
+++ b/lib/private/User/User.php
@@ -197,6 +197,8 @@ class User implements IUser {
if ($this->emitter) {
$this->emitter->emit('\OC\User', 'preDelete', array($this));
}
+ // get the home now because it won't return it after user deletion
+ $homePath = $this->getHome();
$result = $this->backend->deleteUser($this->uid);
if ($result) {
@@ -210,7 +212,11 @@ class User implements IUser {
\OC::$server->getConfig()->deleteAllUserValues($this->uid);
// Delete user files in /data/
- \OC_Helper::rmdirr($this->getHome());
+ if ($homePath !== false) {
+ // FIXME: this operates directly on FS, should use View instead...
+ // also this is not testable/mockable...
+ \OC_Helper::rmdirr($homePath);
+ }
// Delete the users entry in the storage table
Storage::remove('home::' . $this->uid);
@@ -261,7 +267,7 @@ class User implements IUser {
if ($this->backend->implementsActions(Backend::GET_HOME) and $home = $this->backend->getHome($this->uid)) {
$this->home = $home;
} elseif ($this->config) {
- $this->home = $this->config->getSystemValue('datadirectory') . '/' . $this->uid;
+ $this->home = $this->config->getSystemValue('datadirectory', \OC::$SERVERROOT . '/data') . '/' . $this->uid;
} else {
$this->home = \OC::$SERVERROOT . '/data/' . $this->uid;
}
diff --git a/lib/private/legacy/app.php b/lib/private/legacy/app.php
index adf29601ac6..a475b895d9f 100644
--- a/lib/private/legacy/app.php
+++ b/lib/private/legacy/app.php
@@ -273,9 +273,17 @@ class OC_App {
$appTypes = implode(',', $appData['types']);
} else {
$appTypes = '';
+ $appData['types'] = [];
}
\OC::$server->getAppConfig()->setValue($app, 'types', $appTypes);
+
+ if (\OC::$server->getAppManager()->hasProtectedAppType($appData['types'])) {
+ $enabled = \OC::$server->getAppConfig()->getValue($app, 'enabled', 'yes');
+ if ($enabled !== 'yes' && $enabled !== 'no') {
+ \OC::$server->getAppConfig()->setValue($app, 'enabled', 'yes');
+ }
+ }
}
/**
@@ -1296,43 +1304,21 @@ class OC_App {
$data['summary'] = self::findBestL10NOption($data['summary'], $lang);
}
if ($lang && isset($data['description']) && is_array($data['description'])) {
- $data['description'] = self::findBestL10NOption($data['description'], $lang);
- }
-
- // just modify the description if it is available
- // otherwise this will create a $data element with an empty 'description'
- if (isset($data['description'])) {
- if (is_string($data['description'])) {
- // sometimes the description contains line breaks and they are then also
- // shown in this way in the app management which isn't wanted as HTML
- // manages line breaks itself
-
- // first of all we split on empty lines
- $paragraphs = preg_split("!\n[[:space:]]*\n!mu", $data['description']);
-
- $result = [];
- foreach ($paragraphs as $value) {
- // replace multiple whitespace (tabs, space, newlines) inside a paragraph
- // with a single space - also trims whitespace
- $result[] = trim(preg_replace('![[:space:]]+!mu', ' ', $value));
- }
-
- // join the single paragraphs with a empty line in between
- $data['description'] = implode("\n\n", $result);
-
- } else {
- $data['description'] = '';
- }
+ $data['description'] = trim(self::findBestL10NOption($data['description'], $lang));
+ } else if (isset($data['description']) && is_string($data['description'])) {
+ $data['description'] = trim($data['description']);
+ } else {
+ $data['description'] = '';
}
return $data;
}
/**
- * @param $config
- * @param $l
- * @param $info
- * @throws Exception
+ * @param \OCP\IConfig $config
+ * @param \OCP\IL10N $l
+ * @param array $info
+ * @throws \Exception
*/
protected static function checkAppDependencies($config, $l, $info) {
$dependencyAnalyzer = new DependencyAnalyzer(new Platform($config), $l);
diff --git a/lib/private/legacy/files.php b/lib/private/legacy/files.php
index b6c6857a1bf..8c9adad0d49 100644
--- a/lib/private/legacy/files.php
+++ b/lib/private/legacy/files.php
@@ -148,6 +148,7 @@ class OC_Files {
$streamer->sendHeaders($name);
$executionTime = intval(OC::$server->getIniWrapper()->getNumeric('max_execution_time'));
set_time_limit(0);
+ ignore_user_abort(true);
if ($getType === self::ZIP_FILES) {
foreach ($files as $file) {
$file = $dir . '/' . $file;
diff --git a/lib/private/legacy/helper.php b/lib/private/legacy/helper.php
index 9c4bc895fb9..dfe2e09afff 100644
--- a/lib/private/legacy/helper.php
+++ b/lib/private/legacy/helper.php
@@ -52,6 +52,7 @@ class OC_Helper {
/**
* Creates an absolute url for public use
+ *
* @param string $service id
* @param bool $add_slash
* @return string the url
@@ -62,13 +63,14 @@ class OC_Helper {
if ($service === 'files') {
$url = OC::$server->getURLGenerator()->getAbsoluteURL('/s');
} else {
- $url = OC::$server->getURLGenerator()->getAbsoluteURL(OC::$server->getURLGenerator()->linkTo('', 'public.php').'?service='.$service);
+ $url = OC::$server->getURLGenerator()->getAbsoluteURL(OC::$server->getURLGenerator()->linkTo('', 'public.php') . '?service=' . $service);
}
return $url . (($add_slash && $service[strlen($service) - 1] != '/') ? '/' : '');
}
/**
* Make a human file size
+ *
* @param int $bytes file size in bytes
* @return string a human readable file size
*
@@ -104,6 +106,7 @@ class OC_Helper {
/**
* Make a php file size
+ *
* @param int $bytes file size in bytes
* @return string a php parseable file size
*
@@ -130,6 +133,7 @@ class OC_Helper {
/**
* Make a computer file size
+ *
* @param string $str file size in human readable format
* @return float a file size in bytes
*
@@ -172,6 +176,7 @@ class OC_Helper {
/**
* Recursive copying of folders
+ *
* @param string $src source folder
* @param string $dest target folder
*
@@ -194,6 +199,7 @@ class OC_Helper {
/**
* Recursive deletion of folders
+ *
* @param string $dir path to the folder
* @param bool $deleteSelf if set to false only the content of the folder will be deleted
* @return bool
@@ -393,6 +399,7 @@ class OC_Helper {
/**
* performs a search in a nested array
+ *
* @param array $haystack the array to be searched
* @param string $needle the search string
* @param string $index optional, only search this key name
@@ -425,7 +432,7 @@ class OC_Helper {
* @return int number of bytes representing
*/
public static function maxUploadFilesize($dir, $freeSpace = null) {
- if (is_null($freeSpace) || $freeSpace < 0){
+ if (is_null($freeSpace) || $freeSpace < 0) {
$freeSpace = self::freeSpace($dir);
}
return min($freeSpace, self::uploadLimit());
@@ -443,7 +450,7 @@ class OC_Helper {
$freeSpace = max($freeSpace, 0);
return $freeSpace;
} else {
- return (INF > 0)? INF: PHP_INT_MAX; // work around https://bugs.php.net/bug.php?id=69188
+ return (INF > 0) ? INF : PHP_INT_MAX; // work around https://bugs.php.net/bug.php?id=69188
}
}
@@ -510,7 +517,7 @@ class OC_Helper {
if (empty($paths)) {
$paths = '/usr/local/bin /usr/bin /opt/bin /bin';
} else {
- $paths = str_replace(':',' ',getenv('PATH'));
+ $paths = str_replace(':', ' ', getenv('PATH'));
}
$command = 'find ' . $paths . ' -name ' . escapeshellarg($program) . ' 2> /dev/null';
exec($command, $output, $returnCode);
@@ -533,6 +540,12 @@ class OC_Helper {
* @throws \OCP\Files\NotFoundException
*/
public static function getStorageInfo($path, $rootInfo = null) {
+ $memcache = \OC::$server->getMemCacheFactory()->create('storageInfo');
+ $cacheKey = $rootInfo ? '__root__' . md5($path) : md5($path);
+ $cached = $memcache->get($cacheKey);
+ if (is_array($cached)) {
+ return $cached;
+ }
// return storage info without adding mount points
$includeExtStorage = \OC::$server->getSystemConfig()->getValue('quota_include_external_storage', false);
@@ -597,10 +610,20 @@ class OC_Helper {
$ownerId = $storage->getOwner($path);
$ownerDisplayName = '';
$owner = \OC::$server->getUserManager()->get($ownerId);
- if($owner) {
+ if ($owner) {
$ownerDisplayName = $owner->getDisplayName();
}
+ $memcache->set($cacheKey, [
+ 'free' => $free,
+ 'used' => $used,
+ 'quota' => $quota,
+ 'total' => $total,
+ 'relative' => $relative,
+ 'owner' => $ownerId,
+ 'ownerDisplayName' => $ownerDisplayName,
+ ], 5 * 60);
+
return [
'free' => $free,
'used' => $used,
@@ -645,6 +668,7 @@ class OC_Helper {
/**
* Returns whether the config file is set manually to read-only
+ *
* @return bool
*/
public static function isReadOnlyConfigEnabled() {
diff --git a/lib/private/legacy/user.php b/lib/private/legacy/user.php
index ed0d14a1ab9..0a52be7565d 100644
--- a/lib/private/legacy/user.php
+++ b/lib/private/legacy/user.php
@@ -125,9 +125,16 @@ class OC_User {
* setup the configured backends in config.php
*/
public static function setupBackends() {
- OC_App::loadApps(array('prelogin'));
- $backends = \OC::$server->getSystemConfig()->getValue('user_backends', array());
+ OC_App::loadApps(['prelogin']);
+ $backends = \OC::$server->getSystemConfig()->getValue('user_backends', []);
+ if (isset($backends['default']) && !$backends['default']) {
+ // clear default backends
+ self::clearBackends();
+ }
foreach ($backends as $i => $config) {
+ if (!is_array($config)) {
+ continue;
+ }
$class = $config['class'];
$arguments = $config['arguments'];
if (class_exists($class)) {
diff --git a/lib/private/legacy/util.php b/lib/private/legacy/util.php
index 55dc5ae7c15..5ef1130d361 100644
--- a/lib/private/legacy/util.php
+++ b/lib/private/legacy/util.php
@@ -1002,27 +1002,6 @@ class OC_Util {
}
/**
- * Check if it is allowed to remember login.
- *
- * @note Every app can set 'rememberlogin' to 'false' to disable the remember login feature
- *
- * @return bool
- */
- public static function rememberLoginAllowed() {
-
- $apps = OC_App::getEnabledApps();
-
- foreach ($apps as $app) {
- $appInfo = OC_App::getAppInfo($app);
- if (isset($appInfo['rememberlogin']) && $appInfo['rememberlogin'] === 'false') {
- return false;
- }
-
- }
- return true;
- }
-
- /**
* Check if the user is a subadmin, redirects to home if not
*
* @return null|boolean $groups where the current user is subadmin
@@ -1381,12 +1360,12 @@ class OC_Util {
}
/**
- * A human readable string is generated based on version, channel and build number
+ * A human readable string is generated based on version and build number
*
* @return string
*/
public static function getHumanVersion() {
- $version = OC_Util::getVersionString() . ' (' . OC_Util::getChannel() . ')';
+ $version = OC_Util::getVersionString();
$build = OC_Util::getBuild();
if (!empty($build) and OC_Util::getChannel() === 'daily') {
$version .= ' Build:' . $build;