summaryrefslogtreecommitdiffstats
path: root/lib/private
diff options
context:
space:
mode:
authorJasper Weyne <jasperweyne@gmail.com>2022-08-11 08:54:08 +0200
committerGitHub <noreply@github.com>2022-08-11 08:54:08 +0200
commit44f6c931e7c9c74ea4f448d3cdfbaa89f3b7c379 (patch)
tree710a8c1bd1c20c685991de146aa9ef149ec1de7a /lib/private
parent0633a1d9f5a7ef06d577ae6556d09db9e94f5684 (diff)
parenta61331f4560468e6d433cf32e008b157b06e7ea9 (diff)
downloadnextcloud-server-44f6c931e7c9c74ea4f448d3cdfbaa89f3b7c379.tar.gz
nextcloud-server-44f6c931e7c9c74ea4f448d3cdfbaa89f3b7c379.zip
Merge branch 'master' into patch-2
Diffstat (limited to 'lib/private')
-rw-r--r--lib/private/Accounts/AccountManager.php4
-rw-r--r--lib/private/AllConfig.php6
-rw-r--r--lib/private/App/AppStore/Bundles/HubBundle.php4
-rw-r--r--lib/private/App/CompareVersion.php2
-rw-r--r--lib/private/App/Platform.php34
-rw-r--r--lib/private/App/PlatformRepository.php23
-rw-r--r--lib/private/AppFramework/App.php2
-rw-r--r--lib/private/AppFramework/Bootstrap/Coordinator.php2
-rw-r--r--lib/private/AppFramework/Bootstrap/RegistrationContext.php27
-rw-r--r--lib/private/AppFramework/Http/Dispatcher.php4
-rw-r--r--lib/private/AppFramework/Http/Request.php35
-rw-r--r--lib/private/AppFramework/Middleware/MiddlewareDispatcher.php2
-rw-r--r--lib/private/Authentication/Listeners/UserDeletedFilesCleanupListener.php2
-rw-r--r--lib/private/Authentication/LoginCredentials/Store.php2
-rw-r--r--lib/private/Authentication/Token/IProvider.php2
-rw-r--r--lib/private/Authentication/Token/PublicKeyTokenProvider.php20
-rw-r--r--lib/private/Avatar/Avatar.php90
-rw-r--r--lib/private/Avatar/AvatarManager.php25
-rw-r--r--lib/private/Avatar/GuestAvatar.php27
-rw-r--r--lib/private/Avatar/PlaceholderAvatar.php25
-rw-r--r--lib/private/Avatar/UserAvatar.php50
-rw-r--r--lib/private/BackgroundJob/JobList.php113
-rw-r--r--lib/private/Cache/CappedMemoryCache.php1
-rw-r--r--lib/private/Collaboration/Collaborators/UserPlugin.php2
-rw-r--r--lib/private/Command/ClosureJob.php5
-rw-r--r--lib/private/Command/CommandJob.php2
-rw-r--r--lib/private/Command/CronBus.php5
-rw-r--r--lib/private/Comments/Comment.php24
-rw-r--r--lib/private/Comments/Manager.php26
-rw-r--r--lib/private/Config.php8
-rw-r--r--lib/private/Console/Application.php6
-rw-r--r--lib/private/Contacts/ContactsMenu/ContactsStore.php4
-rw-r--r--lib/private/ContactsManager.php3
-rw-r--r--lib/private/DB/MigrationService.php35
-rw-r--r--lib/private/DB/QueryBuilder/ExpressionBuilder/ExpressionBuilder.php96
-rw-r--r--lib/private/DB/QueryBuilder/ExpressionBuilder/MySqlExpressionBuilder.php4
-rw-r--r--lib/private/DB/QueryBuilder/ExpressionBuilder/OCIExpressionBuilder.php50
-rw-r--r--lib/private/DB/QueryBuilder/ExpressionBuilder/PgSqlExpressionBuilder.php4
-rw-r--r--lib/private/DB/QueryBuilder/ExpressionBuilder/SqliteExpressionBuilder.php11
-rw-r--r--lib/private/DB/QueryBuilder/FunctionBuilder/FunctionBuilder.php11
-rw-r--r--lib/private/DB/QueryBuilder/QueryBuilder.php14
-rw-r--r--lib/private/Dashboard/Manager.php35
-rw-r--r--lib/private/Diagnostics/QueryLogger.php2
-rw-r--r--lib/private/Encryption/File.php29
-rw-r--r--lib/private/Encryption/HookManager.php38
-rw-r--r--lib/private/Encryption/Util.php37
-rw-r--r--lib/private/Federation/CloudFederationProviderManager.php3
-rw-r--r--lib/private/Files/AppData/AppData.php2
-rw-r--r--lib/private/Files/Cache/Cache.php43
-rw-r--r--lib/private/Files/Cache/CacheEntry.php8
-rw-r--r--lib/private/Files/Cache/CacheQueryBuilder.php12
-rw-r--r--lib/private/Files/Cache/Propagator.php17
-rw-r--r--lib/private/Files/Cache/QuerySearchHelper.php4
-rw-r--r--lib/private/Files/Cache/Storage.php4
-rw-r--r--lib/private/Files/Cache/StorageGlobal.php2
-rw-r--r--lib/private/Files/Cache/Updater.php4
-rw-r--r--lib/private/Files/Cache/Wrapper/CacheJail.php2
-rw-r--r--lib/private/Files/Cache/Wrapper/CacheWrapper.php2
-rw-r--r--lib/private/Files/Config/UserMountCache.php4
-rw-r--r--lib/private/Files/FileInfo.php29
-rw-r--r--lib/private/Files/Filesystem.php2
-rw-r--r--lib/private/Files/Mount/Manager.php2
-rw-r--r--lib/private/Files/Node/File.php1
-rw-r--r--lib/private/Files/Node/Folder.php3
-rw-r--r--lib/private/Files/Node/Root.php4
-rw-r--r--lib/private/Files/ObjectStore/S3ConnectionTrait.php31
-rw-r--r--lib/private/Files/ObjectStore/S3ObjectTrait.php10
-rw-r--r--lib/private/Files/SetupManager.php43
-rw-r--r--lib/private/Files/SimpleFS/NewSimpleFile.php38
-rw-r--r--lib/private/Files/SimpleFS/SimpleFile.php44
-rw-r--r--lib/private/Files/SimpleFS/SimpleFolder.php13
-rw-r--r--lib/private/Files/Storage/Common.php1
-rw-r--r--lib/private/Files/Storage/Local.php14
-rw-r--r--lib/private/Files/Storage/Wrapper/Encoding.php2
-rw-r--r--lib/private/Files/Storage/Wrapper/Encryption.php98
-rw-r--r--lib/private/Files/Stream/SeekableHttpStream.php13
-rw-r--r--lib/private/Files/View.php4
-rw-r--r--lib/private/HintException.php2
-rw-r--r--lib/private/Http/Client/Client.php2
-rw-r--r--lib/private/Http/Client/LocalAddressChecker.php22
-rw-r--r--lib/private/IntegrityCheck/Checker.php3
-rw-r--r--lib/private/L10N/L10N.php2
-rw-r--r--lib/private/Log.php73
-rw-r--r--lib/private/Log/ExceptionSerializer.php25
-rw-r--r--lib/private/Log/LogDetails.php4
-rw-r--r--lib/private/Mail/EMailTemplate.php2
-rw-r--r--lib/private/Mail/Mailer.php3
-rw-r--r--lib/private/Memcache/Memcached.php30
-rw-r--r--lib/private/MemoryInfo.php2
-rw-r--r--lib/private/Metadata/IMetadataManager.php2
-rw-r--r--lib/private/OCS/DiscoveryService.php2
-rw-r--r--lib/private/Profile/ProfileManager.php4
-rw-r--r--lib/private/Repair/RemoveLinkShares.php2
-rw-r--r--lib/private/Repair/RepairMimeTypes.php13
-rw-r--r--lib/private/Route/Router.php2
-rw-r--r--lib/private/Security/Bruteforce/Throttler.php16
-rw-r--r--lib/private/Security/CSP/ContentSecurityPolicyNonceManager.php6
-rw-r--r--lib/private/Security/TrustedDomainHelper.php2
-rw-r--r--lib/private/Server.php62
-rw-r--r--lib/private/Session/Internal.php2
-rw-r--r--lib/private/Setup/MySQL.php2
-rw-r--r--lib/private/Share/Constants.php2
-rw-r--r--lib/private/Share/Share.php4
-rw-r--r--lib/private/Share20/DefaultShareProvider.php96
-rw-r--r--lib/private/Share20/Manager.php17
-rw-r--r--lib/private/Share20/Share.php29
-rw-r--r--lib/private/Share20/ShareAttributes.php73
-rw-r--r--lib/private/Template/JSConfigHelper.php4
-rw-r--r--lib/private/TemplateLayout.php3
-rw-r--r--lib/private/URLGenerator.php37
-rw-r--r--lib/private/Updater.php2
-rw-r--r--lib/private/User/Database.php6
-rw-r--r--lib/private/User/Listeners/UserChangedListener.php62
-rw-r--r--lib/private/User/Listeners/UserDeletedListener.php65
-rw-r--r--lib/private/User/User.php21
-rw-r--r--lib/private/legacy/OC_App.php21
-rw-r--r--lib/private/legacy/OC_Files.php33
-rw-r--r--lib/private/legacy/OC_User.php8
118 files changed, 1302 insertions, 878 deletions
diff --git a/lib/private/Accounts/AccountManager.php b/lib/private/Accounts/AccountManager.php
index b80c7887591..20e0add1ccb 100644
--- a/lib/private/Accounts/AccountManager.php
+++ b/lib/private/Accounts/AccountManager.php
@@ -42,7 +42,7 @@ use libphonenumber\PhoneNumber;
use libphonenumber\PhoneNumberFormat;
use libphonenumber\PhoneNumberUtil;
use OC\Profile\TProfileHelper;
-use OC\Cache\CappedMemoryCache;
+use OCP\Cache\CappedMemoryCache;
use OCA\Settings\BackgroundJobs\VerifyUserData;
use OCP\Accounts\IAccount;
use OCP\Accounts\IAccountManager;
@@ -220,7 +220,7 @@ class AccountManager implements IAccountManager {
foreach ($properties as $property) {
if (strlen($property->getValue()) > 2048) {
if ($throwOnData) {
- throw new InvalidArgumentException();
+ throw new InvalidArgumentException($property->getName());
} else {
$property->setValue('');
}
diff --git a/lib/private/AllConfig.php b/lib/private/AllConfig.php
index f282baee146..2a0e8f53b14 100644
--- a/lib/private/AllConfig.php
+++ b/lib/private/AllConfig.php
@@ -32,7 +32,7 @@
*/
namespace OC;
-use OC\Cache\CappedMemoryCache;
+use OCP\Cache\CappedMemoryCache;
use OCP\DB\QueryBuilder\IQueryBuilder;
use OCP\IConfig;
use OCP\IDBConnection;
@@ -353,8 +353,8 @@ class AllConfig implements IConfig {
$qb = $this->connection->getQueryBuilder();
$qb->delete('preferences')
->where($qb->expr()->eq('userid', $qb->createNamedParameter($userId, IQueryBuilder::PARAM_STR)))
- ->where($qb->expr()->eq('appid', $qb->createNamedParameter($appName, IQueryBuilder::PARAM_STR)))
- ->where($qb->expr()->eq('configkey', $qb->createNamedParameter($key, IQueryBuilder::PARAM_STR)))
+ ->andWhere($qb->expr()->eq('appid', $qb->createNamedParameter($appName, IQueryBuilder::PARAM_STR)))
+ ->andWhere($qb->expr()->eq('configkey', $qb->createNamedParameter($key, IQueryBuilder::PARAM_STR)))
->executeStatement();
if (isset($this->userCache[$userId][$appName])) {
diff --git a/lib/private/App/AppStore/Bundles/HubBundle.php b/lib/private/App/AppStore/Bundles/HubBundle.php
index a52de1dfbd4..d5d236a1855 100644
--- a/lib/private/App/AppStore/Bundles/HubBundle.php
+++ b/lib/private/App/AppStore/Bundles/HubBundle.php
@@ -39,8 +39,8 @@ class HubBundle extends Bundle {
'mail',
];
- $architecture = php_uname('m');
- if (PHP_OS_FAMILY === 'Linux' && in_array($architecture, ['x86_64', 'aarch64'])) {
+ $architecture = function_exists('php_uname') ? php_uname('m') : null;
+ if (isset($architecture) && PHP_OS_FAMILY === 'Linux' && in_array($architecture, ['x86_64', 'aarch64'])) {
$hubApps[] = 'richdocuments';
$hubApps[] = 'richdocumentscode' . ($architecture === 'aarch64' ? '_arm64' : '');
}
diff --git a/lib/private/App/CompareVersion.php b/lib/private/App/CompareVersion.php
index d155945fff1..a349c7aa6f2 100644
--- a/lib/private/App/CompareVersion.php
+++ b/lib/private/App/CompareVersion.php
@@ -41,7 +41,7 @@ class CompareVersion {
* so '13.0.1', '13.0' and '13' are valid.
*
* @param string $actual version as major.minor.patch notation
- * @param string $required version where major is requried and minor and patch are optional
+ * @param string $required version where major is required and minor and patch are optional
* @param string $comparator passed to `version_compare`
* @return bool whether the requirement is fulfilled
* @throws InvalidArgumentException if versions specified in an invalid format
diff --git a/lib/private/App/Platform.php b/lib/private/App/Platform.php
index 12097abbc78..15966d85c34 100644
--- a/lib/private/App/Platform.php
+++ b/lib/private/App/Platform.php
@@ -35,40 +35,26 @@ use OCP\IConfig;
* @package OC\App
*/
class Platform {
+ private IConfig $config;
- /**
- * @param IConfig $config
- */
public function __construct(IConfig $config) {
$this->config = $config;
}
- /**
- * @return string
- */
- public function getPhpVersion() {
+ public function getPhpVersion(): string {
return phpversion();
}
- /**
- * @return int
- */
- public function getIntSize() {
+ public function getIntSize(): int {
return PHP_INT_SIZE;
}
- /**
- * @return string
- */
- public function getOcVersion() {
+ public function getOcVersion(): string {
$v = \OCP\Util::getVersion();
return implode('.', $v);
}
- /**
- * @return string
- */
- public function getDatabase() {
+ public function getDatabase(): string {
$dbType = $this->config->getSystemValue('dbtype', 'sqlite');
if ($dbType === 'sqlite3') {
$dbType = 'sqlite';
@@ -77,23 +63,19 @@ class Platform {
return $dbType;
}
- /**
- * @return string
- */
- public function getOS() {
+ public function getOS(): string {
return php_uname('s');
}
/**
* @param $command
- * @return bool
*/
- public function isCommandKnown($command) {
+ public function isCommandKnown($command): bool {
$path = \OC_Helper::findBinaryPath($command);
return ($path !== null);
}
- public function getLibraryVersion($name) {
+ public function getLibraryVersion(string $name): ?string {
$repo = new PlatformRepository();
return $repo->findLibrary($name);
}
diff --git a/lib/private/App/PlatformRepository.php b/lib/private/App/PlatformRepository.php
index 94fac5260e1..4166c2ead03 100644
--- a/lib/private/App/PlatformRepository.php
+++ b/lib/private/App/PlatformRepository.php
@@ -31,11 +31,13 @@ namespace OC\App;
* @package OC\App
*/
class PlatformRepository {
+ private array $packages;
+
public function __construct() {
$this->packages = $this->initialize();
}
- protected function initialize() {
+ protected function initialize(): array {
$loadedExtensions = get_loaded_extensions();
$packages = [];
@@ -121,15 +123,11 @@ class PlatformRepository {
return $packages;
}
- private function buildPackageName($name) {
+ private function buildPackageName(string $name): string {
return str_replace(' ', '-', $name);
}
- /**
- * @param $name
- * @return string
- */
- public function findLibrary($name) {
+ public function findLibrary(string $name): ?string {
$extName = $this->buildPackageName($name);
if (isset($this->packages[$extName])) {
return $this->packages[$extName];
@@ -137,19 +135,17 @@ class PlatformRepository {
return null;
}
- private static $modifierRegex = '[._-]?(?:(stable|beta|b|RC|alpha|a|patch|pl|p)(?:[.-]?(\d+))?)?([.-]?dev)?';
+ private static string $modifierRegex = '[._-]?(?:(stable|beta|b|RC|alpha|a|patch|pl|p)(?:[.-]?(\d+))?)?([.-]?dev)?';
/**
* Normalizes a version string to be able to perform comparisons on it
*
* https://github.com/composer/composer/blob/master/src/Composer/Package/Version/VersionParser.php#L94
*
- * @param string $version
* @param string $fullVersion optional complete version string to give more context
* @throws \UnexpectedValueException
- * @return string
*/
- public function normalizeVersion($version, $fullVersion = null) {
+ public function normalizeVersion(string $version, ?string $fullVersion = null): string {
$version = trim($version);
if (null === $fullVersion) {
$fullVersion = $version;
@@ -204,10 +200,7 @@ class PlatformRepository {
throw new \UnexpectedValueException('Invalid version string "' . $version . '"' . $extraMessage);
}
- /**
- * @param string $stability
- */
- private function expandStability($stability) {
+ private function expandStability(string $stability): string {
$stability = strtolower($stability);
switch ($stability) {
case 'a':
diff --git a/lib/private/AppFramework/App.php b/lib/private/AppFramework/App.php
index feebb32d5bc..170acba0689 100644
--- a/lib/private/AppFramework/App.php
+++ b/lib/private/AppFramework/App.php
@@ -237,8 +237,6 @@ class App {
/**
* Shortcut for calling a controller method and printing the result.
* Similar to App:main except that no headers will be sent.
- * This should be used for example when registering sections via
- * \OC\AppFramework\Core\API::registerAdmin()
*
* @param string $controllerName the name of the controller under which it is
* stored in the DI container
diff --git a/lib/private/AppFramework/Bootstrap/Coordinator.php b/lib/private/AppFramework/Bootstrap/Coordinator.php
index b6378830732..3ab6ac4c8b0 100644
--- a/lib/private/AppFramework/Bootstrap/Coordinator.php
+++ b/lib/private/AppFramework/Bootstrap/Coordinator.php
@@ -151,7 +151,7 @@ class Coordinator {
*/
$this->registrationContext->delegateCapabilityRegistrations($apps);
$this->registrationContext->delegateCrashReporterRegistrations($apps, $this->registry);
- $this->registrationContext->delegateDashboardPanelRegistrations($apps, $this->dashboardManager);
+ $this->registrationContext->delegateDashboardPanelRegistrations($this->dashboardManager);
$this->registrationContext->delegateEventListenerRegistrations($this->eventDispatcher);
$this->registrationContext->delegateContainerRegistrations($apps);
$this->registrationContext->delegateMiddlewareRegistrations($apps);
diff --git a/lib/private/AppFramework/Bootstrap/RegistrationContext.php b/lib/private/AppFramework/Bootstrap/RegistrationContext.php
index 7b4d24036e8..c98f968c999 100644
--- a/lib/private/AppFramework/Bootstrap/RegistrationContext.php
+++ b/lib/private/AppFramework/Bootstrap/RegistrationContext.php
@@ -121,6 +121,9 @@ class RegistrationContext {
/** @var ServiceRegistration<ICalendarProvider>[] */
private $calendarProviders = [];
+ /** @var ParameterRegistration[] */
+ private $sensitiveMethods = [];
+
/** @var LoggerInterface */
private $logger;
@@ -304,6 +307,14 @@ class RegistrationContext {
$migratorClass
);
}
+
+ public function registerSensitiveMethods(string $class, array $methods): void {
+ $this->context->registerSensitiveMethods(
+ $this->appId,
+ $class,
+ $methods
+ );
+ }
};
}
@@ -430,6 +441,11 @@ class RegistrationContext {
$this->userMigrators[] = new ServiceRegistration($appId, $migratorClass);
}
+ public function registerSensitiveMethods(string $appId, string $class, array $methods): void {
+ $methods = array_filter($methods, 'is_string');
+ $this->sensitiveMethods[] = new ParameterRegistration($appId, $class, $methods);
+ }
+
/**
* @param App[] $apps
*/
@@ -475,10 +491,10 @@ class RegistrationContext {
/**
* @param App[] $apps
*/
- public function delegateDashboardPanelRegistrations(array $apps, IManager $dashboardManager): void {
+ public function delegateDashboardPanelRegistrations(IManager $dashboardManager): void {
while (($panel = array_shift($this->dashboardPanels)) !== null) {
try {
- $dashboardManager->lazyRegisterWidget($panel->getService());
+ $dashboardManager->lazyRegisterWidget($panel->getService(), $panel->getAppId());
} catch (Throwable $e) {
$appId = $panel->getAppId();
$this->logger->error("Error during dashboard registration of $appId: " . $e->getMessage(), [
@@ -712,4 +728,11 @@ class RegistrationContext {
public function getUserMigrators(): array {
return $this->userMigrators;
}
+
+ /**
+ * @return ParameterRegistration[]
+ */
+ public function getSensitiveMethods(): array {
+ return $this->sensitiveMethods;
+ }
}
diff --git a/lib/private/AppFramework/Http/Dispatcher.php b/lib/private/AppFramework/Http/Dispatcher.php
index 21d61bc95aa..c1a203a7165 100644
--- a/lib/private/AppFramework/Http/Dispatcher.php
+++ b/lib/private/AppFramework/Http/Dispatcher.php
@@ -118,7 +118,7 @@ class Dispatcher {
$out = [null, [], null];
try {
- // prefill reflector with everything thats needed for the
+ // prefill reflector with everything that's needed for the
// middlewares
$this->reflector->reflect($controller, $methodName);
@@ -156,7 +156,7 @@ class Dispatcher {
// if an exception appears, the middleware checks if it can handle the
// exception and creates a response. If no response is created, it is
- // assumed that theres no middleware who can handle it and the error is
+ // assumed that there's no middleware who can handle it and the error is
// thrown again
} catch (\Exception $exception) {
$response = $this->middlewareDispatcher->afterException(
diff --git a/lib/private/AppFramework/Http/Request.php b/lib/private/AppFramework/Http/Request.php
index 010d889070e..496a845dd4a 100644
--- a/lib/private/AppFramework/Http/Request.php
+++ b/lib/private/AppFramework/Http/Request.php
@@ -25,6 +25,7 @@ declare(strict_types=1);
* @author Thomas Müller <thomas.mueller@tmit.eu>
* @author Thomas Tanghus <thomas@tanghus.net>
* @author Vincent Petry <vincent@nextcloud.com>
+ * @author Simon Leiner <simon@leiner.me>
*
* @license AGPL-3.0
*
@@ -50,6 +51,7 @@ use OCP\IConfig;
use OCP\IRequest;
use OCP\IRequestId;
use OCP\Security\ICrypto;
+use Symfony\Component\HttpFoundation\IpUtils;
/**
* Class for accessing variables in the request.
@@ -342,7 +344,7 @@ class Request implements \ArrayAccess, \Countable, IRequest {
/**
* Returns all params that were received, be it from the request
- * (as GET or POST) or throuh the URL by the route
+ * (as GET or POST) or through the URL by the route
* @return array the array with all parameters
*/
public function getParams(): array {
@@ -573,41 +575,12 @@ class Request implements \ArrayAccess, \Countable, IRequest {
}
/**
- * Checks if given $remoteAddress matches given $trustedProxy.
- * If $trustedProxy is an IPv4 IP range given in CIDR notation, true will be returned if
- * $remoteAddress is an IPv4 address within that IP range.
- * Otherwise $remoteAddress will be compared to $trustedProxy literally and the result
- * will be returned.
- * @return boolean true if $remoteAddress matches $trustedProxy, false otherwise
- */
- protected function matchesTrustedProxy($trustedProxy, $remoteAddress) {
- $cidrre = '/^([0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3})\/([0-9]{1,2})$/';
-
- if (preg_match($cidrre, $trustedProxy, $match)) {
- $net = $match[1];
- $shiftbits = min(32, max(0, 32 - intval($match[2])));
- $netnum = ip2long($net) >> $shiftbits;
- $ipnum = ip2long($remoteAddress) >> $shiftbits;
-
- return $ipnum === $netnum;
- }
-
- return $trustedProxy === $remoteAddress;
- }
-
- /**
* Checks if given $remoteAddress matches any entry in the given array $trustedProxies.
* For details regarding what "match" means, refer to `matchesTrustedProxy`.
* @return boolean true if $remoteAddress matches any entry in $trustedProxies, false otherwise
*/
protected function isTrustedProxy($trustedProxies, $remoteAddress) {
- foreach ($trustedProxies as $tp) {
- if ($this->matchesTrustedProxy($tp, $remoteAddress)) {
- return true;
- }
- }
-
- return false;
+ return IpUtils::checkIp($remoteAddress, $trustedProxies);
}
/**
diff --git a/lib/private/AppFramework/Middleware/MiddlewareDispatcher.php b/lib/private/AppFramework/Middleware/MiddlewareDispatcher.php
index 950ef8a13a3..adf17e53caa 100644
--- a/lib/private/AppFramework/Middleware/MiddlewareDispatcher.php
+++ b/lib/private/AppFramework/Middleware/MiddlewareDispatcher.php
@@ -46,7 +46,7 @@ class MiddlewareDispatcher {
private $middlewares;
/**
- * @var int counter which tells us what middlware was executed once an
+ * @var int counter which tells us what middleware was executed once an
* exception occurs
*/
private $middlewareCounter;
diff --git a/lib/private/Authentication/Listeners/UserDeletedFilesCleanupListener.php b/lib/private/Authentication/Listeners/UserDeletedFilesCleanupListener.php
index 2fb05159d09..5e657be0763 100644
--- a/lib/private/Authentication/Listeners/UserDeletedFilesCleanupListener.php
+++ b/lib/private/Authentication/Listeners/UserDeletedFilesCleanupListener.php
@@ -72,12 +72,12 @@ class UserDeletedFilesCleanupListener implements IEventListener {
}
$storage = $this->homeStorageCache[$event->getUser()->getUID()];
$cache = $storage->getCache();
+ $storage->rmdir('');
if ($cache instanceof Cache) {
$cache->clear();
} else {
throw new \Exception("Home storage has invalid cache");
}
- $storage->rmdir('');
}
}
}
diff --git a/lib/private/Authentication/LoginCredentials/Store.php b/lib/private/Authentication/LoginCredentials/Store.php
index 0ab4c9a37cc..d3db0444664 100644
--- a/lib/private/Authentication/LoginCredentials/Store.php
+++ b/lib/private/Authentication/LoginCredentials/Store.php
@@ -100,7 +100,7 @@ class Store implements IStore {
} catch (SessionNotAvailableException $ex) {
$this->logger->debug('could not get login credentials because session is unavailable', ['app' => 'core', 'exception' => $ex]);
} catch (InvalidTokenException $ex) {
- $this->logger->debug('could not get login credentials because the token is invalid: ' . $ex->getMessage(), ['app' => 'core', 'exception' => $ex]);
+ $this->logger->debug('could not get login credentials because the token is invalid: ' . $ex->getMessage(), ['app' => 'core']);
$trySession = true;
} catch (PasswordlessTokenException $ex) {
$this->logger->debug('could not get login credentials because the token has no password', ['app' => 'core', 'exception' => $ex]);
diff --git a/lib/private/Authentication/Token/IProvider.php b/lib/private/Authentication/Token/IProvider.php
index 0a145bfd7e6..33e0ad46263 100644
--- a/lib/private/Authentication/Token/IProvider.php
+++ b/lib/private/Authentication/Token/IProvider.php
@@ -158,7 +158,7 @@ interface IProvider {
public function setPassword(IToken $token, string $tokenId, string $password);
/**
- * Rotate the token. Usefull for for example oauth tokens
+ * Rotate the token. Useful for for example oauth tokens
*
* @param IToken $token
* @param string $oldTokenId
diff --git a/lib/private/Authentication/Token/PublicKeyTokenProvider.php b/lib/private/Authentication/Token/PublicKeyTokenProvider.php
index a1d75828e27..0f1767e845b 100644
--- a/lib/private/Authentication/Token/PublicKeyTokenProvider.php
+++ b/lib/private/Authentication/Token/PublicKeyTokenProvider.php
@@ -34,7 +34,7 @@ use OC\Authentication\Exceptions\InvalidTokenException;
use OC\Authentication\Exceptions\TokenPasswordExpiredException;
use OC\Authentication\Exceptions\PasswordlessTokenException;
use OC\Authentication\Exceptions\WipeTokenException;
-use OC\Cache\CappedMemoryCache;
+use OCP\Cache\CappedMemoryCache;
use OCP\AppFramework\Db\DoesNotExistException;
use OCP\AppFramework\Utility\ITimeFactory;
use OCP\IConfig;
@@ -346,7 +346,7 @@ class PublicKeyTokenProvider implements IProvider {
$config = array_merge([
'digest_alg' => 'sha512',
- 'private_key_bits' => 2048,
+ 'private_key_bits' => $password !== null && strlen($password) > 250 ? 4096 : 2048,
], $this->config->getSystemValue('openssl', []));
// Generate new key
@@ -368,7 +368,10 @@ class PublicKeyTokenProvider implements IProvider {
$dbToken->setPublicKey($publicKey);
$dbToken->setPrivateKey($this->encrypt($privateKey, $token));
- if (!is_null($password)) {
+ if (!is_null($password) && $this->config->getSystemValueBool('auth.storeCryptedPassword', true)) {
+ if (strlen($password) > 469) {
+ throw new \RuntimeException('Trying to save a password with more than 469 characters is not supported. If you want to use big passwords, disable the auth.storeCryptedPassword option in config.php');
+ }
$dbToken->setPassword($this->encryptPassword($password, $publicKey));
}
@@ -398,7 +401,7 @@ class PublicKeyTokenProvider implements IProvider {
$this->cache->clear();
// prevent setting an empty pw as result of pw-less-login
- if ($password === '') {
+ if ($password === '' || !$this->config->getSystemValueBool('auth.storeCryptedPassword', true)) {
return;
}
@@ -406,9 +409,12 @@ class PublicKeyTokenProvider implements IProvider {
$tokens = $this->mapper->getTokenByUser($uid);
foreach ($tokens as $t) {
$publicKey = $t->getPublicKey();
- $t->setPassword($this->encryptPassword($password, $publicKey));
- $t->setPasswordInvalid(false);
- $this->updateToken($t);
+ $encryptedPassword = $this->encryptPassword($password, $publicKey);
+ if ($t->getPassword() !== $encryptedPassword) {
+ $t->setPassword($encryptedPassword);
+ $t->setPasswordInvalid(false);
+ $this->updateToken($t);
+ }
}
}
diff --git a/lib/private/Avatar/Avatar.php b/lib/private/Avatar/Avatar.php
index f0e14ad8b2f..0eb8f8816d8 100644
--- a/lib/private/Avatar/Avatar.php
+++ b/lib/private/Avatar/Avatar.php
@@ -37,7 +37,7 @@ declare(strict_types=1);
namespace OC\Avatar;
use Imagick;
-use OC\Color;
+use OCP\Color;
use OCP\Files\NotFoundException;
use OCP\IAvatar;
use Psr\Log\LoggerInterface;
@@ -46,9 +46,7 @@ use Psr\Log\LoggerInterface;
* This class gets and sets users avatars.
*/
abstract class Avatar implements IAvatar {
-
- /** @var LoggerInterface */
- protected $logger;
+ protected LoggerInterface $logger;
/**
* https://github.com/sebdesign/cap-height -- for 500px height
@@ -57,10 +55,8 @@ abstract class Avatar implements IAvatar {
* (0.4 letter-to-total-height ratio, 500*0.4=200), so: 200/0.715 = 280px.
* Since we start from the baseline (text-anchor) we need to
* shift the y axis by 100px (half the caps height): 500/2+100=350
- *
- * @var string
*/
- private $svgTemplate = '<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+ private string $svgTemplate = '<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg width="{size}" height="{size}" version="1.1" viewBox="0 0 500 500" xmlns="http://www.w3.org/2000/svg">
<rect width="100%" height="100%" fill="#{fill}"></rect>
<text x="50%" y="350" style="font-weight:normal;font-size:280px;font-family:\'Noto Sans\';text-anchor:middle;fill:#fff">{letter}</text>
@@ -72,15 +68,11 @@ abstract class Avatar implements IAvatar {
/**
* Returns the user display name.
- *
- * @return string
*/
abstract public function getDisplayName(): string;
/**
* Returns the first letter of the display name, or "?" if no name given.
- *
- * @return string
*/
private function getAvatarText(): string {
$displayName = $this->getDisplayName();
@@ -96,9 +88,7 @@ abstract class Avatar implements IAvatar {
/**
* @inheritdoc
*/
- public function get($size = 64) {
- $size = (int) $size;
-
+ public function get(int $size = 64) {
try {
$file = $this->getFile($size);
} catch (NotFoundException $e) {
@@ -124,7 +114,7 @@ abstract class Avatar implements IAvatar {
protected function getAvatarVector(int $size): string {
$userDisplayName = $this->getDisplayName();
$bgRGB = $this->avatarBackgroundColor($userDisplayName);
- $bgHEX = sprintf("%02x%02x%02x", $bgRGB->r, $bgRGB->g, $bgRGB->b);
+ $bgHEX = sprintf("%02x%02x%02x", $bgRGB->red(), $bgRGB->green(), $bgRGB->blue());
$text = $this->getAvatarText();
$toReplace = ['{size}', '{fill}', '{letter}'];
return str_replace($toReplace, [$size, $bgHEX, $text], $this->svgTemplate);
@@ -132,13 +122,10 @@ abstract class Avatar implements IAvatar {
/**
* Generate png avatar from svg with Imagick
- *
- * @param int $size
- * @return string|boolean
*/
- protected function generateAvatarFromSvg(int $size) {
+ protected function generateAvatarFromSvg(int $size): ?string {
if (!extension_loaded('imagick')) {
- return false;
+ return null;
}
try {
$font = __DIR__ . '/../../core/fonts/NotoSans-Regular.ttf';
@@ -149,30 +136,25 @@ abstract class Avatar implements IAvatar {
$avatar->setImageFormat('png');
$image = new \OCP\Image();
$image->loadFromData((string)$avatar);
- $data = $image->data();
- return $data === null ? false : $data;
+ return $image->data();
} catch (\Exception $e) {
- return false;
+ return null;
}
}
/**
* Generate png avatar with GD
- *
- * @param string $userDisplayName
- * @param int $size
- * @return string
*/
- protected function generateAvatar($userDisplayName, $size) {
+ protected function generateAvatar(string $userDisplayName, int $size): string {
$text = $this->getAvatarText();
$backgroundColor = $this->avatarBackgroundColor($userDisplayName);
$im = imagecreatetruecolor($size, $size);
$background = imagecolorallocate(
$im,
- $backgroundColor->r,
- $backgroundColor->g,
- $backgroundColor->b
+ $backgroundColor->red(),
+ $backgroundColor->green(),
+ $backgroundColor->blue()
);
$white = imagecolorallocate($im, 255, 255, 255);
imagefilledrectangle($im, 0, 0, $size, $size, $background);
@@ -209,7 +191,7 @@ abstract class Avatar implements IAvatar {
string $text,
string $font,
int $size,
- $angle = 0
+ int $angle = 0
): array {
// Image width & height
$xi = imagesx($image);
@@ -229,37 +211,6 @@ abstract class Avatar implements IAvatar {
return [$x, $y];
}
- /**
- * Calculate steps between two Colors
- * @param object Color $steps start color
- * @param object Color $ends end color
- * @return array [r,g,b] steps for each color to go from $steps to $ends
- */
- private function stepCalc($steps, $ends) {
- $step = [];
- $step[0] = ($ends[1]->r - $ends[0]->r) / $steps;
- $step[1] = ($ends[1]->g - $ends[0]->g) / $steps;
- $step[2] = ($ends[1]->b - $ends[0]->b) / $steps;
- return $step;
- }
-
- /**
- * Convert a string to an integer evenly
- * @param string $hash the text to parse
- * @param int $maximum the maximum range
- * @return int[] between 0 and $maximum
- */
- private function mixPalette($steps, $color1, $color2) {
- $palette = [$color1];
- $step = $this->stepCalc($steps, [$color1, $color2]);
- for ($i = 1; $i < $steps; $i++) {
- $r = intval($color1->r + ($step[0] * $i));
- $g = intval($color1->g + ($step[1] * $i));
- $b = intval($color1->b + ($step[2] * $i));
- $palette[] = new Color($r, $g, $b);
- }
- return $palette;
- }
/**
* Convert a string to an integer evenly
@@ -267,7 +218,7 @@ abstract class Avatar implements IAvatar {
* @param int $maximum the maximum range
* @return int between 0 and $maximum
*/
- private function hashToInt($hash, $maximum) {
+ private function hashToInt(string $hash, int $maximum): int {
$final = 0;
$result = [];
@@ -285,10 +236,9 @@ abstract class Avatar implements IAvatar {
}
/**
- * @param string $hash
- * @return Color Object containting r g b int in the range [0, 255]
+ * @return Color Object containing r g b int in the range [0, 255]
*/
- public function avatarBackgroundColor(string $hash) {
+ public function avatarBackgroundColor(string $hash): Color {
// Normalize hash
$hash = strtolower($hash);
@@ -308,9 +258,9 @@ abstract class Avatar implements IAvatar {
// 3 colors * 6 will result in 18 generated colors
$steps = 6;
- $palette1 = $this->mixPalette($steps, $red, $yellow);
- $palette2 = $this->mixPalette($steps, $yellow, $blue);
- $palette3 = $this->mixPalette($steps, $blue, $red);
+ $palette1 = Color::mixPalette($steps, $red, $yellow);
+ $palette2 = Color::mixPalette($steps, $yellow, $blue);
+ $palette3 = Color::mixPalette($steps, $blue, $red);
$finalPalette = array_merge($palette1, $palette2, $palette3);
diff --git a/lib/private/Avatar/AvatarManager.php b/lib/private/Avatar/AvatarManager.php
index 77138085dc9..ec9bed40850 100644
--- a/lib/private/Avatar/AvatarManager.php
+++ b/lib/private/Avatar/AvatarManager.php
@@ -136,20 +136,23 @@ class AvatarManager implements IAvatarManager {
$avatarScope = '';
}
- if (
+ switch ($avatarScope) {
// v2-private scope hides the avatar from public access and from unknown users
- $avatarScope === IAccountManager::SCOPE_PRIVATE
- && (
- // accessing from public link
- $requestingUser === null
- // logged in, but unknown to user
- || !$this->knownUserService->isKnownToUser($requestingUser->getUID(), $userId)
- )) {
- // use a placeholder avatar which caches the generated images
- return new PlaceholderAvatar($folder, $user, $this->logger);
+ case IAccountManager::SCOPE_PRIVATE:
+ if ($requestingUser !== null && $this->knownUserService->isKnownToUser($requestingUser->getUID(), $userId)) {
+ return new UserAvatar($folder, $this->l, $user, $this->logger, $this->config);
+ }
+ break;
+ case IAccountManager::SCOPE_LOCAL:
+ case IAccountManager::SCOPE_FEDERATED:
+ case IAccountManager::SCOPE_PUBLISHED:
+ return new UserAvatar($folder, $this->l, $user, $this->logger, $this->config);
+ default:
+ // use a placeholder avatar which caches the generated images
+ return new PlaceholderAvatar($folder, $user, $this->logger);
}
- return new UserAvatar($folder, $this->l, $user, $this->logger, $this->config);
+ return new PlaceholderAvatar($folder, $user, $this->logger);
}
/**
diff --git a/lib/private/Avatar/GuestAvatar.php b/lib/private/Avatar/GuestAvatar.php
index f0297b3a93c..79d7e6ee094 100644
--- a/lib/private/Avatar/GuestAvatar.php
+++ b/lib/private/Avatar/GuestAvatar.php
@@ -26,6 +26,7 @@ declare(strict_types=1);
*/
namespace OC\Avatar;
+use OCP\Files\SimpleFS\ISimpleFile;
use OCP\Files\SimpleFS\InMemoryFile;
use Psr\Log\LoggerInterface;
@@ -35,16 +36,13 @@ use Psr\Log\LoggerInterface;
class GuestAvatar extends Avatar {
/**
* Holds the guest user display name.
- *
- * @var string
*/
- private $userDisplayName;
+ private string $userDisplayName;
/**
* GuestAvatar constructor.
*
* @param string $userDisplayName The guest user display name
- * @param LoggerInterface $logger The logger
*/
public function __construct(string $userDisplayName, LoggerInterface $logger) {
parent::__construct($logger);
@@ -53,17 +51,14 @@ class GuestAvatar extends Avatar {
/**
* Tests if the user has an avatar.
- *
- * @return true Guests always have an avatar.
*/
- public function exists() {
+ public function exists(): bool {
+ // Guests always have an avatar.
return true;
}
/**
* Returns the guest user display name.
- *
- * @return string
*/
public function getDisplayName(): string {
return $this->userDisplayName;
@@ -75,24 +70,21 @@ class GuestAvatar extends Avatar {
* @param \OCP\IImage|resource|string $data
* @return void
*/
- public function set($data) {
+ public function set($data): void {
// unimplemented for guest user avatars
}
/**
* Removing avatars isn't implemented for guests.
*/
- public function remove() {
+ public function remove(bool $silent = false): void {
// unimplemented for guest user avatars
}
/**
* Generates an avatar for the guest.
- *
- * @param int $size The desired image size.
- * @return InMemoryFile
*/
- public function getFile($size) {
+ public function getFile(int $size): ISimpleFile {
$avatar = $this->generateAvatar($this->userDisplayName, $size);
return new InMemoryFile('avatar.png', $avatar);
}
@@ -103,9 +95,8 @@ class GuestAvatar extends Avatar {
* @param string $feature The changed feature
* @param mixed $oldValue The previous value
* @param mixed $newValue The new value
- * @return void
*/
- public function userChanged($feature, $oldValue, $newValue) {
+ public function userChanged(string $feature, $oldValue, $newValue): void {
if ($feature === 'displayName') {
$this->userDisplayName = $newValue;
}
@@ -113,8 +104,6 @@ class GuestAvatar extends Avatar {
/**
* Guests don't have custom avatars.
- *
- * @return bool
*/
public function isCustomAvatar(): bool {
return false;
diff --git a/lib/private/Avatar/PlaceholderAvatar.php b/lib/private/Avatar/PlaceholderAvatar.php
index df7a490cbe4..504e5c1457d 100644
--- a/lib/private/Avatar/PlaceholderAvatar.php
+++ b/lib/private/Avatar/PlaceholderAvatar.php
@@ -44,11 +44,8 @@ use Psr\Log\LoggerInterface;
* for faster retrieval, unlike the GuestAvatar.
*/
class PlaceholderAvatar extends Avatar {
- /** @var ISimpleFolder */
- private $folder;
-
- /** @var User */
- private $user;
+ private ISimpleFolder $folder;
+ private User $user;
/**
* UserAvatar constructor.
@@ -71,10 +68,8 @@ class PlaceholderAvatar extends Avatar {
/**
* Check if an avatar exists for the user
- *
- * @return bool
*/
- public function exists() {
+ public function exists(): bool {
return true;
}
@@ -87,14 +82,14 @@ class PlaceholderAvatar extends Avatar {
* @throws NotSquareException if the image is not square
* @return void
*/
- public function set($data) {
+ public function set($data): void {
// unimplemented for placeholder avatars
}
/**
* Removes the users avatar.
*/
- public function remove(bool $silent = false) {
+ public function remove(bool $silent = false): void {
$avatars = $this->folder->getDirectoryListing();
foreach ($avatars as $avatar) {
@@ -113,9 +108,7 @@ class PlaceholderAvatar extends Avatar {
* @throws \OCP\Files\NotPermittedException
* @throws \OCP\PreConditionNotMetException
*/
- public function getFile($size) {
- $size = (int) $size;
-
+ public function getFile(int $size): ISimpleFile {
$ext = 'png';
if ($size === -1) {
@@ -149,8 +142,6 @@ class PlaceholderAvatar extends Avatar {
/**
* Returns the user display name.
- *
- * @return string
*/
public function getDisplayName(): string {
return $this->user->getDisplayName();
@@ -165,14 +156,12 @@ class PlaceholderAvatar extends Avatar {
* @throws NotPermittedException
* @throws \OCP\PreConditionNotMetException
*/
- public function userChanged($feature, $oldValue, $newValue) {
+ public function userChanged(string $feature, $oldValue, $newValue): void {
$this->remove();
}
/**
* Check if the avatar of a user is a custom uploaded one
- *
- * @return bool
*/
public function isCustomAvatar(): bool {
return false;
diff --git a/lib/private/Avatar/UserAvatar.php b/lib/private/Avatar/UserAvatar.php
index 6cca5a1c355..f5a1d7e77b1 100644
--- a/lib/private/Avatar/UserAvatar.php
+++ b/lib/private/Avatar/UserAvatar.php
@@ -44,17 +44,10 @@ use Psr\Log\LoggerInterface;
* This class represents a registered user's avatar.
*/
class UserAvatar extends Avatar {
- /** @var IConfig */
- private $config;
-
- /** @var ISimpleFolder */
- private $folder;
-
- /** @var IL10N */
- private $l;
-
- /** @var User */
- private $user;
+ private IConfig $config;
+ private ISimpleFolder $folder;
+ private IL10N $l;
+ private User $user;
/**
* UserAvatar constructor.
@@ -68,7 +61,7 @@ class UserAvatar extends Avatar {
public function __construct(
ISimpleFolder $folder,
IL10N $l,
- $user,
+ User $user,
LoggerInterface $logger,
IConfig $config) {
parent::__construct($logger);
@@ -80,10 +73,8 @@ class UserAvatar extends Avatar {
/**
* Check if an avatar exists for the user
- *
- * @return bool
*/
- public function exists() {
+ public function exists(): bool {
return $this->folder->fileExists('avatar.jpg') || $this->folder->fileExists('avatar.png');
}
@@ -96,7 +87,7 @@ class UserAvatar extends Avatar {
* @throws NotSquareException if the image is not square
* @return void
*/
- public function set($data) {
+ public function set($data): void {
$img = $this->getAvatarImage($data);
$data = $img->data();
@@ -124,7 +115,7 @@ class UserAvatar extends Avatar {
* @param IImage|resource|string|\GdImage $data An image object, imagedata or path to the avatar
* @return IImage
*/
- private function getAvatarImage($data) {
+ private function getAvatarImage($data): IImage {
if ($data instanceof IImage) {
return $data;
}
@@ -156,11 +147,8 @@ class UserAvatar extends Avatar {
/**
* Returns the avatar image type.
- *
- * @param IImage $avatar
- * @return string
*/
- private function getAvatarImageType(IImage $avatar) {
+ private function getAvatarImageType(IImage $avatar): string {
$type = substr($avatar->mimeType(), -3);
if ($type === 'peg') {
$type = 'jpg';
@@ -179,7 +167,7 @@ class UserAvatar extends Avatar {
* @throws \Exception if the provided image is not valid
* @throws NotSquareException if the image is not square
*/
- private function validateAvatar(IImage $avatar) {
+ private function validateAvatar(IImage $avatar): void {
$type = $this->getAvatarImageType($avatar);
if ($type !== 'jpg' && $type !== 'png') {
@@ -197,15 +185,14 @@ class UserAvatar extends Avatar {
/**
* Removes the users avatar.
- * @return void
* @throws \OCP\Files\NotPermittedException
* @throws \OCP\PreConditionNotMetException
*/
- public function remove(bool $silent = false) {
+ public function remove(bool $silent = false): void {
$avatars = $this->folder->getDirectoryListing();
$this->config->setUserValue($this->user->getUID(), 'avatar', 'version',
- (int) $this->config->getUserValue($this->user->getUID(), 'avatar', 'version', 0) + 1);
+ (string)((int)$this->config->getUserValue($this->user->getUID(), 'avatar', 'version', '0') + 1));
foreach ($avatars as $avatar) {
$avatar->delete();
@@ -219,10 +206,9 @@ class UserAvatar extends Avatar {
/**
* Get the extension of the avatar. If there is no avatar throw Exception
*
- * @return string
* @throws NotFoundException
*/
- private function getExtension() {
+ private function getExtension(): string {
if ($this->folder->fileExists('avatar.jpg')) {
return 'jpg';
} elseif ($this->folder->fileExists('avatar.png')) {
@@ -242,9 +228,7 @@ class UserAvatar extends Avatar {
* @throws \OCP\Files\NotPermittedException
* @throws \OCP\PreConditionNotMetException
*/
- public function getFile($size) {
- $size = (int) $size;
-
+ public function getFile(int $size): ISimpleFile {
try {
$ext = $this->getExtension();
} catch (NotFoundException $e) {
@@ -304,8 +288,6 @@ class UserAvatar extends Avatar {
/**
* Returns the user display name.
- *
- * @return string
*/
public function getDisplayName(): string {
return $this->user->getDisplayName();
@@ -320,7 +302,7 @@ class UserAvatar extends Avatar {
* @throws NotPermittedException
* @throws \OCP\PreConditionNotMetException
*/
- public function userChanged($feature, $oldValue, $newValue) {
+ public function userChanged(string $feature, $oldValue, $newValue): void {
// If the avatar is not generated (so an uploaded image) we skip this
if (!$this->folder->fileExists('generated')) {
return;
@@ -331,8 +313,6 @@ class UserAvatar extends Avatar {
/**
* Check if the avatar of a user is a custom uploaded one
- *
- * @return bool
*/
public function isCustomAvatar(): bool {
return $this->config->getUserValue($this->user->getUID(), 'avatar', 'generated', 'false') !== 'true';
diff --git a/lib/private/BackgroundJob/JobList.php b/lib/private/BackgroundJob/JobList.php
index 7ab86df8455..20176e45125 100644
--- a/lib/private/BackgroundJob/JobList.php
+++ b/lib/private/BackgroundJob/JobList.php
@@ -40,21 +40,10 @@ use OCP\IConfig;
use OCP\IDBConnection;
class JobList implements IJobList {
+ protected IDBConnection $connection;
+ protected IConfig $config;
+ protected ITimeFactory $timeFactory;
- /** @var IDBConnection */
- protected $connection;
-
- /**@var IConfig */
- protected $config;
-
- /**@var ITimeFactory */
- protected $timeFactory;
-
- /**
- * @param IDBConnection $connection
- * @param IConfig $config
- * @param ITimeFactory $timeFactory
- */
public function __construct(IDBConnection $connection, IConfig $config, ITimeFactory $timeFactory) {
$this->connection = $connection;
$this->config = $config;
@@ -62,10 +51,10 @@ class JobList implements IJobList {
}
/**
- * @param IJob|string $job
+ * @param IJob|class-string<IJob> $job
* @param mixed $argument
*/
- public function add($job, $argument = null) {
+ public function add($job, $argument = null): void {
if ($job instanceof IJob) {
$class = get_class($job);
} else {
@@ -101,7 +90,7 @@ class JobList implements IJobList {
* @param IJob|string $job
* @param mixed $argument
*/
- public function remove($job, $argument = null) {
+ public function remove($job, $argument = null): void {
if ($job instanceof IJob) {
$class = get_class($job);
} else {
@@ -133,24 +122,20 @@ class JobList implements IJobList {
}
}
- /**
- * @param int $id
- */
- protected function removeById($id) {
+ protected function removeById(int $id): void {
$query = $this->connection->getQueryBuilder();
$query->delete('jobs')
->where($query->expr()->eq('id', $query->createNamedParameter($id, IQueryBuilder::PARAM_INT)));
- $query->execute();
+ $query->executeStatement();
}
/**
* check if a job is in the list
*
- * @param IJob|string $job
+ * @param IJob|class-string<IJob> $job
* @param mixed $argument
- * @return bool
*/
- public function has($job, $argument) {
+ public function has($job, $argument): bool {
if ($job instanceof IJob) {
$class = get_class($job);
} else {
@@ -165,7 +150,7 @@ class JobList implements IJobList {
->andWhere($query->expr()->eq('argument_hash', $query->createNamedParameter(md5($argument))))
->setMaxResults(1);
- $result = $query->execute();
+ $result = $query->executeQuery();
$row = $result->fetch();
$result->closeCursor();
@@ -177,13 +162,33 @@ class JobList implements IJobList {
*
* @return IJob[]
* @deprecated 9.0.0 - This method is dangerous since it can cause load and
- * memory problems when creating too many instances.
+ * memory problems when creating too many instances. Use getJobs instead.
+ */
+ public function getAll(): array {
+ return $this->getJobs(null, null, 0);
+ }
+
+ /**
+ * @param IJob|class-string<IJob>|null $job
+ * @return IJob[]
*/
- public function getAll() {
+ public function getJobs($job, ?int $limit, int $offset): array {
$query = $this->connection->getQueryBuilder();
$query->select('*')
- ->from('jobs');
- $result = $query->execute();
+ ->from('jobs')
+ ->setMaxResults($limit)
+ ->setFirstResult($offset);
+
+ if ($job !== null) {
+ if ($job instanceof IJob) {
+ $class = get_class($job);
+ } else {
+ $class = $job;
+ }
+ $query->where($query->expr()->eq('class', $query->createNamedParameter($class)));
+ }
+
+ $result = $query->executeQuery();
$jobs = [];
while ($row = $result->fetch()) {
@@ -199,9 +204,6 @@ class JobList implements IJobList {
/**
* get the next job in the list
- *
- * @param bool $onlyTimeSensitive
- * @return IJob|null
*/
public function getNext(bool $onlyTimeSensitive = false): ?IJob {
$query = $this->connection->getQueryBuilder();
@@ -224,7 +226,7 @@ class JobList implements IJobList {
->andWhere($update->expr()->eq('reserved_at', $update->createParameter('reserved_at')))
->andWhere($update->expr()->eq('last_checked', $update->createParameter('last_checked')));
- $result = $query->execute();
+ $result = $query->executeQuery();
$row = $result->fetch();
$result->closeCursor();
@@ -232,7 +234,7 @@ class JobList implements IJobList {
$update->setParameter('jobid', $row['id']);
$update->setParameter('reserved_at', $row['reserved_at']);
$update->setParameter('last_checked', $row['last_checked']);
- $count = $update->execute();
+ $count = $update->executeStatement();
if ($count === 0) {
// Background job already executed elsewhere, try again.
@@ -247,7 +249,7 @@ class JobList implements IJobList {
->set('reserved_at', $reset->expr()->literal(0, IQueryBuilder::PARAM_INT))
->set('last_checked', $reset->createNamedParameter($this->timeFactory->getTime() + 12 * 3600, IQueryBuilder::PARAM_INT))
->where($reset->expr()->eq('id', $reset->createNamedParameter($row['id'], IQueryBuilder::PARAM_INT)));
- $reset->execute();
+ $reset->executeStatement();
// Background job from disabled app, try again.
return $this->getNext($onlyTimeSensitive);
@@ -259,11 +261,7 @@ class JobList implements IJobList {
}
}
- /**
- * @param int $id
- * @return IJob|null
- */
- public function getById($id) {
+ public function getById(int $id): ?IJob {
$row = $this->getDetailsById($id);
if ($row) {
@@ -292,15 +290,14 @@ class JobList implements IJobList {
/**
* get the job object from a row in the db
*
- * @param array $row
- * @return IJob|null
+ * @param array{class:class-string<IJob>, id:mixed, last_run:mixed, argument:string} $row
*/
- private function buildJob($row) {
+ private function buildJob(array $row): ?IJob {
try {
try {
// Try to load the job as a service
/** @var IJob $job */
- $job = \OC::$server->query($row['class']);
+ $job = \OCP\Server::get($row['class']);
} catch (QueryException $e) {
if (class_exists($row['class'])) {
$class = $row['class'];
@@ -327,33 +324,27 @@ class JobList implements IJobList {
/**
* set the job that was last ran
- *
- * @param IJob $job
*/
- public function setLastJob(IJob $job) {
+ public function setLastJob(IJob $job): void {
$this->unlockJob($job);
- $this->config->setAppValue('backgroundjob', 'lastjob', $job->getId());
+ $this->config->setAppValue('backgroundjob', 'lastjob', (string)$job->getId());
}
/**
* Remove the reservation for a job
- *
- * @param IJob $job
*/
- public function unlockJob(IJob $job) {
+ public function unlockJob(IJob $job): void {
$query = $this->connection->getQueryBuilder();
$query->update('jobs')
->set('reserved_at', $query->expr()->literal(0, IQueryBuilder::PARAM_INT))
->where($query->expr()->eq('id', $query->createNamedParameter($job->getId(), IQueryBuilder::PARAM_INT)));
- $query->execute();
+ $query->executeStatement();
}
/**
* set the lastRun of $job to now
- *
- * @param IJob $job
*/
- public function setLastRun(IJob $job) {
+ public function setLastRun(IJob $job): void {
$query = $this->connection->getQueryBuilder();
$query->update('jobs')
->set('last_run', $query->createNamedParameter(time(), IQueryBuilder::PARAM_INT))
@@ -364,25 +355,23 @@ class JobList implements IJobList {
$query->set('time_sensitive', $query->createNamedParameter(IJob::TIME_INSENSITIVE));
}
- $query->execute();
+ $query->executeStatement();
}
/**
- * @param IJob $job
- * @param $timeTaken
+ * @param int $timeTaken
*/
- public function setExecutionTime(IJob $job, $timeTaken) {
+ public function setExecutionTime(IJob $job, $timeTaken): void {
$query = $this->connection->getQueryBuilder();
$query->update('jobs')
->set('execution_duration', $query->createNamedParameter($timeTaken, IQueryBuilder::PARAM_INT))
->where($query->expr()->eq('id', $query->createNamedParameter($job->getId(), IQueryBuilder::PARAM_INT)));
- $query->execute();
+ $query->executeStatement();
}
/**
* Reset the $job so it executes on the next trigger
*
- * @param IJob $job
* @since 23.0.0
*/
public function resetBackgroundJob(IJob $job): void {
diff --git a/lib/private/Cache/CappedMemoryCache.php b/lib/private/Cache/CappedMemoryCache.php
index 6063b5e7110..31e8ef3e720 100644
--- a/lib/private/Cache/CappedMemoryCache.php
+++ b/lib/private/Cache/CappedMemoryCache.php
@@ -28,6 +28,7 @@ use OCP\ICache;
*
* Uses a simple FIFO expiry mechanism
* @template T
+ * @deprecated use OCP\Cache\CappedMemoryCache instead
*/
class CappedMemoryCache implements ICache, \ArrayAccess {
private $capacity;
diff --git a/lib/private/Collaboration/Collaborators/UserPlugin.php b/lib/private/Collaboration/Collaborators/UserPlugin.php
index 12304a66ce9..9beecdaa6cb 100644
--- a/lib/private/Collaboration/Collaborators/UserPlugin.php
+++ b/lib/private/Collaboration/Collaborators/UserPlugin.php
@@ -95,7 +95,7 @@ class UserPlugin implements ISearchPlugin {
$this->shareeEnumerationFullMatch = $this->config->getAppValue('core', 'shareapi_restrict_user_enumeration_full_match', 'yes') === 'yes';
$this->shareeEnumerationFullMatchUserId = $this->config->getAppValue('core', 'shareapi_restrict_user_enumeration_full_match_userid', 'yes') === 'yes';
$this->shareeEnumerationFullMatchEmail = $this->config->getAppValue('core', 'shareapi_restrict_user_enumeration_full_match_email', 'yes') === 'yes';
- $this->shareeEnumerationFullMatchIgnoreSecondDisplayName = $this->config->getAppValue('core', 'shareapi_restrict_user_enumeration_full_match_ignore_second_display_name', 'no') === 'yes';
+ $this->shareeEnumerationFullMatchIgnoreSecondDisplayName = $this->config->getAppValue('core', 'shareapi_restrict_user_enumeration_full_match_ignore_second_dn', 'no') === 'yes';
}
public function search($search, $limit, $offset, ISearchResult $searchResult) {
diff --git a/lib/private/Command/ClosureJob.php b/lib/private/Command/ClosureJob.php
index 498fe6d1d96..5639852e4db 100644
--- a/lib/private/Command/ClosureJob.php
+++ b/lib/private/Command/ClosureJob.php
@@ -23,10 +23,13 @@
namespace OC\Command;
use OC\BackgroundJob\QueuedJob;
+use Laravel\SerializableClosure\SerializableClosure as LaravelClosure;
+use Opis\Closure\SerializableClosure as OpisClosure;
class ClosureJob extends QueuedJob {
protected function run($serializedCallable) {
- $callable = \Opis\Closure\unserialize($serializedCallable);
+ $callable = unserialize($serializedCallable, [LaravelClosure::class, OpisClosure::class]);
+ $callable = $callable->getClosure();
if (is_callable($callable)) {
$callable();
} else {
diff --git a/lib/private/Command/CommandJob.php b/lib/private/Command/CommandJob.php
index 6fa0c6d7ceb..5b267162c81 100644
--- a/lib/private/Command/CommandJob.php
+++ b/lib/private/Command/CommandJob.php
@@ -30,7 +30,7 @@ use OCP\Command\ICommand;
*/
class CommandJob extends QueuedJob {
protected function run($serializedCommand) {
- $command = \Opis\Closure\unserialize($serializedCommand);
+ $command = unserialize($serializedCommand);
if ($command instanceof ICommand) {
$command->handle();
} else {
diff --git a/lib/private/Command/CronBus.php b/lib/private/Command/CronBus.php
index 89a739617d0..8749ad0bff5 100644
--- a/lib/private/Command/CronBus.php
+++ b/lib/private/Command/CronBus.php
@@ -26,6 +26,7 @@
namespace OC\Command;
use OCP\Command\ICommand;
+use Laravel\SerializableClosure\SerializableClosure;
class CronBus extends AsyncBus {
/**
@@ -67,9 +68,9 @@ class CronBus extends AsyncBus {
*/
private function serializeCommand($command) {
if ($command instanceof \Closure) {
- return \Opis\Closure\serialize($command);
+ return serialize(new SerializableClosure($command));
} elseif (is_callable($command) or $command instanceof ICommand) {
- return \Opis\Closure\serialize($command);
+ return serialize($command);
} else {
throw new \InvalidArgumentException('Invalid command');
}
diff --git a/lib/private/Comments/Comment.php b/lib/private/Comments/Comment.php
index 2b338efc75f..c481e36f95b 100644
--- a/lib/private/Comments/Comment.php
+++ b/lib/private/Comments/Comment.php
@@ -45,6 +45,7 @@ class Comment implements IComment {
'creationDT' => null,
'latestChildDT' => null,
'reactions' => null,
+ 'expire_date' => null,
];
/**
@@ -350,13 +351,9 @@ class Comment implements IComment {
}
/**
- * sets the date of the most recent child
- *
- * @param \DateTime $dateTime
- * @return IComment
- * @since 9.0.0
+ * @inheritDoc
*/
- public function setLatestChildDateTime(\DateTime $dateTime = null) {
+ public function setLatestChildDateTime(?\DateTime $dateTime = null) {
$this->data['latestChildDT'] = $dateTime;
return $this;
}
@@ -447,6 +444,21 @@ class Comment implements IComment {
}
/**
+ * @inheritDoc
+ */
+ public function setExpireDate(?\DateTime $dateTime): IComment {
+ $this->data['expire_date'] = $dateTime;
+ return $this;
+ }
+
+ /**
+ * @inheritDoc
+ */
+ public function getExpireDate(): ?\DateTime {
+ return $this->data['expire_date'];
+ }
+
+ /**
* sets the comment data based on an array with keys as taken from the
* database.
*
diff --git a/lib/private/Comments/Manager.php b/lib/private/Comments/Manager.php
index b7532222c33..53603e51e56 100644
--- a/lib/private/Comments/Manager.php
+++ b/lib/private/Comments/Manager.php
@@ -107,6 +107,9 @@ class Manager implements ICommentsManager {
if (!is_null($data['latest_child_timestamp'])) {
$data['latest_child_timestamp'] = new \DateTime($data['latest_child_timestamp']);
}
+ if (!is_null($data['expire_date'])) {
+ $data['expire_date'] = new \DateTime($data['expire_date']);
+ }
$data['children_count'] = (int)$data['children_count'];
$data['reference_id'] = $data['reference_id'] ?? null;
if ($this->supportReactions()) {
@@ -167,7 +170,6 @@ class Manager implements ICommentsManager {
if ($comment->getId() === '') {
$comment->setChildrenCount(0);
- $comment->setLatestChildDateTime(new \DateTime('0000-00-00 00:00:00', new \DateTimeZone('UTC')));
$comment->setLatestChildDateTime(null);
}
@@ -1203,6 +1205,7 @@ class Manager implements ICommentsManager {
'latest_child_timestamp' => $qb->createNamedParameter($comment->getLatestChildDateTime(), 'datetime'),
'object_type' => $qb->createNamedParameter($comment->getObjectType()),
'object_id' => $qb->createNamedParameter($comment->getObjectId()),
+ 'expire_date' => $qb->createNamedParameter($comment->getExpireDate(), 'datetime'),
];
if ($tryWritingReferenceId) {
@@ -1642,4 +1645,25 @@ class Manager implements ICommentsManager {
$this->initialStateService->provideInitialState('comments', 'max-message-length', IComment::MAX_MESSAGE_LENGTH);
Util::addScript('comments', 'comments-app');
}
+
+ /**
+ * @inheritDoc
+ */
+ public function deleteCommentsExpiredAtObject(string $objectType, string $objectId = ''): bool {
+ $qb = $this->dbConn->getQueryBuilder();
+ $qb->delete('comments')
+ ->where($qb->expr()->lte('expire_date',
+ $qb->createNamedParameter($this->timeFactory->getDateTime(), IQueryBuilder::PARAM_DATE)))
+ ->andWhere($qb->expr()->eq('object_type', $qb->createNamedParameter($objectType)));
+
+ if ($objectId !== '') {
+ $qb->andWhere($qb->expr()->eq('object_id', $qb->createNamedParameter($objectId)));
+ }
+
+ $affectedRows = $qb->executeStatement();
+
+ $this->commentsCache = [];
+
+ return $affectedRows > 0;
+ }
}
diff --git a/lib/private/Config.php b/lib/private/Config.php
index b044d0731a3..37708357339 100644
--- a/lib/private/Config.php
+++ b/lib/private/Config.php
@@ -231,6 +231,14 @@ class Config {
unset($CONFIG);
include $file;
+ if (!defined('PHPUNIT_RUN') && headers_sent()) {
+ // syntax issues in the config file like leading spaces causing PHP to send output
+ $errorMessage = sprintf('Config file has leading content, please remove everything before "<?php" in %s', basename($file));
+ if (!defined('OC_CONSOLE')) {
+ print(\OCP\Util::sanitizeHTML($errorMessage));
+ }
+ throw new \Exception($errorMessage);
+ }
if (isset($CONFIG) && is_array($CONFIG)) {
$this->cache = array_merge($this->cache, $CONFIG);
}
diff --git a/lib/private/Console/Application.php b/lib/private/Console/Application.php
index 12d54b48fa9..fc48f57e499 100644
--- a/lib/private/Console/Application.php
+++ b/lib/private/Console/Application.php
@@ -34,6 +34,7 @@ use OC\MemoryInfo;
use OC\NeedsUpdateException;
use OC_App;
use OCP\AppFramework\QueryException;
+use OCP\App\IAppManager;
use OCP\Console\ConsoleEvent;
use OCP\IConfig;
use OCP\IRequest;
@@ -117,13 +118,14 @@ class Application {
$this->writeMaintenanceModeInfo($input, $output);
} else {
OC_App::loadApps();
- foreach (\OC::$server->getAppManager()->getInstalledApps() as $app) {
+ $appManager = \OCP\Server::get(IAppManager::class);
+ foreach ($appManager->getInstalledApps() as $app) {
$appPath = \OC_App::getAppPath($app);
if ($appPath === false) {
continue;
}
// load commands using info.xml
- $info = \OC_App::getAppInfo($app);
+ $info = $appManager->getAppInfo($app);
if (isset($info['commands'])) {
$this->loadCommandsFromInfoXml($info['commands']);
}
diff --git a/lib/private/Contacts/ContactsMenu/ContactsStore.php b/lib/private/Contacts/ContactsMenu/ContactsStore.php
index 020e8604910..dd4bd973fa9 100644
--- a/lib/private/Contacts/ContactsMenu/ContactsStore.php
+++ b/lib/private/Contacts/ContactsMenu/ContactsStore.php
@@ -123,7 +123,7 @@ class ContactsStore implements IContactsStore {
* 2. if the `shareapi_exclude_groups` config option is enabled and the
* current user is in an excluded group it will filter all local users.
* 3. if the `shareapi_only_share_with_group_members` config option is
- * enabled it will filter all users which doens't have a common group
+ * enabled it will filter all users which doesn't have a common group
* with the current user.
*
* @param IUser $self
@@ -150,7 +150,7 @@ class ContactsStore implements IContactsStore {
$selfGroups = $this->groupManager->getUserGroupIds($self);
if ($excludedGroups) {
- $excludedGroups = $this->config->getAppValue('core', 'shareapi_exclude_groups_list');
+ $excludedGroups = $this->config->getAppValue('core', 'shareapi_exclude_groups_list', '');
$decodedExcludeGroups = json_decode($excludedGroups, true);
$excludeGroupsList = $decodedExcludeGroups ?? [];
diff --git a/lib/private/ContactsManager.php b/lib/private/ContactsManager.php
index 937fb94a09a..68783e3f79b 100644
--- a/lib/private/ContactsManager.php
+++ b/lib/private/ContactsManager.php
@@ -6,6 +6,7 @@
* @author Joas Schilling <coding@schilljs.com>
* @author John Molakvoæ <skjnldsv@protonmail.com>
* @author Morris Jobke <hey@morrisjobke.de>
+ * @author Thomas Citharel <nextcloud@tcit.fr>
* @author Thomas Müller <thomas.mueller@tmit.eu>
* @author Tobia De Koninck <tobia@ledfan.be>
*
@@ -85,7 +86,7 @@ class ContactsManager implements IManager {
/**
* This function can be used to delete the contact identified by the given id
*
- * @param object $id the unique identifier to a contact
+ * @param int $id the unique identifier to a contact
* @param string $address_book_key identifier of the address book in which the contact shall be deleted
* @return bool successful or not
*/
diff --git a/lib/private/DB/MigrationService.php b/lib/private/DB/MigrationService.php
index 13bbe8dc5d0..4b7e4d3a040 100644
--- a/lib/private/DB/MigrationService.php
+++ b/lib/private/DB/MigrationService.php
@@ -45,35 +45,25 @@ use OCP\Migration\IOutput;
use Psr\Log\LoggerInterface;
class MigrationService {
-
- /** @var boolean */
- private $migrationTableCreated;
- /** @var array */
- private $migrations;
- /** @var IOutput */
- private $output;
- /** @var Connection */
- private $connection;
- /** @var string */
- private $appName;
- /** @var bool */
- private $checkOracle;
+ private bool $migrationTableCreated;
+ private array $migrations;
+ private string $migrationsPath;
+ private string $migrationsNamespace;
+ private IOutput $output;
+ private Connection $connection;
+ private string $appName;
+ private bool $checkOracle;
/**
- * MigrationService constructor.
- *
- * @param $appName
- * @param Connection $connection
- * @param AppLocator $appLocator
- * @param IOutput|null $output
* @throws \Exception
*/
- public function __construct($appName, Connection $connection, IOutput $output = null, AppLocator $appLocator = null) {
+ public function __construct($appName, Connection $connection, ?IOutput $output = null, ?AppLocator $appLocator = null) {
$this->appName = $appName;
$this->connection = $connection;
- $this->output = $output;
- if (null === $this->output) {
+ if ($output === null) {
$this->output = new SimpleOutput(\OC::$server->get(LoggerInterface::class), $appName);
+ } else {
+ $this->output = $output;
}
if ($appName === 'core') {
@@ -104,6 +94,7 @@ class MigrationService {
}
}
}
+ $this->migrationTableCreated = false;
}
/**
diff --git a/lib/private/DB/QueryBuilder/ExpressionBuilder/ExpressionBuilder.php b/lib/private/DB/QueryBuilder/ExpressionBuilder/ExpressionBuilder.php
index ae4f19f5d18..333984bde71 100644
--- a/lib/private/DB/QueryBuilder/ExpressionBuilder/ExpressionBuilder.php
+++ b/lib/private/DB/QueryBuilder/ExpressionBuilder/ExpressionBuilder.php
@@ -114,12 +114,12 @@ class ExpressionBuilder implements IExpressionBuilder {
* @param mixed|null $type one of the IQueryBuilder::PARAM_* constants
* required when comparing text fields for oci compatibility
*
- * @return string
+ * @return IQueryFunction
*/
- public function comparison($x, string $operator, $y, $type = null): string {
+ public function comparison($x, string $operator, $y, $type = null): IQueryFunction {
$x = $this->helper->quoteColumnName($x);
$y = $this->helper->quoteColumnName($y);
- return $this->expressionBuilder->comparison($x, $operator, $y);
+ return new QueryFunction($this->expressionBuilder->comparison($x, $operator, $y));
}
/**
@@ -137,12 +137,12 @@ class ExpressionBuilder implements IExpressionBuilder {
* @param mixed|null $type one of the IQueryBuilder::PARAM_* constants
* required when comparing text fields for oci compatibility
*
- * @return string
+ * @return IQueryFunction
*/
- public function eq($x, $y, $type = null): string {
+ public function eq($x, $y, $type = null): IQueryFunction {
$x = $this->helper->quoteColumnName($x);
$y = $this->helper->quoteColumnName($y);
- return $this->expressionBuilder->eq($x, $y);
+ return new QueryFunction($this->expressionBuilder->eq($x, $y));
}
/**
@@ -159,12 +159,12 @@ class ExpressionBuilder implements IExpressionBuilder {
* @param mixed|null $type one of the IQueryBuilder::PARAM_* constants
* required when comparing text fields for oci compatibility
*
- * @return string
+ * @return IQueryFunction
*/
- public function neq($x, $y, $type = null): string {
+ public function neq($x, $y, $type = null): IQueryFunction {
$x = $this->helper->quoteColumnName($x);
$y = $this->helper->quoteColumnName($y);
- return $this->expressionBuilder->neq($x, $y);
+ return new QueryFunction($this->expressionBuilder->neq($x, $y));
}
/**
@@ -181,12 +181,12 @@ class ExpressionBuilder implements IExpressionBuilder {
* @param mixed|null $type one of the IQueryBuilder::PARAM_* constants
* required when comparing text fields for oci compatibility
*
- * @return string
+ * @return IQueryFunction
*/
- public function lt($x, $y, $type = null): string {
+ public function lt($x, $y, $type = null): IQueryFunction {
$x = $this->helper->quoteColumnName($x);
$y = $this->helper->quoteColumnName($y);
- return $this->expressionBuilder->lt($x, $y);
+ return new QueryFunction($this->expressionBuilder->lt($x, $y));
}
/**
@@ -203,12 +203,12 @@ class ExpressionBuilder implements IExpressionBuilder {
* @param mixed|null $type one of the IQueryBuilder::PARAM_* constants
* required when comparing text fields for oci compatibility
*
- * @return string
+ * @return IQueryFunction
*/
- public function lte($x, $y, $type = null): string {
+ public function lte($x, $y, $type = null): IQueryFunction {
$x = $this->helper->quoteColumnName($x);
$y = $this->helper->quoteColumnName($y);
- return $this->expressionBuilder->lte($x, $y);
+ return new QueryFunction($this->expressionBuilder->lte($x, $y));
}
/**
@@ -225,12 +225,12 @@ class ExpressionBuilder implements IExpressionBuilder {
* @param mixed|null $type one of the IQueryBuilder::PARAM_* constants
* required when comparing text fields for oci compatibility
*
- * @return string
+ * @return IQueryFunction
*/
- public function gt($x, $y, $type = null): string {
+ public function gt($x, $y, $type = null): IQueryFunction {
$x = $this->helper->quoteColumnName($x);
$y = $this->helper->quoteColumnName($y);
- return $this->expressionBuilder->gt($x, $y);
+ return new QueryFunction($this->expressionBuilder->gt($x, $y));
}
/**
@@ -247,12 +247,12 @@ class ExpressionBuilder implements IExpressionBuilder {
* @param mixed|null $type one of the IQueryBuilder::PARAM_* constants
* required when comparing text fields for oci compatibility
*
- * @return string
+ * @return IQueryFunction
*/
- public function gte($x, $y, $type = null): string {
+ public function gte($x, $y, $type = null): IQueryFunction {
$x = $this->helper->quoteColumnName($x);
$y = $this->helper->quoteColumnName($y);
- return $this->expressionBuilder->gte($x, $y);
+ return new QueryFunction($this->expressionBuilder->gte($x, $y));
}
/**
@@ -260,11 +260,11 @@ class ExpressionBuilder implements IExpressionBuilder {
*
* @param string|ILiteral|IParameter|IQueryFunction $x The field in string format to be restricted by IS NULL.
*
- * @return string
+ * @return IQueryFunction
*/
- public function isNull($x): string {
+ public function isNull($x): IQueryFunction {
$x = $this->helper->quoteColumnName($x);
- return $this->expressionBuilder->isNull($x);
+ return new QueryFunction($this->expressionBuilder->isNull($x));
}
/**
@@ -272,11 +272,11 @@ class ExpressionBuilder implements IExpressionBuilder {
*
* @param string|ILiteral|IParameter|IQueryFunction $x The field in string format to be restricted by IS NOT NULL.
*
- * @return string
+ * @return IQueryFunction
*/
- public function isNotNull($x): string {
+ public function isNotNull($x): IQueryFunction {
$x = $this->helper->quoteColumnName($x);
- return $this->expressionBuilder->isNotNull($x);
+ return new QueryFunction($this->expressionBuilder->isNotNull($x));
}
/**
@@ -287,12 +287,12 @@ class ExpressionBuilder implements IExpressionBuilder {
* @param mixed|null $type one of the IQueryBuilder::PARAM_* constants
* required when comparing text fields for oci compatibility
*
- * @return string
+ * @return IQueryFunction
*/
- public function like($x, $y, $type = null): string {
+ public function like($x, $y, $type = null): IQueryFunction {
$x = $this->helper->quoteColumnName($x);
$y = $this->helper->quoteColumnName($y);
- return $this->expressionBuilder->like($x, $y);
+ return new QueryFunction($this->expressionBuilder->like($x, $y));
}
/**
@@ -303,11 +303,11 @@ class ExpressionBuilder implements IExpressionBuilder {
* @param mixed|null $type one of the IQueryBuilder::PARAM_* constants
* required when comparing text fields for oci compatibility
*
- * @return string
+ * @return IQueryFunction
* @since 9.0.0
*/
- public function iLike($x, $y, $type = null): string {
- return $this->expressionBuilder->like($this->functionBuilder->lower($x), $this->functionBuilder->lower($y));
+ public function iLike($x, $y, $type = null): IQueryFunction {
+ return new QueryFunction($this->expressionBuilder->like($this->functionBuilder->lower($x), $this->functionBuilder->lower($y)));
}
/**
@@ -318,12 +318,12 @@ class ExpressionBuilder implements IExpressionBuilder {
* @param mixed|null $type one of the IQueryBuilder::PARAM_* constants
* required when comparing text fields for oci compatibility
*
- * @return string
+ * @return IQueryFunction
*/
- public function notLike($x, $y, $type = null): string {
+ public function notLike($x, $y, $type = null): IQueryFunction {
$x = $this->helper->quoteColumnName($x);
$y = $this->helper->quoteColumnName($y);
- return $this->expressionBuilder->notLike($x, $y);
+ return new QueryFunction($this->expressionBuilder->notLike($x, $y));
}
/**
@@ -334,12 +334,12 @@ class ExpressionBuilder implements IExpressionBuilder {
* @param mixed|null $type one of the IQueryBuilder::PARAM_* constants
* required when comparing text fields for oci compatibility
*
- * @return string
+ * @return IQueryFunction
*/
- public function in($x, $y, $type = null): string {
+ public function in($x, $y, $type = null): IQueryFunction {
$x = $this->helper->quoteColumnName($x);
$y = $this->helper->quoteColumnNames($y);
- return $this->expressionBuilder->in($x, $y);
+ return new QueryFunction($this->expressionBuilder->in($x, $y));
}
/**
@@ -350,34 +350,34 @@ class ExpressionBuilder implements IExpressionBuilder {
* @param mixed|null $type one of the IQueryBuilder::PARAM_* constants
* required when comparing text fields for oci compatibility
*
- * @return string
+ * @return IQueryFunction
*/
- public function notIn($x, $y, $type = null): string {
+ public function notIn($x, $y, $type = null): IQueryFunction {
$x = $this->helper->quoteColumnName($x);
$y = $this->helper->quoteColumnNames($y);
- return $this->expressionBuilder->notIn($x, $y);
+ return new QueryFunction($this->expressionBuilder->notIn($x, $y));
}
/**
* Creates a $x = '' statement, because Oracle needs a different check
*
* @param string|ILiteral|IParameter|IQueryFunction $x The field in string format to be inspected by the comparison.
- * @return string
+ * @return IQueryFunction
* @since 13.0.0
*/
- public function emptyString($x): string {
- return $this->eq($x, $this->literal('', IQueryBuilder::PARAM_STR));
+ public function emptyString($x): IQueryFunction {
+ return new QueryFunction($this->eq($x, $this->literal('', IQueryBuilder::PARAM_STR)));
}
/**
* Creates a `$x <> ''` statement, because Oracle needs a different check
*
* @param string|ILiteral|IParameter|IQueryFunction $x The field in string format to be inspected by the comparison.
- * @return string
+ * @return IQueryFunction
* @since 13.0.0
*/
- public function nonEmptyString($x): string {
- return $this->neq($x, $this->literal('', IQueryBuilder::PARAM_STR));
+ public function nonEmptyString($x): IQueryFunction {
+ return new QueryFunction($this->neq($x, $this->literal('', IQueryBuilder::PARAM_STR)));
}
/**
diff --git a/lib/private/DB/QueryBuilder/ExpressionBuilder/MySqlExpressionBuilder.php b/lib/private/DB/QueryBuilder/ExpressionBuilder/MySqlExpressionBuilder.php
index 3bb54d4b26e..74209d0c3da 100644
--- a/lib/private/DB/QueryBuilder/ExpressionBuilder/MySqlExpressionBuilder.php
+++ b/lib/private/DB/QueryBuilder/ExpressionBuilder/MySqlExpressionBuilder.php
@@ -49,10 +49,10 @@ class MySqlExpressionBuilder extends ExpressionBuilder {
/**
* @inheritdoc
*/
- public function iLike($x, $y, $type = null): string {
+ public function iLike($x, $y, $type = null): IQueryFunction {
$x = $this->helper->quoteColumnName($x);
$y = $this->helper->quoteColumnName($y);
- return $this->expressionBuilder->comparison($x, ' COLLATE ' . $this->collation . ' LIKE', $y);
+ return new QueryFunction($this->expressionBuilder->comparison($x, ' COLLATE ' . $this->collation . ' LIKE', $y));
}
/**
diff --git a/lib/private/DB/QueryBuilder/ExpressionBuilder/OCIExpressionBuilder.php b/lib/private/DB/QueryBuilder/ExpressionBuilder/OCIExpressionBuilder.php
index f9b58d7d8ed..20d68b30b33 100644
--- a/lib/private/DB/QueryBuilder/ExpressionBuilder/OCIExpressionBuilder.php
+++ b/lib/private/DB/QueryBuilder/ExpressionBuilder/OCIExpressionBuilder.php
@@ -49,101 +49,101 @@ class OCIExpressionBuilder extends ExpressionBuilder {
/**
* @inheritdoc
*/
- public function comparison($x, string $operator, $y, $type = null): string {
+ public function comparison($x, string $operator, $y, $type = null): IQueryFunction {
$x = $this->prepareColumn($x, $type);
$y = $this->prepareColumn($y, $type);
- return $this->expressionBuilder->comparison($x, $operator, $y);
+ return new QueryFunction($this->expressionBuilder->comparison($x, $operator, $y));
}
/**
* @inheritdoc
*/
- public function eq($x, $y, $type = null): string {
+ public function eq($x, $y, $type = null): IQueryFunction {
$x = $this->prepareColumn($x, $type);
$y = $this->prepareColumn($y, $type);
- return $this->expressionBuilder->eq($x, $y);
+ return new QueryFunction($this->expressionBuilder->eq($x, $y));
}
/**
* @inheritdoc
*/
- public function neq($x, $y, $type = null): string {
+ public function neq($x, $y, $type = null): IQueryFunction {
$x = $this->prepareColumn($x, $type);
$y = $this->prepareColumn($y, $type);
- return $this->expressionBuilder->neq($x, $y);
+ return new QueryFunction($this->expressionBuilder->neq($x, $y));
}
/**
* @inheritdoc
*/
- public function lt($x, $y, $type = null): string {
+ public function lt($x, $y, $type = null): IQueryFunction {
$x = $this->prepareColumn($x, $type);
$y = $this->prepareColumn($y, $type);
- return $this->expressionBuilder->lt($x, $y);
+ return new QueryFunction($this->expressionBuilder->lt($x, $y));
}
/**
* @inheritdoc
*/
- public function lte($x, $y, $type = null): string {
+ public function lte($x, $y, $type = null): IQueryFunction {
$x = $this->prepareColumn($x, $type);
$y = $this->prepareColumn($y, $type);
- return $this->expressionBuilder->lte($x, $y);
+ return new QueryFunction($this->expressionBuilder->lte($x, $y));
}
/**
* @inheritdoc
*/
- public function gt($x, $y, $type = null): string {
+ public function gt($x, $y, $type = null): IQueryFunction {
$x = $this->prepareColumn($x, $type);
$y = $this->prepareColumn($y, $type);
- return $this->expressionBuilder->gt($x, $y);
+ return new QueryFunction($this->expressionBuilder->gt($x, $y));
}
/**
* @inheritdoc
*/
- public function gte($x, $y, $type = null): string {
+ public function gte($x, $y, $type = null): IQueryFunction {
$x = $this->prepareColumn($x, $type);
$y = $this->prepareColumn($y, $type);
- return $this->expressionBuilder->gte($x, $y);
+ return new QueryFunction($this->expressionBuilder->gte($x, $y));
}
/**
* @inheritdoc
*/
- public function in($x, $y, $type = null): string {
+ public function in($x, $y, $type = null): IQueryFunction {
$x = $this->prepareColumn($x, $type);
$y = $this->prepareColumn($y, $type);
- return $this->expressionBuilder->in($x, $y);
+ return new QueryFunction($this->expressionBuilder->in($x, $y));
}
/**
* @inheritdoc
*/
- public function notIn($x, $y, $type = null): string {
+ public function notIn($x, $y, $type = null): IQueryFunction {
$x = $this->prepareColumn($x, $type);
$y = $this->prepareColumn($y, $type);
- return $this->expressionBuilder->notIn($x, $y);
+ return new QueryFunction($this->expressionBuilder->notIn($x, $y));
}
/**
* Creates a $x = '' statement, because Oracle needs a different check
*
* @param string|ILiteral|IParameter|IQueryFunction $x The field in string format to be inspected by the comparison.
- * @return string
+ * @return IQueryFunction
* @since 13.0.0
*/
- public function emptyString($x): string {
+ public function emptyString($x): IQueryFunction {
return $this->isNull($x);
}
@@ -151,10 +151,10 @@ class OCIExpressionBuilder extends ExpressionBuilder {
* Creates a `$x <> ''` statement, because Oracle needs a different check
*
* @param string|ILiteral|IParameter|IQueryFunction $x The field in string format to be inspected by the comparison.
- * @return string
+ * @return IQueryFunction
* @since 13.0.0
*/
- public function nonEmptyString($x): string {
+ public function nonEmptyString($x): IQueryFunction {
return $this->isNotNull($x);
}
@@ -182,14 +182,14 @@ class OCIExpressionBuilder extends ExpressionBuilder {
/**
* @inheritdoc
*/
- public function like($x, $y, $type = null): string {
- return parent::like($x, $y, $type) . " ESCAPE '\\'";
+ public function like($x, $y, $type = null): IQueryFunction {
+ return new QueryFunction(parent::like($x, $y, $type) . " ESCAPE '\\'");
}
/**
* @inheritdoc
*/
- public function iLike($x, $y, $type = null): string {
+ public function iLike($x, $y, $type = null): IQueryFunction {
return $this->like($this->functionBuilder->lower($x), $this->functionBuilder->lower($y));
}
}
diff --git a/lib/private/DB/QueryBuilder/ExpressionBuilder/PgSqlExpressionBuilder.php b/lib/private/DB/QueryBuilder/ExpressionBuilder/PgSqlExpressionBuilder.php
index 0fba5363a28..cbebe97ae87 100644
--- a/lib/private/DB/QueryBuilder/ExpressionBuilder/PgSqlExpressionBuilder.php
+++ b/lib/private/DB/QueryBuilder/ExpressionBuilder/PgSqlExpressionBuilder.php
@@ -52,9 +52,9 @@ class PgSqlExpressionBuilder extends ExpressionBuilder {
/**
* @inheritdoc
*/
- public function iLike($x, $y, $type = null): string {
+ public function iLike($x, $y, $type = null): IQueryFunction {
$x = $this->helper->quoteColumnName($x);
$y = $this->helper->quoteColumnName($y);
- return $this->expressionBuilder->comparison($x, 'ILIKE', $y);
+ return new QueryFunction($this->expressionBuilder->comparison($x, 'ILIKE', $y));
}
}
diff --git a/lib/private/DB/QueryBuilder/ExpressionBuilder/SqliteExpressionBuilder.php b/lib/private/DB/QueryBuilder/ExpressionBuilder/SqliteExpressionBuilder.php
index 289aa09b003..5425138fa6c 100644
--- a/lib/private/DB/QueryBuilder/ExpressionBuilder/SqliteExpressionBuilder.php
+++ b/lib/private/DB/QueryBuilder/ExpressionBuilder/SqliteExpressionBuilder.php
@@ -23,15 +23,18 @@
*/
namespace OC\DB\QueryBuilder\ExpressionBuilder;
+use OC\DB\QueryBuilder\QueryFunction;
+use OCP\DB\QueryBuilder\IQueryFunction;
+
class SqliteExpressionBuilder extends ExpressionBuilder {
/**
* @inheritdoc
*/
- public function like($x, $y, $type = null): string {
- return parent::like($x, $y, $type) . " ESCAPE '\\'";
+ public function like($x, $y, $type = null): IQueryFunction {
+ return new QueryFunction(parent::like($x, $y, $type) . " ESCAPE '\\'");
}
- public function iLike($x, $y, $type = null): string {
- return $this->like($this->functionBuilder->lower($x), $this->functionBuilder->lower($y), $type);
+ public function iLike($x, $y, $type = null): IQueryFunction {
+ return new QueryFunction($this->like($this->functionBuilder->lower($x), $this->functionBuilder->lower($y), $type));
}
}
diff --git a/lib/private/DB/QueryBuilder/FunctionBuilder/FunctionBuilder.php b/lib/private/DB/QueryBuilder/FunctionBuilder/FunctionBuilder.php
index e0a7549a0ad..408a879d624 100644
--- a/lib/private/DB/QueryBuilder/FunctionBuilder/FunctionBuilder.php
+++ b/lib/private/DB/QueryBuilder/FunctionBuilder/FunctionBuilder.php
@@ -121,4 +121,15 @@ class FunctionBuilder implements IFunctionBuilder {
public function least($x, $y): IQueryFunction {
return new QueryFunction('LEAST(' . $this->helper->quoteColumnName($x) . ', ' . $this->helper->quoteColumnName($y) . ')');
}
+
+ public function case(array $whens, $else): IQueryFunction {
+ if (count($whens) < 1) {
+ return new QueryFunction($this->helper->quoteColumnName($else));
+ }
+
+ $whenParts = array_map(function (array $when) {
+ return 'WHEN ' . $this->helper->quoteColumnName($when['when']) . ' THEN ' . $this->helper->quoteColumnName($when['then']);
+ }, $whens);
+ return new QueryFunction('CASE ' . implode(' ', $whenParts) . ' ELSE ' . $this->helper->quoteColumnName($else) . ' END');
+ }
}
diff --git a/lib/private/DB/QueryBuilder/QueryBuilder.php b/lib/private/DB/QueryBuilder/QueryBuilder.php
index 71dd18fd68b..d991cbd1dd5 100644
--- a/lib/private/DB/QueryBuilder/QueryBuilder.php
+++ b/lib/private/DB/QueryBuilder/QueryBuilder.php
@@ -715,11 +715,12 @@ class QueryBuilder implements IQueryBuilder {
* @param string $fromAlias The alias that points to a from clause.
* @param string $join The table name to join.
* @param string $alias The alias of the join table.
- * @param string|ICompositeExpression|null $condition The condition for the join.
+ * @param string|IQueryFunction|ICompositeExpression|null $condition The condition for the join.
*
* @return $this This QueryBuilder instance.
*/
public function join($fromAlias, $join, $alias, $condition = null) {
+ $condition = $condition !== null ? (string)$condition : null;
$this->queryBuilder->join(
$this->quoteAlias($fromAlias),
$this->getTableName($join),
@@ -743,11 +744,12 @@ class QueryBuilder implements IQueryBuilder {
* @param string $fromAlias The alias that points to a from clause.
* @param string $join The table name to join.
* @param string $alias The alias of the join table.
- * @param string|ICompositeExpression|null $condition The condition for the join.
+ * @param string|IQueryFunction|ICompositeExpression|null $condition The condition for the join.
*
* @return $this This QueryBuilder instance.
*/
public function innerJoin($fromAlias, $join, $alias, $condition = null) {
+ $condition = $condition !== null ? (string)$condition : null;
$this->queryBuilder->innerJoin(
$this->quoteAlias($fromAlias),
$this->getTableName($join),
@@ -771,11 +773,12 @@ class QueryBuilder implements IQueryBuilder {
* @param string $fromAlias The alias that points to a from clause.
* @param string $join The table name to join.
* @param string $alias The alias of the join table.
- * @param string|ICompositeExpression|null $condition The condition for the join.
+ * @param string|IQueryFunction|ICompositeExpression|null $condition The condition for the join.
*
* @return $this This QueryBuilder instance.
*/
public function leftJoin($fromAlias, $join, $alias, $condition = null) {
+ $condition = $condition !== null ? (string)$condition : null;
$this->queryBuilder->leftJoin(
$this->quoteAlias($fromAlias),
$this->getTableName($join),
@@ -799,11 +802,12 @@ class QueryBuilder implements IQueryBuilder {
* @param string $fromAlias The alias that points to a from clause.
* @param string $join The table name to join.
* @param string $alias The alias of the join table.
- * @param string|ICompositeExpression|null $condition The condition for the join.
+ * @param string|IQueryFunction|ICompositeExpression|null $condition The condition for the join.
*
* @return $this This QueryBuilder instance.
*/
public function rightJoin($fromAlias, $join, $alias, $condition = null) {
+ $condition = $condition !== null ? (string)$condition : null;
$this->queryBuilder->rightJoin(
$this->quoteAlias($fromAlias),
$this->getTableName($join),
@@ -848,7 +852,7 @@ class QueryBuilder implements IQueryBuilder {
* ->from('users', 'u')
* ->where('u.id = ?');
*
- * // You can optionally programatically build and/or expressions
+ * // You can optionally programmatically build and/or expressions
* $qb = $conn->getQueryBuilder();
*
* $or = $qb->expr()->orx();
diff --git a/lib/private/Dashboard/Manager.php b/lib/private/Dashboard/Manager.php
index 09525693b4f..2aeedf3174e 100644
--- a/lib/private/Dashboard/Manager.php
+++ b/lib/private/Dashboard/Manager.php
@@ -27,10 +27,11 @@ declare(strict_types=1);
namespace OC\Dashboard;
use InvalidArgumentException;
-use OCP\AppFramework\QueryException;
+use OCP\App\IAppManager;
use OCP\Dashboard\IManager;
use OCP\Dashboard\IWidget;
-use OCP\IServerContainer;
+use Psr\Container\ContainerExceptionInterface;
+use Psr\Container\ContainerInterface;
use Throwable;
use Psr\Log\LoggerInterface;
@@ -42,10 +43,10 @@ class Manager implements IManager {
/** @var IWidget[] */
private $widgets = [];
- /** @var IServerContainer */
- private $serverContainer;
+ private ContainerInterface $serverContainer;
+ private ?IAppManager $appManager = null;
- public function __construct(IServerContainer $serverContainer) {
+ public function __construct(ContainerInterface $serverContainer) {
$this->serverContainer = $serverContainer;
}
@@ -57,17 +58,25 @@ class Manager implements IManager {
$this->widgets[$widget->getId()] = $widget;
}
- public function lazyRegisterWidget(string $widgetClass): void {
- $this->lazyWidgets[] = $widgetClass;
+ public function lazyRegisterWidget(string $widgetClass, string $appId): void {
+ $this->lazyWidgets[] = ['class' => $widgetClass, 'appId' => $appId];
}
public function loadLazyPanels(): void {
- $classes = $this->lazyWidgets;
- foreach ($classes as $class) {
+ if ($this->appManager === null) {
+ $this->appManager = $this->serverContainer->get(IAppManager::class);
+ }
+ $services = $this->lazyWidgets;
+ foreach ($services as $service) {
+ /** @psalm-suppress InvalidCatch */
try {
+ if (!$this->appManager->isEnabledForUser($service['appId'])) {
+ // all apps are registered, but some may not be enabled for the user
+ continue;
+ }
/** @var IWidget $widget */
- $widget = $this->serverContainer->query($class);
- } catch (QueryException $e) {
+ $widget = $this->serverContainer->get($service['class']);
+ } catch (ContainerExceptionInterface $e) {
/*
* There is a circular dependency between the logger and the registry, so
* we can not inject it. Thus the static call.
@@ -90,7 +99,7 @@ class Manager implements IManager {
*/
\OC::$server->get(LoggerInterface::class)->critical(
'Could not register lazy dashboard widget: ' . $e->getMessage(),
- ['excepiton' => $e]
+ ['exception' => $e]
);
}
@@ -111,7 +120,7 @@ class Manager implements IManager {
} catch (Throwable $e) {
\OC::$server->get(LoggerInterface::class)->critical(
'Error during dashboard widget loading: ' . $e->getMessage(),
- ['excepiton' => $e]
+ ['exception' => $e]
);
}
}
diff --git a/lib/private/Diagnostics/QueryLogger.php b/lib/private/Diagnostics/QueryLogger.php
index 40d68d94ae3..5f401751077 100644
--- a/lib/private/Diagnostics/QueryLogger.php
+++ b/lib/private/Diagnostics/QueryLogger.php
@@ -24,7 +24,7 @@
*/
namespace OC\Diagnostics;
-use OC\Cache\CappedMemoryCache;
+use OCP\Cache\CappedMemoryCache;
use OCP\Diagnostics\IQueryLogger;
class QueryLogger implements IQueryLogger {
diff --git a/lib/private/Encryption/File.php b/lib/private/Encryption/File.php
index 2d7e23a8883..87bc35bc159 100644
--- a/lib/private/Encryption/File.php
+++ b/lib/private/Encryption/File.php
@@ -27,29 +27,24 @@
*/
namespace OC\Encryption;
-use OC\Cache\CappedMemoryCache;
+use OCP\Cache\CappedMemoryCache;
use OCA\Files_External\Service\GlobalStoragesService;
+use OCP\App\IAppManager;
use OCP\Files\IRootFolder;
use OCP\Files\NotFoundException;
use OCP\Share\IManager;
class File implements \OCP\Encryption\IFile {
-
- /** @var Util */
- protected $util;
-
- /** @var IRootFolder */
- private $rootFolder;
-
- /** @var IManager */
- private $shareManager;
+ protected Util $util;
+ private IRootFolder $rootFolder;
+ private IManager $shareManager;
/**
- * cache results of already checked folders
- *
+ * Cache results of already checked folders
* @var CappedMemoryCache<array>
*/
protected CappedMemoryCache $cache;
+ private ?IAppManager $appManager = null;
public function __construct(Util $util,
IRootFolder $rootFolder,
@@ -60,6 +55,14 @@ class File implements \OCP\Encryption\IFile {
$this->shareManager = $shareManager;
}
+ public function getAppManager(): IAppManager {
+ // Lazy evaluate app manager as it initialize the db too early otherwise
+ if ($this->appManager) {
+ return $this->appManager;
+ }
+ $this->appManager = \OCP\Server::get(IAppManager::class);
+ return $this->appManager;
+ }
/**
* Get list of users with access to the file
@@ -110,7 +113,7 @@ class File implements \OCP\Encryption\IFile {
}
// check if it is a group mount
- if (\OCP\App::isEnabled("files_external")) {
+ if ($this->getAppManager()->isEnabledForUser("files_external")) {
/** @var GlobalStoragesService $storageService */
$storageService = \OC::$server->get(GlobalStoragesService::class);
$storages = $storageService->getAllStorages();
diff --git a/lib/private/Encryption/HookManager.php b/lib/private/Encryption/HookManager.php
index a2d6b990a88..5081bcccf94 100644
--- a/lib/private/Encryption/HookManager.php
+++ b/lib/private/Encryption/HookManager.php
@@ -25,39 +25,51 @@ namespace OC\Encryption;
use OC\Files\Filesystem;
use OC\Files\View;
+use OC\Files\SetupManager;
use Psr\Log\LoggerInterface;
class HookManager {
- /**
- * @var Update
- */
- private static $updater;
+ private static ?Update $updater = null;
- public static function postShared($params) {
+ public static function postShared($params): void {
self::getUpdate()->postShared($params);
}
- public static function postUnshared($params) {
- self::getUpdate()->postUnshared($params);
+ public static function postUnshared($params): void {
+ // In case the unsharing happens in a background job, we don't have
+ // a session and we load instead the user from the UserManager
+ $path = Filesystem::getPath($params['fileSource']);
+ $owner = Filesystem::getOwner($path);
+ self::getUpdate($owner)->postUnshared($params);
}
- public static function postRename($params) {
+ public static function postRename($params): void {
self::getUpdate()->postRename($params);
}
- public static function postRestore($params) {
+ public static function postRestore($params): void {
self::getUpdate()->postRestore($params);
}
- /**
- * @return Update
- */
- private static function getUpdate() {
+ private static function getUpdate(?string $owner = null): Update {
if (is_null(self::$updater)) {
$user = \OC::$server->getUserSession()->getUser();
+ if (!$user && $owner) {
+ $user = \OC::$server->getUserManager()->get($owner);
+ }
+ if (!$user) {
+ throw new \Exception("Inconsistent data, File unshared, but owner not found. Should not happen");
+ }
+
$uid = '';
if ($user) {
$uid = $user->getUID();
}
+
+ $setupManager = \OC::$server->get(SetupManager::class);
+ if (!$setupManager->isSetupComplete($user)) {
+ $setupManager->setupForUser($user);
+ }
+
self::$updater = new Update(
new View(),
new Util(
diff --git a/lib/private/Encryption/Util.php b/lib/private/Encryption/Util.php
index 174af2e8b89..410ea19da81 100644
--- a/lib/private/Encryption/Util.php
+++ b/lib/private/Encryption/Util.php
@@ -34,9 +34,12 @@ use OC\Files\Filesystem;
use OC\Files\View;
use OCA\Files_External\Lib\StorageConfig;
use OCA\Files_External\Service\GlobalStoragesService;
+use OCP\App\IAppManager;
use OCP\Encryption\IEncryptionModule;
use OCP\IConfig;
+use OCP\IGroupManager;
use OCP\IUser;
+use OCP\IUserManager;
class Util {
public const HEADER_START = 'HBEGIN';
@@ -65,29 +68,23 @@ class Util {
/** @var array */
protected $ocHeaderKeys;
- /** @var \OC\User\Manager */
- protected $userManager;
-
/** @var IConfig */
protected $config;
/** @var array paths excluded from encryption */
protected $excludedPaths;
-
- /** @var \OC\Group\Manager $manager */
- protected $groupManager;
+ protected IGroupManager $groupManager;
+ protected IUserManager $userManager;
/**
*
* @param View $rootView
- * @param \OC\User\Manager $userManager
- * @param \OC\Group\Manager $groupManager
* @param IConfig $config
*/
public function __construct(
View $rootView,
- \OC\User\Manager $userManager,
- \OC\Group\Manager $groupManager,
+ IUserManager $userManager,
+ IGroupManager $groupManager,
IConfig $config) {
$this->ocHeaderKeys = [
self::HEADER_ENCRYPTION_MODULE_KEY
@@ -275,7 +272,7 @@ class Util {
} else {
$result = array_merge($result, $users);
- $groupManager = \OC::$server->getGroupManager();
+ $groupManager = $this->groupManager;
foreach ($groups as $group) {
$groupObject = $groupManager->get($group);
if ($groupObject) {
@@ -299,7 +296,8 @@ class Util {
* @return boolean
*/
public function isSystemWideMountPoint($path, $uid) {
- if (\OCP\App::isEnabled("files_external")) {
+ // No DI here as this initialise the db too soon
+ if (\OCP\Server::get(IAppManager::class)->isEnabledForUser("files_external")) {
/** @var GlobalStoragesService $storageService */
$storageService = \OC::$server->get(GlobalStoragesService::class);
$storages = $storageService->getAllStorages();
@@ -377,32 +375,29 @@ class Util {
}
/**
- * check if recovery key is enabled for user
- *
- * @param string $uid
- * @return boolean
+ * Check if recovery key is enabled for user
*/
- public function recoveryEnabled($uid) {
+ public function recoveryEnabled(string $uid): bool {
$enabled = $this->config->getUserValue($uid, 'encryption', 'recovery_enabled', '0');
return $enabled === '1';
}
/**
- * set new key storage root
+ * Set new key storage root
*
* @param string $root new key store root relative to the data folder
*/
- public function setKeyStorageRoot($root) {
+ public function setKeyStorageRoot(string $root): void {
$this->config->setAppValue('core', 'encryption_key_storage_root', $root);
}
/**
- * get key storage root
+ * Get key storage root
*
* @return string key storage root
*/
- public function getKeyStorageRoot() {
+ public function getKeyStorageRoot(): string {
return $this->config->getAppValue('core', 'encryption_key_storage_root', '');
}
}
diff --git a/lib/private/Federation/CloudFederationProviderManager.php b/lib/private/Federation/CloudFederationProviderManager.php
index c25d4a40363..f077e36d97d 100644
--- a/lib/private/Federation/CloudFederationProviderManager.php
+++ b/lib/private/Federation/CloudFederationProviderManager.php
@@ -150,11 +150,12 @@ class CloudFederationProviderManager implements ICloudFederationProviderManager
return (is_array($result)) ? $result : [];
}
} catch (\Exception $e) {
+ $this->logger->debug($e->getMessage(), ['exception' => $e]);
+
// if flat re-sharing is not supported by the remote server
// we re-throw the exception and fall back to the old behaviour.
// (flat re-shares has been introduced in Nextcloud 9.1)
if ($e->getCode() === Http::STATUS_INTERNAL_SERVER_ERROR) {
- $this->logger->debug($e->getMessage(), ['exception' => $e]);
throw $e;
}
}
diff --git a/lib/private/Files/AppData/AppData.php b/lib/private/Files/AppData/AppData.php
index 471de799c2f..237fcb42e03 100644
--- a/lib/private/Files/AppData/AppData.php
+++ b/lib/private/Files/AppData/AppData.php
@@ -26,7 +26,7 @@ declare(strict_types=1);
*/
namespace OC\Files\AppData;
-use OC\Cache\CappedMemoryCache;
+use OCP\Cache\CappedMemoryCache;
use OC\Files\SimpleFS\SimpleFolder;
use OC\SystemConfig;
use OCP\Files\Folder;
diff --git a/lib/private/Files/Cache/Cache.php b/lib/private/Files/Cache/Cache.php
index 949079dfa22..f23635aa01b 100644
--- a/lib/private/Files/Cache/Cache.php
+++ b/lib/private/Files/Cache/Cache.php
@@ -37,6 +37,7 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>
*
*/
+
namespace OC\Files\Cache;
use Doctrine\DBAL\Exception\UniqueConstraintViolationException;
@@ -63,7 +64,7 @@ use Psr\Log\LoggerInterface;
/**
* Metadata cache for a storage
*
- * The cache stores the metadata for all files and folders in a storage and is kept up to date trough the following mechanisms:
+ * The cache stores the metadata for all files and folders in a storage and is kept up to date through the following mechanisms:
*
* - Scanner: scans the storage and updates the cache where needed
* - Watcher: checks for changes made to the filesystem outside of the Nextcloud instance and rescans files and folder when a change is detected
@@ -188,6 +189,7 @@ class Cache implements ICache {
$data['fileid'] = (int)$data['fileid'];
$data['parent'] = (int)$data['parent'];
$data['size'] = 0 + $data['size'];
+ $data['unencrypted_size'] = 0 + ($data['unencrypted_size'] ?? 0);
$data['mtime'] = (int)$data['mtime'];
$data['storage_mtime'] = (int)$data['storage_mtime'];
$data['encryptedVersion'] = (int)$data['encrypted'];
@@ -428,7 +430,7 @@ class Cache implements ICache {
protected function normalizeData(array $data): array {
$fields = [
'path', 'parent', 'name', 'mimetype', 'size', 'mtime', 'storage_mtime', 'encrypted',
- 'etag', 'permissions', 'checksum', 'storage'];
+ 'etag', 'permissions', 'checksum', 'storage', 'unencrypted_size'];
$extensionFields = ['metadata_etag', 'creation_time', 'upload_time'];
$doNotCopyStorageMTime = false;
@@ -538,7 +540,7 @@ class Cache implements ICache {
public function remove($file) {
$entry = $this->get($file);
- if ($entry) {
+ if ($entry instanceof ICacheEntry) {
$query = $this->getQueryBuilder();
$query->delete('filecache')
->whereFileId($entry->getId());
@@ -580,7 +582,7 @@ class Cache implements ICache {
$parentIds = [$entry->getId()];
$queue = [$entry->getId()];
- // we walk depth first trough the file tree, removing all filecache_extended attributes while we walk
+ // we walk depth first through the file tree, removing all filecache_extended attributes while we walk
// and collecting all folder ids to later use to delete the filecache entries
while ($entryId = array_pop($queue)) {
$children = $this->getFolderContentsById($entryId);
@@ -873,8 +875,16 @@ class Cache implements ICache {
$id = $entry['fileid'];
$query = $this->getQueryBuilder();
- $query->selectAlias($query->func()->sum('size'), 'f1')
- ->selectAlias($query->func()->min('size'), 'f2')
+ $query->selectAlias($query->func()->sum('size'), 'size_sum')
+ ->selectAlias($query->func()->min('size'), 'size_min')
+ // in case of encryption being enabled after some files are already uploaded, some entries will have an unencrypted_size of 0 and a non-zero size
+ ->selectAlias($query->func()->sum(
+ $query->func()->case([
+ ['when' => $query->expr()->eq('unencrypted_size', $query->expr()->literal(0, IQueryBuilder::PARAM_INT)), 'then' => 'size'],
+ ], 'unencrypted_size')
+ ), 'unencrypted_sum')
+ ->selectAlias($query->func()->min('unencrypted_size'), 'unencrypted_min')
+ ->selectAlias($query->func()->max('unencrypted_size'), 'unencrypted_max')
->from('filecache')
->whereStorageId($this->getNumericStorageId())
->whereParent($id);
@@ -884,7 +894,7 @@ class Cache implements ICache {
$result->closeCursor();
if ($row) {
- [$sum, $min] = array_values($row);
+ ['size_sum' => $sum, 'size_min' => $min, 'unencrypted_sum' => $unencryptedSum, 'unencrypted_min' => $unencryptedMin, 'unencrypted_max' => $unencryptedMax] = $row;
$sum = 0 + $sum;
$min = 0 + $min;
if ($min === -1) {
@@ -892,8 +902,23 @@ class Cache implements ICache {
} else {
$totalSize = $sum;
}
+ if ($unencryptedMin === -1 || $min === -1) {
+ $unencryptedTotal = $unencryptedMin;
+ } else {
+ $unencryptedTotal = $unencryptedSum;
+ }
if ($entry['size'] !== $totalSize) {
- $this->update($id, ['size' => $totalSize]);
+ // only set unencrypted size for a folder if any child entries have it set
+ if ($unencryptedMax > 0) {
+ $this->update($id, [
+ 'size' => $totalSize,
+ 'unencrypted_size' => $unencryptedTotal,
+ ]);
+ } else {
+ $this->update($id, [
+ 'size' => $totalSize,
+ ]);
+ }
}
}
}
@@ -927,7 +952,7 @@ class Cache implements ICache {
* use the one with the highest id gives the best result with the background scanner, since that is most
* likely the folder where we stopped scanning previously
*
- * @return string|bool the path of the folder or false when no folder matched
+ * @return string|false the path of the folder or false when no folder matched
*/
public function getIncomplete() {
$query = $this->getQueryBuilder();
diff --git a/lib/private/Files/Cache/CacheEntry.php b/lib/private/Files/Cache/CacheEntry.php
index 12f0273fb6e..8ac76acf6d1 100644
--- a/lib/private/Files/Cache/CacheEntry.php
+++ b/lib/private/Files/Cache/CacheEntry.php
@@ -132,4 +132,12 @@ class CacheEntry implements ICacheEntry {
public function __clone() {
$this->data = array_merge([], $this->data);
}
+
+ public function getUnencryptedSize(): int {
+ if (isset($this->data['unencrypted_size']) && $this->data['unencrypted_size'] > 0) {
+ return $this->data['unencrypted_size'];
+ } else {
+ return $this->data['size'];
+ }
+ }
}
diff --git a/lib/private/Files/Cache/CacheQueryBuilder.php b/lib/private/Files/Cache/CacheQueryBuilder.php
index b448424c1a8..496a8361d77 100644
--- a/lib/private/Files/Cache/CacheQueryBuilder.php
+++ b/lib/private/Files/Cache/CacheQueryBuilder.php
@@ -41,12 +41,16 @@ class CacheQueryBuilder extends QueryBuilder {
parent::__construct($connection, $systemConfig, $logger);
}
- public function selectFileCache(string $alias = null) {
+ public function selectFileCache(string $alias = null, bool $joinExtendedCache = true) {
$name = $alias ? $alias : 'filecache';
$this->select("$name.fileid", 'storage', 'path', 'path_hash', "$name.parent", "$name.name", 'mimetype', 'mimepart', 'size', 'mtime',
- 'storage_mtime', 'encrypted', 'etag', 'permissions', 'checksum', 'metadata_etag', 'creation_time', 'upload_time')
- ->from('filecache', $name)
- ->leftJoin($name, 'filecache_extended', 'fe', $this->expr()->eq("$name.fileid", 'fe.fileid'));
+ 'storage_mtime', 'encrypted', 'etag', 'permissions', 'checksum', 'unencrypted_size')
+ ->from('filecache', $name);
+
+ if ($joinExtendedCache) {
+ $this->addSelect('metadata_etag', 'creation_time', 'upload_time');
+ $this->leftJoin($name, 'filecache_extended', 'fe', $this->expr()->eq("$name.fileid", 'fe.fileid'));
+ }
$this->alias = $name;
diff --git a/lib/private/Files/Cache/Propagator.php b/lib/private/Files/Cache/Propagator.php
index 270b2b013f5..a0953baa785 100644
--- a/lib/private/Files/Cache/Propagator.php
+++ b/lib/private/Files/Cache/Propagator.php
@@ -24,6 +24,7 @@
namespace OC\Files\Cache;
+use OC\Files\Storage\Wrapper\Encryption;
use OCP\DB\QueryBuilder\IQueryBuilder;
use OCP\Files\Cache\IPropagator;
use OCP\Files\Storage\IReliableEtagStorage;
@@ -65,7 +66,7 @@ class Propagator implements IPropagator {
* @param int $sizeDifference number of bytes the file has grown
*/
public function propagateChange($internalPath, $time, $sizeDifference = 0) {
- // Do not propogate changes in ignored paths
+ // Do not propagate changes in ignored paths
foreach ($this->ignore as $ignore) {
if (strpos($internalPath, $ignore) === 0) {
return;
@@ -113,6 +114,20 @@ class Propagator implements IPropagator {
->andWhere($builder->expr()->in('path_hash', $hashParams))
->andWhere($builder->expr()->gt('size', $builder->expr()->literal(-1, IQueryBuilder::PARAM_INT)));
+ if ($this->storage->instanceOfStorage(Encryption::class)) {
+ // in case of encryption being enabled after some files are already uploaded, some entries will have an unencrypted_size of 0 and a non-zero size
+ $builder->set('unencrypted_size', $builder->func()->greatest(
+ $builder->func()->add(
+ $builder->func()->case([
+ ['when' => $builder->expr()->eq('unencrypted_size', $builder->expr()->literal(0, IQueryBuilder::PARAM_INT)), 'then' => 'size']
+ ], 'unencrypted_size'),
+ $builder->createNamedParameter($sizeDifference)
+ ),
+ $builder->createNamedParameter(-1, IQueryBuilder::PARAM_INT)
+ ));
+ }
+
+ $a = $builder->getSQL();
$builder->execute();
}
}
diff --git a/lib/private/Files/Cache/QuerySearchHelper.php b/lib/private/Files/Cache/QuerySearchHelper.php
index e7bccbf521c..3529ede9746 100644
--- a/lib/private/Files/Cache/QuerySearchHelper.php
+++ b/lib/private/Files/Cache/QuerySearchHelper.php
@@ -103,7 +103,7 @@ class QuerySearchHelper {
$builder = $this->getQueryBuilder();
- $query = $builder->selectFileCache('file');
+ $query = $builder->selectFileCache('file', false);
if ($this->searchBuilder->shouldJoinTags($searchQuery->getSearchOperation())) {
$user = $searchQuery->getUser();
@@ -158,7 +158,7 @@ class QuerySearchHelper {
$result->closeCursor();
- // loop trough all caches for each result to see if the result matches that storage
+ // loop through all caches for each result to see if the result matches that storage
// results are grouped by the same array keys as the caches argument to allow the caller to distringuish the source of the results
$results = array_fill_keys(array_keys($caches), []);
foreach ($rawEntries as $rawEntry) {
diff --git a/lib/private/Files/Cache/Storage.php b/lib/private/Files/Cache/Storage.php
index fb9e5500658..f77c9b71dd7 100644
--- a/lib/private/Files/Cache/Storage.php
+++ b/lib/private/Files/Cache/Storage.php
@@ -40,7 +40,7 @@ use Psr\Log\LoggerInterface;
* a string id which is generated by the storage backend and reflects the configuration of the storage (e.g. 'smb://user@host/share')
* and a numeric storage id which is referenced in the file cache
*
- * A mapping between the two storage ids is stored in the database and accessible trough this class
+ * A mapping between the two storage ids is stored in the database and accessible through this class
*
* @package OC\Files\Cache
*/
@@ -135,7 +135,7 @@ class Storage {
* Get the numeric of the storage with the provided string id
*
* @param $storageId
- * @return int|null either the numeric storage id or null if the storage id is not knwon
+ * @return int|null either the numeric storage id or null if the storage id is not known
*/
public static function getNumericStorageId($storageId) {
$storageId = self::adjustStorageId($storageId);
diff --git a/lib/private/Files/Cache/StorageGlobal.php b/lib/private/Files/Cache/StorageGlobal.php
index a898c435415..74cbd5abdb2 100644
--- a/lib/private/Files/Cache/StorageGlobal.php
+++ b/lib/private/Files/Cache/StorageGlobal.php
@@ -33,7 +33,7 @@ use OCP\IDBConnection;
* a string id which is generated by the storage backend and reflects the configuration of the storage (e.g. 'smb://user@host/share')
* and a numeric storage id which is referenced in the file cache
*
- * A mapping between the two storage ids is stored in the database and accessible trough this class
+ * A mapping between the two storage ids is stored in the database and accessible through this class
*
* @package OC\Files\Cache
*/
diff --git a/lib/private/Files/Cache/Updater.php b/lib/private/Files/Cache/Updater.php
index 98fb51fe264..f8c187996e6 100644
--- a/lib/private/Files/Cache/Updater.php
+++ b/lib/private/Files/Cache/Updater.php
@@ -73,14 +73,14 @@ class Updater implements IUpdater {
}
/**
- * Disable updating the cache trough this updater
+ * Disable updating the cache through this updater
*/
public function disable() {
$this->enabled = false;
}
/**
- * Re-enable the updating of the cache trough this updater
+ * Re-enable the updating of the cache through this updater
*/
public function enable() {
$this->enabled = true;
diff --git a/lib/private/Files/Cache/Wrapper/CacheJail.php b/lib/private/Files/Cache/Wrapper/CacheJail.php
index 7183a6c0d2a..4053042edd9 100644
--- a/lib/private/Files/Cache/Wrapper/CacheJail.php
+++ b/lib/private/Files/Cache/Wrapper/CacheJail.php
@@ -267,7 +267,7 @@ class CacheJail extends CacheWrapper {
* use the one with the highest id gives the best result with the background scanner, since that is most
* likely the folder where we stopped scanning previously
*
- * @return string|bool the path of the folder or false when no folder matched
+ * @return string|false the path of the folder or false when no folder matched
*/
public function getIncomplete() {
// not supported
diff --git a/lib/private/Files/Cache/Wrapper/CacheWrapper.php b/lib/private/Files/Cache/Wrapper/CacheWrapper.php
index e5300dc75f5..66ae83fd144 100644
--- a/lib/private/Files/Cache/Wrapper/CacheWrapper.php
+++ b/lib/private/Files/Cache/Wrapper/CacheWrapper.php
@@ -267,7 +267,7 @@ class CacheWrapper extends Cache {
* use the one with the highest id gives the best result with the background scanner, since that is most
* likely the folder where we stopped scanning previously
*
- * @return string|bool the path of the folder or false when no folder matched
+ * @return string|false the path of the folder or false when no folder matched
*/
public function getIncomplete() {
return $this->getCache()->getIncomplete();
diff --git a/lib/private/Files/Config/UserMountCache.php b/lib/private/Files/Config/UserMountCache.php
index c326eeb0b6c..685057a7860 100644
--- a/lib/private/Files/Config/UserMountCache.php
+++ b/lib/private/Files/Config/UserMountCache.php
@@ -28,7 +28,7 @@
*/
namespace OC\Files\Config;
-use OC\Cache\CappedMemoryCache;
+use OCP\Cache\CappedMemoryCache;
use OCA\Files_Sharing\SharedMount;
use OCP\DB\QueryBuilder\IQueryBuilder;
use OCP\Files\Config\ICachedMountFileInfo;
@@ -42,7 +42,7 @@ use OCP\IUserManager;
use Psr\Log\LoggerInterface;
/**
- * Cache mounts points per user in the cache so we can easilly look them up
+ * Cache mounts points per user in the cache so we can easily look them up
*/
class UserMountCache implements IUserMountCache {
private IDBConnection $connection;
diff --git a/lib/private/Files/FileInfo.php b/lib/private/Files/FileInfo.php
index 6389544184f..47c893ebbf1 100644
--- a/lib/private/Files/FileInfo.php
+++ b/lib/private/Files/FileInfo.php
@@ -101,7 +101,11 @@ class FileInfo implements \OCP\Files\FileInfo, \ArrayAccess {
$this->data = $data;
$this->mount = $mount;
$this->owner = $owner;
- $this->rawSize = $this->data['size'] ?? 0;
+ if (isset($this->data['unencrypted_size']) && $this->data['unencrypted_size'] !== 0) {
+ $this->rawSize = $this->data['unencrypted_size'];
+ } else {
+ $this->rawSize = $this->data['size'] ?? 0;
+ }
}
public function offsetSet($offset, $value): void {
@@ -208,7 +212,12 @@ class FileInfo implements \OCP\Files\FileInfo, \ArrayAccess {
public function getSize($includeMounts = true) {
if ($includeMounts) {
$this->updateEntryfromSubMounts();
- return isset($this->data['size']) ? 0 + $this->data['size'] : 0;
+
+ if (isset($this->data['unencrypted_size']) && $this->data['unencrypted_size'] > 0) {
+ return $this->data['unencrypted_size'];
+ } else {
+ return isset($this->data['size']) ? 0 + $this->data['size'] : 0;
+ }
} else {
return $this->rawSize;
}
@@ -386,14 +395,26 @@ class FileInfo implements \OCP\Files\FileInfo, \ArrayAccess {
* @param string $entryPath full path of the child entry
*/
public function addSubEntry($data, $entryPath) {
- $this->data['size'] += isset($data['size']) ? $data['size'] : 0;
+ if (!$data) {
+ return;
+ }
+ $hasUnencryptedSize = isset($data['unencrypted_size']) && $data['unencrypted_size'] > 0;
+ if ($hasUnencryptedSize) {
+ $subSize = $data['unencrypted_size'];
+ } else {
+ $subSize = $data['size'] ?: 0;
+ }
+ $this->data['size'] += $subSize;
+ if ($hasUnencryptedSize) {
+ $this->data['unencrypted_size'] += $subSize;
+ }
if (isset($data['mtime'])) {
$this->data['mtime'] = max($this->data['mtime'], $data['mtime']);
}
if (isset($data['etag'])) {
// prefix the etag with the relative path of the subentry to propagate etag on mount moves
$relativeEntryPath = substr($entryPath, strlen($this->getPath()));
- // attach the permissions to propagate etag on permision changes of submounts
+ // attach the permissions to propagate etag on permission changes of submounts
$permissions = isset($data['permissions']) ? $data['permissions'] : 0;
$this->childEtags[] = $relativeEntryPath . '/' . $data['etag'] . $permissions;
}
diff --git a/lib/private/Files/Filesystem.php b/lib/private/Files/Filesystem.php
index 20b44e2736a..9542666b03c 100644
--- a/lib/private/Files/Filesystem.php
+++ b/lib/private/Files/Filesystem.php
@@ -37,7 +37,7 @@
*/
namespace OC\Files;
-use OC\Cache\CappedMemoryCache;
+use OCP\Cache\CappedMemoryCache;
use OC\Files\Mount\MountPoint;
use OC\User\NoUserException;
use OCP\EventDispatcher\IEventDispatcher;
diff --git a/lib/private/Files/Mount/Manager.php b/lib/private/Files/Mount/Manager.php
index 69285018d17..9ba0e504058 100644
--- a/lib/private/Files/Mount/Manager.php
+++ b/lib/private/Files/Mount/Manager.php
@@ -29,7 +29,7 @@ declare(strict_types=1);
namespace OC\Files\Mount;
-use OC\Cache\CappedMemoryCache;
+use OCP\Cache\CappedMemoryCache;
use OC\Files\Filesystem;
use OC\Files\SetupManager;
use OC\Files\SetupManagerFactory;
diff --git a/lib/private/Files/Node/File.php b/lib/private/Files/Node/File.php
index e125715e6a8..d8a6741dc6e 100644
--- a/lib/private/Files/Node/File.php
+++ b/lib/private/Files/Node/File.php
@@ -131,7 +131,6 @@ class File extends Node implements \OCP\Files\File {
$this->view->unlink($this->path);
$nonExisting = new NonExistingFile($this->root, $this->view, $this->path, $fileInfo);
$this->sendHooks(['postDelete'], [$nonExisting]);
- $this->exists = false;
$this->fileInfo = null;
} else {
throw new NotPermittedException();
diff --git a/lib/private/Files/Node/Folder.php b/lib/private/Files/Node/Folder.php
index 9c15f0edf41..42562c99bcb 100644
--- a/lib/private/Files/Node/Folder.php
+++ b/lib/private/Files/Node/Folder.php
@@ -262,7 +262,7 @@ class Folder extends Node implements \OCP\Files\Folder {
$searchHelper = \OC::$server->get(QuerySearchHelper::class);
$resultsPerCache = $searchHelper->searchInCaches($query, $caches);
- // loop trough all results per-cache, constructing the FileInfo object from the CacheEntry and merge them all
+ // loop through all results per-cache, constructing the FileInfo object from the CacheEntry and merge them all
$files = array_merge(...array_map(function (array $results, $relativeMountPoint) use ($mountByMountPoint) {
$mount = $mountByMountPoint[$relativeMountPoint];
return array_map(function (ICacheEntry $result) use ($relativeMountPoint, $mount) {
@@ -388,7 +388,6 @@ class Folder extends Node implements \OCP\Files\Folder {
$this->view->rmdir($this->path);
$nonExisting = new NonExistingFolder($this->root, $this->view, $this->path, $fileInfo);
$this->sendHooks(['postDelete'], [$nonExisting]);
- $this->exists = false;
} else {
throw new NotPermittedException('No delete permission for path');
}
diff --git a/lib/private/Files/Node/Root.php b/lib/private/Files/Node/Root.php
index 6dd65a4291d..ca930c1002c 100644
--- a/lib/private/Files/Node/Root.php
+++ b/lib/private/Files/Node/Root.php
@@ -32,7 +32,7 @@
namespace OC\Files\Node;
-use OC\Cache\CappedMemoryCache;
+use OCP\Cache\CappedMemoryCache;
use OC\Files\FileInfo;
use OC\Files\Mount\Manager;
use OC\Files\Mount\MountPoint;
@@ -427,7 +427,7 @@ class Root extends Folder implements IRootFolder {
$mountsContainingFile = $mountCache->getMountsForFileId($id, $user);
}
- // when a user has access trough the same storage trough multiple paths
+ // when a user has access through the same storage through multiple paths
// (such as an external storage that is both mounted for a user and shared to the user)
// the mount cache will only hold a single entry for the storage
// this can lead to issues as the different ways the user has access to a storage can have different permissions
diff --git a/lib/private/Files/ObjectStore/S3ConnectionTrait.php b/lib/private/Files/ObjectStore/S3ConnectionTrait.php
index e00e3332b82..bdda1f8bee8 100644
--- a/lib/private/Files/ObjectStore/S3ConnectionTrait.php
+++ b/lib/private/Files/ObjectStore/S3ConnectionTrait.php
@@ -29,6 +29,7 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
+
namespace OC\Files\ObjectStore;
use Aws\ClientResolver;
@@ -122,15 +123,6 @@ trait S3ConnectionTrait {
)
);
- // since we store the certificate bundles on the primary storage, we can't get the bundle while setting up the primary storage
- if (!isset($this->params['primary_storage'])) {
- /** @var ICertificateManager $certManager */
- $certManager = \OC::$server->get(ICertificateManager::class);
- $certPath = $certManager->getAbsoluteBundlePath();
- } else {
- $certPath = \OC::$SERVERROOT . '/resources/config/ca-bundle.crt';
- }
-
$options = [
'version' => isset($this->params['version']) ? $this->params['version'] : 'latest',
'credentials' => $provider,
@@ -140,7 +132,7 @@ trait S3ConnectionTrait {
'signature_provider' => \Aws\or_chain([self::class, 'legacySignatureProvider'], ClientResolver::_default_signature_provider()),
'csm' => false,
'use_arn_region' => false,
- 'http' => ['verify' => $certPath],
+ 'http' => ['verify' => $this->getCertificateBundlePath()],
'use_aws_shared_config_files' => false,
];
if ($this->getProxy()) {
@@ -154,7 +146,7 @@ trait S3ConnectionTrait {
if (!$this->connection::isBucketDnsCompatible($this->bucket)) {
$logger = \OC::$server->get(LoggerInterface::class);
$logger->debug('Bucket "' . $this->bucket . '" This bucket name is not dns compatible, it may contain invalid characters.',
- ['app' => 'objectstore']);
+ ['app' => 'objectstore']);
}
if ($this->params['verify_bucket_exists'] && !$this->connection->doesBucketExist($this->bucket)) {
@@ -205,7 +197,7 @@ trait S3ConnectionTrait {
/**
* This function creates a credential provider based on user parameter file
*/
- protected function paramCredentialProvider() : callable {
+ protected function paramCredentialProvider(): callable {
return function () {
$key = empty($this->params['key']) ? null : $this->params['key'];
$secret = empty($this->params['secret']) ? null : $this->params['secret'];
@@ -220,4 +212,19 @@ trait S3ConnectionTrait {
return new RejectedPromise(new CredentialsException($msg));
};
}
+
+ protected function getCertificateBundlePath(): ?string {
+ if ((int)($this->params['use_nextcloud_bundle'] ?? "0")) {
+ // since we store the certificate bundles on the primary storage, we can't get the bundle while setting up the primary storage
+ if (!isset($this->params['primary_storage'])) {
+ /** @var ICertificateManager $certManager */
+ $certManager = \OC::$server->get(ICertificateManager::class);
+ return $certManager->getAbsoluteBundlePath();
+ } else {
+ return \OC::$SERVERROOT . '/resources/config/ca-bundle.crt';
+ }
+ } else {
+ return null;
+ }
+ }
}
diff --git a/lib/private/Files/ObjectStore/S3ObjectTrait.php b/lib/private/Files/ObjectStore/S3ObjectTrait.php
index 4e54a26e98a..9d692e01a23 100644
--- a/lib/private/Files/ObjectStore/S3ObjectTrait.php
+++ b/lib/private/Files/ObjectStore/S3ObjectTrait.php
@@ -43,6 +43,8 @@ trait S3ObjectTrait {
*/
abstract protected function getConnection();
+ abstract protected function getCertificateBundlePath(): ?string;
+
/**
* @param string $urn the unified resource name used to identify the object
* @return resource stream with the read data
@@ -67,8 +69,14 @@ trait S3ObjectTrait {
'http' => [
'protocol_version' => $request->getProtocolVersion(),
'header' => $headers,
- ],
+ ]
];
+ $bundle = $this->getCertificateBundlePath();
+ if ($bundle) {
+ $opts['ssl'] = [
+ 'cafile' => $bundle
+ ];
+ }
if ($this->getProxy()) {
$opts['http']['proxy'] = $this->getProxy();
diff --git a/lib/private/Files/SetupManager.php b/lib/private/Files/SetupManager.php
index 040ba6b898f..5782a5a72a6 100644
--- a/lib/private/Files/SetupManager.php
+++ b/lib/private/Files/SetupManager.php
@@ -82,6 +82,7 @@ class SetupManager {
private IConfig $config;
private bool $listeningForProviders;
private array $fullSetupRequired = [];
+ private bool $setupBuiltinWrappersDone = false;
public function __construct(
IEventLogger $eventLogger,
@@ -121,6 +122,15 @@ class SetupManager {
}
private function setupBuiltinWrappers() {
+ if ($this->setupBuiltinWrappersDone) {
+ return;
+ }
+ $this->setupBuiltinWrappersDone = true;
+
+ // load all filesystem apps before, so no setup-hook gets lost
+ OC_App::loadApps(['filesystem']);
+ $prevLogging = Filesystem::logWarningWhenAddingStorageWrapper(false);
+
Filesystem::addStorageWrapper('mount_options', function ($mountPoint, IStorage $storage, IMountPoint $mount) {
if ($storage->instanceOfStorage(Common::class)) {
$storage->setMountOptions($mount->getOptions());
@@ -188,6 +198,8 @@ class SetupManager {
}
return $storage;
});
+
+ Filesystem::logWarningWhenAddingStorageWrapper($prevLogging);
}
/**
@@ -223,6 +235,9 @@ class SetupManager {
return;
}
$this->setupUsers[] = $user->getUID();
+
+ $this->setupBuiltinWrappers();
+
$prevLogging = Filesystem::logWarningWhenAddingStorageWrapper(false);
OC_Hook::emit('OC_Filesystem', 'preSetup', ['user' => $user->getUID()]);
@@ -321,14 +336,8 @@ class SetupManager {
$this->eventLogger->start('setup_root_fs', 'Setup root filesystem');
- // load all filesystem apps before, so no setup-hook gets lost
- OC_App::loadApps(['filesystem']);
- $prevLogging = Filesystem::logWarningWhenAddingStorageWrapper(false);
-
$this->setupBuiltinWrappers();
- Filesystem::logWarningWhenAddingStorageWrapper($prevLogging);
-
$rootMounts = $this->mountProviderCollection->getRootMounts();
foreach ($rootMounts as $rootMountProvider) {
$this->mountManager->addMount($rootMountProvider);
@@ -380,13 +389,9 @@ class SetupManager {
return;
}
- // for the user's home folder, it's always the home mount
- if (rtrim($path) === "/" . $user->getUID() . "/files") {
- if ($includeChildren) {
- $this->setupForUser($user);
- } else {
- $this->oneTimeUserSetup($user);
- }
+ // for the user's home folder, and includes children we need everything always
+ if (rtrim($path) === "/" . $user->getUID() . "/files" && $includeChildren) {
+ $this->setupForUser($user);
return;
}
@@ -403,6 +408,10 @@ class SetupManager {
return;
}
+ if (!$this->isSetupStarted($user)) {
+ $this->oneTimeUserSetup($user);
+ }
+
$mounts = [];
if (!in_array($cachedMount->getMountProvider(), $setupProviders)) {
$setupProviders[] = $cachedMount->getMountProvider();
@@ -554,10 +563,10 @@ class SetupManager {
});
$genericEvents = [
- '\OCA\Circles::onCircleCreation',
- '\OCA\Circles::onCircleDestruction',
- '\OCA\Circles::onMemberNew',
- '\OCA\Circles::onMemberLeaving',
+ 'OCA\Circles\Events\CreatingCircleEvent',
+ 'OCA\Circles\Events\DestroyingCircleEvent',
+ 'OCA\Circles\Events\AddingCircleMemberEvent',
+ 'OCA\Circles\Events\RemovingCircleMemberEvent',
];
foreach ($genericEvents as $genericEvent) {
diff --git a/lib/private/Files/SimpleFS/NewSimpleFile.php b/lib/private/Files/SimpleFS/NewSimpleFile.php
index 76fc69ebbe7..b2a183b7d29 100644
--- a/lib/private/Files/SimpleFS/NewSimpleFile.php
+++ b/lib/private/Files/SimpleFS/NewSimpleFile.php
@@ -34,15 +34,12 @@ use OCP\Files\NotPermittedException;
use OCP\Files\SimpleFS\ISimpleFile;
class NewSimpleFile implements ISimpleFile {
- private $parentFolder;
- private $name;
- /** @var File|null */
- private $file = null;
+ private Folder $parentFolder;
+ private string $name;
+ private ?File $file = null;
/**
* File constructor.
- *
- * @param File $file
*/
public function __construct(Folder $parentFolder, string $name) {
$this->parentFolder = $parentFolder;
@@ -51,19 +48,15 @@ class NewSimpleFile implements ISimpleFile {
/**
* Get the name
- *
- * @return string
*/
- public function getName() {
+ public function getName(): string {
return $this->name;
}
/**
* Get the size in bytes
- *
- * @return int
*/
- public function getSize() {
+ public function getSize(): int {
if ($this->file) {
return $this->file->getSize();
} else {
@@ -73,10 +66,8 @@ class NewSimpleFile implements ISimpleFile {
/**
* Get the ETag
- *
- * @return string
*/
- public function getETag() {
+ public function getETag(): string {
if ($this->file) {
return $this->file->getEtag();
} else {
@@ -86,10 +77,8 @@ class NewSimpleFile implements ISimpleFile {
/**
* Get the last modification time
- *
- * @return int
*/
- public function getMTime() {
+ public function getMTime(): int {
if ($this->file) {
return $this->file->getMTime();
} else {
@@ -100,11 +89,10 @@ class NewSimpleFile implements ISimpleFile {
/**
* Get the content
*
- * @return string
* @throws NotFoundException
* @throws NotPermittedException
*/
- public function getContent() {
+ public function getContent(): string {
if ($this->file) {
$result = $this->file->getContent();
@@ -125,7 +113,7 @@ class NewSimpleFile implements ISimpleFile {
* @throws NotPermittedException
* @throws NotFoundException
*/
- public function putContent($data) {
+ public function putContent($data): void {
try {
if ($this->file) {
$this->file->putContent($data);
@@ -139,7 +127,7 @@ class NewSimpleFile implements ISimpleFile {
/**
* Sometimes there are some issues with the AppData. Most of them are from
- * user error. But we should handle them gracefull anyway.
+ * user error. But we should handle them gracefully anyway.
*
* If for some reason the current file can't be found. We remove it.
* Then traverse up and check all folders if they exists. This so that the
@@ -147,7 +135,7 @@ class NewSimpleFile implements ISimpleFile {
*
* @throws NotFoundException
*/
- private function checkFile() {
+ private function checkFile(): void {
$cur = $this->file;
while ($cur->stat() === false) {
@@ -171,7 +159,7 @@ class NewSimpleFile implements ISimpleFile {
*
* @throws NotPermittedException
*/
- public function delete() {
+ public function delete(): void {
if ($this->file) {
$this->file->delete();
}
@@ -182,7 +170,7 @@ class NewSimpleFile implements ISimpleFile {
*
* @return string
*/
- public function getMimeType() {
+ public function getMimeType(): string {
if ($this->file) {
return $this->file->getMimeType();
} else {
diff --git a/lib/private/Files/SimpleFS/SimpleFile.php b/lib/private/Files/SimpleFS/SimpleFile.php
index 21a2fd92dcb..a2571ac50e8 100644
--- a/lib/private/Files/SimpleFS/SimpleFile.php
+++ b/lib/private/Files/SimpleFS/SimpleFile.php
@@ -30,52 +30,37 @@ use OCP\Files\NotPermittedException;
use OCP\Files\SimpleFS\ISimpleFile;
class SimpleFile implements ISimpleFile {
+ private File $file;
- /** @var File $file */
- private $file;
-
- /**
- * File constructor.
- *
- * @param File $file
- */
public function __construct(File $file) {
$this->file = $file;
}
/**
* Get the name
- *
- * @return string
*/
- public function getName() {
+ public function getName(): string {
return $this->file->getName();
}
/**
* Get the size in bytes
- *
- * @return int
*/
- public function getSize() {
+ public function getSize(): int {
return $this->file->getSize();
}
/**
* Get the ETag
- *
- * @return string
*/
- public function getETag() {
+ public function getETag(): string {
return $this->file->getEtag();
}
/**
* Get the last modification time
- *
- * @return int
*/
- public function getMTime() {
+ public function getMTime(): int {
return $this->file->getMTime();
}
@@ -84,9 +69,8 @@ class SimpleFile implements ISimpleFile {
*
* @throws NotPermittedException
* @throws NotFoundException
- * @return string
*/
- public function getContent() {
+ public function getContent(): string {
$result = $this->file->getContent();
if ($result === false) {
@@ -103,9 +87,9 @@ class SimpleFile implements ISimpleFile {
* @throws NotPermittedException
* @throws NotFoundException
*/
- public function putContent($data) {
+ public function putContent($data): void {
try {
- return $this->file->putContent($data);
+ $this->file->putContent($data);
} catch (NotFoundException $e) {
$this->checkFile();
}
@@ -113,7 +97,7 @@ class SimpleFile implements ISimpleFile {
/**
* Sometimes there are some issues with the AppData. Most of them are from
- * user error. But we should handle them gracefull anyway.
+ * user error. But we should handle them gracefully anyway.
*
* If for some reason the current file can't be found. We remove it.
* Then traverse up and check all folders if they exists. This so that the
@@ -121,7 +105,7 @@ class SimpleFile implements ISimpleFile {
*
* @throws NotFoundException
*/
- private function checkFile() {
+ private function checkFile(): void {
$cur = $this->file;
while ($cur->stat() === false) {
@@ -145,16 +129,14 @@ class SimpleFile implements ISimpleFile {
*
* @throws NotPermittedException
*/
- public function delete() {
+ public function delete(): void {
$this->file->delete();
}
/**
* Get the MimeType
- *
- * @return string
*/
- public function getMimeType() {
+ public function getMimeType(): string {
return $this->file->getMimeType();
}
@@ -179,7 +161,7 @@ class SimpleFile implements ISimpleFile {
/**
* Open the file as stream for writing, resulting resource can be operated as stream like the result from php's own fopen
*
- * @return resource
+ * @return resource|false
* @throws \OCP\Files\NotPermittedException
* @since 14.0.0
*/
diff --git a/lib/private/Files/SimpleFS/SimpleFolder.php b/lib/private/Files/SimpleFS/SimpleFolder.php
index cd2a712019e..263c25a8873 100644
--- a/lib/private/Files/SimpleFS/SimpleFolder.php
+++ b/lib/private/Files/SimpleFS/SimpleFolder.php
@@ -29,6 +29,7 @@ use OCP\Files\Folder;
use OCP\Files\Node;
use OCP\Files\NotFoundException;
use OCP\Files\SimpleFS\ISimpleFolder;
+use OCP\Files\SimpleFS\ISimpleFile;
class SimpleFolder implements ISimpleFolder {
@@ -44,11 +45,11 @@ class SimpleFolder implements ISimpleFolder {
$this->folder = $folder;
}
- public function getName() {
+ public function getName(): string {
return $this->folder->getName();
}
- public function getDirectoryListing() {
+ public function getDirectoryListing(): array {
$listing = $this->folder->getDirectoryListing();
$fileListing = array_map(function (Node $file) {
@@ -63,15 +64,15 @@ class SimpleFolder implements ISimpleFolder {
return array_values($fileListing);
}
- public function delete() {
+ public function delete(): void {
$this->folder->delete();
}
- public function fileExists($name) {
+ public function fileExists(string $name): bool {
return $this->folder->nodeExists($name);
}
- public function getFile($name) {
+ public function getFile(string $name): ISimpleFile {
$file = $this->folder->get($name);
if (!($file instanceof File)) {
@@ -81,7 +82,7 @@ class SimpleFolder implements ISimpleFolder {
return new SimpleFile($file);
}
- public function newFile($name, $content = null) {
+ public function newFile(string $name, $content = null): ISimpleFile {
if ($content === null) {
// delay creating the file until it's written to
return new NewSimpleFile($this->folder, $name);
diff --git a/lib/private/Files/Storage/Common.php b/lib/private/Files/Storage/Common.php
index 3c970ee75f5..a7bc44e10e2 100644
--- a/lib/private/Files/Storage/Common.php
+++ b/lib/private/Files/Storage/Common.php
@@ -228,6 +228,7 @@ abstract class Common implements Storage, ILockingStorage, IWriteStreamStorage {
while ($file = readdir($dir)) {
if (!Filesystem::isIgnoredDir($file)) {
if (!$this->copy($path1 . '/' . $file, $path2 . '/' . $file)) {
+ closedir($dir);
return false;
}
}
diff --git a/lib/private/Files/Storage/Local.php b/lib/private/Files/Storage/Local.php
index ee8a8c7d161..4996572a40e 100644
--- a/lib/private/Files/Storage/Local.php
+++ b/lib/private/Files/Storage/Local.php
@@ -15,6 +15,7 @@
* @author Jörn Friedrich Dreyer <jfd@butonic.de>
* @author Klaas Freitag <freitag@owncloud.com>
* @author Lukas Reschke <lukas@statuscode.ch>
+ * @author Martin Brugnara <martin@0x6d62.eu>
* @author Michael Gapczynski <GapczynskiM@gmail.com>
* @author Morris Jobke <hey@morrisjobke.de>
* @author Robin Appelman <robin@icewind.nl>
@@ -66,6 +67,8 @@ class Local extends \OC\Files\Storage\Common {
private IMimeTypeDetector $mimeTypeDetector;
+ private $defUMask;
+
public function __construct($arguments) {
if (!isset($arguments['datadir']) || !is_string($arguments['datadir'])) {
throw new \InvalidArgumentException('No data directory set for local storage');
@@ -84,6 +87,7 @@ class Local extends \OC\Files\Storage\Common {
$this->dataDirLength = strlen($this->realDataDir);
$this->config = \OC::$server->get(IConfig::class);
$this->mimeTypeDetector = \OC::$server->get(IMimeTypeDetector::class);
+ $this->defUMask = $this->config->getSystemValue('localstorage.umask', 0022);
}
public function __destruct() {
@@ -95,7 +99,7 @@ class Local extends \OC\Files\Storage\Common {
public function mkdir($path) {
$sourcePath = $this->getSourcePath($path);
- $oldMask = umask(022);
+ $oldMask = umask($this->defUMask);
$result = @mkdir($sourcePath, 0777, true);
umask($oldMask);
return $result;
@@ -273,7 +277,7 @@ class Local extends \OC\Files\Storage\Common {
if ($this->file_exists($path) and !$this->isUpdatable($path)) {
return false;
}
- $oldMask = umask(022);
+ $oldMask = umask($this->defUMask);
if (!is_null($mtime)) {
$result = @touch($this->getSourcePath($path), $mtime);
} else {
@@ -292,7 +296,7 @@ class Local extends \OC\Files\Storage\Common {
}
public function file_put_contents($path, $data) {
- $oldMask = umask(022);
+ $oldMask = umask($this->defUMask);
$result = file_put_contents($this->getSourcePath($path), $data);
umask($oldMask);
return $result;
@@ -365,7 +369,7 @@ class Local extends \OC\Files\Storage\Common {
if ($this->is_dir($path1)) {
return parent::copy($path1, $path2);
} else {
- $oldMask = umask(022);
+ $oldMask = umask($this->defUMask);
$result = copy($this->getSourcePath($path1), $this->getSourcePath($path2));
umask($oldMask);
return $result;
@@ -373,7 +377,7 @@ class Local extends \OC\Files\Storage\Common {
}
public function fopen($path, $mode) {
- $oldMask = umask(022);
+ $oldMask = umask($this->defUMask);
$result = fopen($this->getSourcePath($path), $mode);
umask($oldMask);
return $result;
diff --git a/lib/private/Files/Storage/Wrapper/Encoding.php b/lib/private/Files/Storage/Wrapper/Encoding.php
index d6201dc8877..ac9cc248ce6 100644
--- a/lib/private/Files/Storage/Wrapper/Encoding.php
+++ b/lib/private/Files/Storage/Wrapper/Encoding.php
@@ -28,7 +28,7 @@
*/
namespace OC\Files\Storage\Wrapper;
-use OC\Cache\CappedMemoryCache;
+use OCP\Cache\CappedMemoryCache;
use OC\Files\Filesystem;
use OCP\Files\Storage\IStorage;
use OCP\ICache;
diff --git a/lib/private/Files/Storage/Wrapper/Encryption.php b/lib/private/Files/Storage/Wrapper/Encryption.php
index 4cfe932cc9f..d5bf929101f 100644
--- a/lib/private/Files/Storage/Wrapper/Encryption.php
+++ b/lib/private/Files/Storage/Wrapper/Encryption.php
@@ -33,6 +33,7 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>
*
*/
+
namespace OC\Files\Storage\Wrapper;
use OC\Encryption\Exceptions\ModuleDoesNotExistsException;
@@ -41,6 +42,7 @@ use OC\Encryption\Util;
use OC\Files\Cache\CacheEntry;
use OC\Files\Filesystem;
use OC\Files\Mount\Manager;
+use OC\Files\ObjectStore\ObjectStoreStorage;
use OC\Files\Storage\LocalTempFileTrait;
use OC\Memcache\ArrayCache;
use OCP\Encryption\Exceptions\GenericEncryptionException;
@@ -139,28 +141,36 @@ class Encryption extends Wrapper {
$size = $this->unencryptedSize[$fullPath];
// update file cache
if ($info instanceof ICacheEntry) {
- $info = $info->getData();
$info['encrypted'] = $info['encryptedVersion'];
} else {
if (!is_array($info)) {
$info = [];
}
$info['encrypted'] = true;
+ $info = new CacheEntry($info);
}
- $info['size'] = $size;
- $this->getCache()->put($path, $info);
+ if ($size !== $info->getUnencryptedSize()) {
+ $this->getCache()->update($info->getId(), [
+ 'unencrypted_size' => $size
+ ]);
+ }
return $size;
}
if (isset($info['fileid']) && $info['encrypted']) {
- return $this->verifyUnencryptedSize($path, $info['size']);
+ return $this->verifyUnencryptedSize($path, $info->getUnencryptedSize());
}
return $this->storage->filesize($path);
}
+ /**
+ * @param string $path
+ * @param array $data
+ * @return array
+ */
private function modifyMetaData(string $path, array $data): array {
$fullPath = $this->getFullPath($path);
$info = $this->getCache()->get($path);
@@ -170,7 +180,7 @@ class Encryption extends Wrapper {
$data['size'] = $this->unencryptedSize[$fullPath];
} else {
if (isset($info['fileid']) && $info['encrypted']) {
- $data['size'] = $this->verifyUnencryptedSize($path, $info['size']);
+ $data['size'] = $this->verifyUnencryptedSize($path, $info->getUnencryptedSize());
$data['encrypted'] = true;
}
}
@@ -478,7 +488,7 @@ class Encryption extends Wrapper {
*
* @return int unencrypted size
*/
- protected function verifyUnencryptedSize($path, $unencryptedSize) {
+ protected function verifyUnencryptedSize(string $path, int $unencryptedSize): int {
$size = $this->storage->filesize($path);
$result = $unencryptedSize;
@@ -510,7 +520,7 @@ class Encryption extends Wrapper {
*
* @return int calculated unencrypted size
*/
- protected function fixUnencryptedSize($path, $size, $unencryptedSize) {
+ protected function fixUnencryptedSize(string $path, int $size, int $unencryptedSize): int {
$headerSize = $this->getHeaderSize($path);
$header = $this->getHeader($path);
$encryptionModule = $this->getEncryptionModule($path);
@@ -581,7 +591,9 @@ class Encryption extends Wrapper {
$cache = $this->storage->getCache();
if ($cache) {
$entry = $cache->get($path);
- $cache->update($entry['fileid'], ['size' => $newUnencryptedSize]);
+ $cache->update($entry['fileid'], [
+ 'unencrypted_size' => $newUnencryptedSize
+ ]);
}
return $newUnencryptedSize;
@@ -621,7 +633,12 @@ class Encryption extends Wrapper {
* @param bool $preserveMtime
* @return bool
*/
- public function moveFromStorage(Storage\IStorage $sourceStorage, $sourceInternalPath, $targetInternalPath, $preserveMtime = true) {
+ public function moveFromStorage(
+ Storage\IStorage $sourceStorage,
+ $sourceInternalPath,
+ $targetInternalPath,
+ $preserveMtime = true
+ ) {
if ($sourceStorage === $this) {
return $this->rename($sourceInternalPath, $targetInternalPath);
}
@@ -656,7 +673,13 @@ class Encryption extends Wrapper {
* @param bool $isRename
* @return bool
*/
- public function copyFromStorage(Storage\IStorage $sourceStorage, $sourceInternalPath, $targetInternalPath, $preserveMtime = false, $isRename = false) {
+ public function copyFromStorage(
+ Storage\IStorage $sourceStorage,
+ $sourceInternalPath,
+ $targetInternalPath,
+ $preserveMtime = false,
+ $isRename = false
+ ) {
// TODO clean this up once the underlying moveFromStorage in OC\Files\Storage\Wrapper\Common is fixed:
// - call $this->storage->copyFromStorage() instead of $this->copyBetweenStorage
@@ -676,7 +699,13 @@ class Encryption extends Wrapper {
* @param bool $isRename
* @param bool $keepEncryptionVersion
*/
- private function updateEncryptedVersion(Storage\IStorage $sourceStorage, $sourceInternalPath, $targetInternalPath, $isRename, $keepEncryptionVersion) {
+ private function updateEncryptedVersion(
+ Storage\IStorage $sourceStorage,
+ $sourceInternalPath,
+ $targetInternalPath,
+ $isRename,
+ $keepEncryptionVersion
+ ) {
$isEncrypted = $this->encryptionManager->isEnabled() && $this->shouldEncrypt($targetInternalPath);
$cacheInformation = [
'encrypted' => $isEncrypted,
@@ -725,7 +754,13 @@ class Encryption extends Wrapper {
* @return bool
* @throws \Exception
*/
- private function copyBetweenStorage(Storage\IStorage $sourceStorage, $sourceInternalPath, $targetInternalPath, $preserveMtime, $isRename) {
+ private function copyBetweenStorage(
+ Storage\IStorage $sourceStorage,
+ $sourceInternalPath,
+ $targetInternalPath,
+ $preserveMtime,
+ $isRename
+ ) {
// for versions we have nothing to do, because versions should always use the
// key from the original file. Just create a 1:1 copy and done
@@ -743,7 +778,7 @@ class Encryption extends Wrapper {
if (isset($info['encrypted']) && $info['encrypted'] === true) {
$this->updateUnencryptedSize(
$this->getFullPath($targetInternalPath),
- $info['size']
+ $info->getUnencryptedSize()
);
}
$this->updateEncryptedVersion($sourceStorage, $sourceInternalPath, $targetInternalPath, $isRename, true);
@@ -808,13 +843,6 @@ class Encryption extends Wrapper {
return (bool)$result;
}
- /**
- * get the path to a local version of the file.
- * The local version of the file can be temporary and doesn't have to be persistent across requests
- *
- * @param string $path
- * @return string
- */
public function getLocalFile($path) {
if ($this->encryptionManager->isEnabled()) {
$cachedFile = $this->getCachedFile($path);
@@ -825,11 +853,6 @@ class Encryption extends Wrapper {
return $this->storage->getLocalFile($path);
}
- /**
- * Returns the wrapped storage's value for isLocal()
- *
- * @return bool wrapped storage's isLocal() value
- */
public function isLocal() {
if ($this->encryptionManager->isEnabled()) {
return false;
@@ -837,15 +860,11 @@ class Encryption extends Wrapper {
return $this->storage->isLocal();
}
- /**
- * see https://www.php.net/manual/en/function.stat.php
- * only the following keys are required in the result: size and mtime
- *
- * @param string $path
- * @return array
- */
public function stat($path) {
$stat = $this->storage->stat($path);
+ if (!$stat) {
+ return false;
+ }
$fileSize = $this->filesize($path);
$stat['size'] = $fileSize;
$stat[7] = $fileSize;
@@ -853,14 +872,6 @@ class Encryption extends Wrapper {
return $stat;
}
- /**
- * see https://www.php.net/manual/en/function.hash.php
- *
- * @param string $type
- * @param string $path
- * @param bool $raw
- * @return string
- */
public function hash($type, $path, $raw = false) {
$fh = $this->fopen($path, 'rb');
$ctx = hash_init($type);
@@ -1068,6 +1079,13 @@ class Encryption extends Wrapper {
[$count, $result] = \OC_Helper::streamCopy($stream, $target);
fclose($stream);
fclose($target);
+
+ // object store, stores the size after write and doesn't update this during scan
+ // manually store the unencrypted size
+ if ($result && $this->getWrapperStorage()->instanceOfStorage(ObjectStoreStorage::class)) {
+ $this->getCache()->put($path, ['unencrypted_size' => $count]);
+ }
+
return $count;
}
}
diff --git a/lib/private/Files/Stream/SeekableHttpStream.php b/lib/private/Files/Stream/SeekableHttpStream.php
index af797c7720d..820a681bd07 100644
--- a/lib/private/Files/Stream/SeekableHttpStream.php
+++ b/lib/private/Files/Stream/SeekableHttpStream.php
@@ -24,6 +24,7 @@
namespace OC\Files\Stream;
use Icewind\Streams\File;
+use Icewind\Streams\Wrapper;
/**
* A stream wrapper that uses http range requests to provide a seekable stream for http reading
@@ -92,6 +93,18 @@ class SeekableHttpStream implements File {
}
$responseHead = stream_get_meta_data($this->current)['wrapper_data'];
+
+ while ($responseHead instanceof Wrapper) {
+ $wrapperOptions = stream_context_get_options($responseHead->context);
+ foreach ($wrapperOptions as $options) {
+ if (isset($options['source']) && is_resource($options['source'])) {
+ $responseHead = stream_get_meta_data($options['source'])['wrapper_data'];
+ continue 2;
+ }
+ }
+ throw new \Exception("Failed to get source stream from stream wrapper of " . get_class($responseHead));
+ }
+
$rangeHeaders = array_values(array_filter($responseHead, function ($v) {
return preg_match('#^content-range:#i', $v) === 1;
}));
diff --git a/lib/private/Files/View.php b/lib/private/Files/View.php
index 2b6732e2ba0..d12869fbdaa 100644
--- a/lib/private/Files/View.php
+++ b/lib/private/Files/View.php
@@ -1164,7 +1164,7 @@ class View {
try {
$this->changeLock($path, ILockingProvider::LOCK_EXCLUSIVE);
} catch (LockedException $e) {
- // release the shared lock we acquired before quiting
+ // release the shared lock we acquired before quitting
$this->unlockFile($path, ILockingProvider::LOCK_SHARED);
throw $e;
}
@@ -1725,7 +1725,7 @@ class View {
/**
* Get the path of a file by id, relative to the view
*
- * Note that the resulting path is not guarantied to be unique for the id, multiple paths can point to the same file
+ * Note that the resulting path is not guaranteed to be unique for the id, multiple paths can point to the same file
*
* @param int $id
* @param int|null $storageId
diff --git a/lib/private/HintException.php b/lib/private/HintException.php
index 735832266cf..20f7142d1c0 100644
--- a/lib/private/HintException.php
+++ b/lib/private/HintException.php
@@ -31,7 +31,7 @@ namespace OC;
* An Exception class with the intention to be presented to the end user
*
* @package OC
- * @depreacted 23.0.0 Use \OCP\HintException
+ * @deprecated 23.0.0 Use \OCP\HintException
*/
class HintException extends \OCP\HintException {
}
diff --git a/lib/private/Http/Client/Client.php b/lib/private/Http/Client/Client.php
index 3ba85a2dd9f..4bf7fd02400 100644
--- a/lib/private/Http/Client/Client.php
+++ b/lib/private/Http/Client/Client.php
@@ -128,7 +128,7 @@ class Client implements IClient {
}
/**
- * Returns a null or an associative array specifiying the proxy URI for
+ * Returns a null or an associative array specifying the proxy URI for
* 'http' and 'https' schemes, in addition to a 'no' key value pair
* providing a list of host names that should not be proxied to.
*
diff --git a/lib/private/Http/Client/LocalAddressChecker.php b/lib/private/Http/Client/LocalAddressChecker.php
index 2789b1b5935..f4fea503ab9 100644
--- a/lib/private/Http/Client/LocalAddressChecker.php
+++ b/lib/private/Http/Client/LocalAddressChecker.php
@@ -27,6 +27,7 @@ namespace OC\Http\Client;
use OCP\Http\Client\LocalServerException;
use Psr\Log\LoggerInterface;
+use Symfony\Component\HttpFoundation\IpUtils;
class LocalAddressChecker {
private LoggerInterface $logger;
@@ -36,7 +37,16 @@ class LocalAddressChecker {
}
public function ThrowIfLocalIp(string $ip) : void {
- if ((bool)filter_var($ip, FILTER_VALIDATE_IP) && !filter_var($ip, FILTER_VALIDATE_IP, FILTER_FLAG_NO_PRIV_RANGE | FILTER_FLAG_NO_RES_RANGE)) {
+ $localRanges = [
+ '100.64.0.0/10', // See RFC 6598
+ '192.0.0.0/24', // See RFC 6890
+ ];
+ if (
+ (bool)filter_var($ip, FILTER_VALIDATE_IP) &&
+ (
+ !filter_var($ip, FILTER_VALIDATE_IP, FILTER_FLAG_NO_PRIV_RANGE | FILTER_FLAG_NO_RES_RANGE) ||
+ IpUtils::checkIp($ip, $localRanges)
+ )) {
$this->logger->warning("Host $ip was not connected to because it violates local access rules");
throw new LocalServerException('Host violates local access rules');
}
@@ -46,7 +56,9 @@ class LocalAddressChecker {
$delimiter = strrpos($ip, ':'); // Get last colon
$ipv4Address = substr($ip, $delimiter + 1);
- if (!filter_var($ipv4Address, FILTER_VALIDATE_IP, FILTER_FLAG_NO_PRIV_RANGE | FILTER_FLAG_NO_RES_RANGE)) {
+ if (
+ !filter_var($ipv4Address, FILTER_VALIDATE_IP, FILTER_FLAG_NO_PRIV_RANGE | FILTER_FLAG_NO_RES_RANGE) ||
+ IpUtils::checkIp($ip, $localRanges)) {
$this->logger->warning("Host $ip was not connected to because it violates local access rules");
throw new LocalServerException('Host violates local access rules');
}
@@ -66,8 +78,10 @@ class LocalAddressChecker {
$host = substr($host, 1, -1);
}
- // Disallow localhost and local network
- if ($host === 'localhost' || substr($host, -6) === '.local' || substr($host, -10) === '.localhost') {
+ // Disallow local network top-level domains from RFC 6762
+ $localTopLevelDomains = ['local','localhost','intranet','internal','private','corp','home','lan'];
+ $topLevelDomain = substr((strrchr($host, '.') ?: ''), 1);
+ if (in_array($topLevelDomain, $localTopLevelDomains)) {
$this->logger->warning("Host $host was not connected to because it violates local access rules");
throw new LocalServerException('Host violates local access rules');
}
diff --git a/lib/private/IntegrityCheck/Checker.php b/lib/private/IntegrityCheck/Checker.php
index 273eba35446..ba555cff438 100644
--- a/lib/private/IntegrityCheck/Checker.php
+++ b/lib/private/IntegrityCheck/Checker.php
@@ -201,7 +201,8 @@ class Checker {
if ($filename === $this->environmentHelper->getServerRoot() . '/core/js/mimetypelist.js') {
$oldMimetypeList = new GenerateMimetypeFileBuilder();
$newFile = $oldMimetypeList->generateFile($this->mimeTypeDetector->getAllAliases());
- if ($newFile === file_get_contents($filename)) {
+ $oldFile = $this->fileAccessHelper->file_get_contents($filename);
+ if ($newFile === $oldFile) {
$hashes[$relativeFileName] = hash('sha512', $oldMimetypeList->generateFile($this->mimeTypeDetector->getOnlyDefaultAliases()));
continue;
}
diff --git a/lib/private/L10N/L10N.php b/lib/private/L10N/L10N.php
index 09c0f1cdb35..82ef3350b1f 100644
--- a/lib/private/L10N/L10N.php
+++ b/lib/private/L10N/L10N.php
@@ -122,7 +122,7 @@ class L10N implements IL10N {
*
*/
public function n(string $text_singular, string $text_plural, int $count, array $parameters = []): string {
- $identifier = "_${text_singular}_::_${text_plural}_";
+ $identifier = "_{$text_singular}_::_{$text_plural}_";
if (isset($this->translations[$identifier])) {
return (string) new L10NString($this, $identifier, $parameters, $count);
}
diff --git a/lib/private/Log.php b/lib/private/Log.php
index 0415967f0f0..4ab647bc6c1 100644
--- a/lib/private/Log.php
+++ b/lib/private/Log.php
@@ -15,6 +15,7 @@ declare(strict_types=1);
* @author Olivier Paroz <github@oparoz.com>
* @author Robin Appelman <robin@icewind.nl>
* @author Roeland Jago Douma <roeland@famdouma.nl>
+ * @author Thomas Citharel <nextcloud@tcit.fr>
* @author Thomas Müller <thomas.mueller@tmit.eu>
* @author Victor Dubiniuk <dubiniuk@owncloud.com>
*
@@ -35,8 +36,11 @@ declare(strict_types=1);
*/
namespace OC;
+use Exception;
use Nextcloud\LogNormalizer\Normalizer;
+use OC\AppFramework\Bootstrap\Coordinator;
use OCP\Log\IDataLogger;
+use Throwable;
use function array_merge;
use OC\Log\ExceptionSerializer;
use OCP\ILogger;
@@ -207,11 +211,11 @@ class Log implements ILogger, IDataLogger {
array_walk($context, [$this->normalizer, 'format']);
$app = $context['app'] ?? 'no app in context';
- $message = $this->interpolateMessage($context, $message);
+ $entry = $this->interpolateMessage($context, $message);
try {
if ($level >= $minLevel) {
- $this->writeLog($app, $message, $level);
+ $this->writeLog($app, $entry, $level);
if ($this->crashReporters !== null) {
$messageContext = array_merge(
@@ -220,14 +224,14 @@ class Log implements ILogger, IDataLogger {
'level' => $level
]
);
- $this->crashReporters->delegateMessage($message, $messageContext);
+ $this->crashReporters->delegateMessage($entry['message'], $messageContext);
}
} else {
if ($this->crashReporters !== null) {
- $this->crashReporters->delegateBreadcrumb($message, 'log', $context);
+ $this->crashReporters->delegateBreadcrumb($entry['message'], 'log', $context);
}
}
- } catch (\Throwable $e) {
+ } catch (Throwable $e) {
// make sure we dont hard crash if logging fails
}
}
@@ -299,24 +303,27 @@ class Log implements ILogger, IDataLogger {
/**
* Logs an exception very detailed
*
- * @param \Exception|\Throwable $exception
+ * @param Exception|Throwable $exception
* @param array $context
* @return void
* @since 8.2.0
*/
- public function logException(\Throwable $exception, array $context = []) {
+ public function logException(Throwable $exception, array $context = []) {
$app = $context['app'] ?? 'no app in context';
$level = $context['level'] ?? ILogger::ERROR;
// if an error is raised before the autoloader is properly setup, we can't serialize exceptions
try {
- $serializer = new ExceptionSerializer($this->config);
- } catch (\Throwable $e) {
+ $serializer = $this->getSerializer();
+ } catch (Throwable $e) {
$this->error("Failed to load ExceptionSerializer serializer while trying to log " . $exception->getMessage());
return;
}
- $data = $serializer->serializeException($exception);
- $data['CustomMessage'] = $this->interpolateMessage($context, $context['message'] ?? '--');
+ $data = $context;
+ unset($data['app']);
+ unset($data['level']);
+ $data = array_merge($serializer->serializeException($exception), $data);
+ $data = $this->interpolateMessage($data, $context['message'] ?? '--', 'CustomMessage');
$minLevel = $this->getLogLevel($context);
@@ -334,7 +341,7 @@ class Log implements ILogger, IDataLogger {
if (!is_null($this->crashReporters)) {
$this->crashReporters->delegateReport($exception, $context);
}
- } catch (\Throwable $e) {
+ } catch (Throwable $e) {
// make sure we dont hard crash if logging fails
}
}
@@ -357,7 +364,7 @@ class Log implements ILogger, IDataLogger {
}
$context['level'] = $level;
- } catch (\Throwable $e) {
+ } catch (Throwable $e) {
// make sure we dont hard crash if logging fails
}
}
@@ -381,16 +388,42 @@ class Log implements ILogger, IDataLogger {
/**
* Interpolate $message as defined in PSR-3
*
- * @param array $context
- * @param string $message
- *
- * @return string
+ * Returns an array containing the context without the interpolated
+ * parameters placeholders and the message as the 'message' - or
+ * user-defined - key.
*/
- private function interpolateMessage(array $context, string $message): string {
+ private function interpolateMessage(array $context, string $message, string $messageKey = 'message'): array {
$replace = [];
+ $usedContextKeys = [];
foreach ($context as $key => $val) {
- $replace['{' . $key . '}'] = $val;
+ $fullKey = '{' . $key . '}';
+ $replace[$fullKey] = $val;
+ if (strpos($message, $fullKey) !== false) {
+ $usedContextKeys[$key] = true;
+ }
+ }
+ return array_merge(array_diff_key($context, $usedContextKeys), [$messageKey => strtr($message, $replace)]);
+ }
+
+ /**
+ * @throws Throwable
+ */
+ protected function getSerializer(): ExceptionSerializer {
+ $serializer = new ExceptionSerializer($this->config);
+ try {
+ /** @var Coordinator $coordinator */
+ $coordinator = \OCP\Server::get(Coordinator::class);
+ foreach ($coordinator->getRegistrationContext()->getSensitiveMethods() as $registration) {
+ $serializer->enlistSensitiveMethods($registration->getName(), $registration->getValue());
+ }
+ // For not every app might be initialized at this time, we cannot assume that the return value
+ // of getSensitiveMethods() is complete. Running delegates in Coordinator::registerApps() is
+ // not possible due to dependencies on the one hand. On the other it would work only with
+ // adding public methods to the PsrLoggerAdapter and this class.
+ // Thus, serializer cannot be a property.
+ } catch (Throwable $t) {
+ // ignore app-defined sensitive methods in this case - they weren't loaded anyway
}
- return strtr($message, $replace);
+ return $serializer;
}
}
diff --git a/lib/private/Log/ExceptionSerializer.php b/lib/private/Log/ExceptionSerializer.php
index dab134b26a4..aaf6a39235e 100644
--- a/lib/private/Log/ExceptionSerializer.php
+++ b/lib/private/Log/ExceptionSerializer.php
@@ -42,6 +42,8 @@ use OCA\Encryption\Session;
use OCP\HintException;
class ExceptionSerializer {
+ public const SENSITIVE_VALUE_PLACEHOLDER = '*** sensitive parameters replaced ***';
+
public const methodsWithSensitiveParameters = [
// Session/User
'completeLogin',
@@ -107,7 +109,7 @@ class ExceptionSerializer {
$this->systemConfig = $systemConfig;
}
- public const methodsWithSensitiveParametersByClass = [
+ protected array $methodsWithSensitiveParametersByClass = [
SetupController::class => [
'run',
'display',
@@ -180,7 +182,7 @@ class ExceptionSerializer {
if (isset($traceLine['args'])) {
$sensitiveValues = array_merge($sensitiveValues, $traceLine['args']);
}
- $traceLine['args'] = ['*** sensitive parameters replaced ***'];
+ $traceLine['args'] = [self::SENSITIVE_VALUE_PLACEHOLDER];
return $traceLine;
}
@@ -188,8 +190,8 @@ class ExceptionSerializer {
$sensitiveValues = [];
$trace = array_map(function (array $traceLine) use (&$sensitiveValues) {
$className = $traceLine['class'] ?? '';
- if ($className && isset(self::methodsWithSensitiveParametersByClass[$className])
- && in_array($traceLine['function'], self::methodsWithSensitiveParametersByClass[$className], true)) {
+ if ($className && isset($this->methodsWithSensitiveParametersByClass[$className])
+ && in_array($traceLine['function'], $this->methodsWithSensitiveParametersByClass[$className], true)) {
return $this->editTrace($sensitiveValues, $traceLine);
}
foreach (self::methodsWithSensitiveParameters as $sensitiveMethod) {
@@ -208,14 +210,16 @@ class ExceptionSerializer {
}
private function removeValuesFromArgs($args, $values) {
- foreach ($args as &$arg) {
+ $workArgs = [];
+ foreach ($args as $arg) {
if (in_array($arg, $values, true)) {
- $arg = '*** sensitive parameter replaced ***';
+ $arg = self::SENSITIVE_VALUE_PLACEHOLDER;
} elseif (is_array($arg)) {
$arg = $this->removeValuesFromArgs($arg, $values);
}
+ $workArgs[] = $arg;
}
- return $args;
+ return $workArgs;
}
private function encodeTrace($trace) {
@@ -285,4 +289,11 @@ class ExceptionSerializer {
return $data;
}
+
+ public function enlistSensitiveMethods(string $class, array $methods): void {
+ if (!isset($this->methodsWithSensitiveParametersByClass[$class])) {
+ $this->methodsWithSensitiveParametersByClass[$class] = [];
+ }
+ $this->methodsWithSensitiveParametersByClass[$class] = array_merge($this->methodsWithSensitiveParametersByClass[$class], $methods);
+ }
}
diff --git a/lib/private/Log/LogDetails.php b/lib/private/Log/LogDetails.php
index 3353ea3f4cc..b3544572708 100644
--- a/lib/private/Log/LogDetails.php
+++ b/lib/private/Log/LogDetails.php
@@ -5,6 +5,7 @@
* @author Arthur Schiwon <blizzz@arthur-schiwon.de>
* @author Christoph Wurst <christoph@winzerhof-wurst.at>
* @author Julius Härtl <jus@bitgrid.net>
+ * @author Thomas Citharel <nextcloud@tcit.fr>
*
* @license GNU AGPL version 3 or any later version
*
@@ -90,8 +91,9 @@ abstract class LogDetails {
$entry['exception'] = $message;
$entry['message'] = $message['CustomMessage'] !== '--' ? $message['CustomMessage'] : $message['Message'];
} else {
- $entry['data'] = $message;
$entry['message'] = $message['message'] ?? '(no message provided)';
+ unset($message['message']);
+ $entry['data'] = $message;
}
}
diff --git a/lib/private/Mail/EMailTemplate.php b/lib/private/Mail/EMailTemplate.php
index 2c091deb2a1..7ec2b46bad3 100644
--- a/lib/private/Mail/EMailTemplate.php
+++ b/lib/private/Mail/EMailTemplate.php
@@ -497,7 +497,7 @@ EOF;
*/
/** @var string $label */
$label = ($plainMetaInfo !== false)? $plainMetaInfo : '';
- $this->plainBody .= sprintf("%${plainIndent}s %s\n",
+ $this->plainBody .= sprintf("%{$plainIndent}s %s\n",
$label,
str_replace("\n", "\n" . str_repeat(' ', $plainIndent + 1), $plainText));
}
diff --git a/lib/private/Mail/Mailer.php b/lib/private/Mail/Mailer.php
index 2f3480498be..991d1b202ec 100644
--- a/lib/private/Mail/Mailer.php
+++ b/lib/private/Mail/Mailer.php
@@ -196,6 +196,9 @@ class Mailer implements IMailer {
// Debugging logging
$logMessage = sprintf('Sent mail to "%s" with subject "%s"', print_r($message->getTo(), true), $message->getSubject());
+ if (!empty($failedRecipients)) {
+ $logMessage .= sprintf(' (failed for "%s")', print_r($failedRecipients, true));
+ }
$this->logger->debug($logMessage, ['app' => 'core']);
if ($debugMode && isset($mailLogger)) {
$this->logger->debug($mailLogger->dump(), ['app' => 'core']);
diff --git a/lib/private/Memcache/Memcached.php b/lib/private/Memcache/Memcached.php
index db4aa7ba9cc..7d512d4d1ae 100644
--- a/lib/private/Memcache/Memcached.php
+++ b/lib/private/Memcache/Memcached.php
@@ -63,7 +63,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
@@ -119,10 +119,7 @@ class Memcached extends Cache implements IMemcache {
} else {
$result = self::$cache->set($this->getNameSpace() . $key, $value);
}
- if ($result !== true) {
- $this->verifyReturnCode();
- }
- return $result;
+ return $result || $this->isSuccess();
}
public function hasKey($key) {
@@ -132,10 +129,7 @@ class Memcached extends Cache implements IMemcache {
public function remove($key) {
$result = self::$cache->delete($this->getNameSpace() . $key);
- if (self::$cache->getResultCode() !== \Memcached::RES_NOTFOUND) {
- $this->verifyReturnCode();
- }
- return $result;
+ return $result || $this->isSuccess() || self::$cache->getResultCode() === \Memcached::RES_NOTFOUND;
}
public function clear($prefix = '') {
@@ -151,14 +145,10 @@ class Memcached extends Cache implements IMemcache {
* @param mixed $value
* @param int $ttl Time To Live in seconds. Defaults to 60*60*24
* @return bool
- * @throws \Exception
*/
public function add($key, $value, $ttl = 0) {
$result = self::$cache->add($this->getPrefix() . $key, $value, $ttl);
- if (self::$cache->getResultCode() !== \Memcached::RES_NOTSTORED) {
- $this->verifyReturnCode();
- }
- return $result;
+ return $result || $this->isSuccess();
}
/**
@@ -200,15 +190,7 @@ class Memcached extends Cache implements IMemcache {
return extension_loaded('memcached');
}
- /**
- * @throws \Exception
- */
- private function verifyReturnCode() {
- $code = self::$cache->getResultCode();
- if ($code === \Memcached::RES_SUCCESS) {
- return;
- }
- $message = self::$cache->getResultMessage();
- throw new \Exception("Error $code interacting with memcached : $message");
+ private function isSuccess(): bool {
+ return self::$cache->getResultCode() === \Memcached::RES_SUCCESS;
}
}
diff --git a/lib/private/MemoryInfo.php b/lib/private/MemoryInfo.php
index 074e9f915fe..ed6617d879d 100644
--- a/lib/private/MemoryInfo.php
+++ b/lib/private/MemoryInfo.php
@@ -68,7 +68,7 @@ class MemoryInfo {
$last = strtolower(substr($memoryLimit, -1));
$memoryLimit = (int)substr($memoryLimit, 0, -1);
- // intended fall trough
+ // intended fall through
switch ($last) {
case 'g':
$memoryLimit *= 1024;
diff --git a/lib/private/Metadata/IMetadataManager.php b/lib/private/Metadata/IMetadataManager.php
index d2d37f15c25..fa0bcc22801 100644
--- a/lib/private/Metadata/IMetadataManager.php
+++ b/lib/private/Metadata/IMetadataManager.php
@@ -29,7 +29,7 @@ interface IMetadataManager {
public function fetchMetadataFor(string $group, array $fileIds): array;
/**
- * Get the capabilites as an array of mimetype regex to the type provided
+ * Get the capabilities as an array of mimetype regex to the type provided
*/
public function getCapabilities(): array;
}
diff --git a/lib/private/OCS/DiscoveryService.php b/lib/private/OCS/DiscoveryService.php
index 1d10bbac870..7ab876811e7 100644
--- a/lib/private/OCS/DiscoveryService.php
+++ b/lib/private/OCS/DiscoveryService.php
@@ -62,7 +62,7 @@ class DiscoveryService implements IDiscoveryService {
*
* @param string $remote
* @param string $service the service you want to discover
- * @param bool $skipCache We won't check if the data is in the cache. This is usefull if a background job is updating the status
+ * @param bool $skipCache We won't check if the data is in the cache. This is useful if a background job is updating the status
* @return array
*/
public function discover(string $remote, string $service, bool $skipCache = false): array {
diff --git a/lib/private/Profile/ProfileManager.php b/lib/private/Profile/ProfileManager.php
index edb51458c66..f2eacd1ef25 100644
--- a/lib/private/Profile/ProfileManager.php
+++ b/lib/private/Profile/ProfileManager.php
@@ -348,13 +348,13 @@ class ProfileManager {
* Return the default profile config
*/
private function getDefaultProfileConfig(IUser $targetUser, ?IUser $visitingUser): array {
- // Contruct the default config for actions
+ // Construct the default config for actions
$actionsConfig = [];
foreach ($this->getActions($targetUser, $visitingUser) as $action) {
$actionsConfig[$action->getId()] = ['visibility' => ProfileConfig::DEFAULT_VISIBILITY];
}
- // Contruct the default config for account properties
+ // Construct the default config for account properties
$propertiesConfig = [];
foreach (ProfileConfig::DEFAULT_PROPERTY_VISIBILITY as $property => $visibility) {
$propertiesConfig[$property] = ['visibility' => $visibility];
diff --git a/lib/private/Repair/RemoveLinkShares.php b/lib/private/Repair/RemoveLinkShares.php
index 1b0270e928d..e1ce78cdbf3 100644
--- a/lib/private/Repair/RemoveLinkShares.php
+++ b/lib/private/Repair/RemoveLinkShares.php
@@ -217,7 +217,7 @@ class RemoveLinkShares implements IRepairStep {
$output->finishProgress();
$shareResult->closeCursor();
- // Notifiy all admins
+ // Notify all admins
$adminGroup = $this->groupManager->get('admin');
$adminUsers = $adminGroup->getUsers();
foreach ($adminUsers as $user) {
diff --git a/lib/private/Repair/RepairMimeTypes.php b/lib/private/Repair/RepairMimeTypes.php
index 61512627258..5b216331dc7 100644
--- a/lib/private/Repair/RepairMimeTypes.php
+++ b/lib/private/Repair/RepairMimeTypes.php
@@ -211,6 +211,15 @@ class RepairMimeTypes implements IRepairStep {
return $this->updateMimetypes($updatedMimetypes);
}
+ private function introduceOnlyofficeFormType() {
+ $updatedMimetypes = [
+ "oform" => "application/vnd.openxmlformats-officedocument.wordprocessingml.document.oform",
+ "docxf" => "application/vnd.openxmlformats-officedocument.wordprocessingml.document.docxf",
+ ];
+
+ return $this->updateMimetypes($updatedMimetypes);
+ }
+
/**
* Fix mime types
@@ -260,5 +269,9 @@ class RepairMimeTypes implements IRepairStep {
if (version_compare($ocVersionFromBeforeUpdate, '23.0.0.2', '<') && $this->introduceFlatOpenDocumentType()) {
$out->info('Fixed Flat OpenDocument mime types');
}
+
+ if (version_compare($ocVersionFromBeforeUpdate, '25.0.0.2', '<') && $this->introduceOnlyofficeFormType()) {
+ $out->info('Fixed ONLYOFFICE Forms OpenXML mime types');
+ }
}
}
diff --git a/lib/private/Route/Router.php b/lib/private/Route/Router.php
index b957173cacc..7e1acd49800 100644
--- a/lib/private/Route/Router.php
+++ b/lib/private/Route/Router.php
@@ -409,7 +409,7 @@ class Router implements IRouter {
* register the routes for the app. The application class will be chosen by
* camelcasing the appname, e.g.: my_app will be turned into
* \OCA\MyApp\AppInfo\Application. If that class does not exist, a default
- * App will be intialized. This makes it optional to ship an
+ * App will be initialized. This makes it optional to ship an
* appinfo/application.php by using the built in query resolver
*
* @param array $routes the application routes
diff --git a/lib/private/Security/Bruteforce/Throttler.php b/lib/private/Security/Bruteforce/Throttler.php
index c47d102b881..299cab93eb3 100644
--- a/lib/private/Security/Bruteforce/Throttler.php
+++ b/lib/private/Security/Bruteforce/Throttler.php
@@ -36,6 +36,7 @@ use OC\Security\Normalizer\IpAddress;
use OCP\AppFramework\Utility\ITimeFactory;
use OCP\IConfig;
use OCP\IDBConnection;
+use OCP\Security\Bruteforce\IThrottler;
use OCP\Security\Bruteforce\MaxDelayReached;
use Psr\Log\LoggerInterface;
@@ -52,11 +53,8 @@ use Psr\Log\LoggerInterface;
*
* @package OC\Security\Bruteforce
*/
-class Throttler {
+class Throttler implements IThrottler {
public const LOGIN_ACTION = 'login';
- public const MAX_DELAY = 25;
- public const MAX_DELAY_MS = 25000; // in milliseconds
- public const MAX_ATTEMPTS = 10;
/** @var IDBConnection */
private $db;
@@ -65,8 +63,8 @@ class Throttler {
private LoggerInterface $logger;
/** @var IConfig */
private $config;
- /** @var bool */
- private $hasAttemptsDeleted = false;
+ /** @var bool[] */
+ private $hasAttemptsDeleted = [];
public function __construct(IDBConnection $db,
ITimeFactory $timeFactory,
@@ -225,7 +223,7 @@ class Throttler {
$maxAgeHours = 48;
}
- if ($ip === '' || $this->hasAttemptsDeleted) {
+ if ($ip === '' || isset($this->hasAttemptsDeleted[$action])) {
return 0;
}
@@ -303,7 +301,7 @@ class Throttler {
$qb->executeStatement();
- $this->hasAttemptsDeleted = true;
+ $this->hasAttemptsDeleted[$action] = true;
}
/**
@@ -311,7 +309,7 @@ class Throttler {
*
* @param string $ip
*/
- public function resetDelayForIP($ip) {
+ public function resetDelayForIP(string $ip): void {
$cutoffTime = $this->getCutoffTimestamp();
$qb = $this->db->getQueryBuilder();
diff --git a/lib/private/Security/CSP/ContentSecurityPolicyNonceManager.php b/lib/private/Security/CSP/ContentSecurityPolicyNonceManager.php
index f3329135727..1167b3358d2 100644
--- a/lib/private/Security/CSP/ContentSecurityPolicyNonceManager.php
+++ b/lib/private/Security/CSP/ContentSecurityPolicyNonceManager.php
@@ -80,10 +80,8 @@ class ContentSecurityPolicyNonceManager {
public function browserSupportsCspV3(): bool {
$browserWhitelist = [
Request::USER_AGENT_CHROME,
- // Firefox 45+
- '/^Mozilla\/5\.0 \([^)]+\) Gecko\/[0-9.]+ Firefox\/(4[5-9]|[5-9][0-9])\.[0-9.]+$/',
- // Safari 12+
- '/^Mozilla\/5\.0 \([^)]+\) AppleWebKit\/[0-9.]+ \(KHTML, like Gecko\) Version\/(?:1[2-9]|[2-9][0-9])\.[0-9]+(?:\.[0-9]+)? Safari\/[0-9.A-Z]+$/',
+ Request::USER_AGENT_FIREFOX,
+ Request::USER_AGENT_SAFARI,
];
if ($this->request->isUserAgent($browserWhitelist)) {
diff --git a/lib/private/Security/TrustedDomainHelper.php b/lib/private/Security/TrustedDomainHelper.php
index 0688ebba5b3..1927af9cb1d 100644
--- a/lib/private/Security/TrustedDomainHelper.php
+++ b/lib/private/Security/TrustedDomainHelper.php
@@ -97,7 +97,7 @@ class TrustedDomainHelper implements ITrustedDomainHelper {
if (preg_match(Request::REGEX_LOCALHOST, $domain) === 1) {
return true;
}
- // Reject misformed domains in any case
+ // Reject malformed domains in any case
if (strpos($domain, '-') === 0 || strpos($domain, '..') !== false) {
return false;
}
diff --git a/lib/private/Server.php b/lib/private/Server.php
index 6e6fa430489..b473f4e75c0 100644
--- a/lib/private/Server.php
+++ b/lib/private/Server.php
@@ -151,6 +151,8 @@ use OC\SystemTag\ManagerFactory as SystemTagManagerFactory;
use OC\Tagging\TagMapper;
use OC\Talk\Broker;
use OC\Template\JSCombiner;
+use OC\User\Listeners\UserChangedListener;
+use OC\User\Listeners\UserDeletedListener;
use OCA\Theming\ImageManager;
use OCA\Theming\ThemingDefaults;
use OCA\Theming\Util;
@@ -180,7 +182,6 @@ use OCP\Files\IMimeTypeLoader;
use OCP\Files\IRootFolder;
use OCP\Files\Lock\ILockManager;
use OCP\Files\Mount\IMountManager;
-use OCP\Files\NotFoundException;
use OCP\Files\Storage\IStorageFactory;
use OCP\Files\Template\ITemplateManager;
use OCP\FullTextSearch\IFullTextSearchManager;
@@ -217,7 +218,6 @@ use OCP\ISession;
use OCP\ITagManager;
use OCP\ITempManager;
use OCP\IURLGenerator;
-use OCP\IUser;
use OCP\IUserManager;
use OCP\IUserSession;
use OCP\L10N\IFactory;
@@ -231,6 +231,7 @@ use OCP\Remote\Api\IApiFactory;
use OCP\Remote\IInstanceFactory;
use OCP\RichObjectStrings\IValidator;
use OCP\Route\IRouter;
+use OCP\Security\Bruteforce\IThrottler;
use OCP\Security\IContentSecurityPolicyManager;
use OCP\Security\ICredentialsManager;
use OCP\Security\ICrypto;
@@ -249,6 +250,7 @@ use OCP\User\Events\BeforeUserLoggedOutEvent;
use OCP\User\Events\PasswordUpdatedEvent;
use OCP\User\Events\PostLoginEvent;
use OCP\User\Events\UserChangedEvent;
+use OCP\User\Events\UserDeletedEvent;
use OCP\User\Events\UserLoggedInEvent;
use OCP\User\Events\UserLoggedInWithCookieEvent;
use OCP\User\Events\UserLoggedOutEvent;
@@ -351,7 +353,7 @@ class Server extends ServerContainer implements IServerContainer {
return new Profiler($c->get(SystemConfig::class));
});
- $this->registerService(\OCP\Encryption\IManager::class, function (Server $c) {
+ $this->registerService(\OCP\Encryption\IManager::class, function (Server $c): Encryption\Manager {
$view = new View();
$util = new Encryption\Util(
$view,
@@ -1001,6 +1003,7 @@ class Server extends ServerContainer implements IServerContainer {
$this->registerAlias(ITrustedDomainHelper::class, TrustedDomainHelper::class);
/** @deprecated 19.0.0 */
$this->registerDeprecatedAlias('Throttler', Throttler::class);
+ $this->registerAlias(IThrottler::class, Throttler::class);
$this->registerService('IntegrityCodeChecker', function (ContainerInterface $c) {
// IConfig and IAppManager requires a working database. This code
// might however be called when ownCloud is not yet setup.
@@ -1473,52 +1476,13 @@ class Server extends ServerContainer implements IServerContainer {
return $this->get(\OC\Calendar\Room\Manager::class);
}
- private function connectDispatcher() {
- $dispatcher = $this->get(SymfonyAdapter::class);
-
- // Delete avatar on user deletion
- $dispatcher->addListener('OCP\IUser::preDelete', function (GenericEvent $e) {
- $logger = $this->get(LoggerInterface::class);
- $manager = $this->getAvatarManager();
- /** @var IUser $user */
- $user = $e->getSubject();
-
- try {
- $avatar = $manager->getAvatar($user->getUID());
- $avatar->remove();
- } catch (NotFoundException $e) {
- // no avatar to remove
- } catch (\Exception $e) {
- // Ignore exceptions
- $logger->info('Could not cleanup avatar of ' . $user->getUID());
- }
- });
-
- $dispatcher->addListener('OCP\IUser::changeUser', function (GenericEvent $e) {
- $manager = $this->getAvatarManager();
- /** @var IUser $user */
- $user = $e->getSubject();
- $feature = $e->getArgument('feature');
- $oldValue = $e->getArgument('oldValue');
- $value = $e->getArgument('value');
-
- // We only change the avatar on display name changes
- if ($feature !== 'displayName') {
- return;
- }
-
- try {
- $avatar = $manager->getAvatar($user->getUID());
- $avatar->userChanged($feature, $oldValue, $value);
- } catch (NotFoundException $e) {
- // no avatar to remove
- }
- });
-
- /** @var IEventDispatcher $eventDispatched */
- $eventDispatched = $this->get(IEventDispatcher::class);
- $eventDispatched->addServiceListener(LoginFailed::class, LoginFailedListener::class);
- $eventDispatched->addServiceListener(PostLoginEvent::class, UserLoggedInListener::class);
+ private function connectDispatcher(): void {
+ /** @var IEventDispatcher $eventDispatcher */
+ $eventDispatcher = $this->get(IEventDispatcher::class);
+ $eventDispatcher->addServiceListener(LoginFailed::class, LoginFailedListener::class);
+ $eventDispatcher->addServiceListener(PostLoginEvent::class, UserLoggedInListener::class);
+ $eventDispatcher->addServiceListener(UserChangedEvent::class, UserChangedListener::class);
+ $eventDispatcher->addServiceListener(UserDeletedEvent::class, UserDeletedListener::class);
}
/**
diff --git a/lib/private/Session/Internal.php b/lib/private/Session/Internal.php
index 285b6fd7960..6e0c54c6fab 100644
--- a/lib/private/Session/Internal.php
+++ b/lib/private/Session/Internal.php
@@ -172,7 +172,7 @@ class Internal extends Session {
* @throws \Exception
*/
public function reopen() {
- throw new \Exception('The session cannot be reopened - reopen() is ony to be used in unit testing.');
+ throw new \Exception('The session cannot be reopened - reopen() is only to be used in unit testing.');
}
/**
diff --git a/lib/private/Setup/MySQL.php b/lib/private/Setup/MySQL.php
index 7bd8fa7b1ec..e878ed4d9aa 100644
--- a/lib/private/Setup/MySQL.php
+++ b/lib/private/Setup/MySQL.php
@@ -80,7 +80,7 @@ class MySQL extends AbstractDatabase {
$user = $this->dbUser;
//we can't use OC_DB functions here because we need to connect as the administrative user.
$characterSet = $this->config->getValue('mysql.utf8mb4', false) ? 'utf8mb4' : 'utf8';
- $query = "CREATE DATABASE IF NOT EXISTS `$name` CHARACTER SET $characterSet COLLATE ${characterSet}_bin;";
+ $query = "CREATE DATABASE IF NOT EXISTS `$name` CHARACTER SET $characterSet COLLATE {$characterSet}_bin;";
$connection->executeUpdate($query);
} catch (\Exception $ex) {
$this->logger->error('Database creation failed.', [
diff --git a/lib/private/Share/Constants.php b/lib/private/Share/Constants.php
index 31c734f94aa..3632a2a26d1 100644
--- a/lib/private/Share/Constants.php
+++ b/lib/private/Share/Constants.php
@@ -79,7 +79,7 @@ class Constants {
public const FORMAT_STATUSES = -2;
public const FORMAT_SOURCES = -3; // ToDo Check if it is still in use otherwise remove it
- public const RESPONSE_FORMAT = 'json'; // default resonse format for ocs calls
+ public const RESPONSE_FORMAT = 'json'; // default response format for ocs calls
public const TOKEN_LENGTH = 15; // old (oc7) length is 32, keep token length in db at least that for compatibility
diff --git a/lib/private/Share/Share.php b/lib/private/Share/Share.php
index 9018f35ac2a..f47c042df29 100644
--- a/lib/private/Share/Share.php
+++ b/lib/private/Share/Share.php
@@ -732,7 +732,7 @@ class Share extends Constants {
foreach ($result as $key => $r) {
// for file/folder shares we need to compare file_source, otherwise we compare item_source
// only group shares if they already point to the same target, otherwise the file where shared
- // before grouping of shares was added. In this case we don't group them toi avoid confusions
+ // before grouping of shares was added. In this case we don't group them to avoid confusions
if (($fileSharing && $item['file_source'] === $r['file_source'] && $item['file_target'] === $r['file_target']) ||
(!$fileSharing && $item['item_source'] === $r['item_source'] && $item['item_target'] === $r['item_target'])) {
// add the first item to the list of grouped shares
@@ -757,7 +757,7 @@ class Share extends Constants {
/**
* construct select statement
* @param int $format
- * @param boolean $fileDependent ist it a file/folder share or a generla share
+ * @param boolean $fileDependent ist it a file/folder share or a general share
* @param string $uidOwner
* @return string select statement
*/
diff --git a/lib/private/Share20/DefaultShareProvider.php b/lib/private/Share20/DefaultShareProvider.php
index 520bd17d3cf..70f9b8665f9 100644
--- a/lib/private/Share20/DefaultShareProvider.php
+++ b/lib/private/Share20/DefaultShareProvider.php
@@ -52,6 +52,7 @@ use OCP\IUserManager;
use OCP\L10N\IFactory;
use OCP\Mail\IMailer;
use OCP\Share\Exceptions\ShareNotFound;
+use OCP\Share\IAttributes;
use OCP\Share\IShare;
use OCP\Share\IShareProvider;
@@ -174,6 +175,8 @@ class DefaultShareProvider implements IShareProvider {
if (method_exists($share, 'getParent')) {
$qb->setValue('parent', $qb->createNamedParameter($share->getParent()));
}
+
+ $qb->setValue('hide_download', $qb->createNamedParameter($share->getHideDownload() ? 1 : 0, IQueryBuilder::PARAM_INT));
} else {
throw new \Exception('invalid share type!');
}
@@ -193,6 +196,12 @@ class DefaultShareProvider implements IShareProvider {
// set the permissions
$qb->setValue('permissions', $qb->createNamedParameter($share->getPermissions()));
+ // set share attributes
+ $shareAttributes = $this->formatShareAttributes(
+ $share->getAttributes()
+ );
+ $qb->setValue('attributes', $qb->createNamedParameter($shareAttributes));
+
// Set who created this share
$qb->setValue('uid_initiator', $qb->createNamedParameter($share->getSharedBy()));
@@ -248,6 +257,8 @@ class DefaultShareProvider implements IShareProvider {
public function update(\OCP\Share\IShare $share) {
$originalShare = $this->getShareById($share->getId());
+ $shareAttributes = $this->formatShareAttributes($share->getAttributes());
+
if ($share->getShareType() === IShare::TYPE_USER) {
/*
* We allow updating the recipient on user shares.
@@ -259,6 +270,7 @@ class DefaultShareProvider implements IShareProvider {
->set('uid_owner', $qb->createNamedParameter($share->getShareOwner()))
->set('uid_initiator', $qb->createNamedParameter($share->getSharedBy()))
->set('permissions', $qb->createNamedParameter($share->getPermissions()))
+ ->set('attributes', $qb->createNamedParameter($shareAttributes))
->set('item_source', $qb->createNamedParameter($share->getNode()->getId()))
->set('file_source', $qb->createNamedParameter($share->getNode()->getId()))
->set('expiration', $qb->createNamedParameter($share->getExpirationDate(), IQueryBuilder::PARAM_DATE))
@@ -272,6 +284,7 @@ class DefaultShareProvider implements IShareProvider {
->set('uid_owner', $qb->createNamedParameter($share->getShareOwner()))
->set('uid_initiator', $qb->createNamedParameter($share->getSharedBy()))
->set('permissions', $qb->createNamedParameter($share->getPermissions()))
+ ->set('attributes', $qb->createNamedParameter($shareAttributes))
->set('item_source', $qb->createNamedParameter($share->getNode()->getId()))
->set('file_source', $qb->createNamedParameter($share->getNode()->getId()))
->set('expiration', $qb->createNamedParameter($share->getExpirationDate(), IQueryBuilder::PARAM_DATE))
@@ -301,6 +314,7 @@ class DefaultShareProvider implements IShareProvider {
->where($qb->expr()->eq('parent', $qb->createNamedParameter($share->getId())))
->andWhere($qb->expr()->neq('permissions', $qb->createNamedParameter(0)))
->set('permissions', $qb->createNamedParameter($share->getPermissions()))
+ ->set('attributes', $qb->createNamedParameter($shareAttributes))
->execute();
} elseif ($share->getShareType() === IShare::TYPE_LINK) {
$qb = $this->dbConn->getQueryBuilder();
@@ -311,6 +325,7 @@ class DefaultShareProvider implements IShareProvider {
->set('uid_owner', $qb->createNamedParameter($share->getShareOwner()))
->set('uid_initiator', $qb->createNamedParameter($share->getSharedBy()))
->set('permissions', $qb->createNamedParameter($share->getPermissions()))
+ ->set('attributes', $qb->createNamedParameter($shareAttributes))
->set('item_source', $qb->createNamedParameter($share->getNode()->getId()))
->set('file_source', $qb->createNamedParameter($share->getNode()->getId()))
->set('token', $qb->createNamedParameter($share->getToken()))
@@ -611,6 +626,10 @@ class DefaultShareProvider implements IShareProvider {
$data = $stmt->fetch();
$stmt->closeCursor();
+ $shareAttributes = $this->formatShareAttributes(
+ $share->getAttributes()
+ );
+
if ($data === false) {
// No usergroup share yet. Create one.
$qb = $this->dbConn->getQueryBuilder();
@@ -626,6 +645,7 @@ class DefaultShareProvider implements IShareProvider {
'file_source' => $qb->createNamedParameter($share->getNodeId()),
'file_target' => $qb->createNamedParameter($share->getTarget()),
'permissions' => $qb->createNamedParameter($share->getPermissions()),
+ 'attributes' => $qb->createNamedParameter($shareAttributes),
'stime' => $qb->createNamedParameter($share->getShareTime()->getTimestamp()),
])->execute();
} else {
@@ -641,9 +661,12 @@ class DefaultShareProvider implements IShareProvider {
return $share;
}
- public function getSharesInFolder($userId, Folder $node, $reshares) {
+ public function getSharesInFolder($userId, Folder $node, $reshares, $shallow = true) {
$qb = $this->dbConn->getQueryBuilder();
- $qb->select('*')
+ $qb->select('s.*',
+ 'f.fileid', 'f.path', 'f.permissions AS f_permissions', 'f.storage', 'f.path_hash',
+ 'f.parent AS f_parent', 'f.name', 'f.mimetype', 'f.mimepart', 'f.size', 'f.mtime', 'f.storage_mtime',
+ 'f.encrypted', 'f.unencrypted_size', 'f.etag', 'f.checksum')
->from('share', 's')
->andWhere($qb->expr()->orX(
$qb->expr()->eq('item_type', $qb->createNamedParameter('file')),
@@ -679,12 +702,21 @@ class DefaultShareProvider implements IShareProvider {
}, $childMountNodes);
$qb->innerJoin('s', 'filecache', 'f', $qb->expr()->eq('s.file_source', 'f.fileid'));
- $qb->andWhere(
- $qb->expr()->orX(
- $qb->expr()->eq('f.parent', $qb->createNamedParameter($node->getId())),
- $qb->expr()->in('f.fileid', $qb->createParameter('chunk'))
- )
- );
+ if ($shallow) {
+ $qb->andWhere(
+ $qb->expr()->orX(
+ $qb->expr()->eq('f.parent', $qb->createNamedParameter($node->getId())),
+ $qb->expr()->in('f.fileid', $qb->createParameter('chunk'))
+ )
+ );
+ } else {
+ $qb->andWhere(
+ $qb->expr()->orX(
+ $qb->expr()->like('f.path', $qb->createNamedParameter($this->dbConn->escapeLikeParameter($node->getInternalPath()) . '/%')),
+ $qb->expr()->in('f.fileid', $qb->createParameter('chunk'))
+ )
+ );
+ }
$qb->orderBy('id');
@@ -1061,6 +1093,8 @@ class DefaultShareProvider implements IShareProvider {
$share->setToken($data['token']);
}
+ $share = $this->updateShareAttributes($share, $data['attributes']);
+
$share->setSharedBy($data['uid_initiator']);
$share->setShareOwner($data['uid_owner']);
@@ -1282,7 +1316,7 @@ class DefaultShareProvider implements IShareProvider {
$chunks = array_chunk($ids, 100);
foreach ($chunks as $chunk) {
/*
- * Delete all special shares wit this users for the found group shares
+ * Delete all special shares with this users for the found group shares
*/
$qb->delete('share')
->where($qb->expr()->eq('share_type', $qb->createNamedParameter(IShare::TYPE_USERGROUP)))
@@ -1528,4 +1562,48 @@ class DefaultShareProvider implements IShareProvider {
}
$cursor->closeCursor();
}
+
+ /**
+ * Load from database format (JSON string) to IAttributes
+ *
+ * @return IShare the modified share
+ */
+ private function updateShareAttributes(IShare $share, ?string $data): IShare {
+ if ($data !== null && $data !== '') {
+ $attributes = new ShareAttributes();
+ $compressedAttributes = \json_decode($data, true);
+ if ($compressedAttributes === false || $compressedAttributes === null) {
+ return $share;
+ }
+ foreach ($compressedAttributes as $compressedAttribute) {
+ $attributes->setAttribute(
+ $compressedAttribute[0],
+ $compressedAttribute[1],
+ $compressedAttribute[2]
+ );
+ }
+ $share->setAttributes($attributes);
+ }
+
+ return $share;
+ }
+
+ /**
+ * Format IAttributes to database format (JSON string)
+ */
+ private function formatShareAttributes(?IAttributes $attributes): ?string {
+ if ($attributes === null || empty($attributes->toArray())) {
+ return null;
+ }
+
+ $compressedAttributes = [];
+ foreach ($attributes->toArray() as $attribute) {
+ $compressedAttributes[] = [
+ 0 => $attribute['scope'],
+ 1 => $attribute['key'],
+ 2 => $attribute['enabled']
+ ];
+ }
+ return \json_encode($compressedAttributes);
+ }
}
diff --git a/lib/private/Share20/Manager.php b/lib/private/Share20/Manager.php
index eed86bb41c3..2ef61cf3404 100644
--- a/lib/private/Share20/Manager.php
+++ b/lib/private/Share20/Manager.php
@@ -41,7 +41,7 @@
*/
namespace OC\Share20;
-use OC\Cache\CappedMemoryCache;
+use OCP\Cache\CappedMemoryCache;
use OC\Files\Mount\MoveableMount;
use OC\KnownUser\KnownUserService;
use OC\Share20\Exception\ProviderException;
@@ -650,7 +650,7 @@ class Manager implements IManager {
}
// Check if public upload is allowed
- if (!$this->shareApiLinkAllowPublicUpload() &&
+ if ($share->getNodeType() === 'folder' && !$this->shareApiLinkAllowPublicUpload() &&
($share->getPermissions() & (\OCP\Constants::PERMISSION_CREATE | \OCP\Constants::PERMISSION_UPDATE | \OCP\Constants::PERMISSION_DELETE))) {
throw new \InvalidArgumentException('Public upload is not allowed');
}
@@ -1093,6 +1093,7 @@ class Manager implements IManager {
'shareWith' => $share->getSharedWith(),
'uidOwner' => $share->getSharedBy(),
'permissions' => $share->getPermissions(),
+ 'attributes' => $share->getAttributes() !== null ? $share->getAttributes()->toArray() : null,
'path' => $userFolder->getRelativePath($share->getNode()->getPath()),
]);
}
@@ -1303,11 +1304,11 @@ class Manager implements IManager {
return $provider->move($share, $recipientId);
}
- public function getSharesInFolder($userId, Folder $node, $reshares = false) {
+ public function getSharesInFolder($userId, Folder $node, $reshares = false, $shallow = true) {
$providers = $this->factory->getAllProviders();
- return array_reduce($providers, function ($shares, IShareProvider $provider) use ($userId, $node, $reshares) {
- $newShares = $provider->getSharesInFolder($userId, $node, $reshares);
+ return array_reduce($providers, function ($shares, IShareProvider $provider) use ($userId, $node, $reshares, $shallow) {
+ $newShares = $provider->getSharesInFolder($userId, $node, $reshares, $shallow);
foreach ($newShares as $fid => $data) {
if (!isset($shares[$fid])) {
$shares[$fid] = [];
@@ -1543,7 +1544,7 @@ class Manager implements IManager {
* Reduce the permissions for link or email shares if public upload is not enabled
*/
if (($share->getShareType() === IShare::TYPE_LINK || $share->getShareType() === IShare::TYPE_EMAIL)
- && !$this->shareApiLinkAllowPublicUpload()) {
+ && $share->getNodeType() === 'folder' && !$this->shareApiLinkAllowPublicUpload()) {
$share->setPermissions($share->getPermissions() & ~(\OCP\Constants::PERMISSION_CREATE | \OCP\Constants::PERMISSION_UPDATE));
}
@@ -1968,7 +1969,7 @@ class Manager implements IManager {
}
public function ignoreSecondDisplayName(): bool {
- return $this->config->getAppValue('core', 'shareapi_restrict_user_enumeration_full_match_ignore_second_display_name', 'no') === 'yes';
+ return $this->config->getAppValue('core', 'shareapi_restrict_user_enumeration_full_match_ignore_second_dn', 'no') === 'yes';
}
public function currentUserCanEnumerateTargetUser(?IUser $currentUser, IUser $targetUser): bool {
@@ -2010,7 +2011,7 @@ class Manager implements IManager {
/**
* Copied from \OC_Util::isSharingDisabledForUser
*
- * TODO: Deprecate fuction from OC_Util
+ * TODO: Deprecate function from OC_Util
*
* @param string $userId
* @return bool
diff --git a/lib/private/Share20/Share.php b/lib/private/Share20/Share.php
index 7ed03832e4c..c2d45503696 100644
--- a/lib/private/Share20/Share.php
+++ b/lib/private/Share20/Share.php
@@ -37,6 +37,7 @@ use OCP\Files\Node;
use OCP\Files\NotFoundException;
use OCP\IUserManager;
use OCP\Share\Exceptions\IllegalIDChangeException;
+use OCP\Share\IAttributes;
use OCP\Share\IShare;
class Share implements IShare {
@@ -65,6 +66,8 @@ class Share implements IShare {
private $shareOwner;
/** @var int */
private $permissions;
+ /** @var IAttributes */
+ private $attributes;
/** @var int */
private $status;
/** @var string */
@@ -319,7 +322,7 @@ class Share implements IShare {
* @inheritdoc
*/
public function setPermissions($permissions) {
- //TODO checkes
+ //TODO checks
$this->permissions = $permissions;
return $this;
@@ -335,6 +338,28 @@ class Share implements IShare {
/**
* @inheritdoc
*/
+ public function newAttributes(): IAttributes {
+ return new ShareAttributes();
+ }
+
+ /**
+ * @inheritdoc
+ */
+ public function setAttributes(?IAttributes $attributes) {
+ $this->attributes = $attributes;
+ return $this;
+ }
+
+ /**
+ * @inheritdoc
+ */
+ public function getAttributes(): ?IAttributes {
+ return $this->attributes;
+ }
+
+ /**
+ * @inheritdoc
+ */
public function setStatus(int $status): IShare {
$this->status = $status;
return $this;
@@ -511,7 +536,7 @@ class Share implements IShare {
* Set the parent of this share
*
* @param int parent
- * @return \OCP\Share\IShare
+ * @return IShare
* @deprecated The new shares do not have parents. This is just here for legacy reasons.
*/
public function setParent($parent) {
diff --git a/lib/private/Share20/ShareAttributes.php b/lib/private/Share20/ShareAttributes.php
new file mode 100644
index 00000000000..92f034e6783
--- /dev/null
+++ b/lib/private/Share20/ShareAttributes.php
@@ -0,0 +1,73 @@
+<?php
+/**
+ * @author Piotr Mrowczynski <piotr@owncloud.com>
+ *
+ * @copyright Copyright (c) 2019, ownCloud GmbH
+ * @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\Share20;
+
+use OCP\Share\IAttributes;
+
+class ShareAttributes implements IAttributes {
+
+ /** @var array */
+ private $attributes;
+
+ public function __construct() {
+ $this->attributes = [];
+ }
+
+ /**
+ * @inheritdoc
+ */
+ public function setAttribute($scope, $key, $enabled) {
+ if (!\array_key_exists($scope, $this->attributes)) {
+ $this->attributes[$scope] = [];
+ }
+ $this->attributes[$scope][$key] = $enabled;
+ return $this;
+ }
+
+ /**
+ * @inheritdoc
+ */
+ public function getAttribute($scope, $key) {
+ if (\array_key_exists($scope, $this->attributes) &&
+ \array_key_exists($key, $this->attributes[$scope])) {
+ return $this->attributes[$scope][$key];
+ }
+ return null;
+ }
+
+ /**
+ * @inheritdoc
+ */
+ public function toArray() {
+ $result = [];
+ foreach ($this->attributes as $scope => $keys) {
+ foreach ($keys as $key => $enabled) {
+ $result[] = [
+ "scope" => $scope,
+ "key" => $key,
+ "enabled" => $enabled
+ ];
+ }
+ }
+
+ return $result;
+ }
+}
diff --git a/lib/private/Template/JSConfigHelper.php b/lib/private/Template/JSConfigHelper.php
index a7b9fd35fd8..5f23b471837 100644
--- a/lib/private/Template/JSConfigHelper.php
+++ b/lib/private/Template/JSConfigHelper.php
@@ -47,6 +47,7 @@ use OCP\IInitialStateService;
use OCP\IL10N;
use OCP\ISession;
use OCP\IURLGenerator;
+use OCP\ILogger;
use OCP\IUser;
use OCP\User\Backend\IPasswordConfirmationBackend;
use OCP\Util;
@@ -176,6 +177,9 @@ class JSConfigHelper {
'sharing.maxAutocompleteResults' => max(0, $this->config->getSystemValueInt('sharing.maxAutocompleteResults', Constants::SHARING_MAX_AUTOCOMPLETE_RESULTS_DEFAULT)),
'sharing.minSearchStringLength' => $this->config->getSystemValueInt('sharing.minSearchStringLength', 0),
'blacklist_files_regex' => FileInfo::BLACKLIST_FILES_REGEX,
+ 'loglevel' => $this->config->getSystemValue('loglevel_frontend',
+ $this->config->getSystemValue('loglevel', ILogger::WARN)
+ ),
];
$array = [
diff --git a/lib/private/TemplateLayout.php b/lib/private/TemplateLayout.php
index a5aabc04b61..37f459ca52d 100644
--- a/lib/private/TemplateLayout.php
+++ b/lib/private/TemplateLayout.php
@@ -281,6 +281,9 @@ class TemplateLayout extends \OC_Template {
}
$this->assign('initialStates', $this->initialState->getInitialStates());
+
+ $this->assign('id-app-content', $renderAs === TemplateResponse::RENDER_AS_USER ? '#app-content' : '#content');
+ $this->assign('id-app-navigation', $renderAs === TemplateResponse::RENDER_AS_USER ? '#app-navigation' : null);
}
/**
diff --git a/lib/private/URLGenerator.php b/lib/private/URLGenerator.php
index 753a4a217d1..6115d4a221e 100644
--- a/lib/private/URLGenerator.php
+++ b/lib/private/URLGenerator.php
@@ -42,6 +42,8 @@ namespace OC;
use OC\Route\Router;
use OCA\Theming\ThemingDefaults;
+use OCP\App\AppPathNotFoundException;
+use OCP\App\IAppManager;
use OCP\ICacheFactory;
use OCP\IConfig;
use OCP\IRequest;
@@ -65,12 +67,14 @@ class URLGenerator implements IURLGenerator {
private $router;
/** @var null|string */
private $baseUrl = null;
+ private ?IAppManager $appManager = null;
public function __construct(IConfig $config,
IUserSession $userSession,
ICacheFactory $cacheFactory,
IRequest $request,
- Router $router) {
+ Router $router
+ ) {
$this->config = $config;
$this->userSession = $userSession;
$this->cacheFactory = $cacheFactory;
@@ -78,6 +82,14 @@ class URLGenerator implements IURLGenerator {
$this->router = $router;
}
+ private function getAppManager(): IAppManager {
+ if ($this->appManager !== null) {
+ return $this->appManager;
+ }
+ $this->appManager = \OCP\Server::get(IAppManager::class);
+ return $this->appManager;
+ }
+
/**
* Creates an url using a defined route
*
@@ -132,7 +144,7 @@ class URLGenerator implements IURLGenerator {
$frontControllerActive = ($this->config->getSystemValue('htaccess.IgnoreFrontController', false) === true || getenv('front_controller_active') === 'true');
if ($appName !== '') {
- $app_path = \OC_App::getAppPath($appName);
+ $app_path = $this->getAppManager()->getAppPath($appName);
// Check if the app is in the app folder
if ($app_path && file_exists($app_path . '/' . $file)) {
if (substr($file, -3) === 'php') {
@@ -142,7 +154,7 @@ class URLGenerator implements IURLGenerator {
}
$urlLinkTo .= ($file !== 'index.php') ? '/' . $file : '';
} else {
- $urlLinkTo = \OC_App::getAppWebPath($appName) . '/' . $file;
+ $urlLinkTo = $this->getAppManager()->getAppWebPath($appName) . '/' . $file;
}
} else {
$urlLinkTo = \OC::$WEBROOT . '/' . $appName . '/' . $file;
@@ -189,11 +201,20 @@ class URLGenerator implements IURLGenerator {
//if a theme has a png but not an svg always use the png
$basename = substr(basename($file), 0, -4);
- $appPath = \OC_App::getAppPath($appName);
+ try {
+ $appPath = $this->getAppManager()->getAppPath($appName);
+ } catch (AppPathNotFoundException $e) {
+ if ($appName === 'core' || $appName === '') {
+ $appName = 'core';
+ $appPath = false;
+ } else {
+ throw new RuntimeException('image not found: image: ' . $file . ' webroot: ' . \OC::$WEBROOT . ' serverroot: ' . \OC::$SERVERROOT);
+ }
+ }
// Check if the app is in the app folder
$path = '';
- $themingEnabled = $this->config->getSystemValue('installed', false) && \OCP\App::isEnabled('theming') && \OC_App::isAppLoaded('theming');
+ $themingEnabled = $this->config->getSystemValue('installed', false) && $this->getAppManager()->isEnabledForUser('theming');
$themingImagePath = false;
if ($themingEnabled) {
$themingDefaults = \OC::$server->getThemingDefaults();
@@ -220,10 +241,10 @@ class URLGenerator implements IURLGenerator {
} elseif ($themingEnabled && $themingImagePath) {
$path = $themingImagePath;
} elseif ($appPath && file_exists($appPath . "/img/$file")) {
- $path = \OC_App::getAppWebPath($appName) . "/img/$file";
+ $path = $this->getAppManager()->getAppWebPath($appName) . "/img/$file";
} elseif ($appPath && !file_exists($appPath . "/img/$basename.svg")
&& file_exists($appPath . "/img/$basename.png")) {
- $path = \OC_App::getAppWebPath($appName) . "/img/$basename.png";
+ $path = $this->getAppManager()->getAppWebPath($appName) . "/img/$basename.png";
} elseif (!empty($appName) and file_exists(\OC::$SERVERROOT . "/$appName/img/$file")) {
$path = \OC::$WEBROOT . "/$appName/img/$file";
} elseif (!empty($appName) and (!file_exists(\OC::$SERVERROOT . "/$appName/img/$basename.svg")
@@ -320,7 +341,7 @@ class URLGenerator implements IURLGenerator {
* @return string base url of the current request
*/
public function getBaseUrl(): string {
- // BaseUrl can be equal to 'http(s)://' during the first steps of the intial setup.
+ // BaseUrl can be equal to 'http(s)://' during the first steps of the initial setup.
if ($this->baseUrl === null || $this->baseUrl === "http://" || $this->baseUrl === "https://") {
$this->baseUrl = $this->request->getServerProtocol() . '://' . $this->request->getServerHost() . \OC::$WEBROOT;
}
diff --git a/lib/private/Updater.php b/lib/private/Updater.php
index 2c06cffcb19..da989c4db91 100644
--- a/lib/private/Updater.php
+++ b/lib/private/Updater.php
@@ -378,7 +378,7 @@ class Updater extends BasicEmitter {
$appManager = \OC::$server->getAppManager();
foreach ($apps as $app) {
// check if the app is compatible with this version of Nextcloud
- $info = OC_App::getAppInfo($app);
+ $info = $appManager->getAppInfo($app);
if ($info === null || !OC_App::isAppCompatible($version, $info)) {
if ($appManager->isShipped($app)) {
throw new \UnexpectedValueException('The files of the app "' . $app . '" were not correctly replaced before running the update');
diff --git a/lib/private/User/Database.php b/lib/private/User/Database.php
index 4821a2fc632..0b38f04bfe3 100644
--- a/lib/private/User/Database.php
+++ b/lib/private/User/Database.php
@@ -45,7 +45,7 @@ declare(strict_types=1);
*/
namespace OC\User;
-use OC\Cache\CappedMemoryCache;
+use OCP\Cache\CappedMemoryCache;
use OCP\EventDispatcher\IEventDispatcher;
use OCP\IDBConnection;
use OCP\Security\Events\ValidatePasswordPolicyEvent;
@@ -215,6 +215,10 @@ class Database extends ABackend implements
* Change the display name of a user
*/
public function setDisplayName(string $uid, string $displayName): bool {
+ if (mb_strlen($displayName) > 64) {
+ return false;
+ }
+
$this->fixDI();
if ($this->userExists($uid)) {
diff --git a/lib/private/User/Listeners/UserChangedListener.php b/lib/private/User/Listeners/UserChangedListener.php
new file mode 100644
index 00000000000..a561db2423d
--- /dev/null
+++ b/lib/private/User/Listeners/UserChangedListener.php
@@ -0,0 +1,62 @@
+<?php
+
+declare(strict_types=1);
+
+/**
+ * @copyright Copyright (c) 2022 Carl Schwan <carl@carlschwan.eu>
+ *
+ * @license AGPL-3.0-or-later
+ *
+ * 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\User\Listeners;
+
+use OCP\EventDispatcher\Event;
+use OCP\EventDispatcher\IEventListener;
+use OCP\User\Events\UserChangedEvent;
+use OCP\Files\NotFoundException;
+use OCP\IAvatarManager;
+
+/**
+ * @template-implements IEventListener<UserChangedEvent>
+ */
+class UserChangedListener implements IEventListener {
+ private IAvatarManager $avatarManager;
+
+ public function __construct(IAvatarManager $avatarManager) {
+ $this->avatarManager = $avatarManager;
+ }
+
+ public function handle(Event $event): void {
+ if (!($event instanceof UserChangedEvent)) {
+ return;
+ }
+
+ $user = $event->getUser();
+ $feature = $event->getFeature();
+ $oldValue = $event->getOldValue();
+ $value = $event->getValue();
+
+ // We only change the avatar on display name changes
+ if ($feature === 'displayName') {
+ try {
+ $avatar = $this->avatarManager->getAvatar($user->getUID());
+ $avatar->userChanged($feature, $oldValue, $value);
+ } catch (NotFoundException $e) {
+ // no avatar to remove
+ }
+ }
+ }
+}
diff --git a/lib/private/User/Listeners/UserDeletedListener.php b/lib/private/User/Listeners/UserDeletedListener.php
new file mode 100644
index 00000000000..7c9c46ef371
--- /dev/null
+++ b/lib/private/User/Listeners/UserDeletedListener.php
@@ -0,0 +1,65 @@
+<?php
+
+declare(strict_types=1);
+
+/**
+ * @copyright Copyright (c) 2022 Carl Schwan <carl@carlschwan.eu>
+ *
+ * @license AGPL-3.0-or-later
+ *
+ * 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\User\Listeners;
+
+use OCP\EventDispatcher\Event;
+use OCP\EventDispatcher\IEventListener;
+use OCP\User\Events\UserDeletedEvent;
+use OCP\Files\NotFoundException;
+use OCP\IAvatarManager;
+use Psr\Log\LoggerInterface;
+
+/**
+ * @template-implements IEventListener<UserDeletedEvent>
+ */
+class UserDeletedListener implements IEventListener {
+ private IAvatarManager $avatarManager;
+ private LoggerInterface $logger;
+
+ public function __construct(LoggerInterface $logger, IAvatarManager $avatarManager) {
+ $this->avatarManager = $avatarManager;
+ $this->logger = $logger;
+ }
+
+ public function handle(Event $event): void {
+ if (!($event instanceof UserDeletedEvent)) {
+ return;
+ }
+
+ $user = $event->getUser();
+
+ // Delete avatar on user deletion
+ try {
+ $avatar = $this->avatarManager->getAvatar($user->getUID());
+ $avatar->remove(true);
+ } catch (NotFoundException $e) {
+ // no avatar to remove
+ } catch (\Exception $e) {
+ // Ignore exceptions
+ $this->logger->info('Could not cleanup avatar of ' . $user->getUID(), [
+ 'exception' => $e,
+ ]);
+ }
+ }
+}
diff --git a/lib/private/User/User.php b/lib/private/User/User.php
index de9af35f541..7f7d6273e30 100644
--- a/lib/private/User/User.php
+++ b/lib/private/User/User.php
@@ -244,10 +244,15 @@ class User implements IUser {
* updates the timestamp of the most recent login of this user
*/
public function updateLastLoginTimestamp() {
- $firstTimeLogin = ($this->getLastLogin() === 0);
- $this->lastLogin = time();
- $this->config->setUserValue(
- $this->uid, 'login', 'lastLogin', (string)$this->lastLogin);
+ $previousLogin = $this->getLastLogin();
+ $now = time();
+ $firstTimeLogin = $previousLogin === 0;
+
+ if ($now - $previousLogin > 60) {
+ $this->lastLogin = time();
+ $this->config->setUserValue(
+ $this->uid, 'login', 'lastLogin', (string)$this->lastLogin);
+ }
return $firstTimeLogin;
}
@@ -555,15 +560,9 @@ class User implements IUser {
return $uid . '@' . $server;
}
- /**
- * @param string $url
- * @return string
- */
- private function removeProtocolFromUrl($url) {
+ private function removeProtocolFromUrl(string $url): string {
if (strpos($url, 'https://') === 0) {
return substr($url, strlen('https://'));
- } elseif (strpos($url, 'http://') === 0) {
- return substr($url, strlen('http://'));
}
return $url;
diff --git a/lib/private/legacy/OC_App.php b/lib/private/legacy/OC_App.php
index f290b7a610c..482fc4e88e7 100644
--- a/lib/private/legacy/OC_App.php
+++ b/lib/private/legacy/OC_App.php
@@ -183,7 +183,7 @@ class OC_App {
'app' => $app,
]);
try {
- self::requireAppFile($app);
+ self::requireAppFile($appPath);
} catch (Throwable $ex) {
if ($ex instanceof ServerNotAvailableException) {
throw $ex;
@@ -679,25 +679,6 @@ class OC_App {
}
/**
- * register an admin form to be shown
- *
- * @param string $app
- * @param string $page
- */
- public static function registerAdmin(string $app, string $page) {
- self::$adminForms[] = $app . '/' . $page . '.php';
- }
-
- /**
- * register a personal form to be shown
- * @param string $app
- * @param string $page
- */
- public static function registerPersonal(string $app, string $page) {
- self::$personalForms[] = $app . '/' . $page . '.php';
- }
-
- /**
* @param array $entry
* @deprecated 20.0.0 Please register your alternative login option using the registerAlternativeLogin() on the RegistrationContext in your Application class implementing the OCP\Authentication\IAlternativeLogin interface
*/
diff --git a/lib/private/legacy/OC_Files.php b/lib/private/legacy/OC_Files.php
index 02e15fd08d5..6a3a44d6cc0 100644
--- a/lib/private/legacy/OC_Files.php
+++ b/lib/private/legacy/OC_Files.php
@@ -44,10 +44,12 @@ use bantu\IniGetWrapper\IniGetWrapper;
use OC\Files\View;
use OC\Streamer;
use OCP\Lock\ILockingProvider;
+use OCP\Files\Events\BeforeZipCreatedEvent;
+use OCP\Files\Events\BeforeDirectFileDownloadEvent;
+use OCP\EventDispatcher\IEventDispatcher;
/**
* Class for file server access
- *
*/
class OC_Files {
public const FILE = 1;
@@ -167,6 +169,14 @@ class OC_Files {
}
}
+ //Dispatch an event to see if any apps have problem with download
+ $event = new BeforeZipCreatedEvent($dir, is_array($files) ? $files : [$files]);
+ $dispatcher = \OCP\Server::get(IEventDispatcher::class);
+ $dispatcher->dispatchTyped($event);
+ if ((!$event->isSuccessful()) || $event->getErrorMessage() !== null) {
+ throw new \OC\ForbiddenException($event->getErrorMessage());
+ }
+
$streamer = new Streamer(\OC::$server->getRequest(), $fileSize, $numberOfFiles);
OC_Util::obEnd();
@@ -222,13 +232,16 @@ class OC_Files {
self::unlockAllTheFiles($dir, $files, $getType, $view, $filename);
OC::$server->getLogger()->logException($ex);
$l = \OC::$server->getL10N('lib');
- \OC_Template::printErrorPage($l->t('Cannot read file'), $ex->getMessage(), 200);
+ \OC_Template::printErrorPage($l->t('Cannot download file'), $ex->getMessage(), 200);
} catch (\Exception $ex) {
self::unlockAllTheFiles($dir, $files, $getType, $view, $filename);
OC::$server->getLogger()->logException($ex);
$l = \OC::$server->getL10N('lib');
$hint = method_exists($ex, 'getHint') ? $ex->getHint() : '';
- \OC_Template::printErrorPage($l->t('Cannot read file'), $hint, 200);
+ if ($event && $event->getErrorMessage() !== null) {
+ $hint .= ' ' . $event->getErrorMessage();
+ }
+ \OC_Template::printErrorPage($l->t('Cannot download file'), $hint, 200);
}
}
@@ -287,6 +300,7 @@ class OC_Files {
* @param string $name
* @param string $dir
* @param array $params ; 'head' boolean to only send header of the request ; 'range' http range header
+ * @throws \OC\ForbiddenException
*/
private static function getSingleFile($view, $dir, $name, $params) {
$filename = $dir . '/' . $name;
@@ -322,6 +336,19 @@ class OC_Files {
$rangeArray = self::parseHttpRangeHeader(substr($params['range'], 6), $fileSize);
}
+ $dispatcher = \OC::$server->query(IEventDispatcher::class);
+ $event = new BeforeDirectFileDownloadEvent($filename);
+ $dispatcher->dispatchTyped($event);
+
+ if (!\OC\Files\Filesystem::isReadable($filename) || $event->getErrorMessage()) {
+ if ($event->getErrorMessage()) {
+ $msg = $event->getErrorMessage();
+ } else {
+ $msg = 'Access denied';
+ }
+ throw new \OC\ForbiddenException($msg);
+ }
+
self::sendHeaders($filename, $name, $rangeArray);
if (isset($params['head']) && $params['head']) {
diff --git a/lib/private/legacy/OC_User.php b/lib/private/legacy/OC_User.php
index b7547be5e82..de066e143b4 100644
--- a/lib/private/legacy/OC_User.php
+++ b/lib/private/legacy/OC_User.php
@@ -178,7 +178,11 @@ class OC_User {
}
$userSession->setLoginName($uid);
$request = OC::$server->getRequest();
- $userSession->createSessionToken($request, $uid, $uid);
+ $password = null;
+ if ($backend instanceof \OCP\Authentication\IProvideUserSecretBackend) {
+ $password = $backend->getCurrentUserSecret();
+ }
+ $userSession->createSessionToken($request, $uid, $uid, $password);
$userSession->createRememberMeToken($userSession->getUser());
// setup the filesystem
OC_Util::setupFS($uid);
@@ -191,7 +195,7 @@ class OC_User {
'post_login',
[
'uid' => $uid,
- 'password' => null,
+ 'password' => $password,
'isTokenLogin' => false,
]
);