diff options
Diffstat (limited to 'lib/private')
31 files changed, 213 insertions, 214 deletions
diff --git a/lib/private/Accounts/AccountManager.php b/lib/private/Accounts/AccountManager.php index 1d64d604578..60065272a58 100644 --- a/lib/private/Accounts/AccountManager.php +++ b/lib/private/Accounts/AccountManager.php @@ -38,7 +38,6 @@ namespace OC\Accounts; use Exception; use InvalidArgumentException; use libphonenumber\NumberParseException; -use libphonenumber\PhoneNumber; use libphonenumber\PhoneNumberFormat; use libphonenumber\PhoneNumberUtil; use OC\Profile\TProfileHelper; @@ -186,7 +185,7 @@ class AccountManager implements IAccountManager { $phoneUtil = PhoneNumberUtil::getInstance(); try { $phoneNumber = $phoneUtil->parse($input, $defaultRegion); - if ($phoneNumber instanceof PhoneNumber && $phoneUtil->isValidNumber($phoneNumber)) { + if ($phoneUtil->isValidNumber($phoneNumber)) { return $phoneUtil->format($phoneNumber, PhoneNumberFormat::E164); } } catch (NumberParseException $e) { diff --git a/lib/private/AppFramework/App.php b/lib/private/AppFramework/App.php index 0cbe47b75a5..ffd77da888e 100644 --- a/lib/private/AppFramework/App.php +++ b/lib/private/AppFramework/App.php @@ -34,6 +34,7 @@ namespace OC\AppFramework; use OC\AppFramework\DependencyInjection\DIContainer; use OC\AppFramework\Http\Dispatcher; use OC\AppFramework\Http\Request; +use OCP\App\IAppManager; use OCP\Profiler\IProfiler; use OC\Profiler\RoutingDataCollector; use OCP\AppFramework\QueryException; @@ -68,7 +69,7 @@ class App { return $topNamespace . self::$nameSpaceCache[$appId]; } - $appInfo = \OC_App::getAppInfo($appId); + $appInfo = \OCP\Server::get(IAppManager::class)->getAppInfo($appId); if (isset($appInfo['namespace'])) { self::$nameSpaceCache[$appId] = trim($appInfo['namespace']); } else { diff --git a/lib/private/AppFramework/Middleware/Security/BruteForceMiddleware.php b/lib/private/AppFramework/Middleware/Security/BruteForceMiddleware.php index ba8c7f45b49..2ecd26a68e1 100644 --- a/lib/private/AppFramework/Middleware/Security/BruteForceMiddleware.php +++ b/lib/private/AppFramework/Middleware/Security/BruteForceMiddleware.php @@ -90,32 +90,40 @@ class BruteForceMiddleware extends Middleware { */ public function afterController($controller, $methodName, Response $response) { if ($response->isThrottled()) { - if ($this->reflector->hasAnnotation('BruteForceProtection')) { - $action = $this->reflector->getAnnotationParameter('BruteForceProtection', 'action'); - $ip = $this->request->getRemoteAddress(); - $this->throttler->sleepDelay($ip, $action); - $this->throttler->registerAttempt($action, $ip, $response->getThrottleMetadata()); - } else { - $reflectionMethod = new ReflectionMethod($controller, $methodName); - $attributes = $reflectionMethod->getAttributes(BruteForceProtection::class); - - if (!empty($attributes)) { + try { + if ($this->reflector->hasAnnotation('BruteForceProtection')) { + $action = $this->reflector->getAnnotationParameter('BruteForceProtection', 'action'); $ip = $this->request->getRemoteAddress(); - $metaData = $response->getThrottleMetadata(); - - foreach ($attributes as $attribute) { - /** @var BruteForceProtection $protection */ - $protection = $attribute->newInstance(); - $action = $protection->getAction(); - - if (!isset($metaData['action']) || $metaData['action'] === $action) { - $this->throttler->sleepDelay($ip, $action); - $this->throttler->registerAttempt($action, $ip, $metaData); + $this->throttler->registerAttempt($action, $ip, $response->getThrottleMetadata()); + $this->throttler->sleepDelayOrThrowOnMax($ip, $action); + } else { + $reflectionMethod = new ReflectionMethod($controller, $methodName); + $attributes = $reflectionMethod->getAttributes(BruteForceProtection::class); + + if (!empty($attributes)) { + $ip = $this->request->getRemoteAddress(); + $metaData = $response->getThrottleMetadata(); + + foreach ($attributes as $attribute) { + /** @var BruteForceProtection $protection */ + $protection = $attribute->newInstance(); + $action = $protection->getAction(); + + if (!isset($metaData['action']) || $metaData['action'] === $action) { + $this->throttler->registerAttempt($action, $ip, $metaData); + $this->throttler->sleepDelayOrThrowOnMax($ip, $action); + } } + } else { + $this->logger->debug('Response for ' . get_class($controller) . '::' . $methodName . ' got bruteforce throttled but has no annotation nor attribute defined.'); } - } else { - $this->logger->debug('Response for ' . get_class($controller) . '::' . $methodName . ' got bruteforce throttled but has no annotation nor attribute defined.'); } + } catch (MaxDelayReached $e) { + if ($controller instanceof OCSController) { + throw new OCSException($e->getMessage(), Http::STATUS_TOO_MANY_REQUESTS); + } + + return new TooManyRequestsResponse(); } } diff --git a/lib/private/Avatar/AvatarManager.php b/lib/private/Avatar/AvatarManager.php index 5c4f04de6d6..4125c8eb0a8 100644 --- a/lib/private/Avatar/AvatarManager.php +++ b/lib/private/Avatar/AvatarManager.php @@ -160,13 +160,8 @@ class AvatarManager implements IAvatarManager { public function clearCachedAvatars() { $users = $this->config->getUsersForUserValue('avatar', 'generated', 'true'); foreach ($users as $userId) { - try { - $folder = $this->appData->getFolder($userId); - $folder->delete(); - } catch (NotFoundException $e) { - $this->logger->debug("No cache for the user $userId. Ignoring..."); - } - $this->config->setUserValue($userId, 'avatar', 'generated', 'false'); + // This also bumps the avatar version leading to cache invalidation in browsers + $this->getAvatar($userId)->remove(); } } diff --git a/lib/private/Files/Cache/Cache.php b/lib/private/Files/Cache/Cache.php index f085f8ade5e..5af15700764 100644 --- a/lib/private/Files/Cache/Cache.php +++ b/lib/private/Files/Cache/Cache.php @@ -60,6 +60,7 @@ use OCP\Files\Search\ISearchOperator; use OCP\Files\Search\ISearchQuery; use OCP\Files\Storage\IStorage; use OCP\IDBConnection; +use OCP\Util; use Psr\Log\LoggerInterface; /** @@ -191,8 +192,8 @@ class Cache implements ICache { $data['path'] = (string)$data['path']; $data['fileid'] = (int)$data['fileid']; $data['parent'] = (int)$data['parent']; - $data['size'] = 0 + $data['size']; - $data['unencrypted_size'] = 0 + ($data['unencrypted_size'] ?? 0); + $data['size'] = Util::numericToNumber($data['size']); + $data['unencrypted_size'] = Util::numericToNumber($data['unencrypted_size'] ?? 0); $data['mtime'] = (int)$data['mtime']; $data['storage_mtime'] = (int)$data['storage_mtime']; $data['encryptedVersion'] = (int)$data['encrypted']; @@ -900,7 +901,7 @@ class Cache implements ICache { * * @param string $path * @param array|null|ICacheEntry $entry (optional) meta data of the folder - * @return int + * @return int|float */ public function calculateFolderSize($path, $entry = null) { return $this->calculateFolderSizeInner($path, $entry); @@ -913,7 +914,7 @@ class Cache implements ICache { * @param string $path * @param array|null|ICacheEntry $entry (optional) meta data of the folder * @param bool $ignoreUnknown don't mark the folder size as unknown if any of it's children are unknown - * @return int + * @return int|float */ protected function calculateFolderSizeInner(string $path, $entry = null, bool $ignoreUnknown = false) { $totalSize = 0; @@ -937,13 +938,13 @@ class Cache implements ICache { if ($rows) { $sizes = array_map(function (array $row) { - return (int)$row['size']; + return Util::numericToNumber($row['size']); }, $rows); $unencryptedOnlySizes = array_map(function (array $row) { - return (int)$row['unencrypted_size']; + return Util::numericToNumber($row['unencrypted_size']); }, $rows); $unencryptedSizes = array_map(function (array $row) { - return (int)(($row['unencrypted_size'] > 0) ? $row['unencrypted_size'] : $row['size']); + return Util::numericToNumber(($row['unencrypted_size'] > 0) ? $row['unencrypted_size'] : $row['size']); }, $rows); $sum = array_sum($sizes); diff --git a/lib/private/Files/Cache/HomeCache.php b/lib/private/Files/Cache/HomeCache.php index 9dbbb46c57b..83e5cb89b8a 100644 --- a/lib/private/Files/Cache/HomeCache.php +++ b/lib/private/Files/Cache/HomeCache.php @@ -36,7 +36,7 @@ class HomeCache extends Cache { * * @param string $path * @param array|null|ICacheEntry $entry (optional) meta data of the folder - * @return int + * @return int|float */ public function calculateFolderSize($path, $entry = null) { if ($path !== '/' and $path !== '' and $path !== 'files' and $path !== 'files_trashbin' and $path !== 'files_versions') { diff --git a/lib/private/Files/Cache/Scanner.php b/lib/private/Files/Cache/Scanner.php index a34f1db3195..edf9b5697e7 100644 --- a/lib/private/Files/Cache/Scanner.php +++ b/lib/private/Files/Cache/Scanner.php @@ -386,7 +386,7 @@ class Scanner extends BasicEmitter implements IScanner { * @param int $folderId id for the folder to be scanned * @param bool $lock set to false to disable getting an additional read lock during scanning * @param array $data the data of the folder before (re)scanning the children - * @return int the size of the scanned folder or -1 if the size is unknown at this stage + * @return int|float the size of the scanned folder or -1 if the size is unknown at this stage */ protected function scanChildren($path, $recursive = self::SCAN_RECURSIVE, $reuse = -1, $folderId = null, $lock = true, array $data = []) { if ($reuse === -1) { diff --git a/lib/private/Files/Cache/Wrapper/CacheJail.php b/lib/private/Files/Cache/Wrapper/CacheJail.php index 2885a8bf5e0..d8cf3eb61d7 100644 --- a/lib/private/Files/Cache/Wrapper/CacheJail.php +++ b/lib/private/Files/Cache/Wrapper/CacheJail.php @@ -240,7 +240,7 @@ class CacheJail extends CacheWrapper { * * @param string $path * @param array|null|ICacheEntry $entry (optional) meta data of the folder - * @return int + * @return int|float */ public function calculateFolderSize($path, $entry = null) { if ($this->getCache() instanceof Cache) { diff --git a/lib/private/Files/Cache/Wrapper/CacheWrapper.php b/lib/private/Files/Cache/Wrapper/CacheWrapper.php index c1043fbc6fb..6479ea793b0 100644 --- a/lib/private/Files/Cache/Wrapper/CacheWrapper.php +++ b/lib/private/Files/Cache/Wrapper/CacheWrapper.php @@ -250,7 +250,7 @@ class CacheWrapper extends Cache { * * @param string $path * @param array|null|ICacheEntry $entry (optional) meta data of the folder - * @return int + * @return int|float */ public function calculateFolderSize($path, $entry = null) { if ($this->getCache() instanceof Cache) { diff --git a/lib/private/Files/Node/LazyFolder.php b/lib/private/Files/Node/LazyFolder.php index cc1f64889a1..c843baabade 100644 --- a/lib/private/Files/Node/LazyFolder.php +++ b/lib/private/Files/Node/LazyFolder.php @@ -27,6 +27,7 @@ declare(strict_types=1); namespace OC\Files\Node; use OC\Files\Utils\PathHelper; +use OCP\Files\Folder; use OCP\Constants; /** @@ -37,8 +38,8 @@ use OCP\Constants; * * @package OC\Files\Node */ -class LazyFolder implements \OCP\Files\Folder { - /** @var \Closure */ +class LazyFolder implements Folder { + /** @var \Closure(): Folder */ private $folderClosure; /** @var LazyFolder | null */ @@ -49,7 +50,7 @@ class LazyFolder implements \OCP\Files\Folder { /** * LazyFolder constructor. * - * @param \Closure $folderClosure + * @param \Closure(): Folder $folderClosure */ public function __construct(\Closure $folderClosure, array $data = []) { $this->folderClosure = $folderClosure; diff --git a/lib/private/Files/Node/LazyUserFolder.php b/lib/private/Files/Node/LazyUserFolder.php index 81009532dbf..8fbdec4b49d 100644 --- a/lib/private/Files/Node/LazyUserFolder.php +++ b/lib/private/Files/Node/LazyUserFolder.php @@ -28,7 +28,10 @@ use OCP\Constants; use OCP\Files\IRootFolder; use OCP\Files\Mount\IMountManager; use OCP\Files\NotFoundException; +use OCP\Files\Folder; +use OCP\Files\File; use OCP\IUser; +use Psr\Log\LoggerInterface; class LazyUserFolder extends LazyFolder { private IRootFolder $root; @@ -41,14 +44,22 @@ class LazyUserFolder extends LazyFolder { $this->user = $user; $this->mountManager = $mountManager; $this->path = '/' . $user->getUID() . '/files'; - parent::__construct(function () use ($user) { + parent::__construct(function () use ($user): Folder { try { - return $this->root->get('/' . $user->getUID() . '/files'); + $node = $this->root->get($this->path); + if ($node instanceof File) { + $e = new \RuntimeException(); + \OCP\Server::get(LoggerInterface::class)->error('User root storage is not a folder: ' . $this->path, [ + 'exception' => $e, + ]); + throw $e; + } + return $node; } catch (NotFoundException $e) { if (!$this->root->nodeExists('/' . $user->getUID())) { $this->root->newFolder('/' . $user->getUID()); } - return $this->root->newFolder('/' . $user->getUID() . '/files'); + return $this->root->newFolder($this->path); } }, [ 'path' => $this->path, diff --git a/lib/private/Files/ObjectStore/ObjectStoreStorage.php b/lib/private/Files/ObjectStore/ObjectStoreStorage.php index ea60de137d2..978b5b0451c 100644 --- a/lib/private/Files/ObjectStore/ObjectStoreStorage.php +++ b/lib/private/Files/ObjectStore/ObjectStoreStorage.php @@ -87,17 +87,13 @@ class ObjectStoreStorage extends \OC\Files\Storage\Common implements IChunkedFil if (isset($params['validateWrites'])) { $this->validateWrites = (bool)$params['validateWrites']; } - //initialize cache with root directory in cache - if (!$this->is_dir('/')) { - $this->mkdir('/'); - } $this->logger = \OC::$server->getLogger(); } - public function mkdir($path) { + public function mkdir($path, bool $force = false) { $path = $this->normalizePath($path); - if ($this->file_exists($path)) { + if (!$force && $this->file_exists($path)) { $this->logger->warning("Tried to create an object store folder that already exists: $path"); return false; } @@ -246,6 +242,13 @@ class ObjectStoreStorage extends \OC\Files\Storage\Common implements IChunkedFil if ($cacheEntry instanceof CacheEntry) { return $cacheEntry->getData(); } else { + if ($path === '') { + $this->mkdir('', true); + $cacheEntry = $this->getCache()->get($path); + if ($cacheEntry instanceof CacheEntry) { + return $cacheEntry->getData(); + } + } return false; } } @@ -357,6 +360,12 @@ class ObjectStoreStorage extends \OC\Files\Storage\Common implements IChunkedFil case 'wb': case 'w+': case 'wb+': + $dirName = dirname($path); + $parentExists = $this->is_dir($dirName); + if (!$parentExists) { + return false; + } + $tmpFile = \OC::$server->getTempManager()->getTemporaryFile($ext); $handle = fopen($tmpFile, $mode); return CallbackWrapper::wrap($handle, null, null, function () use ($path, $tmpFile) { @@ -469,6 +478,9 @@ class ObjectStoreStorage extends \OC\Files\Storage\Common implements IChunkedFil public function file_put_contents($path, $data) { $handle = $this->fopen($path, 'w+'); + if (!$handle) { + return false; + } $result = fwrite($handle, $data); fclose($handle); return $result; diff --git a/lib/private/Files/Storage/DAV.php b/lib/private/Files/Storage/DAV.php index 6a05f54a0b4..733aa10cde6 100644 --- a/lib/private/Files/Storage/DAV.php +++ b/lib/private/Files/Storage/DAV.php @@ -51,6 +51,7 @@ use OCP\Files\StorageInvalidException; use OCP\Files\StorageNotAvailableException; use OCP\Http\Client\IClientService; use OCP\ICertificateManager; +use OCP\Util; use Psr\Http\Message\ResponseInterface; use Sabre\DAV\Client; use Sabre\DAV\Xml\Property\ResourceType; @@ -451,7 +452,7 @@ class DAV extends Common { return FileInfo::SPACE_UNKNOWN; } if (isset($response['{DAV:}quota-available-bytes'])) { - return (int)$response['{DAV:}quota-available-bytes']; + return Util::numericToNumber($response['{DAV:}quota-available-bytes']); } else { return FileInfo::SPACE_UNKNOWN; } @@ -605,7 +606,7 @@ class DAV extends Common { } return [ 'mtime' => isset($response['{DAV:}getlastmodified']) ? strtotime($response['{DAV:}getlastmodified']) : null, - 'size' => (int)($response['{DAV:}getcontentlength'] ?? 0), + 'size' => Util::numericToNumber($response['{DAV:}getcontentlength'] ?? 0), ]; } catch (\Exception $e) { $this->convertException($e, $path); diff --git a/lib/private/Files/Storage/Local.php b/lib/private/Files/Storage/Local.php index ee86fe356f7..c0ce0e7021a 100644 --- a/lib/private/Files/Storage/Local.php +++ b/lib/private/Files/Storage/Local.php @@ -52,6 +52,7 @@ use OCP\Files\GenericFileException; use OCP\Files\IMimeTypeDetector; use OCP\Files\Storage\IStorage; use OCP\IConfig; +use OCP\Util; use Psr\Log\LoggerInterface; /** @@ -135,10 +136,10 @@ class Local extends \OC\Files\Storage\Common { if (in_array($file->getBasename(), ['.', '..'])) { $it->next(); continue; - } elseif ($file->isDir()) { - rmdir($file->getPathname()); } elseif ($file->isFile() || $file->isLink()) { unlink($file->getPathname()); + } elseif ($file->isDir()) { + rmdir($file->getPathname()); } $it->next(); } @@ -422,7 +423,7 @@ class Local extends \OC\Files\Storage\Common { if ($space === false || is_null($space)) { return \OCP\Files\FileInfo::SPACE_UNKNOWN; } - return (int)$space; + return Util::numericToNumber($space); } public function search($query) { diff --git a/lib/private/Files/Type/Loader.php b/lib/private/Files/Type/Loader.php index 32013bc3786..20c298f21b3 100644 --- a/lib/private/Files/Type/Loader.php +++ b/lib/private/Files/Type/Loader.php @@ -116,7 +116,7 @@ class Loader implements IMimeTypeLoader { * @return int inserted ID */ protected function store($mimetype) { - $row = $this->atomic(function () use ($mimetype) { + $mimetypeId = $this->atomic(function () use ($mimetype) { try { $insert = $this->dbConnection->getQueryBuilder(); $insert->insert('mimetypes') @@ -124,34 +124,28 @@ class Loader implements IMimeTypeLoader { 'mimetype' => $insert->createNamedParameter($mimetype) ]) ->executeStatement(); - return [ - 'mimetype' => $mimetype, - 'id' => $insert->getLastInsertId(), - ]; + return $insert->getLastInsertId(); } catch (DbalException $e) { if ($e->getReason() !== DBException::REASON_UNIQUE_CONSTRAINT_VIOLATION) { throw $e; } $qb = $this->dbConnection->getQueryBuilder(); - $row = $qb->select('id') + $qb->select('id') ->from('mimetypes') - ->where($qb->expr()->eq('mimetype', $qb->createNamedParameter($mimetype))) - ->executeQuery() - ->fetchOne(); - if ($row) { - return [ - 'mimetype' => $mimetype, - 'id' => $row['id'], - ]; + ->where($qb->expr()->eq('mimetype', $qb->createNamedParameter($mimetype))); + $result = $qb->executeQuery(); + $id = $result->fetchOne(); + $result->closeCursor(); + if ($id !== false) { + return (int) $id; } throw new \Exception("Database threw an unique constraint on inserting a new mimetype, but couldn't return the ID for this very mimetype"); } }, $this->dbConnection); - if (!$row) { + if (!$mimetypeId) { throw new \Exception("Failed to get mimetype id for $mimetype after trying to store it"); } - $mimetypeId = (int) $row['id']; $this->mimetypes[$mimetypeId] = $mimetype; $this->mimetypeIds[$mimetype] = $mimetypeId; diff --git a/lib/private/Installer.php b/lib/private/Installer.php index 43c3db7c3fd..9b286cc85b0 100644 --- a/lib/private/Installer.php +++ b/lib/private/Installer.php @@ -48,6 +48,7 @@ use OC\DB\Connection; use OC\DB\MigrationService; use OC_App; use OC_Helper; +use OCP\App\IAppManager; use OCP\HintException; use OCP\Http\Client\IClientService; use OCP\IConfig; @@ -113,7 +114,7 @@ class Installer { } $l = \OC::$server->getL10N('core'); - $info = OC_App::getAppInfo($basedir.'/appinfo/info.xml', true, $l->getLanguageCode()); + $info = \OCP\Server::get(IAppManager::class)->getAppInfo($basedir . '/appinfo/info.xml', true, $l->getLanguageCode()); if (!is_array($info)) { throw new \Exception( @@ -164,7 +165,7 @@ class Installer { OC_App::executeRepairSteps($appId, $info['repair-steps']['install']); //set the installed version - \OC::$server->getConfig()->setAppValue($info['id'], 'installed_version', OC_App::getAppVersion($info['id'], false)); + \OC::$server->getConfig()->setAppValue($info['id'], 'installed_version', \OCP\Server::get(IAppManager::class)->getAppVersion($info['id'], false)); \OC::$server->getConfig()->setAppValue($info['id'], 'enabled', 'no'); //set remote/public handlers @@ -349,7 +350,7 @@ class Installer { } // Check if the version is lower than before - $currentVersion = OC_App::getAppVersion($appId); + $currentVersion = \OCP\Server::get(IAppManager::class)->getAppVersion($appId, true); $newVersion = (string)$xml->version; if (version_compare($currentVersion, $newVersion) === 1) { throw new \Exception( @@ -423,7 +424,7 @@ class Installer { foreach ($this->apps as $app) { if ($app['id'] === $appId) { - $currentVersion = OC_App::getAppVersion($appId); + $currentVersion = \OCP\Server::get(IAppManager::class)->getAppVersion($appId, true); if (!isset($app['releases'][0]['version'])) { return false; @@ -593,7 +594,7 @@ class Installer { //run appinfo/install.php self::includeAppScript("$appPath/appinfo/install.php"); - $info = OC_App::getAppInfo($app); + $info = \OCP\Server::get(IAppManager::class)->getAppInfo($app); if (is_null($info)) { return false; } @@ -601,7 +602,7 @@ class Installer { OC_App::executeRepairSteps($app, $info['repair-steps']['install']); - $config->setAppValue($app, 'installed_version', OC_App::getAppVersion($app)); + $config->setAppValue($app, 'installed_version', \OCP\Server::get(IAppManager::class)->getAppVersion($app)); if (array_key_exists('ocsid', $info)) { $config->setAppValue($app, 'ocsid', $info['ocsid']); } diff --git a/lib/private/Log.php b/lib/private/Log.php index 99797b938db..c27c42fe271 100644 --- a/lib/private/Log.php +++ b/lib/private/Log.php @@ -359,6 +359,7 @@ class Log implements ILogger, IDataLogger { $context['level'] = $level; } catch (Throwable $e) { // make sure we dont hard crash if logging fails + error_log('Error when trying to log exception: ' . $e->getMessage() . ' ' . $e->getTraceAsString()); } } diff --git a/lib/private/Memcache/Redis.php b/lib/private/Memcache/Redis.php index b6f96fffba4..bde25a3385a 100644 --- a/lib/private/Memcache/Redis.php +++ b/lib/private/Memcache/Redis.php @@ -32,6 +32,22 @@ namespace OC\Memcache; use OCP\IMemcacheTTL; class Redis extends Cache implements IMemcacheTTL { + /** name => [script, sha1] */ + public const LUA_SCRIPTS = [ + 'dec' => [ + 'if redis.call("exists", KEYS[1]) == 1 then return redis.call("decrby", KEYS[1], ARGV[1]) else return "NEX" end', + '720b40cb66cef1579f2ef16ec69b3da8c85510e9', + ], + 'cas' => [ + 'if redis.call("get", KEYS[1]) == ARGV[1] then redis.call("set", KEYS[1], ARGV[2]) return 1 else return 0 end', + '94eac401502554c02b811e3199baddde62d976d4', + ], + 'cad' => [ + 'if redis.call("get", KEYS[1]) == ARGV[1] then return redis.call("del", KEYS[1]) else return 0 end', + 'cf0e94b2e9ffc7e04395cf88f7583fc309985910', + ], + ]; + /** * @var \Redis|\RedisCluster $cache */ @@ -54,18 +70,19 @@ class Redis extends Cache implements IMemcacheTTL { public function get($key) { $result = $this->getCache()->get($this->getPrefix() . $key); - if ($result === false && !$this->getCache()->exists($this->getPrefix() . $key)) { + if ($result === false) { return null; - } else { - return json_decode($result, true); } + + return self::decodeValue($result); } public function set($key, $value, $ttl = 0) { + $value = self::encodeValue($value); if ($ttl > 0) { - return $this->getCache()->setex($this->getPrefix() . $key, $ttl, json_encode($value)); + return $this->getCache()->setex($this->getPrefix() . $key, $ttl, $value); } else { - return $this->getCache()->set($this->getPrefix() . $key, json_encode($value)); + return $this->getCache()->set($this->getPrefix() . $key, $value); } } @@ -82,6 +99,7 @@ class Redis extends Cache implements IMemcacheTTL { } public function clear($prefix = '') { + // TODO: this is slow and would fail with Redis cluster $prefix = $this->getPrefix() . $prefix . '*'; $keys = $this->getCache()->keys($prefix); $deleted = $this->getCache()->del($keys); @@ -98,17 +116,14 @@ class Redis extends Cache implements IMemcacheTTL { * @return bool */ public function add($key, $value, $ttl = 0) { - // don't encode ints for inc/dec - if (!is_int($value)) { - $value = json_encode($value); - } + $value = self::encodeValue($value); $args = ['nx']; if ($ttl !== 0 && is_int($ttl)) { $args['ex'] = $ttl; } - return $this->getCache()->set($this->getPrefix() . $key, (string)$value, $args); + return $this->getCache()->set($this->getPrefix() . $key, $value, $args); } /** @@ -130,10 +145,8 @@ class Redis extends Cache implements IMemcacheTTL { * @return int | bool */ public function dec($key, $step = 1) { - if (!$this->hasKey($key)) { - return false; - } - return $this->getCache()->decrBy($this->getPrefix() . $key, $step); + $res = $this->evalLua('dec', [$key], [$step]); + return ($res === 'NEX') ? false : $res; } /** @@ -145,18 +158,10 @@ class Redis extends Cache implements IMemcacheTTL { * @return bool */ public function cas($key, $old, $new) { - if (!is_int($new)) { - $new = json_encode($new); - } - $this->getCache()->watch($this->getPrefix() . $key); - if ($this->get($key) === $old) { - $result = $this->getCache()->multi() - ->set($this->getPrefix() . $key, $new) - ->exec(); - return $result !== false; - } - $this->getCache()->unwatch(); - return false; + $old = self::encodeValue($old); + $new = self::encodeValue($new); + + return $this->evalLua('cas', [$key], [$old, $new]) > 0; } /** @@ -167,15 +172,9 @@ class Redis extends Cache implements IMemcacheTTL { * @return bool */ public function cad($key, $old) { - $this->getCache()->watch($this->getPrefix() . $key); - if ($this->get($key) === $old) { - $result = $this->getCache()->multi() - ->unlink($this->getPrefix() . $key) - ->exec(); - return $result !== false; - } - $this->getCache()->unwatch(); - return false; + $old = self::encodeValue($old); + + return $this->evalLua('cad', [$key], [$old]) > 0; } public function setTTL($key, $ttl) { @@ -185,4 +184,25 @@ class Redis extends Cache implements IMemcacheTTL { public static function isAvailable(): bool { return \OC::$server->getGetRedisFactory()->isAvailable(); } + + protected function evalLua(string $scriptName, array $keys, array $args) { + $keys = array_map(fn ($key) => $this->getPrefix() . $key, $keys); + $args = array_merge($keys, $args); + $script = self::LUA_SCRIPTS[$scriptName]; + + $result = $this->getCache()->evalSha($script[1], $args, count($keys)); + if (false === $result) { + $result = $this->getCache()->eval($script[0], $args, count($keys)); + } + + return $result; + } + + protected static function encodeValue(mixed $value): string { + return is_int($value) ? (string) $value : json_encode($value); + } + + protected static function decodeValue(string $value): mixed { + return is_numeric($value) ? (int) $value : json_decode($value, true); + } } diff --git a/lib/private/Preview/Generator.php b/lib/private/Preview/Generator.php index 3ed263b7826..3ce44913498 100644 --- a/lib/private/Preview/Generator.php +++ b/lib/private/Preview/Generator.php @@ -172,6 +172,10 @@ class Generator { [$maxWidth, $maxHeight] = $this->getPreviewSize($maxPreview, $previewVersion); + if ($maxWidth <= 0 || $maxHeight <= 0) { + throw new NotFoundException('The maximum preview sizes are zero or less pixels'); + } + $preview = null; foreach ($specifications as $specification) { diff --git a/lib/private/Preview/Imaginary.php b/lib/private/Preview/Imaginary.php index ed8b9eaa838..74f04e09250 100644 --- a/lib/private/Preview/Imaginary.php +++ b/lib/private/Preview/Imaginary.php @@ -136,9 +136,10 @@ class Imaginary extends ProviderV2 { ]; try { + $imaginaryKey = $this->config->getSystemValueString('preview_imaginary_key', ''); $response = $httpClient->post( $imaginaryUrl . '/pipeline', [ - 'query' => ['operations' => json_encode($operations)], + 'query' => ['operations' => json_encode($operations), 'key' => $imaginaryKey], 'stream' => true, 'content-type' => $file->getMimeType(), 'body' => $stream, diff --git a/lib/private/Preview/MP3.php b/lib/private/Preview/MP3.php index 47d752af477..ff6ff86c966 100644 --- a/lib/private/Preview/MP3.php +++ b/lib/private/Preview/MP3.php @@ -32,6 +32,7 @@ use ID3Parser\ID3Parser; use OCP\Files\File; use OCP\IImage; +use Psr\Log\LoggerInterface; class MP3 extends ProviderV2 { /** @@ -48,8 +49,18 @@ class MP3 extends ProviderV2 { $getID3 = new ID3Parser(); $tmpPath = $this->getLocalFile($file); - $tags = $getID3->analyze($tmpPath); - $this->cleanTmpFiles(); + try { + $tags = $getID3->analyze($tmpPath); + } catch (\Throwable $e) { + \OC::$server->get(LoggerInterface::class)->info($e->getMessage(), [ + 'exception' => $e, + 'app' => 'core', + ]); + return null; + } finally { + $this->cleanTmpFiles(); + } + $picture = isset($tags['id3v2']['APIC'][0]['data']) ? $tags['id3v2']['APIC'][0]['data'] : null; if (is_null($picture) && isset($tags['id3v2']['PIC'][0]['data'])) { $picture = $tags['id3v2']['PIC'][0]['data']; diff --git a/lib/private/RedisFactory.php b/lib/private/RedisFactory.php index 5c38ed0807d..6b509b1c1a9 100644 --- a/lib/private/RedisFactory.php +++ b/lib/private/RedisFactory.php @@ -72,8 +72,8 @@ class RedisFactory { } $auth = null; - if (isset($config['password']) && $config['password'] !== '') { - if (isset($config['user']) && $config['user'] !== '') { + if (isset($config['password']) && (string)$config['password'] !== '') { + if (isset($config['user']) && (string)$config['user'] !== '') { $auth = [$config['user'], $config['password']]; } else { $auth = $config['password']; diff --git a/lib/private/Repair/ClearGeneratedAvatarCache.php b/lib/private/Repair/ClearGeneratedAvatarCache.php index 844bf6cf346..fb3b42847dc 100644 --- a/lib/private/Repair/ClearGeneratedAvatarCache.php +++ b/lib/private/Repair/ClearGeneratedAvatarCache.php @@ -42,7 +42,7 @@ class ClearGeneratedAvatarCache implements IRepairStep { } public function getName(): string { - return 'Clear every generated avatar on major updates'; + return 'Clear every generated avatar'; } /** @@ -51,8 +51,9 @@ class ClearGeneratedAvatarCache implements IRepairStep { private function shouldRun(): bool { $versionFromBeforeUpdate = $this->config->getSystemValueString('version', '0.0.0.0'); - // was added to 25.0.0.10 - return version_compare($versionFromBeforeUpdate, '25.0.0.10', '<='); + // This job only runs if the server was on a version lower than or equal to 27.0.0 before the upgrade. + // To clear the avatar cache again, bump the version to the currently released version (and change the operator to <= if it's not the master branch) and wait for the next release. + return version_compare($versionFromBeforeUpdate, '27.0.0', '<'); } public function run(IOutput $output): void { diff --git a/lib/private/Security/CertificateManager.php b/lib/private/Security/CertificateManager.php index b9fbdcc9f02..3a87b7f1a00 100644 --- a/lib/private/Security/CertificateManager.php +++ b/lib/private/Security/CertificateManager.php @@ -83,8 +83,14 @@ class CertificateManager implements ICertificateManager { while (false !== ($file = readdir($handle))) { if ($file != '.' && $file != '..') { try { - $result[] = new Certificate($this->view->file_get_contents($path . $file), $file); + $content = $this->view->file_get_contents($path . $file); + if ($content !== false) { + $result[] = new Certificate($content, $file); + } else { + $this->logger->error("Failed to read certificate from $path"); + } } catch (\Exception $e) { + $this->logger->error("Failed to read certificate from $path", ['exception' => $e]); } } } diff --git a/lib/private/Server.php b/lib/private/Server.php index bb4e217efa3..84860f8c494 100644 --- a/lib/private/Server.php +++ b/lib/private/Server.php @@ -730,7 +730,13 @@ class Server extends ServerContainer implements IServerContainer { if ($config->getSystemValueBool('installed', false) && !(defined('PHPUNIT_RUN') && PHPUNIT_RUN)) { if (!$config->getSystemValueBool('log_query')) { - $v = \OC_App::getAppVersions(); + try { + $v = \OC_App::getAppVersions(); + } catch (\Doctrine\DBAL\Exception $e) { + // Database service probably unavailable + // Probably related to https://github.com/nextcloud/server/issues/37424 + return $arrayCacheFactory; + } } else { // If the log_query is enabled, we can not get the app versions // as that does a query, which will be logged and the logging diff --git a/lib/private/Template/JSResourceLocator.php b/lib/private/Template/JSResourceLocator.php index ab5de182d89..b283f0b610f 100644 --- a/lib/private/Template/JSResourceLocator.php +++ b/lib/private/Template/JSResourceLocator.php @@ -100,7 +100,7 @@ class JSResourceLocator extends ResourceLocator { $app_path = realpath($app_path); // check combined files - if ($this->cacheAndAppendCombineJsonIfExist($app_path, $script.'.json', $app)) { + if (!str_starts_with($script, 'l10n/') && $this->cacheAndAppendCombineJsonIfExist($app_path, $script.'.json', $app)) { return; } diff --git a/lib/private/Updater.php b/lib/private/Updater.php index 56285166322..5a14bb17507 100644 --- a/lib/private/Updater.php +++ b/lib/private/Updater.php @@ -40,6 +40,7 @@ declare(strict_types=1); */ namespace OC; +use OCP\App\IAppManager; use OCP\EventDispatcher\Event; use OCP\EventDispatcher\IEventDispatcher; use OCP\HintException; @@ -355,9 +356,9 @@ class Updater extends BasicEmitter { $stack = $stacks[$type]; foreach ($stack as $appId) { if (\OC_App::shouldUpgrade($appId)) { - $this->emit('\OC\Updater', 'appUpgradeStarted', [$appId, \OC_App::getAppVersion($appId)]); + $this->emit('\OC\Updater', 'appUpgradeStarted', [$appId, \OCP\Server::get(IAppManager::class)->getAppVersion($appId)]); \OC_App::updateApp($appId); - $this->emit('\OC\Updater', 'appUpgrade', [$appId, \OC_App::getAppVersion($appId)]); + $this->emit('\OC\Updater', 'appUpgrade', [$appId, \OCP\Server::get(IAppManager::class)->getAppVersion($appId)]); } if ($type !== $pseudoOtherType) { // load authentication, filesystem and logging apps after diff --git a/lib/private/User/User.php b/lib/private/User/User.php index bdc9a4d6fb7..b7f3b0e3740 100644 --- a/lib/private/User/User.php +++ b/lib/private/User/User.php @@ -571,7 +571,7 @@ class User implements IUser { } $avatar = $this->avatarManager->getAvatar($this->uid); - $image = $avatar->get(-1); + $image = $avatar->get($size); if ($image) { return $image; } diff --git a/lib/private/legacy/OC_App.php b/lib/private/legacy/OC_App.php index d790c1c2261..a1aeb50bbf4 100644 --- a/lib/private/legacy/OC_App.php +++ b/lib/private/legacy/OC_App.php @@ -242,19 +242,6 @@ class OC_App { } /** - * checks whether or not an app is enabled - * - * @param string $app app - * @return bool - * @deprecated 13.0.0 use \OC::$server->getAppManager()->isEnabledForUser($appId) - * - * This function checks whether or not an app is enabled. - */ - public static function isEnabled(string $app): bool { - return \OC::$server->getAppManager()->isEnabledForUser($app); - } - - /** * enables an app * * @param string $appId @@ -396,18 +383,6 @@ class OC_App { } /** - * get the last version of the app from appinfo/info.xml - * - * @param string $appId - * @param bool $useCache - * @return string - * @deprecated 14.0.0 use \OC::$server->getAppManager()->getAppVersion() - */ - public static function getAppVersion(string $appId, bool $useCache = true): string { - return \OC::$server->getAppManager()->getAppVersion($appId, $useCache); - } - - /** * get app's version based on it's path * * @param string $path @@ -419,49 +394,6 @@ class OC_App { return isset($appData['version']) ? $appData['version'] : ''; } - - /** - * Read all app metadata from the info.xml file - * - * @param string $appId id of the app or the path of the info.xml file - * @param bool $path - * @param string $lang - * @return array|null - * @note all data is read from info.xml, not just pre-defined fields - * @deprecated 14.0.0 use \OC::$server->getAppManager()->getAppInfo() - */ - public static function getAppInfo(string $appId, bool $path = false, string $lang = null) { - return \OC::$server->getAppManager()->getAppInfo($appId, $path, $lang); - } - - /** - * Returns the navigation - * - * @return array - * @deprecated 14.0.0 use \OC::$server->getNavigationManager()->getAll() - * - * This function returns an array containing all entries added. The - * entries are sorted by the key 'order' ascending. Additional to the keys - * given for each app the following keys exist: - * - active: boolean, signals if the user is on this navigation entry - */ - public static function getNavigation(): array { - return OC::$server->getNavigationManager()->getAll(); - } - - /** - * Returns the Settings Navigation - * - * @return string[] - * @deprecated 14.0.0 use \OC::$server->getNavigationManager()->getAll('settings') - * - * This function returns an array containing all settings pages added. The - * entries are sorted by the key 'order' ascending. - */ - public static function getSettingsNavigation(): array { - return OC::$server->getNavigationManager()->getAll('settings'); - } - /** * get the id of loaded app * @@ -634,7 +566,7 @@ class OC_App { foreach ($installedApps as $app) { if (array_search($app, $blacklist) === false) { - $info = OC_App::getAppInfo($app, false, $langCode); + $info = $appManager->getAppInfo($app, false, $langCode); if (!is_array($info)) { \OCP\Util::writeLog('core', 'Could not read app info file for app "' . $app . '"', ILogger::ERROR); continue; @@ -698,7 +630,7 @@ class OC_App { } } - $info['version'] = OC_App::getAppVersion($app); + $info['version'] = $appManager->getAppVersion($app); $appList[] = $info; } } @@ -708,7 +640,7 @@ class OC_App { public static function shouldUpgrade(string $app): bool { $versions = self::getAppVersions(); - $currentVersion = OC_App::getAppVersion($app); + $currentVersion = \OCP\Server::get(\OCP\App\IAppManager::class)->getAppVersion($app); if ($currentVersion && isset($versions[$app])) { $installedVersion = $versions[$app]; if (!version_compare($currentVersion, $installedVersion, '=')) { @@ -826,7 +758,7 @@ class OC_App { \OC::$server->getAppManager()->clearAppsCache(); $l = \OC::$server->getL10N('core'); - $appData = self::getAppInfo($appId, false, $l->getLanguageCode()); + $appData = \OCP\Server::get(\OCP\App\IAppManager::class)->getAppInfo($appId, false, $l->getLanguageCode()); $ignoreMaxApps = \OC::$server->getConfig()->getSystemValue('app_install_overwrite', []); $ignoreMax = in_array($appId, $ignoreMaxApps, true); @@ -866,7 +798,7 @@ class OC_App { self::setAppTypes($appId); - $version = \OC_App::getAppVersion($appId); + $version = \OCP\Server::get(\OCP\App\IAppManager::class)->getAppVersion($appId); \OC::$server->getConfig()->setAppValue($appId, 'installed_version', $version); \OC::$server->get(IEventDispatcher::class)->dispatchTyped(new AppUpdateEvent($appId)); diff --git a/lib/private/legacy/OC_Defaults.php b/lib/private/legacy/OC_Defaults.php index 707df7279bb..ce89a642741 100644 --- a/lib/private/legacy/OC_Defaults.php +++ b/lib/private/legacy/OC_Defaults.php @@ -245,15 +245,6 @@ class OC_Defaults { } /** - * Returns logo claim - * @return string logo claim - * @deprecated 13.0.0 - */ - public function getLogoClaim() { - return ''; - } - - /** * Returns short version of the footer * @return string short footer */ diff --git a/lib/private/legacy/OC_Helper.php b/lib/private/legacy/OC_Helper.php index 07d81933d00..8c8be0e1069 100644 --- a/lib/private/legacy/OC_Helper.php +++ b/lib/private/legacy/OC_Helper.php @@ -487,7 +487,7 @@ class OC_Helper { $rootInfo = \OC\Files\Filesystem::getFileInfo($path, $includeExtStorage ? 'ext' : false); } if (!$rootInfo instanceof \OCP\Files\FileInfo) { - throw new \OCP\Files\NotFoundException(); + throw new \OCP\Files\NotFoundException('The root directory of the user\'s files is missing'); } $used = $rootInfo->getSize($includeMountPoints); if ($used < 0) { |