diff options
Diffstat (limited to 'lib/private')
45 files changed, 374 insertions, 1426 deletions
diff --git a/lib/private/Accounts/AccountManager.php b/lib/private/Accounts/AccountManager.php index 5792ba1dc5d..b80c7887591 100644 --- a/lib/private/Accounts/AccountManager.php +++ b/lib/private/Accounts/AccountManager.php @@ -14,6 +14,7 @@ * @author Lukas Reschke <lukas@statuscode.ch> * @author Morris Jobke <hey@morrisjobke.de> * @author Roeland Jago Douma <roeland@famdouma.nl> + * @author Thomas Citharel <nextcloud@tcit.fr> * @author Vincent Petry <vincent@nextcloud.com> * * @license AGPL-3.0 @@ -119,6 +120,23 @@ class AccountManager implements IAccountManager { private $l10nfactory; private CappedMemoryCache $internalCache; + /** + * The list of default scopes for each property. + */ + public const DEFAULT_SCOPES = [ + self::PROPERTY_DISPLAYNAME => self::SCOPE_FEDERATED, + self::PROPERTY_ADDRESS => self::SCOPE_LOCAL, + self::PROPERTY_WEBSITE => self::SCOPE_LOCAL, + self::PROPERTY_EMAIL => self::SCOPE_FEDERATED, + self::PROPERTY_AVATAR => self::SCOPE_FEDERATED, + self::PROPERTY_PHONE => self::SCOPE_LOCAL, + self::PROPERTY_TWITTER => self::SCOPE_LOCAL, + self::PROPERTY_ORGANISATION => self::SCOPE_LOCAL, + self::PROPERTY_ROLE => self::SCOPE_LOCAL, + self::PROPERTY_HEADLINE => self::SCOPE_LOCAL, + self::PROPERTY_BIOGRAPHY => self::SCOPE_LOCAL, + ]; + public function __construct( IDBConnection $connection, IConfig $config, @@ -269,12 +287,15 @@ class AccountManager implements IAccountManager { } } - protected function updateUser(IUser $user, array $data, bool $throwOnData = false): array { - $oldUserData = $this->getUser($user, false); + protected function updateUser(IUser $user, array $data, ?array $oldUserData, bool $throwOnData = false): array { + if ($oldUserData === null) { + $oldUserData = $this->getUser($user, false); + } + $updated = true; if ($oldUserData !== $data) { - $this->updateExistingUser($user, $data); + $this->updateExistingUser($user, $data, $oldUserData); } else { // nothing needs to be done if new and old data set are the same $updated = false; @@ -601,12 +622,9 @@ class AccountManager implements IAccountManager { } /** - * update existing user in accounts table - * - * @param IUser $user - * @param array $data + * Update existing user in accounts table */ - protected function updateExistingUser(IUser $user, array $data): void { + protected function updateExistingUser(IUser $user, array $data, array $oldData): void { $uid = $user->getUID(); $jsonEncodedData = $this->prepareJson($data); $query = $this->connection->getQueryBuilder(); @@ -649,81 +667,84 @@ class AccountManager implements IAccountManager { /** * build default user record in case not data set exists yet - * - * @param IUser $user - * @return array */ - protected function buildDefaultUserRecord(IUser $user) { + protected function buildDefaultUserRecord(IUser $user): array { + $scopes = array_merge(self::DEFAULT_SCOPES, array_filter($this->config->getSystemValue('account_manager.default_property_scope', []), static function (string $scope, string $property) { + return in_array($property, self::ALLOWED_PROPERTIES, true) && in_array($scope, self::ALLOWED_SCOPES, true); + }, ARRAY_FILTER_USE_BOTH)); + return [ [ 'name' => self::PROPERTY_DISPLAYNAME, 'value' => $user->getDisplayName(), - 'scope' => self::SCOPE_FEDERATED, + // Display name must be at least SCOPE_LOCAL + 'scope' => $scopes[self::PROPERTY_DISPLAYNAME] === self::SCOPE_PRIVATE ? self::SCOPE_LOCAL : $scopes[self::PROPERTY_DISPLAYNAME], 'verified' => self::NOT_VERIFIED, ], [ 'name' => self::PROPERTY_ADDRESS, 'value' => '', - 'scope' => self::SCOPE_LOCAL, + 'scope' => $scopes[self::PROPERTY_ADDRESS], 'verified' => self::NOT_VERIFIED, ], [ 'name' => self::PROPERTY_WEBSITE, 'value' => '', - 'scope' => self::SCOPE_LOCAL, + 'scope' => $scopes[self::PROPERTY_WEBSITE], 'verified' => self::NOT_VERIFIED, ], [ 'name' => self::PROPERTY_EMAIL, 'value' => $user->getEMailAddress(), - 'scope' => self::SCOPE_FEDERATED, + // Email must be at least SCOPE_LOCAL + 'scope' => $scopes[self::PROPERTY_EMAIL] === self::SCOPE_PRIVATE ? self::SCOPE_LOCAL : $scopes[self::PROPERTY_EMAIL], 'verified' => self::NOT_VERIFIED, ], [ 'name' => self::PROPERTY_AVATAR, - 'scope' => self::SCOPE_FEDERATED + 'scope' => $scopes[self::PROPERTY_AVATAR], ], [ 'name' => self::PROPERTY_PHONE, 'value' => '', - 'scope' => self::SCOPE_LOCAL, + 'scope' => $scopes[self::PROPERTY_PHONE], 'verified' => self::NOT_VERIFIED, ], [ 'name' => self::PROPERTY_TWITTER, 'value' => '', - 'scope' => self::SCOPE_LOCAL, + 'scope' => $scopes[self::PROPERTY_TWITTER], 'verified' => self::NOT_VERIFIED, ], [ 'name' => self::PROPERTY_ORGANISATION, 'value' => '', - 'scope' => self::SCOPE_LOCAL, + 'scope' => $scopes[self::PROPERTY_ORGANISATION], ], [ 'name' => self::PROPERTY_ROLE, 'value' => '', - 'scope' => self::SCOPE_LOCAL, + 'scope' => $scopes[self::PROPERTY_ROLE], ], [ 'name' => self::PROPERTY_HEADLINE, 'value' => '', - 'scope' => self::SCOPE_LOCAL, + 'scope' => $scopes[self::PROPERTY_HEADLINE], ], [ 'name' => self::PROPERTY_BIOGRAPHY, 'value' => '', - 'scope' => self::SCOPE_LOCAL, + 'scope' => $scopes[self::PROPERTY_BIOGRAPHY], ], [ @@ -790,17 +811,8 @@ class AccountManager implements IAccountManager { // valid case, nothing to do } - static $allowedScopes = [ - self::SCOPE_PRIVATE, - self::SCOPE_LOCAL, - self::SCOPE_FEDERATED, - self::SCOPE_PUBLISHED, - self::VISIBILITY_PRIVATE, - self::VISIBILITY_CONTACTS_ONLY, - self::VISIBILITY_PUBLIC, - ]; foreach ($account->getAllProperties() as $property) { - $this->testPropertyScope($property, $allowedScopes, true); + $this->testPropertyScope($property, self::ALLOWED_SCOPES, true); } $oldData = $this->getUser($account->getUser(), false); @@ -820,7 +832,7 @@ class AccountManager implements IAccountManager { ]; } - $this->updateUser($account->getUser(), $data, true); + $this->updateUser($account->getUser(), $data, $oldData, true); $this->internalCache->set($account->getUser()->getUID(), $account); } } diff --git a/lib/private/AppConfig.php b/lib/private/AppConfig.php index 1324d7f3056..00e5dddc1f9 100644 --- a/lib/private/AppConfig.php +++ b/lib/private/AppConfig.php @@ -423,14 +423,4 @@ class AppConfig implements IAppConfig { $this->configLoaded = true; } - - /** - * Clear all the cached app config values - * - * WARNING: do not use this - this is only for usage with the SCSSCacher to - * clear the memory cache of the app config - */ - public function clearCachedConfig() { - $this->configLoaded = false; - } } diff --git a/lib/private/Authentication/Listeners/UserDeletedFilesCleanupListener.php b/lib/private/Authentication/Listeners/UserDeletedFilesCleanupListener.php index fa7459e10ff..2fb05159d09 100644 --- a/lib/private/Authentication/Listeners/UserDeletedFilesCleanupListener.php +++ b/lib/private/Authentication/Listeners/UserDeletedFilesCleanupListener.php @@ -26,6 +26,7 @@ declare(strict_types=1); namespace OC\Authentication\Listeners; use OC\Files\Cache\Cache; +use OC\Files\Storage\Wrapper\Wrapper; use OCP\EventDispatcher\Event; use OCP\EventDispatcher\IEventListener; use OCP\Files\Config\IMountProviderCollection; @@ -56,6 +57,13 @@ class UserDeletedFilesCleanupListener implements IEventListener { if (!$storage) { throw new \Exception("User has no home storage"); } + + // remove all wrappers, so we do the delete directly on the home storage bypassing any wrapper + while ($storage->instanceOfStorage(Wrapper::class)) { + /** @var Wrapper $storage */ + $storage = $storage->getWrapperStorage(); + } + $this->homeStorageCache[$event->getUser()->getUID()] = $storage; } if ($event instanceof UserDeletedEvent) { diff --git a/lib/private/Authentication/Token/PublicKeyTokenProvider.php b/lib/private/Authentication/Token/PublicKeyTokenProvider.php index 26337029d77..a1d75828e27 100644 --- a/lib/private/Authentication/Token/PublicKeyTokenProvider.php +++ b/lib/private/Authentication/Token/PublicKeyTokenProvider.php @@ -85,7 +85,7 @@ class PublicKeyTokenProvider implements IProvider { int $type = IToken::TEMPORARY_TOKEN, int $remember = IToken::DO_NOT_REMEMBER): IToken { if (mb_strlen($name) > 128) { - throw new InvalidTokenException('The given name is too long'); + $name = mb_substr($name, 0, 120) . '…'; } $dbToken = $this->newToken($token, $uid, $loginName, $password, $name, $type, $remember); diff --git a/lib/private/Cache/CappedMemoryCache.php b/lib/private/Cache/CappedMemoryCache.php index 0a3300435eb..6063b5e7110 100644 --- a/lib/private/Cache/CappedMemoryCache.php +++ b/lib/private/Cache/CappedMemoryCache.php @@ -88,7 +88,7 @@ class CappedMemoryCache implements ICache, \ArrayAccess { } /** - * @param string $key + * @param string $offset * @param T $value * @return void */ diff --git a/lib/private/Collaboration/Collaborators/MailPlugin.php b/lib/private/Collaboration/Collaborators/MailPlugin.php index aae6f305981..8c2efce6f0d 100644 --- a/lib/private/Collaboration/Collaborators/MailPlugin.php +++ b/lib/private/Collaboration/Collaborators/MailPlugin.php @@ -51,6 +51,8 @@ class MailPlugin implements ISearchPlugin { protected $shareeEnumerationPhone; /* @var bool */ protected $shareeEnumerationFullMatch; + /* @var bool */ + protected $shareeEnumerationFullMatchEmail; /** @var IManager */ private $contactsManager; @@ -88,12 +90,17 @@ class MailPlugin implements ISearchPlugin { $this->shareeEnumerationInGroupOnly = $this->shareeEnumeration && $this->config->getAppValue('core', 'shareapi_restrict_user_enumeration_to_group', 'no') === 'yes'; $this->shareeEnumerationPhone = $this->shareeEnumeration && $this->config->getAppValue('core', 'shareapi_restrict_user_enumeration_to_phone', 'no') === 'yes'; $this->shareeEnumerationFullMatch = $this->config->getAppValue('core', 'shareapi_restrict_user_enumeration_full_match', 'yes') === 'yes'; + $this->shareeEnumerationFullMatchEmail = $this->config->getAppValue('core', 'shareapi_restrict_user_enumeration_full_match_email', 'yes') === 'yes'; } /** * {@inheritdoc} */ public function search($search, $limit, $offset, ISearchResult $searchResult) { + if ($this->shareeEnumerationFullMatch && !$this->shareeEnumerationFullMatchEmail) { + return false; + } + $currentUserId = $this->userSession->getUser()->getUID(); $result = $userResults = ['wide' => [], 'exact' => []]; diff --git a/lib/private/Collaboration/Collaborators/UserPlugin.php b/lib/private/Collaboration/Collaborators/UserPlugin.php index af4d8b2ccca..12304a66ce9 100644 --- a/lib/private/Collaboration/Collaborators/UserPlugin.php +++ b/lib/private/Collaboration/Collaborators/UserPlugin.php @@ -57,6 +57,8 @@ class UserPlugin implements ISearchPlugin { /* @var bool */ protected $shareeEnumerationFullMatchUserId; /* @var bool */ + protected $shareeEnumerationFullMatchEmail; + /* @var bool */ protected $shareeEnumerationFullMatchIgnoreSecondDisplayName; /** @var IConfig */ @@ -92,6 +94,7 @@ class UserPlugin implements ISearchPlugin { $this->shareeEnumerationPhone = $this->shareeEnumeration && $this->config->getAppValue('core', 'shareapi_restrict_user_enumeration_to_phone', 'no') === 'yes'; $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'; } @@ -185,7 +188,7 @@ class UserPlugin implements ISearchPlugin { $lowerSearch !== '' && (strtolower($uid) === $lowerSearch || strtolower($userDisplayName) === $lowerSearch || ($this->shareeEnumerationFullMatchIgnoreSecondDisplayName && trim(strtolower(preg_replace('/ \(.*\)$/', '', $userDisplayName))) === $lowerSearch) || - strtolower($userEmail ?? '') === $lowerSearch) + ($this->shareeEnumerationFullMatchEmail && strtolower($userEmail ?? '') === $lowerSearch)) ) { if (strtolower($uid) === $lowerSearch) { $foundUserById = true; diff --git a/lib/private/Comments/Manager.php b/lib/private/Comments/Manager.php index 3275658d555..4a06ac62f1e 100644 --- a/lib/private/Comments/Manager.php +++ b/lib/private/Comments/Manager.php @@ -110,18 +110,24 @@ class Manager implements ICommentsManager { $data['children_count'] = (int)$data['children_count']; $data['reference_id'] = $data['reference_id'] ?? null; if ($this->supportReactions()) { - $list = json_decode($data['reactions'], true); - // Ordering does not work on the database with group concat and Oracle, - // So we simply sort on the output. - if (is_array($list)) { - uasort($list, static function ($a, $b) { - if ($a === $b) { - return 0; - } - return ($a > $b) ? -1 : 1; - }); + if ($data['reactions'] !== null) { + $list = json_decode($data['reactions'], true); + // Ordering does not work on the database with group concat and Oracle, + // So we simply sort on the output. + if (is_array($list)) { + uasort($list, static function ($a, $b) { + if ($a === $b) { + return 0; + } + return ($a > $b) ? -1 : 1; + }); + $data['reactions'] = $list; + } else { + $data['reactions'] = []; + } + } else { + $data['reactions'] = []; } - $data['reactions'] = $list; } return $data; } diff --git a/lib/private/Contacts/ContactsMenu/ActionProviderStore.php b/lib/private/Contacts/ContactsMenu/ActionProviderStore.php index 1db99497a21..c93879afa5b 100644 --- a/lib/private/Contacts/ContactsMenu/ActionProviderStore.php +++ b/lib/private/Contacts/ContactsMenu/ActionProviderStore.php @@ -38,15 +38,9 @@ use OCP\IUser; use Psr\Log\LoggerInterface; class ActionProviderStore { - - /** @var IServerContainer */ - private $serverContainer; - - /** @var AppManager */ - private $appManager; - - /** @var LoggerInterface */ - private $logger; + private IServerContainer $serverContainer; + private AppManager $appManager; + private LoggerInterface $logger; public function __construct(IServerContainer $serverContainer, AppManager $appManager, LoggerInterface $logger) { $this->serverContainer = $serverContainer; @@ -67,7 +61,7 @@ class ActionProviderStore { foreach ($allClasses as $class) { try { - $providers[] = $this->serverContainer->query($class); + $providers[] = $this->serverContainer->get($class); } catch (QueryException $ex) { $this->logger->error( 'Could not load contacts menu action provider ' . $class, diff --git a/lib/private/Contacts/ContactsMenu/Actions/LinkAction.php b/lib/private/Contacts/ContactsMenu/Actions/LinkAction.php index 3f917854aac..a3054c9ee52 100644 --- a/lib/private/Contacts/ContactsMenu/Actions/LinkAction.php +++ b/lib/private/Contacts/ContactsMenu/Actions/LinkAction.php @@ -25,73 +25,44 @@ namespace OC\Contacts\ContactsMenu\Actions; use OCP\Contacts\ContactsMenu\ILinkAction; class LinkAction implements ILinkAction { - - /** @var string */ - private $icon; - - /** @var string */ - private $name; - - /** @var string */ - private $href; - - /** @var int */ - private $priority = 10; - - /** @var string */ - private $appId; + private string $icon = ''; + private string $name = ''; + private string $href = ''; + private int $priority = 10; + private string $appId = ''; /** * @param string $icon absolute URI to an icon */ - public function setIcon($icon) { + public function setIcon(string $icon) { $this->icon = $icon; } - /** - * @param string $name - */ - public function setName($name) { + public function setName(string $name) { $this->name = $name; } - /** - * @return string - */ - public function getName() { + public function getName(): string { return $this->name; } - /** - * @param int $priority - */ - public function setPriority($priority) { + public function setPriority(int $priority) { $this->priority = $priority; } - /** - * @return int - */ - public function getPriority() { + public function getPriority(): int { return $this->priority; } - /** - * @param string $href - */ - public function setHref($href) { + public function setHref(string $href) { $this->href = $href; } - /** - * @return string - */ - public function getHref() { + public function getHref(): string { return $this->href; } /** - * @param string $appId * @since 23.0.0 */ public function setAppId(string $appId) { @@ -99,7 +70,6 @@ class LinkAction implements ILinkAction { } /** - * @return string * @since 23.0.0 */ public function getAppId(): string { diff --git a/lib/private/Contacts/ContactsMenu/ContactsStore.php b/lib/private/Contacts/ContactsMenu/ContactsStore.php index 0ac388ce00a..020e8604910 100644 --- a/lib/private/Contacts/ContactsMenu/ContactsStore.php +++ b/lib/private/Contacts/ContactsMenu/ContactsStore.php @@ -44,30 +44,14 @@ use OCP\IUserManager; use OCP\L10N\IFactory as IL10NFactory; class ContactsStore implements IContactsStore { - - /** @var IManager */ - private $contactsManager; - - /** @var IConfig */ - private $config; - - /** @var ProfileManager */ - private $profileManager; - - /** @var IUserManager */ - private $userManager; - - /** @var IURLGenerator */ - private $urlGenerator; - - /** @var IGroupManager */ - private $groupManager; - - /** @var KnownUserService */ - private $knownUserService; - - /** @var IL10NFactory */ - private $l10nFactory; + private IManager $contactsManager; + private IConfig $config; + private ProfileManager $profileManager; + private IUserManager $userManager; + private IURLGenerator $urlGenerator; + private IGroupManager $groupManager; + private KnownUserService $knownUserService; + private IL10NFactory $l10nFactory; public function __construct( IManager $contactsManager, @@ -90,11 +74,9 @@ class ContactsStore implements IContactsStore { } /** - * @param IUser $user - * @param string|null $filter * @return IEntry[] */ - public function getContacts(IUser $user, $filter, ?int $limit = null, ?int $offset = null) { + public function getContacts(IUser $user, ?string $filter, ?int $limit = null, ?int $offset = null): array { $options = [ 'enumeration' => $this->config->getAppValue('core', 'shareapi_allow_share_dialog_user_enumeration', 'yes') === 'yes', 'fullmatch' => $this->config->getAppValue('core', 'shareapi_restrict_user_enumeration_full_match', 'yes') === 'yes', @@ -152,8 +134,8 @@ class ContactsStore implements IContactsStore { private function filterContacts( IUser $self, array $entries, - $filter - ) { + ?string $filter + ): array { $disallowEnumeration = $this->config->getAppValue('core', 'shareapi_allow_share_dialog_user_enumeration', 'yes') !== 'yes'; $restrictEnumerationGroup = $this->config->getAppValue('core', 'shareapi_restrict_user_enumeration_to_group', 'no') === 'yes'; $restrictEnumerationPhone = $this->config->getAppValue('core', 'shareapi_restrict_user_enumeration_to_phone', 'no') === 'yes'; @@ -168,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 ?? []; @@ -253,13 +235,7 @@ class ContactsStore implements IContactsStore { })); } - /** - * @param IUser $user - * @param integer $shareType - * @param string $shareWith - * @return IEntry|null - */ - public function findOne(IUser $user, $shareType, $shareWith) { + public function findOne(IUser $user, int $shareType, string $shareWith): ?IEntry { switch ($shareType) { case 0: case 6: @@ -305,11 +281,7 @@ class ContactsStore implements IContactsStore { return $match; } - /** - * @param array $contact - * @return Entry - */ - private function contactArrayToEntry(array $contact) { + private function contactArrayToEntry(array $contact): Entry { $entry = new Entry(); if (isset($contact['id'])) { diff --git a/lib/private/Contacts/ContactsMenu/Entry.php b/lib/private/Contacts/ContactsMenu/Entry.php index 915a0434cc8..3bbe679e999 100644 --- a/lib/private/Contacts/ContactsMenu/Entry.php +++ b/lib/private/Contacts/ContactsMenu/Entry.php @@ -35,51 +35,34 @@ class Entry implements IEntry { /** @var string|int|null */ private $id = null; - /** @var string */ - private $fullName = ''; + private string $fullName = ''; /** @var string[] */ - private $emailAddresses = []; + private array $emailAddresses = []; - /** @var string|null */ - private $avatar; + private ?string $avatar = null; - /** @var string|null */ - private $profileTitle; + private ?string $profileTitle = null; - /** @var string|null */ - private $profileUrl; + private ?string $profileUrl = null; /** @var IAction[] */ - private $actions = []; + private array $actions = []; - /** @var array */ - private $properties = []; + private array $properties = []; - /** - * @param string $id - */ public function setId(string $id): void { $this->id = $id; } - /** - * @param string $displayName - */ public function setFullName(string $displayName): void { $this->fullName = $displayName; } - /** - * @return string - */ public function getFullName(): string { return $this->fullName; } - /** - * @param string $address - */ public function addEMailAddress(string $address): void { $this->emailAddresses[] = $address; } @@ -91,51 +74,30 @@ class Entry implements IEntry { return $this->emailAddresses; } - /** - * @param string $avatar - */ public function setAvatar(string $avatar): void { $this->avatar = $avatar; } - /** - * @return string - */ public function getAvatar(): ?string { return $this->avatar; } - /** - * @param string $profileTitle - */ public function setProfileTitle(string $profileTitle): void { $this->profileTitle = $profileTitle; } - /** - * @return string - */ public function getProfileTitle(): ?string { return $this->profileTitle; } - /** - * @param string $profileUrl - */ public function setProfileUrl(string $profileUrl): void { $this->profileUrl = $profileUrl; } - /** - * @return string - */ public function getProfileUrl(): ?string { return $this->profileUrl; } - /** - * @param IAction $action - */ public function addAction(IAction $action): void { $this->actions[] = $action; $this->sortActions(); diff --git a/lib/private/Contacts/ContactsMenu/Manager.php b/lib/private/Contacts/ContactsMenu/Manager.php index 73a5a475d85..5c3367a3d09 100644 --- a/lib/private/Contacts/ContactsMenu/Manager.php +++ b/lib/private/Contacts/ContactsMenu/Manager.php @@ -25,6 +25,7 @@ */ namespace OC\Contacts\ContactsMenu; +use Exception; use OCP\App\IAppManager; use OCP\Constants; use OCP\Contacts\ContactsMenu\IEntry; @@ -32,24 +33,11 @@ use OCP\IConfig; use OCP\IUser; class Manager { + private ContactsStore $store; + private ActionProviderStore $actionProviderStore; + private IAppManager $appManager; + private IConfig $config; - /** @var ContactsStore */ - private $store; - - /** @var ActionProviderStore */ - private $actionProviderStore; - - /** @var IAppManager */ - private $appManager; - - /** @var IConfig */ - private $config; - - /** - * @param ContactsStore $store - * @param ActionProviderStore $actionProviderStore - * @param IAppManager $appManager - */ public function __construct(ContactsStore $store, ActionProviderStore $actionProviderStore, IAppManager $appManager, IConfig $config) { $this->store = $store; $this->actionProviderStore = $actionProviderStore; @@ -61,10 +49,11 @@ class Manager { * @param IUser $user * @param string|null $filter * @return array + * @throws Exception */ - public function getEntries(IUser $user, $filter) { + public function getEntries(IUser $user, ?string $filter): array { $maxAutocompleteResults = max(0, $this->config->getSystemValueInt('sharing.maxAutocompleteResults', Constants::SHARING_MAX_AUTOCOMPLETE_RESULTS_DEFAULT)); - $minSearchStringLength = $this->config->getSystemValueInt('sharing.minSearchStringLength', 0); + $minSearchStringLength = $this->config->getSystemValueInt('sharing.minSearchStringLength'); $topEntries = []; if (strlen($filter ?? '') >= $minSearchStringLength) { $entries = $this->store->getContacts($user, $filter, $maxAutocompleteResults); @@ -82,12 +71,9 @@ class Manager { } /** - * @param IUser $user - * @param integer $shareType - * @param string $shareWith - * @return IEntry + * @throws Exception */ - public function findOne(IUser $user, $shareType, $shareWith) { + public function findOne(IUser $user, int $shareType, string $shareWith): ?IEntry { $entry = $this->store->findOne($user, $shareType, $shareWith); if ($entry) { $this->processEntries([$entry], $user); @@ -100,7 +86,7 @@ class Manager { * @param IEntry[] $entries * @return IEntry[] */ - private function sortEntries(array $entries) { + private function sortEntries(array $entries): array { usort($entries, function (IEntry $entryA, IEntry $entryB) { return strcasecmp($entryA->getFullName(), $entryB->getFullName()); }); @@ -110,6 +96,7 @@ class Manager { /** * @param IEntry[] $entries * @param IUser $user + * @throws Exception */ private function processEntries(array $entries, IUser $user) { $providers = $this->actionProviderStore->getProviders($user); diff --git a/lib/private/Contacts/ContactsMenu/Providers/EMailProvider.php b/lib/private/Contacts/ContactsMenu/Providers/EMailProvider.php index d69f219e84c..b79052e1f5d 100644 --- a/lib/private/Contacts/ContactsMenu/Providers/EMailProvider.php +++ b/lib/private/Contacts/ContactsMenu/Providers/EMailProvider.php @@ -28,17 +28,9 @@ use OCP\Contacts\ContactsMenu\IProvider; use OCP\IURLGenerator; class EMailProvider implements IProvider { + private IActionFactory $actionFactory; + private IURLGenerator $urlGenerator; - /** @var IActionFactory */ - private $actionFactory; - - /** @var IURLGenerator */ - private $urlGenerator; - - /** - * @param IActionFactory $actionFactory - * @param IURLGenerator $urlGenerator - */ public function __construct(IActionFactory $actionFactory, IURLGenerator $urlGenerator) { $this->actionFactory = $actionFactory; $this->urlGenerator = $urlGenerator; diff --git a/lib/private/Contacts/ContactsMenu/Providers/ProfileProvider.php b/lib/private/Contacts/ContactsMenu/Providers/ProfileProvider.php index e654319c3fa..af941fd7fd1 100644 --- a/lib/private/Contacts/ContactsMenu/Providers/ProfileProvider.php +++ b/lib/private/Contacts/ContactsMenu/Providers/ProfileProvider.php @@ -33,29 +33,12 @@ use OCP\IUserManager; use OCP\L10N\IFactory as IL10NFactory; class ProfileProvider implements IProvider { + private IActionFactory $actionFactory; + private ProfileManager $profileManager; + private IL10NFactory $l10nFactory; + private IURLGenerator $urlGenerator; + private IUserManager $userManager; - /** @var IActionFactory */ - private $actionFactory; - - /** @var ProfileManager */ - private $profileManager; - - /** @var IL10NFactory */ - private $l10nFactory; - - /** @var IURLGenerator */ - private $urlGenerator; - - /** @var IUserManager */ - private $userManager; - - /** - * @param IActionFactory $actionFactory - * @param ProfileManager $profileManager - * @param IL10NFactory $l10nFactory - * @param IURLGenerator $urlGenerator - * @param IUserManager $userManager - */ public function __construct( IActionFactory $actionFactory, ProfileManager $profileManager, diff --git a/lib/private/Diagnostics/EventLogger.php b/lib/private/Diagnostics/EventLogger.php index c7b89002ea9..7b9bd9630ab 100644 --- a/lib/private/Diagnostics/EventLogger.php +++ b/lib/private/Diagnostics/EventLogger.php @@ -126,7 +126,7 @@ class EventLogger implements IEventLogger { $timeInMs = round($duration * 1000, 4); $loggingMinimum = (int)$this->config->getValue('diagnostics.logging.threshold', 0); - if ($loggingMinimum > 0 && $timeInMs < $loggingMinimum) { + if ($loggingMinimum === 0 || $timeInMs < $loggingMinimum) { return; } diff --git a/lib/private/Diagnostics/QueryLogger.php b/lib/private/Diagnostics/QueryLogger.php index 499947178a3..40d68d94ae3 100644 --- a/lib/private/Diagnostics/QueryLogger.php +++ b/lib/private/Diagnostics/QueryLogger.php @@ -28,15 +28,10 @@ use OC\Cache\CappedMemoryCache; use OCP\Diagnostics\IQueryLogger; class QueryLogger implements IQueryLogger { - /** - * @var \OC\Diagnostics\Query - */ - protected $activeQuery; - - /** - * @var CappedMemoryCache - */ - protected $queries; + protected int $index = 0; + protected ?Query $activeQuery = null; + /** @var CappedMemoryCache<Query> */ + protected CappedMemoryCache $queries; /** * QueryLogger constructor. @@ -74,7 +69,8 @@ class QueryLogger implements IQueryLogger { public function stopQuery() { if ($this->activated && $this->activeQuery) { $this->activeQuery->end(microtime(true)); - $this->queries[] = $this->activeQuery; + $this->queries[(string)$this->index] = $this->activeQuery; + $this->index++; $this->activeQuery = null; } } diff --git a/lib/private/Encryption/File.php b/lib/private/Encryption/File.php index 2c486dfade6..2d7e23a8883 100644 --- a/lib/private/Encryption/File.php +++ b/lib/private/Encryption/File.php @@ -47,9 +47,9 @@ class File implements \OCP\Encryption\IFile { /** * cache results of already checked folders * - * @var array + * @var CappedMemoryCache<array> */ - protected $cache; + protected CappedMemoryCache $cache; public function __construct(Util $util, IRootFolder $rootFolder, @@ -62,10 +62,10 @@ class File implements \OCP\Encryption\IFile { /** - * get list of users with access to the file + * Get list of users with access to the file * * @param string $path to the file - * @return array ['users' => $uniqueUserIds, 'public' => $public] + * @return array{users: string[], public: bool} */ public function getAccessList($path) { diff --git a/lib/private/Encryption/Util.php b/lib/private/Encryption/Util.php index dc878ba8fc1..693e24c4721 100644 --- a/lib/private/Encryption/Util.php +++ b/lib/private/Encryption/Util.php @@ -220,7 +220,7 @@ class Util { * get the owner and the path for the file relative to the owners files folder * * @param string $path - * @return array + * @return array{0: string, 1: string} * @throws \BadMethodCallException */ public function getUidAndFilename($path) { diff --git a/lib/private/Files/AppData/AppData.php b/lib/private/Files/AppData/AppData.php index 53f69be7127..471de799c2f 100644 --- a/lib/private/Files/AppData/AppData.php +++ b/lib/private/Files/AppData/AppData.php @@ -38,21 +38,12 @@ use OCP\Files\NotPermittedException; use OCP\Files\SimpleFS\ISimpleFolder; class AppData implements IAppData { - - /** @var IRootFolder */ - private $rootFolder; - - /** @var SystemConfig */ - private $config; - - /** @var string */ - private $appId; - - /** @var Folder */ - private $folder; - - /** @var (ISimpleFolder|NotFoundException)[]|CappedMemoryCache */ - private $folders; + private IRootFolder $rootFolder; + private SystemConfig $config; + private string $appId; + private ?Folder $folder = null; + /** @var CappedMemoryCache<ISimpleFolder|NotFoundException> */ + private CappedMemoryCache $folders; /** * AppData constructor. diff --git a/lib/private/Files/AppData/Factory.php b/lib/private/Files/AppData/Factory.php index 6d7483158f6..03f8fdedcbd 100644 --- a/lib/private/Files/AppData/Factory.php +++ b/lib/private/Files/AppData/Factory.php @@ -27,17 +27,16 @@ declare(strict_types=1); namespace OC\Files\AppData; use OC\SystemConfig; +use OCP\Files\AppData\IAppDataFactory; +use OCP\Files\IAppData; use OCP\Files\IRootFolder; -class Factory { +class Factory implements IAppDataFactory { + private IRootFolder $rootFolder; + private SystemConfig $config; - /** @var IRootFolder */ - private $rootFolder; - - /** @var SystemConfig */ - private $config; - - private $folders = []; + /** @var array<string, IAppData> */ + private array $folders = []; public function __construct(IRootFolder $rootFolder, SystemConfig $systemConfig) { @@ -45,11 +44,7 @@ class Factory { $this->config = $systemConfig; } - /** - * @param string $appId - * @return AppData - */ - public function get(string $appId): AppData { + public function get(string $appId): IAppData { if (!isset($this->folders[$appId])) { $this->folders[$appId] = new AppData($this->rootFolder, $this->config, $appId); } diff --git a/lib/private/Files/Config/UserMountCache.php b/lib/private/Files/Config/UserMountCache.php index a5fe04c2cac..c326eeb0b6c 100644 --- a/lib/private/Files/Config/UserMountCache.php +++ b/lib/private/Files/Config/UserMountCache.php @@ -36,7 +36,6 @@ use OCP\Files\Config\ICachedMountInfo; use OCP\Files\Config\IUserMountCache; use OCP\Files\Mount\IMountPoint; use OCP\Files\NotFoundException; -use OCP\ICache; use OCP\IDBConnection; use OCP\IUser; use OCP\IUserManager; @@ -46,30 +45,17 @@ use Psr\Log\LoggerInterface; * Cache mounts points per user in the cache so we can easilly look them up */ class UserMountCache implements IUserMountCache { - /** - * @var IDBConnection - */ - private $connection; - - /** - * @var IUserManager - */ - private $userManager; + private IDBConnection $connection; + private IUserManager $userManager; /** * Cached mount info. - * Map of $userId to ICachedMountInfo. - * - * @var ICache + * @var CappedMemoryCache<ICachedMountInfo[]> **/ - private $mountsForUsers; - + private CappedMemoryCache $mountsForUsers; private LoggerInterface $logger; - - /** - * @var ICache - */ - private $cacheInfoCache; + /** @var CappedMemoryCache<array> */ + private CappedMemoryCache $cacheInfoCache; /** * UserMountCache constructor. @@ -132,6 +118,7 @@ class UserMountCache implements IUserMountCache { foreach ($addedMounts as $mount) { $this->addToCache($mount); + /** @psalm-suppress InvalidArgument */ $this->mountsForUsers[$user->getUID()][] = $mount; } foreach ($removedMounts as $mount) { diff --git a/lib/private/Files/Stream/Encryption.php b/lib/private/Files/Stream/Encryption.php index e1233e76b93..0f1838c97c8 100644 --- a/lib/private/Files/Stream/Encryption.php +++ b/lib/private/Files/Stream/Encryption.php @@ -259,7 +259,6 @@ class Encryption extends Wrapper { $this->cache = ''; $this->writeFlag = false; $this->fileUpdated = false; - $this->unencryptedBlockSize = $this->encryptionModule->getUnencryptedBlockSize($this->signed); if ( $mode === 'w' @@ -284,6 +283,7 @@ class Encryption extends Wrapper { $accessList = $this->file->getAccessList($sharePath); } $this->newHeader = $this->encryptionModule->begin($this->fullPath, $this->uid, $mode, $this->header, $accessList); + $this->unencryptedBlockSize = $this->encryptionModule->getUnencryptedBlockSize($this->signed); if ( $mode === 'w' diff --git a/lib/private/Lock/AbstractLockingProvider.php b/lib/private/Lock/AbstractLockingProvider.php index f4899bbb2b2..6e8289db12e 100644 --- a/lib/private/Lock/AbstractLockingProvider.php +++ b/lib/private/Lock/AbstractLockingProvider.php @@ -30,24 +30,18 @@ use OCP\Lock\ILockingProvider; /** * Base locking provider that keeps track of locks acquired during the current request - * to release any left over locks at the end of the request + * to release any leftover locks at the end of the request */ abstract class AbstractLockingProvider implements ILockingProvider { - /** @var int $ttl */ - protected $ttl; // how long until we clear stray locks in seconds + /** how long until we clear stray locks in seconds */ + protected int $ttl; protected $acquiredLocks = [ 'shared' => [], 'exclusive' => [] ]; - /** - * Check if we've locally acquired a lock - * - * @param string $path - * @param int $type - * @return bool - */ + /** @inheritDoc */ protected function hasAcquiredLock(string $path, int $type): bool { if ($type === self::LOCK_SHARED) { return isset($this->acquiredLocks['shared'][$path]) && $this->acquiredLocks['shared'][$path] > 0; @@ -56,14 +50,9 @@ abstract class AbstractLockingProvider implements ILockingProvider { } } - /** - * Mark a locally acquired lock - * - * @param string $path - * @param int $type self::LOCK_SHARED or self::LOCK_EXCLUSIVE - */ - protected function markAcquire(string $path, int $type) { - if ($type === self::LOCK_SHARED) { + /** @inheritDoc */ + protected function markAcquire(string $path, int $targetType): void { + if ($targetType === self::LOCK_SHARED) { if (!isset($this->acquiredLocks['shared'][$path])) { $this->acquiredLocks['shared'][$path] = 0; } @@ -73,13 +62,8 @@ abstract class AbstractLockingProvider implements ILockingProvider { } } - /** - * Mark a release of a locally acquired lock - * - * @param string $path - * @param int $type self::LOCK_SHARED or self::LOCK_EXCLUSIVE - */ - protected function markRelease(string $path, int $type) { + /** @inheritDoc */ + protected function markRelease(string $path, int $type): void { if ($type === self::LOCK_SHARED) { if (isset($this->acquiredLocks['shared'][$path]) and $this->acquiredLocks['shared'][$path] > 0) { $this->acquiredLocks['shared'][$path]--; @@ -92,13 +76,8 @@ abstract class AbstractLockingProvider implements ILockingProvider { } } - /** - * Change the type of an existing tracked lock - * - * @param string $path - * @param int $targetType self::LOCK_SHARED or self::LOCK_EXCLUSIVE - */ - protected function markChange(string $path, int $targetType) { + /** @inheritDoc */ + protected function markChange(string $path, int $targetType): void { if ($targetType === self::LOCK_SHARED) { unset($this->acquiredLocks['exclusive'][$path]); if (!isset($this->acquiredLocks['shared'][$path])) { @@ -111,10 +90,8 @@ abstract class AbstractLockingProvider implements ILockingProvider { } } - /** - * release all lock acquired by this instance which were marked using the mark* methods - */ - public function releaseAll() { + /** @inheritDoc */ + public function releaseAll(): void { foreach ($this->acquiredLocks['shared'] as $path => $count) { for ($i = 0; $i < $count; $i++) { $this->releaseLock($path, self::LOCK_SHARED); @@ -126,7 +103,7 @@ abstract class AbstractLockingProvider implements ILockingProvider { } } - protected function getOwnSharedLockCount(string $path) { - return isset($this->acquiredLocks['shared'][$path]) ? $this->acquiredLocks['shared'][$path] : 0; + protected function getOwnSharedLockCount(string $path): int { + return $this->acquiredLocks['shared'][$path] ?? 0; } } diff --git a/lib/private/Lock/DBLockingProvider.php b/lib/private/Lock/DBLockingProvider.php index 5eb03ad856b..fb8af8ac55b 100644 --- a/lib/private/Lock/DBLockingProvider.php +++ b/lib/private/Lock/DBLockingProvider.php @@ -9,6 +9,7 @@ * @author Ole Ostergaard <ole.c.ostergaard@gmail.com> * @author Robin Appelman <robin@icewind.nl> * @author Roeland Jago Douma <roeland@famdouma.nl> + * @author Carl Schwan <carl@carlschwan.eu> * * @license AGPL-3.0 * @@ -33,51 +34,40 @@ use OCP\DB\QueryBuilder\IQueryBuilder; use OCP\IDBConnection; use OCP\Lock\ILockingProvider; use OCP\Lock\LockedException; -use Psr\Log\LoggerInterface; /** * Locking provider that stores the locks in the database */ class DBLockingProvider extends AbstractLockingProvider { - /** - * @var \OCP\IDBConnection - */ - private $connection; - - private LoggerInterface $logger; - - /** - * @var \OCP\AppFramework\Utility\ITimeFactory - */ - private $timeFactory; - - private $sharedLocks = []; + private IDBConnection $connection; + private ITimeFactory $timeFactory; + private array $sharedLocks = []; + private bool $cacheSharedLocks; - /** - * @var bool - */ - private $cacheSharedLocks; + public function __construct( + IDBConnection $connection, + ITimeFactory $timeFactory, + int $ttl = 3600, + bool $cacheSharedLocks = true + ) { + $this->connection = $connection; + $this->timeFactory = $timeFactory; + $this->ttl = $ttl; + $this->cacheSharedLocks = $cacheSharedLocks; + } /** * Check if we have an open shared lock for a path - * - * @param string $path - * @return bool */ protected function isLocallyLocked(string $path): bool { return isset($this->sharedLocks[$path]) && $this->sharedLocks[$path]; } - /** - * Mark a locally acquired lock - * - * @param string $path - * @param int $type self::LOCK_SHARED or self::LOCK_EXCLUSIVE - */ - protected function markAcquire(string $path, int $type) { - parent::markAcquire($path, $type); + /** @inheritDoc */ + protected function markAcquire(string $path, int $targetType): void { + parent::markAcquire($path, $targetType); if ($this->cacheSharedLocks) { - if ($type === self::LOCK_SHARED) { + if ($targetType === self::LOCK_SHARED) { $this->sharedLocks[$path] = true; } } @@ -85,11 +75,8 @@ class DBLockingProvider extends AbstractLockingProvider { /** * Change the type of an existing tracked lock - * - * @param string $path - * @param int $targetType self::LOCK_SHARED or self::LOCK_EXCLUSIVE */ - protected function markChange(string $path, int $targetType) { + protected function markChange(string $path, int $targetType): void { parent::markChange($path, $targetType); if ($this->cacheSharedLocks) { if ($targetType === self::LOCK_SHARED) { @@ -101,28 +88,7 @@ class DBLockingProvider extends AbstractLockingProvider { } /** - * @param bool $cacheSharedLocks - */ - public function __construct( - IDBConnection $connection, - LoggerInterface $logger, - ITimeFactory $timeFactory, - int $ttl = 3600, - $cacheSharedLocks = true - ) { - $this->connection = $connection; - $this->logger = $logger; - $this->timeFactory = $timeFactory; - $this->ttl = $ttl; - $this->cacheSharedLocks = $cacheSharedLocks; - } - - /** * Insert a file locking row if it does not exists. - * - * @param string $path - * @param int $lock - * @return int number of inserted rows */ protected function initLockField(string $path, int $lock = 0): int { $expire = $this->getExpireTime(); @@ -133,25 +99,21 @@ class DBLockingProvider extends AbstractLockingProvider { ]); } - /** - * @return int - */ protected function getExpireTime(): int { return $this->timeFactory->getTime() + $this->ttl; } - /** - * @param string $path - * @param int $type self::LOCK_SHARED or self::LOCK_EXCLUSIVE - * @return bool - */ + /** @inheritDoc */ public function isLocked(string $path, int $type): bool { if ($this->hasAcquiredLock($path, $type)) { return true; } - $query = $this->connection->prepare('SELECT `lock` from `*PREFIX*file_locks` WHERE `key` = ?'); - $query->execute([$path]); - $lockValue = (int)$query->fetchOne(); + $query = $this->connection->getQueryBuilder(); + $query->select('lock') + ->from('file_locks') + ->where($query->expr()->eq('key', $query->createNamedParameter($path))); + $result = $query->executeQuery(); + $lockValue = (int)$result->fetchOne(); if ($type === self::LOCK_SHARED) { if ($this->isLocallyLocked($path)) { // if we have a shared lock we kept open locally but it's released we always have at least 1 shared lock in the db @@ -166,21 +128,20 @@ class DBLockingProvider extends AbstractLockingProvider { } } - /** - * @param string $path - * @param int $type self::LOCK_SHARED or self::LOCK_EXCLUSIVE - * @throws \OCP\Lock\LockedException - */ - public function acquireLock(string $path, int $type, string $readablePath = null) { + /** @inheritDoc */ + public function acquireLock(string $path, int $type, ?string $readablePath = null): void { $expire = $this->getExpireTime(); if ($type === self::LOCK_SHARED) { if (!$this->isLocallyLocked($path)) { $result = $this->initLockField($path, 1); if ($result <= 0) { - $result = $this->connection->executeUpdate( - 'UPDATE `*PREFIX*file_locks` SET `lock` = `lock` + 1, `ttl` = ? WHERE `key` = ? AND `lock` >= 0', - [$expire, $path] - ); + $query = $this->connection->getQueryBuilder(); + $query->update('file_locks') + ->set('lock', $query->func()->add('lock', $query->createNamedParameter(1, IQueryBuilder::PARAM_INT))) + ->set('ttl', $query->createNamedParameter($expire)) + ->where($query->expr()->eq('key', $query->createNamedParameter($path))) + ->andWhere($query->expr()->gte('lock', $query->createNamedParameter(0, IQueryBuilder::PARAM_INT))); + $result = $query->executeStatement(); } } else { $result = 1; @@ -192,10 +153,13 @@ class DBLockingProvider extends AbstractLockingProvider { } $result = $this->initLockField($path, -1); if ($result <= 0) { - $result = $this->connection->executeUpdate( - 'UPDATE `*PREFIX*file_locks` SET `lock` = -1, `ttl` = ? WHERE `key` = ? AND `lock` = ?', - [$expire, $path, $existing] - ); + $query = $this->connection->getQueryBuilder(); + $query->update('file_locks') + ->set('lock', $query->createNamedParameter(-1, IQueryBuilder::PARAM_INT)) + ->set('ttl', $query->createNamedParameter($expire, IQueryBuilder::PARAM_INT)) + ->where($query->expr()->eq('key', $query->createNamedParameter($path))) + ->andWhere($query->expr()->eq('lock', $query->createNamedParameter($existing))); + $result = $query->executeStatement(); } } if ($result !== 1) { @@ -204,52 +168,53 @@ class DBLockingProvider extends AbstractLockingProvider { $this->markAcquire($path, $type); } - /** - * @param string $path - * @param int $type self::LOCK_SHARED or self::LOCK_EXCLUSIVE - */ - public function releaseLock(string $path, int $type) { + /** @inheritDoc */ + public function releaseLock(string $path, int $type): void { $this->markRelease($path, $type); // we keep shared locks till the end of the request so we can re-use them if ($type === self::LOCK_EXCLUSIVE) { - $this->connection->executeUpdate( - 'UPDATE `*PREFIX*file_locks` SET `lock` = 0 WHERE `key` = ? AND `lock` = -1', - [$path] - ); + $qb = $this->connection->getQueryBuilder(); + $qb->update('file_locks') + ->set('lock', $qb->createNamedParameter(0, IQueryBuilder::PARAM_INT)) + ->where($qb->expr()->eq('key', $qb->createNamedParameter($path))) + ->andWhere($qb->expr()->eq('lock', $qb->createNamedParameter(-1, IQueryBuilder::PARAM_INT))); + $qb->executeStatement(); } elseif (!$this->cacheSharedLocks) { - $query = $this->connection->getQueryBuilder(); - $query->update('file_locks') - ->set('lock', $query->func()->subtract('lock', $query->createNamedParameter(1))) - ->where($query->expr()->eq('key', $query->createNamedParameter($path))) - ->andWhere($query->expr()->gt('lock', $query->createNamedParameter(0))); - $query->execute(); + $qb = $this->connection->getQueryBuilder(); + $qb->update('file_locks') + ->set('lock', $qb->func()->subtract('lock', $qb->createNamedParameter(1, IQueryBuilder::PARAM_INT))) + ->where($qb->expr()->eq('key', $qb->createNamedParameter($path))) + ->andWhere($qb->expr()->gt('lock', $qb->createNamedParameter(0, IQueryBuilder::PARAM_INT))); + $qb->executeStatement(); } } - /** - * Change the type of an existing lock - * - * @param string $path - * @param int $targetType self::LOCK_SHARED or self::LOCK_EXCLUSIVE - * @throws \OCP\Lock\LockedException - */ - public function changeLock(string $path, int $targetType) { + /** @inheritDoc */ + public function changeLock(string $path, int $targetType): void { $expire = $this->getExpireTime(); if ($targetType === self::LOCK_SHARED) { - $result = $this->connection->executeUpdate( - 'UPDATE `*PREFIX*file_locks` SET `lock` = 1, `ttl` = ? WHERE `key` = ? AND `lock` = -1', - [$expire, $path] - ); + $qb = $this->connection->getQueryBuilder(); + $result = $qb->update('file_locks') + ->set('lock', $qb->createNamedParameter(1, IQueryBuilder::PARAM_INT)) + ->set('ttl', $qb->createNamedParameter($expire, IQueryBuilder::PARAM_INT)) + ->where($qb->expr()->andX( + $qb->expr()->eq('key', $qb->createNamedParameter($path)), + $qb->expr()->eq('lock', $qb->createNamedParameter(-1, IQueryBuilder::PARAM_INT)) + ))->executeStatement(); } else { // since we only keep one shared lock in the db we need to check if we have more then one shared lock locally manually if (isset($this->acquiredLocks['shared'][$path]) && $this->acquiredLocks['shared'][$path] > 1) { throw new LockedException($path); } - $result = $this->connection->executeUpdate( - 'UPDATE `*PREFIX*file_locks` SET `lock` = -1, `ttl` = ? WHERE `key` = ? AND `lock` = 1', - [$expire, $path] - ); + $qb = $this->connection->getQueryBuilder(); + $result = $qb->update('file_locks') + ->set('lock', $qb->createNamedParameter(-1, IQueryBuilder::PARAM_INT)) + ->set('ttl', $qb->createNamedParameter($expire, IQueryBuilder::PARAM_INT)) + ->where($qb->expr()->andX( + $qb->expr()->eq('key', $qb->createNamedParameter($path)), + $qb->expr()->eq('lock', $qb->createNamedParameter(1, IQueryBuilder::PARAM_INT)) + ))->executeStatement(); } if ($result !== 1) { throw new LockedException($path); @@ -257,16 +222,14 @@ class DBLockingProvider extends AbstractLockingProvider { $this->markChange($path, $targetType); } - /** - * cleanup empty locks - */ - public function cleanExpiredLocks() { + /** @inheritDoc */ + public function cleanExpiredLocks(): void { $expire = $this->timeFactory->getTime(); try { - $this->connection->executeUpdate( - 'DELETE FROM `*PREFIX*file_locks` WHERE `ttl` < ?', - [$expire] - ); + $qb = $this->connection->getQueryBuilder(); + $qb->delete('file_locks') + ->where($qb->expr()->lt('ttl', $qb->createNamedParameter($expire, IQueryBuilder::PARAM_INT))) + ->executeStatement(); } catch (\Exception $e) { // If the table is missing, the clean up was successful if ($this->connection->tableExists('file_locks')) { @@ -275,10 +238,8 @@ class DBLockingProvider extends AbstractLockingProvider { } } - /** - * release all lock acquired by this instance which were marked using the mark* methods - */ - public function releaseAll() { + /** @inheritDoc */ + public function releaseAll(): void { parent::releaseAll(); if (!$this->cacheSharedLocks) { @@ -292,15 +253,15 @@ class DBLockingProvider extends AbstractLockingProvider { $chunkedPaths = array_chunk($lockedPaths, 100); - foreach ($chunkedPaths as $chunk) { - $builder = $this->connection->getQueryBuilder(); - - $query = $builder->update('file_locks') - ->set('lock', $builder->func()->subtract('lock', $builder->expr()->literal(1))) - ->where($builder->expr()->in('key', $builder->createNamedParameter($chunk, IQueryBuilder::PARAM_STR_ARRAY))) - ->andWhere($builder->expr()->gt('lock', new Literal(0))); + $qb = $this->connection->getQueryBuilder(); + $qb->update('file_locks') + ->set('lock', $qb->func()->subtract('lock', $qb->expr()->literal(1))) + ->where($qb->expr()->in('key', $qb->createParameter('chunk'))) + ->andWhere($qb->expr()->gt('lock', new Literal(0))); - $query->execute(); + foreach ($chunkedPaths as $chunk) { + $qb->setParameter('chunk', $chunk, IQueryBuilder::PARAM_STR_ARRAY); + $qb->executeStatement(); } } } diff --git a/lib/private/Lock/MemcacheLockingProvider.php b/lib/private/Lock/MemcacheLockingProvider.php index 008a7875d7e..d4eebd7c302 100644 --- a/lib/private/Lock/MemcacheLockingProvider.php +++ b/lib/private/Lock/MemcacheLockingProvider.php @@ -32,31 +32,19 @@ use OCP\IMemcacheTTL; use OCP\Lock\LockedException; class MemcacheLockingProvider extends AbstractLockingProvider { - /** - * @var \OCP\IMemcache - */ - private $memcache; + private IMemcache $memcache; - /** - * @param \OCP\IMemcache $memcache - * @param int $ttl - */ public function __construct(IMemcache $memcache, int $ttl = 3600) { $this->memcache = $memcache; $this->ttl = $ttl; } - private function setTTL(string $path) { + private function setTTL(string $path): void { if ($this->memcache instanceof IMemcacheTTL) { $this->memcache->setTTL($path, $this->ttl); } } - /** - * @param string $path - * @param int $type self::LOCK_SHARED or self::LOCK_EXCLUSIVE - * @return bool - */ public function isLocked(string $path, int $type): bool { $lockValue = $this->memcache->get($path); if ($type === self::LOCK_SHARED) { @@ -68,13 +56,7 @@ class MemcacheLockingProvider extends AbstractLockingProvider { } } - /** - * @param string $path - * @param int $type self::LOCK_SHARED or self::LOCK_EXCLUSIVE - * @param string $readablePath human readable path to use in error messages - * @throws \OCP\Lock\LockedException - */ - public function acquireLock(string $path, int $type, string $readablePath = null) { + public function acquireLock(string $path, int $type, ?string $readablePath = null): void { if ($type === self::LOCK_SHARED) { if (!$this->memcache->inc($path)) { throw new LockedException($path, null, $this->getExistingLockForException($path), $readablePath); @@ -89,11 +71,7 @@ class MemcacheLockingProvider extends AbstractLockingProvider { $this->markAcquire($path, $type); } - /** - * @param string $path - * @param int $type self::LOCK_SHARED or self::LOCK_EXCLUSIVE - */ - public function releaseLock(string $path, int $type) { + public function releaseLock(string $path, int $type): void { if ($type === self::LOCK_SHARED) { $ownSharedLockCount = $this->getOwnSharedLockCount($path); $newValue = 0; @@ -120,14 +98,7 @@ class MemcacheLockingProvider extends AbstractLockingProvider { $this->markRelease($path, $type); } - /** - * Change the type of an existing lock - * - * @param string $path - * @param int $targetType self::LOCK_SHARED or self::LOCK_EXCLUSIVE - * @throws \OCP\Lock\LockedException - */ - public function changeLock(string $path, int $targetType) { + public function changeLock(string $path, int $targetType): void { if ($targetType === self::LOCK_SHARED) { if (!$this->memcache->cas($path, 'exclusive', 1)) { throw new LockedException($path, null, $this->getExistingLockForException($path)); @@ -142,7 +113,7 @@ class MemcacheLockingProvider extends AbstractLockingProvider { $this->markChange($path, $targetType); } - private function getExistingLockForException($path) { + private function getExistingLockForException(string $path): string { $existing = $this->memcache->get($path); if (!$existing) { return 'none'; diff --git a/lib/private/Lock/NoopLockingProvider.php b/lib/private/Lock/NoopLockingProvider.php index 95ae8cf0cda..ff56932a894 100644 --- a/lib/private/Lock/NoopLockingProvider.php +++ b/lib/private/Lock/NoopLockingProvider.php @@ -45,28 +45,28 @@ class NoopLockingProvider implements ILockingProvider { /** * {@inheritdoc} */ - public function acquireLock(string $path, int $type, string $readablePath = null) { + public function acquireLock(string $path, int $type, ?string $readablePath = null): void { // do nothing } /** * {@inheritdoc} */ - public function releaseLock(string $path, int $type) { + public function releaseLock(string $path, int $type): void { // do nothing } /**1 * {@inheritdoc} */ - public function releaseAll() { + public function releaseAll(): void { // do nothing } /** * {@inheritdoc} */ - public function changeLock(string $path, int $targetType) { + public function changeLock(string $path, int $targetType): void { // do nothing } } diff --git a/lib/private/Preview/Bitmap.php b/lib/private/Preview/Bitmap.php index 3a4108664dd..f7c8cf3543b 100644 --- a/lib/private/Preview/Bitmap.php +++ b/lib/private/Preview/Bitmap.php @@ -55,7 +55,7 @@ abstract class Bitmap extends ProviderV2 { try { $bp = $this->getResizedPreview($tmpPath, $maxX, $maxY); } catch (\Exception $e) { - \OC::$server->get(LoggerInterface::class)->error( + \OC::$server->get(LoggerInterface::class)->info( 'File: ' . $file->getPath() . ' Imagick says:', [ 'exception' => $e, diff --git a/lib/private/Preview/Generator.php b/lib/private/Preview/Generator.php index cef3fa4039a..e058a15bfa5 100644 --- a/lib/private/Preview/Generator.php +++ b/lib/private/Preview/Generator.php @@ -31,6 +31,7 @@ namespace OC\Preview; use OCP\Files\File; use OCP\Files\IAppData; +use OCP\Files\InvalidPathException; use OCP\Files\NotFoundException; use OCP\Files\NotPermittedException; use OCP\Files\SimpleFS\ISimpleFile; @@ -549,12 +550,19 @@ class Generator { * * @param File $file * @return ISimpleFolder + * + * @throws InvalidPathException + * @throws NotFoundException + * @throws NotPermittedException */ private function getPreviewFolder(File $file) { + // Obtain file id outside of try catch block to prevent the creation of an existing folder + $fileId = (string)$file->getId(); + try { - $folder = $this->appData->getFolder($file->getId()); + $folder = $this->appData->getFolder($fileId); } catch (NotFoundException $e) { - $folder = $this->appData->newFolder($file->getId()); + $folder = $this->appData->newFolder($fileId); } return $folder; diff --git a/lib/private/Repair.php b/lib/private/Repair.php index 91bfd47f5de..fb65789dd8a 100644 --- a/lib/private/Repair.php +++ b/lib/private/Repair.php @@ -72,7 +72,6 @@ use OC\Repair\RepairInvalidShares; use OC\Repair\RepairMimeTypes; use OC\Repair\SqliteAutoincrement; use OC\Template\JSCombiner; -use OC\Template\SCSSCacher; use OCP\AppFramework\QueryException; use OCP\AppFramework\Utility\ITimeFactory; use OCP\Collaboration\Resources\IManager; @@ -194,7 +193,7 @@ class Repair implements IOutput { \OC::$server->query(Installer::class) ), new AddLogRotateJob(\OC::$server->getJobList()), - new ClearFrontendCaches(\OC::$server->getMemCacheFactory(), \OC::$server->query(SCSSCacher::class), \OC::$server->query(JSCombiner::class)), + new ClearFrontendCaches(\OC::$server->getMemCacheFactory(), \OC::$server->query(JSCombiner::class)), new ClearGeneratedAvatarCache(\OC::$server->getConfig(), \OC::$server->query(AvatarManager::class)), new AddPreviewBackgroundCleanupJob(\OC::$server->getJobList()), new AddCleanupUpdaterBackupsJob(\OC::$server->getJobList()), diff --git a/lib/private/Repair/ClearFrontendCaches.php b/lib/private/Repair/ClearFrontendCaches.php index 96a7833fecc..47f037fd626 100644 --- a/lib/private/Repair/ClearFrontendCaches.php +++ b/lib/private/Repair/ClearFrontendCaches.php @@ -25,7 +25,6 @@ namespace OC\Repair; use OC\Template\JSCombiner; -use OC\Template\SCSSCacher; use OCP\ICacheFactory; use OCP\Migration\IOutput; use OCP\Migration\IRepairStep; @@ -35,17 +34,12 @@ class ClearFrontendCaches implements IRepairStep { /** @var ICacheFactory */ protected $cacheFactory; - /** @var SCSSCacher */ - protected $scssCacher; - /** @var JSCombiner */ protected $jsCombiner; public function __construct(ICacheFactory $cacheFactory, - SCSSCacher $SCSSCacher, JSCombiner $JSCombiner) { $this->cacheFactory = $cacheFactory; - $this->scssCacher = $SCSSCacher; $this->jsCombiner = $JSCombiner; } @@ -59,9 +53,6 @@ class ClearFrontendCaches implements IRepairStep { $c->clear(); $output->info('Image cache cleared'); - $this->scssCacher->resetCache(); - $output->info('SCSS cache cleared'); - $this->jsCombiner->resetCache(); $output->info('JS cache cleared'); } catch (\Exception $e) { diff --git a/lib/private/Security/CSP/ContentSecurityPolicy.php b/lib/private/Security/CSP/ContentSecurityPolicy.php index 8a72934d4c9..8d9551c8978 100644 --- a/lib/private/Security/CSP/ContentSecurityPolicy.php +++ b/lib/private/Security/CSP/ContentSecurityPolicy.php @@ -246,6 +246,13 @@ class ContentSecurityPolicy extends \OCP\AppFramework\Http\ContentSecurityPolicy } /** + * @return boolean + */ + public function isStrictDynamicAllowed(): bool { + return $this->strictDynamicAllowed; + } + + /** * @param boolean $strictDynamicAllowed */ public function setStrictDynamicAllowed(bool $strictDynamicAllowed) { diff --git a/lib/private/Security/CSP/ContentSecurityPolicyManager.php b/lib/private/Security/CSP/ContentSecurityPolicyManager.php index ff770435eda..4930dcb759c 100644 --- a/lib/private/Security/CSP/ContentSecurityPolicyManager.php +++ b/lib/private/Security/CSP/ContentSecurityPolicyManager.php @@ -82,7 +82,12 @@ class ContentSecurityPolicyManager implements IContentSecurityPolicyManager { $currentValues = \is_array($defaultPolicy->$getter()) ? $defaultPolicy->$getter() : []; $defaultPolicy->$setter(array_values(array_unique(array_merge($currentValues, $value)))); } elseif (\is_bool($value)) { - $defaultPolicy->$setter($value); + $getter = 'is'.ucfirst($name); + $currentValue = $defaultPolicy->$getter(); + // true wins over false + if ($value > $currentValue) { + $defaultPolicy->$setter($value); + } } } diff --git a/lib/private/Security/SecureRandom.php b/lib/private/Security/SecureRandom.php index 4bf8995d737..cbd1dc8db6d 100644 --- a/lib/private/Security/SecureRandom.php +++ b/lib/private/Security/SecureRandom.php @@ -40,14 +40,19 @@ use OCP\Security\ISecureRandom; */ class SecureRandom implements ISecureRandom { /** - * Generate a random string of specified length. + * Generate a secure random string of specified length. * @param int $length The length of the generated string * @param string $characters An optional list of characters to use if no character list is * specified all valid base64 characters are used. * @return string + * @throws \LengthException if an invalid length is requested */ public function generate(int $length, string $characters = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/'): string { + if ($length <= 0) { + throw new \LengthException('Invalid length specified: ' . $length . ' must be bigger than 0'); + } + $maxCharIndex = \strlen($characters) - 1; $randomString = ''; diff --git a/lib/private/Server.php b/lib/private/Server.php index 5308de21f51..6e6fa430489 100644 --- a/lib/private/Server.php +++ b/lib/private/Server.php @@ -1109,7 +1109,6 @@ class Server extends ServerContainer implements IServerContainer { } return new DBLockingProvider( $c->get(IDBConnection::class), - $c->get(LoggerInterface::class), new TimeFactory(), $ttl, !\OC::$CLI @@ -1439,6 +1438,8 @@ class Server extends ServerContainer implements IServerContainer { $this->registerAlias(IMetadataManager::class, MetadataManager::class); + $this->registerAlias(\OCP\Files\AppData\IAppDataFactory::class, \OC\Files\AppData\Factory::class); + $this->connectDispatcher(); } @@ -2300,7 +2301,7 @@ class Server extends ServerContainer implements IServerContainer { /** * @return \OCP\Files\IAppData - * @deprecated 20.0.0 + * @deprecated 20.0.0 Use get(\OCP\Files\AppData\IAppDataFactory::class)->get($app) instead */ public function getAppDataDir($app) { /** @var \OC\Files\AppData\Factory $factory */ diff --git a/lib/private/Share20/DefaultShareProvider.php b/lib/private/Share20/DefaultShareProvider.php index 9638706025b..520bd17d3cf 100644 --- a/lib/private/Share20/DefaultShareProvider.php +++ b/lib/private/Share20/DefaultShareProvider.php @@ -671,10 +671,10 @@ class DefaultShareProvider implements IShareProvider { } // todo? maybe get these from the oc_mounts table - $childMountNodes = array_filter($node->getDirectoryListing(), function (Node $node) { + $childMountNodes = array_filter($node->getDirectoryListing(), function (Node $node): bool { return $node->getInternalPath() === ''; }); - $childMountRootIds = array_map(function (Node $node) { + $childMountRootIds = array_map(function (Node $node): int { return $node->getId(); }, $childMountNodes); @@ -682,18 +682,29 @@ class DefaultShareProvider implements IShareProvider { $qb->andWhere( $qb->expr()->orX( $qb->expr()->eq('f.parent', $qb->createNamedParameter($node->getId())), - $qb->expr()->in('f.fileid', $qb->createNamedParameter($childMountRootIds, IQueryBuilder::PARAM_INT_ARRAY)) + $qb->expr()->in('f.fileid', $qb->createParameter('chunk')) ) ); $qb->orderBy('id'); - $cursor = $qb->execute(); $shares = []; - while ($data = $cursor->fetch()) { - $shares[$data['fileid']][] = $this->createShare($data); + + $chunks = array_chunk($childMountRootIds, 1000); + + // Force the request to be run when there is 0 mount. + if (count($chunks) === 0) { + $chunks = [[]]; + } + + foreach ($chunks as $chunk) { + $qb->setParameter('chunk', $chunk, IQueryBuilder::PARAM_INT_ARRAY); + $cursor = $qb->executeQuery(); + while ($data = $cursor->fetch()) { + $shares[$data['fileid']][] = $this->createShare($data); + } + $cursor->closeCursor(); } - $cursor->closeCursor(); return $shares; } diff --git a/lib/private/Template/CSSResourceLocator.php b/lib/private/Template/CSSResourceLocator.php index a038ba7ce9b..2cbf12ce65d 100644 --- a/lib/private/Template/CSSResourceLocator.php +++ b/lib/private/Template/CSSResourceLocator.php @@ -35,18 +35,12 @@ use Psr\Log\LoggerInterface; class CSSResourceLocator extends ResourceLocator { - /** @var SCSSCacher */ - protected $scssCacher; - /** * @param string $theme * @param array $core_map * @param array $party_map - * @param SCSSCacher $scssCacher */ - public function __construct(LoggerInterface $logger, $theme, $core_map, $party_map, $scssCacher) { - $this->scssCacher = $scssCacher; - + public function __construct(LoggerInterface $logger, $theme, $core_map, $party_map) { parent::__construct($logger, $theme, $core_map, $party_map); } @@ -57,8 +51,6 @@ class CSSResourceLocator extends ResourceLocator { $app = substr($style, 0, strpos($style, '/')); if (strpos($style, '3rdparty') === 0 && $this->appendIfExist($this->thirdpartyroot, $style.'.css') - || $this->cacheAndAppendScssIfExist($this->serverroot, $style.'.scss', $app) - || $this->cacheAndAppendScssIfExist($this->serverroot, 'core/'.$style.'.scss') || $this->appendIfExist($this->serverroot, $style.'.css') || $this->appendIfExist($this->serverroot, 'core/'.$style.'.css') ) { @@ -81,9 +73,7 @@ class CSSResourceLocator extends ResourceLocator { // turned into cwd. $app_path = realpath($app_path); - if (!$this->cacheAndAppendScssIfExist($app_path, $style.'.scss', $app)) { - $this->append($app_path, $style.'.css', $app_url); - } + $this->append($app_path, $style.'.css', $app_url); } /** @@ -96,30 +86,6 @@ class CSSResourceLocator extends ResourceLocator { || $this->appendIfExist($this->serverroot, $theme_dir.'core/'.$style.'.css'); } - /** - * cache and append the scss $file if exist at $root - * - * @param string $root path to check - * @param string $file the filename - * @return bool True if the resource was found and cached, false otherwise - */ - protected function cacheAndAppendScssIfExist($root, $file, $app = 'core') { - if (is_file($root.'/'.$file)) { - if ($this->scssCacher !== null) { - if ($this->scssCacher->process($root, $file, $app)) { - $this->append($this->serverroot, $this->scssCacher->getCachedSCSS($app, $file), \OC::$WEBROOT, true, true); - return true; - } else { - $this->logger->warning('Failed to compile and/or save '.$root.'/'.$file, ['app' => 'core']); - return false; - } - } else { - return true; - } - } - return false; - } - public function append($root, $file, $webRoot = null, $throw = true, $scss = false) { if (!$scss) { parent::append($root, $file, $webRoot, $throw); diff --git a/lib/private/Template/IconsCacher.php b/lib/private/Template/IconsCacher.php deleted file mode 100644 index c2956d5712a..00000000000 --- a/lib/private/Template/IconsCacher.php +++ /dev/null @@ -1,263 +0,0 @@ -<?php - -declare(strict_types=1); - -/** - * @copyright Copyright (c) 2018, John Molakvoæ (skjnldsv@protonmail.com) - * - * @author Christoph Wurst <christoph@winzerhof-wurst.at> - * @author Joas Schilling <coding@schilljs.com> - * @author John Molakvoæ <skjnldsv@protonmail.com> - * @author Julius Härtl <jus@bitgrid.net> - * @author Robin Appelman <robin@icewind.nl> - * @author Roeland Jago Douma <roeland@famdouma.nl> - * - * @license GNU AGPL version 3 or any later version - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as - * published by the Free Software Foundation, either version 3 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - * - */ -namespace OC\Template; - -use OC\Files\AppData\Factory; -use OCP\AppFramework\Utility\ITimeFactory; -use OCP\Files\IAppData; -use OCP\Files\NotFoundException; -use OCP\Files\SimpleFS\ISimpleFile; -use OCP\Files\SimpleFS\ISimpleFolder; -use OCP\IURLGenerator; -use Psr\Log\LoggerInterface; - -class IconsCacher { - protected LoggerInterface $logger; - - /** @var IAppData */ - protected $appData; - - /** @var ISimpleFolder */ - private $folder; - - /** @var IURLGenerator */ - protected $urlGenerator; - - /** @var ITimeFactory */ - protected $timeFactory; - - /** @var string */ - private $iconVarRE = '/--(icon-[a-zA-Z0-9-]+):\s?url\(["\']?([a-zA-Z0-9-_\~\/\.\?\&\=\:\;\+\,]+)[^;]+;/m'; - - /** @var string */ - private $fileName = 'icons-vars.css'; - - private $iconList = 'icons-list.template'; - - private $cachedCss; - private $cachedList; - - /** - * @throws \OCP\Files\NotPermittedException - */ - public function __construct(LoggerInterface $logger, - Factory $appDataFactory, - IURLGenerator $urlGenerator, - ITimeFactory $timeFactory) { - $this->logger = $logger; - $this->appData = $appDataFactory->get('css'); - $this->urlGenerator = $urlGenerator; - $this->timeFactory = $timeFactory; - - try { - $this->folder = $this->appData->getFolder('icons'); - } catch (NotFoundException $e) { - $this->folder = $this->appData->newFolder('icons'); - } - } - - private function getIconsFromCss(string $css): array { - preg_match_all($this->iconVarRE, $css, $matches, PREG_SET_ORDER); - $icons = []; - foreach ($matches as $icon) { - $icons[$icon[1]] = $icon[2]; - } - - return $icons; - } - - /** - * @param string $css - * @return string - * @throws NotFoundException - * @throws \OCP\Files\NotPermittedException - */ - public function setIconsCss(string $css): string { - $cachedFile = $this->getCachedList(); - if (!$cachedFile) { - $currentData = ''; - $cachedFile = $this->folder->newFile($this->iconList); - } else { - $currentData = $cachedFile->getContent(); - } - - $cachedVarsCssFile = $this->getCachedCSS(); - if (!$cachedVarsCssFile) { - $cachedVarsCssFile = $this->folder->newFile($this->fileName); - } - - $icons = $this->getIconsFromCss($currentData . $css); - - $data = ''; - $list = ''; - foreach ($icons as $icon => $url) { - $list .= "--$icon: url('$url');"; - [$location,$color] = $this->parseUrl($url); - $svg = false; - if ($location !== '' && \file_exists($location)) { - $svg = \file_get_contents($location); - } - if ($svg === false) { - $this->logger->debug('Failed to get icon file ' . $location); - $data .= "--$icon: url('$url');"; - continue; - } - $encode = base64_encode($this->colorizeSvg($svg, $color)); - $data .= '--' . $icon . ': url(data:image/svg+xml;base64,' . $encode . ');'; - } - - if (\strlen($data) > 0 && \strlen($list) > 0) { - $data = ":root {\n$data\n}"; - $cachedVarsCssFile->putContent($data); - $list = ":root {\n$list\n}"; - $cachedFile->putContent($list); - $this->cachedList = null; - $this->cachedCss = null; - } - - return preg_replace($this->iconVarRE, '', $css); - } - - /** - * @param $url - * @return array - */ - private function parseUrl($url): array { - $location = ''; - $color = ''; - $base = $this->getRoutePrefix() . '/svg/'; - $cleanUrl = \substr($url, \strlen($base)); - if (\strpos($url, $base . 'core') === 0) { - $cleanUrl = \substr($cleanUrl, \strlen('core')); - if (\preg_match('/\/([a-zA-Z0-9-_\~\/\.\=\:\;\+\,]+)\?color=([0-9a-fA-F]{3,6})/', $cleanUrl, $matches)) { - [,$cleanUrl,$color] = $matches; - $location = \OC::$SERVERROOT . '/core/img/' . $cleanUrl . '.svg'; - } - } elseif (\strpos($url, $base) === 0) { - if (\preg_match('/([A-z0-9\_\-]+)\/([a-zA-Z0-9-_\~\/\.\=\:\;\+\,]+)\?color=([0-9a-fA-F]{3,6})/', $cleanUrl, $matches)) { - [,$app,$cleanUrl, $color] = $matches; - $appPath = \OC_App::getAppPath($app); - if ($appPath !== false) { - $location = $appPath . '/img/' . $cleanUrl . '.svg'; - } - if ($app === 'settings') { - $location = \OC::$SERVERROOT . '/settings/img/' . $cleanUrl . '.svg'; - } - } - } - return [ - $location, - $color - ]; - } - - /** - * @param $svg - * @param $color - * @return string - */ - public function colorizeSvg($svg, $color): string { - if (!preg_match('/^[0-9a-f]{3,6}$/i', $color)) { - // Prevent not-sane colors from being written into the SVG - $color = '000'; - } - - // add fill (fill is not present on black elements) - $fillRe = '/<((circle|rect|path)((?!fill)[a-z0-9 =".\-#():;,])+)\/>/mi'; - $svg = preg_replace($fillRe, '<$1 fill="#' . $color . '"/>', $svg); - - // replace any fill or stroke colors - $svg = preg_replace('/stroke="#([a-z0-9]{3,6})"/mi', 'stroke="#' . $color . '"', $svg); - $svg = preg_replace('/fill="#([a-z0-9]{3,6})"/mi', 'fill="#' . $color . '"', $svg); - return $svg; - } - - private function getRoutePrefix() { - $frontControllerActive = (\OC::$server->getConfig()->getSystemValue('htaccess.IgnoreFrontController', false) === true || getenv('front_controller_active') === 'true'); - $prefix = \OC::$WEBROOT . '/index.php'; - if ($frontControllerActive) { - $prefix = \OC::$WEBROOT; - } - return $prefix; - } - - /** - * Get icons css file - * @return ISimpleFile|boolean - */ - public function getCachedCSS() { - try { - if (!$this->cachedCss) { - $this->cachedCss = $this->folder->getFile($this->fileName); - } - return $this->cachedCss; - } catch (NotFoundException $e) { - return false; - } - } - - /** - * Get icon-vars list template - * @return ISimpleFile|boolean - */ - public function getCachedList() { - try { - if (!$this->cachedList) { - $this->cachedList = $this->folder->getFile($this->iconList); - } - return $this->cachedList; - } catch (NotFoundException $e) { - return false; - } - } - - /** - * Add the icons cache css into the header - */ - public function injectCss() { - $mtime = $this->timeFactory->getTime(); - $file = $this->getCachedList(); - if ($file) { - $mtime = $file->getMTime(); - } - // Only inject once - foreach (\OC_Util::$headers as $header) { - if ( - array_key_exists('attributes', $header) && - array_key_exists('href', $header['attributes']) && - strpos($header['attributes']['href'], $this->fileName) !== false) { - return; - } - } - $linkToCSS = $this->urlGenerator->linkToRoute('core.Css.getCss', ['appName' => 'icons', 'fileName' => $this->fileName, 'v' => $mtime]); - \OC_Util::addHeader('link', ['rel' => 'stylesheet', 'href' => $linkToCSS], null, true); - } -} diff --git a/lib/private/Template/SCSSCacher.php b/lib/private/Template/SCSSCacher.php deleted file mode 100644 index a552122b358..00000000000 --- a/lib/private/Template/SCSSCacher.php +++ /dev/null @@ -1,528 +0,0 @@ -<?php -/** - * @copyright Copyright (c) 2016, John Molakvoæ (skjnldsv@protonmail.com) - * - * @author Christoph Wurst <christoph@winzerhof-wurst.at> - * @author John Molakvoæ <skjnldsv@protonmail.com> - * @author Julius Haertl <jus@bitgrid.net> - * @author Julius Härtl <jus@bitgrid.net> - * @author Morris Jobke <hey@morrisjobke.de> - * @author Robin Appelman <robin@icewind.nl> - * @author Roeland Jago Douma <roeland@famdouma.nl> - * @author Roland Tapken <roland@bitarbeiter.net> - * - * @license GNU AGPL version 3 or any later version - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as - * published by the Free Software Foundation, either version 3 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - * - */ -namespace OC\Template; - -use OC\AppConfig; -use OC\Files\AppData\Factory; -use OC\Memcache\NullCache; -use OCP\AppFramework\Utility\ITimeFactory; -use OCP\Files\IAppData; -use OCP\Files\NotFoundException; -use OCP\Files\NotPermittedException; -use OCP\Files\SimpleFS\ISimpleFile; -use OCP\Files\SimpleFS\ISimpleFolder; -use OCP\ICache; -use OCP\ICacheFactory; -use OCP\IConfig; -use OCP\IMemcache; -use OCP\IURLGenerator; -use Psr\Log\LoggerInterface; -use ScssPhp\ScssPhp\Compiler; -use ScssPhp\ScssPhp\OutputStyle; - -class SCSSCacher { - protected LoggerInterface $logger; - - /** @var IAppData */ - protected $appData; - - /** @var IURLGenerator */ - protected $urlGenerator; - - /** @var IConfig */ - protected $config; - - /** @var \OC_Defaults */ - private $defaults; - - /** @var string */ - protected $serverRoot; - - /** @var ICache */ - protected $depsCache; - - /** @var null|string */ - private $injectedVariables; - - /** @var ICacheFactory */ - private $cacheFactory; - - /** @var IconsCacher */ - private $iconsCacher; - - /** @var ICache */ - private $isCachedCache; - - /** @var ITimeFactory */ - private $timeFactory; - - /** @var IMemcache */ - private $lockingCache; - /** @var AppConfig */ - private $appConfig; - - /** - * @param string $serverRoot - */ - public function __construct(LoggerInterface $logger, - Factory $appDataFactory, - IURLGenerator $urlGenerator, - IConfig $config, - \OC_Defaults $defaults, - $serverRoot, - ICacheFactory $cacheFactory, - IconsCacher $iconsCacher, - ITimeFactory $timeFactory, - AppConfig $appConfig) { - $this->logger = $logger; - $this->appData = $appDataFactory->get('css'); - $this->urlGenerator = $urlGenerator; - $this->config = $config; - $this->defaults = $defaults; - $this->serverRoot = $serverRoot; - $this->cacheFactory = $cacheFactory; - $this->depsCache = $cacheFactory->createDistributed('SCSS-deps-' . md5($this->urlGenerator->getBaseUrl())); - $this->isCachedCache = $cacheFactory->createDistributed('SCSS-cached-' . md5($this->urlGenerator->getBaseUrl())); - $lockingCache = $cacheFactory->createDistributed('SCSS-locks-' . md5($this->urlGenerator->getBaseUrl())); - if (!($lockingCache instanceof IMemcache)) { - $lockingCache = new NullCache(); - } - $this->lockingCache = $lockingCache; - $this->iconsCacher = $iconsCacher; - $this->timeFactory = $timeFactory; - $this->appConfig = $appConfig; - } - - /** - * Process the caching process if needed - * - * @param string $root Root path to the nextcloud installation - * @param string $file - * @param string $app The app name - * @return boolean - * @throws NotPermittedException - */ - public function process(string $root, string $file, string $app): bool { - $path = explode('/', $root . '/' . $file); - - $fileNameSCSS = array_pop($path); - $fileNameCSS = $this->prependVersionPrefix($this->prependBaseurlPrefix(str_replace('.scss', '.css', $fileNameSCSS)), $app); - - $path = implode('/', $path); - $webDir = $this->getWebDir($path, $app, $this->serverRoot, \OC::$WEBROOT); - - $this->logger->debug('SCSSCacher::process ordinary check follows', ['app' => 'scss_cacher']); - if (!$this->variablesChanged() && $this->isCached($fileNameCSS, $app)) { - // Inject icons vars css if any - return $this->injectCssVariablesIfAny(); - } - - try { - $folder = $this->appData->getFolder($app); - } catch (NotFoundException $e) { - // creating css appdata folder - $folder = $this->appData->newFolder($app); - } - - $lockKey = $webDir . '/' . $fileNameSCSS; - - if (!$this->lockingCache->add($lockKey, 'locked!', 120)) { - $this->logger->debug('SCSSCacher::process could not get lock for ' . $lockKey . ' and will wait 10 seconds for cached file to be available', ['app' => 'scss_cacher']); - $retry = 0; - sleep(1); - while ($retry < 10) { - $this->appConfig->clearCachedConfig(); - $this->logger->debug('SCSSCacher::process check in while loop follows', ['app' => 'scss_cacher']); - if (!$this->variablesChanged() && $this->isCached($fileNameCSS, $app)) { - // Inject icons vars css if any - $this->logger->debug("SCSSCacher::process cached file for app '$app' and file '$fileNameCSS' is now available after $retry s. Moving on...", ['app' => 'scss_cacher']); - return $this->injectCssVariablesIfAny(); - } - sleep(1); - $retry++; - } - $this->logger->debug('SCSSCacher::process Giving up scss caching for ' . $lockKey, ['app' => 'scss_cacher']); - return false; - } - - $this->logger->debug('SCSSCacher::process Lock acquired for ' . $lockKey, ['app' => 'scss_cacher']); - try { - $cached = $this->cache($path, $fileNameCSS, $fileNameSCSS, $folder, $webDir); - } catch (\Exception $e) { - $this->lockingCache->remove($lockKey); - throw $e; - } - - // Cleaning lock - $this->lockingCache->remove($lockKey); - $this->logger->debug('SCSSCacher::process Lock removed for ' . $lockKey, ['app' => 'scss_cacher']); - - // Inject icons vars css if any - if ($this->iconsCacher->getCachedCSS() && $this->iconsCacher->getCachedCSS()->getSize() > 0) { - $this->iconsCacher->injectCss(); - } - - return $cached; - } - - /** - * @param $appName - * @param $fileName - * @return ISimpleFile - */ - public function getCachedCSS(string $appName, string $fileName): ISimpleFile { - $folder = $this->appData->getFolder($appName); - $cachedFileName = $this->prependVersionPrefix($this->prependBaseurlPrefix($fileName), $appName); - - return $folder->getFile($cachedFileName); - } - - /** - * Check if the file is cached or not - * @param string $fileNameCSS - * @param string $app - * @return boolean - */ - private function isCached(string $fileNameCSS, string $app) { - $key = $this->config->getSystemValue('version') . '/' . $app . '/' . $fileNameCSS; - - // If the file mtime is more recent than our cached one, - // let's consider the file is properly cached - if ($cacheValue = $this->isCachedCache->get($key)) { - if ($cacheValue > $this->timeFactory->getTime()) { - return true; - } - } - $this->logger->debug("SCSSCacher::isCached $fileNameCSS isCachedCache is expired or unset", ['app' => 'scss_cacher']); - - // Creating file cache if none for further checks - try { - $folder = $this->appData->getFolder($app); - } catch (NotFoundException $e) { - $this->logger->debug("SCSSCacher::isCached app data folder for $app could not be fetched", ['app' => 'scss_cacher']); - return false; - } - - // Checking if file size is coherent - // and if one of the css dependency changed - try { - $cachedFile = $folder->getFile($fileNameCSS); - if ($cachedFile->getSize() > 0) { - $depFileName = $fileNameCSS . '.deps'; - $deps = $this->depsCache->get($folder->getName() . '-' . $depFileName); - if ($deps === null) { - $depFile = $folder->getFile($depFileName); - $deps = $depFile->getContent(); - // Set to memcache for next run - $this->depsCache->set($folder->getName() . '-' . $depFileName, $deps); - } - $deps = json_decode($deps, true); - - foreach ((array) $deps as $file => $mtime) { - if (!file_exists($file) || filemtime($file) > $mtime) { - $this->logger->debug("SCSSCacher::isCached $fileNameCSS is not considered as cached due to deps file $file", ['app' => 'scss_cacher']); - return false; - } - } - - $this->logger->debug("SCSSCacher::isCached $fileNameCSS dependencies successfully cached for 5 minutes", ['app' => 'scss_cacher']); - // It would probably make sense to adjust this timeout to something higher and see if that has some effect then - $this->isCachedCache->set($key, $this->timeFactory->getTime() + 5 * 60); - return true; - } - $this->logger->debug("SCSSCacher::isCached $fileNameCSS is not considered as cached cacheValue: $cacheValue", ['app' => 'scss_cacher']); - return false; - } catch (NotFoundException $e) { - $this->logger->debug("SCSSCacher::isCached NotFoundException " . $e->getMessage(), ['app' => 'scss_cacher']); - return false; - } - } - - /** - * Check if the variables file has changed - * @return bool - */ - private function variablesChanged(): bool { - $cachedVariables = $this->config->getAppValue('core', 'theming.variables', ''); - $injectedVariables = $this->getInjectedVariables($cachedVariables); - if ($cachedVariables !== md5($injectedVariables)) { - $this->logger->debug('SCSSCacher::variablesChanged storedVariables: ' . json_encode($this->config->getAppValue('core', 'theming.variables')) . ' currentInjectedVariables: ' . json_encode($injectedVariables), ['app' => 'scss_cacher']); - $this->config->setAppValue('core', 'theming.variables', md5($injectedVariables)); - $this->resetCache(); - return true; - } - return false; - } - - /** - * Cache the file with AppData - * - * @param string $path - * @param string $fileNameCSS - * @param string $fileNameSCSS - * @param ISimpleFolder $folder - * @param string $webDir - * @return boolean - * @throws NotPermittedException - */ - private function cache(string $path, string $fileNameCSS, string $fileNameSCSS, ISimpleFolder $folder, string $webDir) { - $scss = new Compiler(); - $scss->setImportPaths([ - $path, - $this->serverRoot . '/core/css/' - ]); - - // Continue after throw - if ($this->config->getSystemValue('debug')) { - // Debug mode - $scss->setOutputStyle(OutputStyle::EXPANDED); - } else { - // Compression - $scss->setOutputStyle(OutputStyle::COMPRESSED); - } - - try { - $cachedfile = $folder->getFile($fileNameCSS); - } catch (NotFoundException $e) { - $cachedfile = $folder->newFile($fileNameCSS); - } - - $depFileName = $fileNameCSS . '.deps'; - try { - $depFile = $folder->getFile($depFileName); - } catch (NotFoundException $e) { - $depFile = $folder->newFile($depFileName); - } - - // Compile - try { - $compiledScss = $scss->compile( - '$webroot: \'' . $this->getRoutePrefix() . '\';' . - $this->getInjectedVariables() . - '@import "variables.scss";' . - '@import "functions.scss";' . - '@import "' . $fileNameSCSS . '";'); - } catch (\Exception $e) { - $this->logger->error($e->getMessage(), ['app' => 'scss_cacher', 'exception' => $e]); - - return false; - } - - // Parse Icons and create related css variables - $compiledScss = $this->iconsCacher->setIconsCss($compiledScss); - - // Gzip file - try { - $gzipFile = $folder->getFile($fileNameCSS . '.gzip'); # Safari doesn't like .gz - } catch (NotFoundException $e) { - $gzipFile = $folder->newFile($fileNameCSS . '.gzip'); # Safari doesn't like .gz - } - - try { - $data = $this->rebaseUrls($compiledScss, $webDir); - $cachedfile->putContent($data); - $deps = json_encode($scss->getParsedFiles()); - $depFile->putContent($deps); - $this->depsCache->set($folder->getName() . '-' . $depFileName, $deps); - $gzipFile->putContent(gzencode($data, 9)); - $this->logger->debug('SCSSCacher::cache ' . $webDir . '/' . $fileNameSCSS . ' compiled and successfully cached', ['app' => 'scss_cacher']); - - return true; - } catch (NotPermittedException $e) { - $this->logger->error('SCSSCacher::cache unable to cache: ' . $fileNameSCSS, ['app' => 'scss_cacher']); - - return false; - } - } - - /** - * Reset scss cache by deleting all generated css files - * We need to regenerate all files when variables change - */ - public function resetCache() { - $this->logger->debug('SCSSCacher::resetCache', ['app' => 'scss_cacher']); - if (!$this->lockingCache->add('resetCache', 'locked!', 120)) { - $this->logger->debug('SCSSCacher::resetCache Locked', ['app' => 'scss_cacher']); - return; - } - $this->logger->debug('SCSSCacher::resetCache Lock acquired', ['app' => 'scss_cacher']); - $this->injectedVariables = null; - - // do not clear locks - $this->depsCache->clear(); - $this->isCachedCache->clear(); - - $appDirectory = $this->appData->getDirectoryListing(); - foreach ($appDirectory as $folder) { - foreach ($folder->getDirectoryListing() as $file) { - try { - $file->delete(); - } catch (NotPermittedException $e) { - $this->logger->error('SCSSCacher::resetCache unable to delete file: ' . $file->getName(), ['exception' => $e, 'app' => 'scss_cacher']); - } - } - } - $this->logger->debug('SCSSCacher::resetCache css cache cleared!', ['app' => 'scss_cacher']); - $this->lockingCache->remove('resetCache'); - $this->logger->debug('SCSSCacher::resetCache Locking removed', ['app' => 'scss_cacher']); - } - - /** - * @return string SCSS code for variables from OC_Defaults - */ - private function getInjectedVariables(string $cache = ''): string { - if ($this->injectedVariables !== null) { - return $this->injectedVariables; - } - $variables = ''; - foreach ($this->defaults->getScssVariables() as $key => $value) { - $variables .= '$' . $key . ': ' . $value . ' !default;'; - } - - /* - * If we are trying to return the same variables as that are cached - * Then there is no need to do the compile step - */ - if ($cache === md5($variables)) { - $this->injectedVariables = $variables; - return $variables; - } - - // check for valid variables / otherwise fall back to defaults - try { - $scss = new Compiler(); - $scss->compile($variables); - $this->injectedVariables = $variables; - } catch (\Exception $e) { - $this->logger->error($e->getMessage(), ['exception' => $e, 'app' => 'scss_cacher']); - } - - return $variables; - } - - /** - * Add the correct uri prefix to make uri valid again - * @param string $css - * @param string $webDir - * @return string - */ - private function rebaseUrls(string $css, string $webDir): string { - $re = '/url\([\'"]([^\/][\.\w?=\/-]*)[\'"]\)/x'; - $subst = 'url(\'' . $webDir . '/$1\')'; - - return preg_replace($re, $subst, $css); - } - - /** - * Return the cached css file uri - * @param string $appName the app name - * @param string $fileName - * @return string - */ - public function getCachedSCSS(string $appName, string $fileName): string { - $tmpfileLoc = explode('/', $fileName); - $fileName = array_pop($tmpfileLoc); - $fileName = $this->prependVersionPrefix($this->prependBaseurlPrefix(str_replace('.scss', '.css', $fileName)), $appName); - - return substr($this->urlGenerator->linkToRoute('core.Css.getCss', [ - 'fileName' => $fileName, - 'appName' => $appName, - 'v' => $this->config->getAppValue('core', 'theming.variables', '0') - ]), \strlen(\OC::$WEBROOT) + 1); - } - - /** - * Prepend hashed base url to the css file - * @param string $cssFile - * @return string - */ - private function prependBaseurlPrefix(string $cssFile): string { - return substr(md5($this->urlGenerator->getBaseUrl() . $this->getRoutePrefix()), 0, 4) . '-' . $cssFile; - } - - private function getRoutePrefix() { - $frontControllerActive = ($this->config->getSystemValue('htaccess.IgnoreFrontController', false) === true || getenv('front_controller_active') === 'true'); - $prefix = \OC::$WEBROOT . '/index.php'; - if ($frontControllerActive) { - $prefix = \OC::$WEBROOT; - } - return $prefix; - } - - /** - * Prepend hashed app version hash - * @param string $cssFile - * @param string $appId - * @return string - */ - private function prependVersionPrefix(string $cssFile, string $appId): string { - $appVersion = \OC_App::getAppVersion($appId); - if ($appVersion !== '0') { - return substr(md5($appVersion), 0, 4) . '-' . $cssFile; - } - $coreVersion = \OC_Util::getVersionString(); - - return substr(md5($coreVersion), 0, 4) . '-' . $cssFile; - } - - /** - * Get WebDir root - * @param string $path the css file path - * @param string $appName the app name - * @param string $serverRoot the server root path - * @param string $webRoot the nextcloud installation root path - * @return string the webDir - */ - private function getWebDir(string $path, string $appName, string $serverRoot, string $webRoot): string { - // Detect if path is within server root AND if path is within an app path - if (strpos($path, $serverRoot) === false && $appWebPath = \OC_App::getAppWebPath($appName)) { - // Get the file path within the app directory - $appDirectoryPath = explode($appName, $path)[1]; - // Remove the webroot - - return str_replace($webRoot, '', $appWebPath . $appDirectoryPath); - } - - return $webRoot . substr($path, strlen($serverRoot)); - } - - /** - * Add the icons css cache in the header if needed - * - * @return boolean true - */ - private function injectCssVariablesIfAny() { - // Inject icons vars css if any - if ($this->iconsCacher->getCachedCSS() && $this->iconsCacher->getCachedCSS()->getSize() > 0) { - $this->iconsCacher->injectCss(); - } - return true; - } -} diff --git a/lib/private/TemplateLayout.php b/lib/private/TemplateLayout.php index a25e23e9fc6..a5aabc04b61 100644 --- a/lib/private/TemplateLayout.php +++ b/lib/private/TemplateLayout.php @@ -46,7 +46,6 @@ use bantu\IniGetWrapper\IniGetWrapper; use OC\Search\SearchQuery; use OC\Template\JSCombiner; use OC\Template\JSConfigHelper; -use OC\Template\SCSSCacher; use OCP\AppFramework\Http\TemplateResponse; use OCP\Defaults; use OCP\IConfig; @@ -332,18 +331,11 @@ class TemplateLayout extends \OC_Template { // Read the selected theme from the config file $theme = \OC_Util::getTheme(); - if ($compileScss) { - $SCSSCacher = \OC::$server->query(SCSSCacher::class); - } else { - $SCSSCacher = null; - } - $locator = new \OC\Template\CSSResourceLocator( \OC::$server->get(LoggerInterface::class), $theme, [ \OC::$SERVERROOT => \OC::$WEBROOT ], [ \OC::$SERVERROOT => \OC::$WEBROOT ], - $SCSSCacher ); $locator->find($styles); return $locator->getResources(); diff --git a/lib/private/User/User.php b/lib/private/User/User.php index 0a51622428b..e7aa72fafba 100644 --- a/lib/private/User/User.php +++ b/lib/private/User/User.php @@ -320,13 +320,17 @@ class User implements IUser { } if ($this->backend->implementsActions(Backend::SET_PASSWORD)) { $result = $this->backend->setPassword($this->uid, $password); - $this->legacyDispatcher->dispatch(IUser::class . '::postSetPassword', new GenericEvent($this, [ - 'password' => $password, - 'recoveryPassword' => $recoveryPassword, - ])); - if ($this->emitter) { - $this->emitter->emit('\OC\User', 'postSetPassword', [$this, $password, $recoveryPassword]); + + if ($result !== false) { + $this->legacyDispatcher->dispatch(IUser::class . '::postSetPassword', new GenericEvent($this, [ + 'password' => $password, + 'recoveryPassword' => $recoveryPassword, + ])); + if ($this->emitter) { + $this->emitter->emit('\OC\User', 'postSetPassword', [$this, $password, $recoveryPassword]); + } } + return !($result === false); } else { return false; diff --git a/lib/private/legacy/OC_Files.php b/lib/private/legacy/OC_Files.php index 41ac20577b2..02e15fd08d5 100644 --- a/lib/private/legacy/OC_Files.php +++ b/lib/private/legacy/OC_Files.php @@ -145,21 +145,26 @@ class OC_Files { } self::lockFiles($view, $dir, $files); + $numberOfFiles = 0; + $fileSize = 0; /* Calculate filesize and number of files */ if ($getType === self::ZIP_FILES) { $fileInfos = []; - $fileSize = 0; foreach ($files as $file) { $fileInfo = \OC\Files\Filesystem::getFileInfo($dir . '/' . $file); - $fileSize += $fileInfo->getSize(); - $fileInfos[] = $fileInfo; + if ($fileInfo) { + $fileSize += $fileInfo->getSize(); + $fileInfos[] = $fileInfo; + } } $numberOfFiles = self::getNumberOfFiles($fileInfos); } elseif ($getType === self::ZIP_DIR) { $fileInfo = \OC\Files\Filesystem::getFileInfo($dir . '/' . $files); - $fileSize = $fileInfo->getSize(); - $numberOfFiles = self::getNumberOfFiles([$fileInfo]); + if ($fileInfo) { + $fileSize = $fileInfo->getSize(); + $numberOfFiles = self::getNumberOfFiles([$fileInfo]); + } } $streamer = new Streamer(\OC::$server->getRequest(), $fileSize, $numberOfFiles); diff --git a/lib/private/legacy/OC_Helper.php b/lib/private/legacy/OC_Helper.php index 0d1903007c2..226f73a0711 100644 --- a/lib/private/legacy/OC_Helper.php +++ b/lib/private/legacy/OC_Helper.php @@ -95,7 +95,7 @@ class OC_Helper { /** * Make a computer file size * @param string $str file size in human readable format - * @return float|bool a file size in bytes + * @return float|false a file size in bytes * * Makes 2kB to 2048. * @@ -420,11 +420,11 @@ class OC_Helper { */ public static function uploadLimit() { $ini = \OC::$server->get(IniGetWrapper::class); - $upload_max_filesize = OCP\Util::computerFileSize($ini->get('upload_max_filesize')); - $post_max_size = OCP\Util::computerFileSize($ini->get('post_max_size')); - if ((int)$upload_max_filesize === 0 and (int)$post_max_size === 0) { + $upload_max_filesize = (int)OCP\Util::computerFileSize($ini->get('upload_max_filesize')); + $post_max_size = (int)OCP\Util::computerFileSize($ini->get('post_max_size')); + if ($upload_max_filesize === 0 && $post_max_size === 0) { return INF; - } elseif ((int)$upload_max_filesize === 0 or (int)$post_max_size === 0) { + } elseif ($upload_max_filesize === 0 || $post_max_size === 0) { return max($upload_max_filesize, $post_max_size); //only the non 0 value counts } else { return min($upload_max_filesize, $post_max_size); @@ -519,9 +519,6 @@ class OC_Helper { $sourceStorage = $storage; if ($storage->instanceOfStorage('\OCA\Files_Sharing\SharedStorage')) { $includeExtStorage = false; - $internalPath = $storage->getUnjailedPath($rootInfo->getInternalPath()); - } else { - $internalPath = $rootInfo->getInternalPath(); } if ($includeExtStorage) { if ($storage->instanceOfStorage('\OC\Files\Storage\Home') @@ -545,7 +542,7 @@ class OC_Helper { $quota = $sourceStorage->getQuota(); } try { - $free = $sourceStorage->free_space($internalPath); + $free = $sourceStorage->free_space($rootInfo->getInternalPath()); } catch (\Exception $e) { if ($path === "") { throw $e; diff --git a/lib/private/legacy/OC_Template.php b/lib/private/legacy/OC_Template.php index b7a400d3269..813cedd0740 100644 --- a/lib/private/legacy/OC_Template.php +++ b/lib/private/legacy/OC_Template.php @@ -105,7 +105,6 @@ class OC_Template extends \OC\Template\Base { // apps that started before the template initialization can load their own scripts/styles // so to make sure this scripts/styles here are loaded first we put all core scripts first // check lib/public/Util.php - // OC_Util::addStyle('css-variables', null, true); OC_Util::addStyle('server', null, true); // include common nextcloud webpack bundle diff --git a/lib/private/legacy/OC_Util.php b/lib/private/legacy/OC_Util.php index ee7fb517d98..516ccc8283c 100644 --- a/lib/private/legacy/OC_Util.php +++ b/lib/private/legacy/OC_Util.php @@ -158,7 +158,7 @@ class OC_Util { * Get the quota of a user * * @param IUser|null $user - * @return float Quota bytes + * @return float|\OCP\Files\FileInfo::SPACE_UNLIMITED|false Quota bytes */ public static function getUserQuota(?IUser $user) { if (is_null($user)) { @@ -657,20 +657,8 @@ class OC_Util { } } foreach ($dependencies['ini'] as $setting => $expected) { - if (is_bool($expected)) { - if ($iniWrapper->getBool($setting) !== $expected) { - $invalidIniSettings[] = [$setting, $expected]; - } - } - if (is_int($expected)) { - if ($iniWrapper->getNumeric($setting) !== $expected) { - $invalidIniSettings[] = [$setting, $expected]; - } - } - if (is_string($expected)) { - if (strtolower($iniWrapper->getString($setting)) !== strtolower($expected)) { - $invalidIniSettings[] = [$setting, $expected]; - } + if (strtolower($iniWrapper->getString($setting)) !== strtolower($expected)) { + $invalidIniSettings[] = [$setting, $expected]; } } @@ -682,9 +670,6 @@ class OC_Util { $webServerRestart = true; } foreach ($invalidIniSettings as $setting) { - if (is_bool($setting[1])) { - $setting[1] = $setting[1] ? 'on' : 'off'; - } $errors[] = [ 'error' => $l->t('PHP setting "%s" is not set to "%s".', [$setting[0], var_export($setting[1], true)]), 'hint' => $l->t('Adjusting this setting in php.ini will make Nextcloud run again') |