diff options
author | John Molakvoæ <skjnldsv@users.noreply.github.com> | 2024-05-30 14:17:52 +0200 |
---|---|---|
committer | GitHub <noreply@github.com> | 2024-05-30 14:17:52 +0200 |
commit | 7f2a1e5b7a1724727bc274e73a9a6fd09249c7a6 (patch) | |
tree | fbc014b411855706acf31e082373e45b96640287 /lib/private | |
parent | 7292a8d8fe7e4cf8667194f4af587f6923a0f884 (diff) | |
parent | 31b0a44cf65b6625636ea0fa15fb1a1122b525e1 (diff) | |
download | nextcloud-server-7f2a1e5b7a1724727bc274e73a9a6fd09249c7a6.tar.gz nextcloud-server-7f2a1e5b7a1724727bc274e73a9a6fd09249c7a6.zip |
Merge branch 'master' into refactor/OC-Server-getMailer
Signed-off-by: John Molakvoæ <skjnldsv@users.noreply.github.com>
Diffstat (limited to 'lib/private')
846 files changed, 18271 insertions, 26135 deletions
diff --git a/lib/private/Accounts/Account.php b/lib/private/Accounts/Account.php index 22bbe3d11a0..946168d7755 100644 --- a/lib/private/Accounts/Account.php +++ b/lib/private/Accounts/Account.php @@ -3,27 +3,8 @@ declare(strict_types=1); /** - * @copyright Copyright (c) 2018 Julius Härtl <jus@bitgrid.net> - * - * @author Arthur Schiwon <blizzz@arthur-schiwon.de> - * @author Christoph Wurst <christoph@winzerhof-wurst.at> - * @author Julius Härtl <jus@bitgrid.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/>. - * + * SPDX-FileCopyrightText: 2018 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later */ namespace OC\Accounts; @@ -104,7 +85,7 @@ class Account implements IAccount { } } - public function getFilteredProperties(string $scope = null, string $verified = null): array { + public function getFilteredProperties(?string $scope = null, ?string $verified = null): array { $result = $incrementals = []; /** @var IAccountProperty $obj */ foreach ($this->getAllProperties() as $obj) { diff --git a/lib/private/Accounts/AccountManager.php b/lib/private/Accounts/AccountManager.php index 9865438161b..cc81a852cdf 100644 --- a/lib/private/Accounts/AccountManager.php +++ b/lib/private/Accounts/AccountManager.php @@ -1,61 +1,32 @@ <?php /** - * @copyright Copyright (c) 2016, ownCloud, Inc. - * @copyright Copyright (c) 2016, Björn Schießle - * - * @author Bjoern Schiessle <bjoern@schiessle.org> - * @author Björn Schießle <bjoern@schiessle.org> - * @author Christoph Wurst <christoph@winzerhof-wurst.at> - * @author Daniel Calviño Sánchez <danxuliu@gmail.com> - * @author Daniel Kesselberg <mail@danielkesselberg.de> - * @author Joas Schilling <coding@schilljs.com> - * @author Julius Härtl <jus@bitgrid.net> - * @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 - * - * This code is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License, version 3, - * as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License, version 3, - * along with this program. If not, see <http://www.gnu.org/licenses/> - * + * SPDX-FileCopyrightText: 2016 Nextcloud GmbH and Nextcloud contributors + * SPDX-FileCopyrightText: 2016 ownCloud, Inc. + * SPDX-License-Identifier: AGPL-3.0-only */ namespace OC\Accounts; use Exception; use InvalidArgumentException; -use libphonenumber\NumberParseException; -use libphonenumber\PhoneNumberFormat; -use libphonenumber\PhoneNumberUtil; use OC\Profile\TProfileHelper; -use OCP\Accounts\UserUpdatedEvent; -use OCP\Cache\CappedMemoryCache; use OCA\Settings\BackgroundJobs\VerifyUserData; use OCP\Accounts\IAccount; use OCP\Accounts\IAccountManager; use OCP\Accounts\IAccountProperty; use OCP\Accounts\IAccountPropertyCollection; use OCP\Accounts\PropertyDoesNotExistException; +use OCP\Accounts\UserUpdatedEvent; use OCP\BackgroundJob\IJobList; +use OCP\Cache\CappedMemoryCache; use OCP\DB\QueryBuilder\IQueryBuilder; use OCP\Defaults; use OCP\EventDispatcher\IEventDispatcher; use OCP\IConfig; use OCP\IDBConnection; use OCP\IL10N; +use OCP\IPhoneNumberUtil; use OCP\IURLGenerator; use OCP\IUser; use OCP\L10N\IFactory; @@ -105,6 +76,7 @@ class AccountManager implements IAccountManager { self::PROPERTY_ROLE => self::SCOPE_LOCAL, self::PROPERTY_HEADLINE => self::SCOPE_LOCAL, self::PROPERTY_BIOGRAPHY => self::SCOPE_LOCAL, + self::PROPERTY_BIRTHDATE => self::SCOPE_LOCAL, ]; public function __construct( @@ -119,6 +91,7 @@ class AccountManager implements IAccountManager { private IFactory $l10nFactory, private IURLGenerator $urlGenerator, private ICrypto $crypto, + private IPhoneNumberUtil $phoneNumberUtil, ) { $this->internalCache = new CappedMemoryCache(); } @@ -139,13 +112,9 @@ class AccountManager implements IAccountManager { $defaultRegion = 'EN'; } - $phoneUtil = PhoneNumberUtil::getInstance(); - try { - $phoneNumber = $phoneUtil->parse($input, $defaultRegion); - if ($phoneUtil->isValidNumber($phoneNumber)) { - return $phoneUtil->format($phoneNumber, PhoneNumberFormat::E164); - } - } catch (NumberParseException $e) { + $phoneNumber = $this->phoneNumberUtil->convertToStandardFormat($input, $defaultRegion); + if ($phoneNumber !== null) { + return $phoneNumber; } throw new InvalidArgumentException(self::PROPERTY_PHONE); @@ -701,6 +670,12 @@ class AccountManager implements IAccountManager { ], [ + 'name' => self::PROPERTY_BIRTHDATE, + 'value' => '', + 'scope' => $scopes[self::PROPERTY_BIRTHDATE], + ], + + [ 'name' => self::PROPERTY_PROFILE_ENABLED, 'value' => $this->isProfileEnabledByDefault($this->config) ? '1' : '0', ], diff --git a/lib/private/Accounts/AccountProperty.php b/lib/private/Accounts/AccountProperty.php index 207dc1d139d..0c4ad568709 100644 --- a/lib/private/Accounts/AccountProperty.php +++ b/lib/private/Accounts/AccountProperty.php @@ -3,27 +3,8 @@ declare(strict_types=1); /** - * @copyright Copyright (c) 2018 Julius Härtl <jus@bitgrid.net> - * - * @author Arthur Schiwon <blizzz@arthur-schiwon.de> - * @author Julius Härtl <jus@bitgrid.net> - * @author Vincent Petry <vincent@nextcloud.com> - * - * @license GNU AGPL version 3 or any later version - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as - * published by the Free Software Foundation, either version 3 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - * + * SPDX-FileCopyrightText: 2018 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later */ namespace OC\Accounts; @@ -32,6 +13,9 @@ use OCP\Accounts\IAccountManager; use OCP\Accounts\IAccountProperty; class AccountProperty implements IAccountProperty { + /** + * @var IAccountManager::SCOPE_* + */ private string $scope; private string $locallyVerified = IAccountManager::NOT_VERIFIED; diff --git a/lib/private/Accounts/AccountPropertyCollection.php b/lib/private/Accounts/AccountPropertyCollection.php index 660a245714d..75eea76e686 100644 --- a/lib/private/Accounts/AccountPropertyCollection.php +++ b/lib/private/Accounts/AccountPropertyCollection.php @@ -1,29 +1,10 @@ <?php declare(strict_types=1); - /** - * @copyright Copyright (c) 2021 Arthur Schiwon <blizzz@arthur-schiwon.de> - * - * @author Arthur Schiwon <blizzz@arthur-schiwon.de> - * - * @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 <https://www.gnu.org/licenses/>. - * + * SPDX-FileCopyrightText: 2021 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later */ - namespace OC\Accounts; use InvalidArgumentException; diff --git a/lib/private/Accounts/Hooks.php b/lib/private/Accounts/Hooks.php index b449c41c1da..0235879e8e7 100644 --- a/lib/private/Accounts/Hooks.php +++ b/lib/private/Accounts/Hooks.php @@ -1,26 +1,7 @@ <?php /** - * @copyright Copyright (c) 2016 Bjoern Schiessle <bjoern@schiessle.org> - * - * @author Bjoern Schiessle <bjoern@schiessle.org> - * @author Christoph Wurst <christoph@winzerhof-wurst.at> - * @author Joas Schilling <coding@schilljs.com> - * - * @license GNU AGPL version 3 or any later version - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as - * published by the Free Software Foundation, either version 3 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - * + * SPDX-FileCopyrightText: 2016 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later */ namespace OC\Accounts; diff --git a/lib/private/Accounts/TAccountsHelper.php b/lib/private/Accounts/TAccountsHelper.php index f3be6523d29..69d25d9a933 100644 --- a/lib/private/Accounts/TAccountsHelper.php +++ b/lib/private/Accounts/TAccountsHelper.php @@ -1,29 +1,10 @@ <?php declare(strict_types=1); - /** - * @copyright Copyright (c) 2021 Arthur Schiwon <blizzz@arthur-schiwon.de> - * - * @author Arthur Schiwon <blizzz@arthur-schiwon.de> - * - * @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 <https://www.gnu.org/licenses/>. - * + * SPDX-FileCopyrightText: 2021 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later */ - namespace OC\Accounts; use OCP\Accounts\IAccountManager; diff --git a/lib/private/Activity/ActivitySettingsAdapter.php b/lib/private/Activity/ActivitySettingsAdapter.php index eba13ad0350..bc0e60c1acb 100644 --- a/lib/private/Activity/ActivitySettingsAdapter.php +++ b/lib/private/Activity/ActivitySettingsAdapter.php @@ -3,25 +3,8 @@ declare(strict_types=1); /** - * @copyright Copyright (c) 2020 Robin Appelman <robin@icewind.nl> - * - * @author Robin Appelman <robin@icewind.nl> - * - * @license GNU AGPL version 3 or any later version - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as - * published by the Free Software Foundation, either version 3 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - * + * SPDX-FileCopyrightText: 2020 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later */ namespace OC\Activity; diff --git a/lib/private/Activity/Event.php b/lib/private/Activity/Event.php index cc1135cf179..97ab7d1c935 100644 --- a/lib/private/Activity/Event.php +++ b/lib/private/Activity/Event.php @@ -3,31 +3,13 @@ declare(strict_types=1); /** - * @copyright Copyright (c) 2016, ownCloud, Inc. - * @copyright Copyright (c) 2016 Joas Schilling <coding@schilljs.com> - * - * @author Christoph Wurst <christoph@winzerhof-wurst.at> - * @author Joas Schilling <coding@schilljs.com> - * @author Phil Davis <phil.davis@inf.org> - * @author Robin Appelman <robin@icewind.nl> - * - * @license AGPL-3.0 - * - * This code is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License, version 3, - * as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License, version 3, - * along with this program. If not, see <http://www.gnu.org/licenses/> - * + * SPDX-FileCopyrightText: 2016 Nextcloud GmbH and Nextcloud contributors + * SPDX-FileCopyrightText: 2016 ownCloud, Inc. + * SPDX-License-Identifier: AGPL-3.0-only */ namespace OC\Activity; +use OCP\Activity\Exceptions\InvalidValueException; use OCP\Activity\IEvent; use OCP\RichObjectStrings\InvalidObjectExeption; use OCP\RichObjectStrings\IValidator; @@ -89,16 +71,11 @@ class Event implements IEvent { } /** - * Set the app of the activity - * - * @param string $app - * @return IEvent - * @throws \InvalidArgumentException if the app id is invalid - * @since 8.2.0 + * {@inheritDoc} */ public function setApp(string $app): IEvent { if ($app === '' || isset($app[32])) { - throw new \InvalidArgumentException('The given app is invalid'); + throw new InvalidValueException('app'); } $this->app = $app; return $this; @@ -112,16 +89,11 @@ class Event implements IEvent { } /** - * Set the type of the activity - * - * @param string $type - * @return IEvent - * @throws \InvalidArgumentException if the type is invalid - * @since 8.2.0 + * {@inheritDoc} */ public function setType(string $type): IEvent { if ($type === '' || isset($type[255])) { - throw new \InvalidArgumentException('The given type is invalid'); + throw new InvalidValueException('type'); } $this->type = $type; return $this; @@ -135,16 +107,11 @@ class Event implements IEvent { } /** - * Set the affected user of the activity - * - * @param string $affectedUser - * @return IEvent - * @throws \InvalidArgumentException if the affected user is invalid - * @since 8.2.0 + * {@inheritDoc} */ public function setAffectedUser(string $affectedUser): IEvent { if ($affectedUser === '' || isset($affectedUser[64])) { - throw new \InvalidArgumentException('The given affected user is invalid'); + throw new InvalidValueException('affectedUser'); } $this->affectedUser = $affectedUser; return $this; @@ -158,16 +125,11 @@ class Event implements IEvent { } /** - * Set the author of the activity - * - * @param string $author - * @return IEvent - * @throws \InvalidArgumentException if the author is invalid - * @since 8.2.0 + * {@inheritDoc} */ public function setAuthor(string $author): IEvent { if (isset($author[64])) { - throw new \InvalidArgumentException('The given author user is invalid'); + throw new InvalidValueException('author'); } $this->author = $author; return $this; @@ -181,14 +143,12 @@ class Event implements IEvent { } /** - * Set the timestamp of the activity - * - * @param int $timestamp - * @return IEvent - * @throws \InvalidArgumentException if the timestamp is invalid - * @since 8.2.0 + * {@inheritDoc} */ public function setTimestamp(int $timestamp): IEvent { + if ($timestamp < 0) { + throw new InvalidValueException('timestamp'); + } $this->timestamp = $timestamp; return $this; } @@ -201,17 +161,11 @@ class Event implements IEvent { } /** - * Set the subject of the activity - * - * @param string $subject - * @param array $parameters - * @return IEvent - * @throws \InvalidArgumentException if the subject or parameters are invalid - * @since 8.2.0 + * {@inheritDoc} */ public function setSubject(string $subject, array $parameters = []): IEvent { if (isset($subject[255])) { - throw new \InvalidArgumentException('The given subject is invalid'); + throw new InvalidValueException('subject'); } $this->subject = $subject; $this->subjectParameters = $parameters; @@ -233,14 +187,11 @@ class Event implements IEvent { } /** - * @param string $subject - * @return $this - * @throws \InvalidArgumentException if the subject is invalid - * @since 11.0.0 + * {@inheritDoc} */ public function setParsedSubject(string $subject): IEvent { if ($subject === '') { - throw new \InvalidArgumentException('The given parsed subject is invalid'); + throw new InvalidValueException('parsedSubject'); } $this->subjectParsed = $subject; return $this; @@ -255,21 +206,21 @@ class Event implements IEvent { } /** - * @param string $subject - * @param array $parameters - * @return $this - * @throws \InvalidArgumentException if the subject or parameters are invalid - * @since 11.0.0 + * {@inheritDoc} */ public function setRichSubject(string $subject, array $parameters = []): IEvent { if ($subject === '') { - throw new \InvalidArgumentException('The given parsed subject is invalid'); + throw new InvalidValueException('richSubject'); } $this->subjectRich = $subject; $this->subjectRichParameters = $parameters; if ($this->subjectParsed === '') { - $this->subjectParsed = $this->richToParsed($subject, $parameters); + try { + $this->subjectParsed = $this->richToParsed($subject, $parameters); + } catch (\InvalidArgumentException $e) { + throw new InvalidValueException('richSubjectParameters', $e); + } } return $this; @@ -316,17 +267,11 @@ class Event implements IEvent { } /** - * Set the message of the activity - * - * @param string $message - * @param array $parameters - * @return IEvent - * @throws \InvalidArgumentException if the message or parameters are invalid - * @since 8.2.0 + * {@inheritDoc} */ public function setMessage(string $message, array $parameters = []): IEvent { if (isset($message[255])) { - throw new \InvalidArgumentException('The given message is invalid'); + throw new InvalidValueException('message'); } $this->message = $message; $this->messageParameters = $parameters; @@ -348,10 +293,7 @@ class Event implements IEvent { } /** - * @param string $message - * @return $this - * @throws \InvalidArgumentException if the message is invalid - * @since 11.0.0 + * {@inheritDoc} */ public function setParsedMessage(string $message): IEvent { $this->messageParsed = $message; @@ -367,18 +309,18 @@ class Event implements IEvent { } /** - * @param string $message - * @param array $parameters - * @return $this - * @throws \InvalidArgumentException if the subject or parameters are invalid - * @since 11.0.0 + * {@inheritDoc} */ public function setRichMessage(string $message, array $parameters = []): IEvent { $this->messageRich = $message; $this->messageRichParameters = $parameters; if ($this->messageParsed === '') { - $this->messageParsed = $this->richToParsed($message, $parameters); + try { + $this->messageParsed = $this->richToParsed($message, $parameters); + } catch (\InvalidArgumentException $e) { + throw new InvalidValueException('richMessageParameters', $e); + } } return $this; @@ -401,21 +343,14 @@ class Event implements IEvent { } /** - * Set the object of the activity - * - * @param string $objectType - * @param int $objectId - * @param string $objectName - * @return IEvent - * @throws \InvalidArgumentException if the object is invalid - * @since 8.2.0 + * {@inheritDoc} */ public function setObject(string $objectType, int $objectId, string $objectName = ''): IEvent { if (isset($objectType[255])) { - throw new \InvalidArgumentException('The given object type is invalid'); + throw new InvalidValueException('objectType'); } if (isset($objectName[4000])) { - throw new \InvalidArgumentException('The given object name is invalid'); + throw new InvalidValueException('objectName'); } $this->objectType = $objectType; $this->objectId = $objectId; @@ -445,16 +380,11 @@ class Event implements IEvent { } /** - * Set the link of the activity - * - * @param string $link - * @return IEvent - * @throws \InvalidArgumentException if the link is invalid - * @since 8.2.0 + * {@inheritDoc} */ public function setLink(string $link): IEvent { if (isset($link[4000])) { - throw new \InvalidArgumentException('The given link is invalid'); + throw new InvalidValueException('link'); } $this->link = $link; return $this; @@ -468,14 +398,11 @@ class Event implements IEvent { } /** - * @param string $icon - * @return $this - * @throws \InvalidArgumentException if the icon is invalid - * @since 11.0.0 + * {@inheritDoc} */ public function setIcon(string $icon): IEvent { if (isset($icon[4000])) { - throw new \InvalidArgumentException('The given icon is invalid'); + throw new InvalidValueException('icon'); } $this->icon = $icon; return $this; diff --git a/lib/private/Activity/EventMerger.php b/lib/private/Activity/EventMerger.php index 9332d8b111a..504f9088f24 100644 --- a/lib/private/Activity/EventMerger.php +++ b/lib/private/Activity/EventMerger.php @@ -1,26 +1,7 @@ <?php /** - * @copyright Copyright (c) 2016 Joas Schilling <coding@schilljs.com> - * - * @author Christoph Wurst <christoph@winzerhof-wurst.at> - * @author Joas Schilling <coding@schilljs.com> - * @author Julius Härtl <jus@bitgrid.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/>. - * + * SPDX-FileCopyrightText: 2016 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later */ namespace OC\Activity; @@ -67,7 +48,7 @@ class EventMerger implements IEventMerger { * @param IEvent|null $previousEvent * @return IEvent */ - public function mergeEvents($mergeParameter, IEvent $event, IEvent $previousEvent = null) { + public function mergeEvents($mergeParameter, IEvent $event, ?IEvent $previousEvent = null) { // No second event => can not combine if (!$previousEvent instanceof IEvent) { return $event; diff --git a/lib/private/Activity/Manager.php b/lib/private/Activity/Manager.php index a7d24510d53..8b60dc49ec9 100644 --- a/lib/private/Activity/Manager.php +++ b/lib/private/Activity/Manager.php @@ -1,34 +1,15 @@ <?php /** - * @copyright Copyright (c) 2016, ownCloud, Inc. - * @copyright Copyright (c) 2016 Joas Schilling <coding@schilljs.com> - * - * @author Christoph Wurst <christoph@winzerhof-wurst.at> - * @author Daniel Kesselberg <mail@danielkesselberg.de> - * @author Joas Schilling <coding@schilljs.com> - * @author Morris Jobke <hey@morrisjobke.de> - * @author Robin Appelman <robin@icewind.nl> - * @author Roeland Jago Douma <roeland@famdouma.nl> - * @author Thomas Müller <thomas.mueller@tmit.eu> - * - * @license AGPL-3.0 - * - * This code is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License, version 3, - * as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License, version 3, - * along with this program. If not, see <http://www.gnu.org/licenses/> - * + * SPDX-FileCopyrightText: 2016 Nextcloud GmbH and Nextcloud contributors + * SPDX-FileCopyrightText: 2016 ownCloud, Inc. + * SPDX-License-Identifier: AGPL-3.0-only */ namespace OC\Activity; use OCP\Activity\ActivitySettings; +use OCP\Activity\Exceptions\FilterNotFoundException; +use OCP\Activity\Exceptions\IncompleteActivityException; +use OCP\Activity\Exceptions\SettingNotFoundException; use OCP\Activity\IConsumer; use OCP\Activity\IEvent; use OCP\Activity\IFilter; @@ -70,11 +51,11 @@ class Manager implements IManager { protected $l10n; public function __construct( - IRequest $request, - IUserSession $session, - IConfig $config, - IValidator $validator, - IL10N $l10n + IRequest $request, + IUserSession $session, + IConfig $config, + IValidator $validator, + IL10N $l10n ) { $this->request = $request; $this->session = $session; @@ -127,16 +108,7 @@ class Manager implements IManager { } /** - * Publish an event to the activity consumers - * - * Make sure to call at least the following methods before sending an Event: - * - setApp() - * - setType() - * - setAffectedUser() - * - setSubject() - * - * @param IEvent $event - * @throws \BadMethodCallException if required values have not been set + * {@inheritDoc} */ public function publish(IEvent $event): void { if ($event->getAuthor() === '') { @@ -150,7 +122,7 @@ class Manager implements IManager { } if (!$event->isValid()) { - throw new \BadMethodCallException('The given event is invalid'); + throw new IncompleteActivityException('The given event is invalid'); } foreach ($this->getConsumers() as $c) { @@ -206,10 +178,7 @@ class Manager implements IManager { } /** - * @param string $id - * @return IFilter - * @throws \InvalidArgumentException when the filter was not found - * @since 11.0.0 + * {@inheritDoc} */ public function getFilterById(string $id): IFilter { $filters = $this->getFilters(); @@ -218,7 +187,7 @@ class Manager implements IManager { return $filters[$id]; } - throw new \InvalidArgumentException('Requested filter does not exist'); + throw new FilterNotFoundException($id); } /** @var string[] */ @@ -294,10 +263,7 @@ class Manager implements IManager { } /** - * @param string $id - * @return ActivitySettings - * @throws \InvalidArgumentException when the setting was not found - * @since 11.0.0 + * {@inheritDoc} */ public function getSettingById(string $id): ActivitySettings { $settings = $this->getSettings(); @@ -306,7 +272,7 @@ class Manager implements IManager { return $settings[$id]; } - throw new \InvalidArgumentException('Requested setting does not exist'); + throw new SettingNotFoundException($id); } @@ -346,12 +312,8 @@ class Manager implements IManager { * Set the user we need to use * * @param string|null $currentUserId - * @throws \UnexpectedValueException If the user is invalid */ - public function setCurrentUserId(string $currentUserId = null): void { - if (!is_string($currentUserId) && $currentUserId !== null) { - throw new \UnexpectedValueException('The given current user is invalid'); - } + public function setCurrentUserId(?string $currentUserId = null): void { $this->currentUserId = $currentUserId; } diff --git a/lib/private/AllConfig.php b/lib/private/AllConfig.php index 2a0e8f53b14..d05fe440202 100644 --- a/lib/private/AllConfig.php +++ b/lib/private/AllConfig.php @@ -1,37 +1,12 @@ <?php /** - * @copyright Copyright (c) 2016, ownCloud, Inc. - * - * @author Bart Visscher <bartv@thisnet.nl> - * @author Christoph Wurst <christoph@winzerhof-wurst.at> - * @author Joas Schilling <coding@schilljs.com> - * @author Jörn Friedrich Dreyer <jfd@butonic.de> - * @author Loki3000 <github@labcms.ru> - * @author Lukas Reschke <lukas@statuscode.ch> - * @author MichaIng <micha@dietpi.com> - * @author Morris Jobke <hey@morrisjobke.de> - * @author Robin Appelman <robin@icewind.nl> - * @author Robin McCorkell <robin@mccorkell.me.uk> - * @author Roeland Jago Douma <roeland@famdouma.nl> - * @author Thomas Müller <thomas.mueller@tmit.eu> - * - * @license AGPL-3.0 - * - * This code is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License, version 3, - * as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License, version 3, - * along with this program. If not, see <http://www.gnu.org/licenses/> - * + * SPDX-FileCopyrightText: 2016-2024 Nextcloud GmbH and Nextcloud contributors + * SPDX-FileCopyrightText: 2016 ownCloud, Inc. + * SPDX-License-Identifier: AGPL-3.0-only */ namespace OC; +use Doctrine\DBAL\Platforms\OraclePlatform; use OCP\Cache\CappedMemoryCache; use OCP\DB\QueryBuilder\IQueryBuilder; use OCP\IConfig; @@ -42,7 +17,6 @@ use OCP\PreConditionNotMetException; * Class to combine all the configuration options ownCloud offers */ class AllConfig implements IConfig { - private SystemConfig $systemConfig; private ?IDBConnection $connection = null; /** @@ -67,9 +41,10 @@ class AllConfig implements IConfig { */ private CappedMemoryCache $userCache; - public function __construct(SystemConfig $systemConfig) { + public function __construct( + private SystemConfig $systemConfig + ) { $this->userCache = new CappedMemoryCache(); - $this->systemConfig = $systemConfig; } /** @@ -189,6 +164,7 @@ class AllConfig implements IConfig { * * @param string $appName the appName that we stored the value under * @return string[] the keys stored for the app + * @deprecated 29.0.0 Use {@see IAppConfig} directly */ public function getAppKeys($appName) { return \OC::$server->get(AppConfig::class)->getKeys($appName); @@ -200,6 +176,7 @@ class AllConfig implements IConfig { * @param string $appName the appName that we want to store the value under * @param string $key the key of the value, under which will be saved * @param string|float|int $value the value that should be stored + * @deprecated 29.0.0 Use {@see IAppConfig} directly */ public function setAppValue($appName, $key, $value) { \OC::$server->get(AppConfig::class)->setValue($appName, $key, $value); @@ -212,6 +189,7 @@ class AllConfig implements IConfig { * @param string $key the key of the value, under which it was saved * @param string $default the default value to be returned if the value isn't set * @return string the saved value + * @deprecated 29.0.0 Use {@see IAppConfig} directly */ public function getAppValue($appName, $key, $default = '') { return \OC::$server->get(AppConfig::class)->getValue($appName, $key, $default); @@ -222,6 +200,7 @@ class AllConfig implements IConfig { * * @param string $appName the appName that we stored the value under * @param string $key the key of the value, under which it was saved + * @deprecated 29.0.0 Use {@see IAppConfig} directly */ public function deleteAppValue($appName, $key) { \OC::$server->get(AppConfig::class)->deleteKey($appName, $key); @@ -231,6 +210,7 @@ class AllConfig implements IConfig { * Removes all keys in appconfig belonging to the app * * @param string $appName the appName the configs are stored under + * @deprecated 29.0.0 Use {@see IAppConfig} directly */ public function deleteAppValues($appName) { \OC::$server->get(AppConfig::class)->deleteApp($appName); @@ -333,7 +313,7 @@ class AllConfig implements IConfig { public function getUserKeys($userId, $appName) { $data = $this->getAllUserValues($userId); if (isset($data[$appName])) { - return array_keys($data[$appName]); + return array_map('strval', array_keys($data[$appName])); } else { return []; } @@ -490,12 +470,15 @@ class AllConfig implements IConfig { $this->fixDIInit(); $qb = $this->connection->getQueryBuilder(); + $configValueColumn = ($this->connection->getDatabasePlatform() instanceof OraclePlatform) + ? $qb->expr()->castColumn('configvalue', IQueryBuilder::PARAM_STR) + : 'configvalue'; $result = $qb->select('userid') ->from('preferences') ->where($qb->expr()->eq('appid', $qb->createNamedParameter($appName, IQueryBuilder::PARAM_STR))) ->andWhere($qb->expr()->eq('configkey', $qb->createNamedParameter($key, IQueryBuilder::PARAM_STR))) ->andWhere($qb->expr()->eq( - $qb->expr()->castColumn('configvalue', IQueryBuilder::PARAM_STR), + $configValueColumn, $qb->createNamedParameter($value, IQueryBuilder::PARAM_STR)) )->orderBy('userid') ->executeQuery(); @@ -524,13 +507,18 @@ class AllConfig implements IConfig { // Email address is always stored lowercase in the database return $this->getUsersForUserValue($appName, $key, strtolower($value)); } + $qb = $this->connection->getQueryBuilder(); + $configValueColumn = ($this->connection->getDatabasePlatform() instanceof OraclePlatform) + ? $qb->expr()->castColumn('configvalue', IQueryBuilder::PARAM_STR) + : 'configvalue'; + $result = $qb->select('userid') ->from('preferences') ->where($qb->expr()->eq('appid', $qb->createNamedParameter($appName, IQueryBuilder::PARAM_STR))) ->andWhere($qb->expr()->eq('configkey', $qb->createNamedParameter($key, IQueryBuilder::PARAM_STR))) ->andWhere($qb->expr()->eq( - $qb->func()->lower($qb->expr()->castColumn('configvalue', IQueryBuilder::PARAM_STR)), + $qb->func()->lower($configValueColumn), $qb->createNamedParameter(strtolower($value), IQueryBuilder::PARAM_STR)) )->orderBy('userid') ->executeQuery(); diff --git a/lib/private/App/AppManager.php b/lib/private/App/AppManager.php index 88044fbf7b6..99d1c913ced 100644 --- a/lib/private/App/AppManager.php +++ b/lib/private/App/AppManager.php @@ -1,43 +1,12 @@ <?php /** - * @copyright Copyright (c) 2016, ownCloud, Inc. - * - * @author Arthur Schiwon <blizzz@arthur-schiwon.de> - * @author Bjoern Schiessle <bjoern@schiessle.org> - * @author Christoph Schaefer "christophł@wolkesicher.de" - * @author Christoph Wurst <christoph@winzerhof-wurst.at> - * @author Daniel Kesselberg <mail@danielkesselberg.de> - * @author Daniel Rudolf <github.com@daniel-rudolf.de> - * @author Greta Doci <gretadoci@gmail.com> - * @author Joas Schilling <coding@schilljs.com> - * @author Julius Haertl <jus@bitgrid.net> - * @author Julius Härtl <jus@bitgrid.net> - * @author Lukas Reschke <lukas@statuscode.ch> - * @author Maxence Lange <maxence@artificial-owl.com> - * @author Morris Jobke <hey@morrisjobke.de> - * @author Robin Appelman <robin@icewind.nl> - * @author Roeland Jago Douma <roeland@famdouma.nl> - * @author Thomas Müller <thomas.mueller@tmit.eu> - * @author Tobia De Koninck <tobia@ledfan.be> - * @author Vincent Petry <vincent@nextcloud.com> - * - * @license AGPL-3.0 - * - * This code is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License, version 3, - * as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License, version 3, - * along with this program. If not, see <http://www.gnu.org/licenses/> - * + * SPDX-FileCopyrightText: 2016-2024 Nextcloud GmbH and Nextcloud contributors + * SPDX-FileCopyrightText: 2016 ownCloud, Inc. + * SPDX-License-Identifier: AGPL-3.0-only */ namespace OC\App; +use InvalidArgumentException; use OC\AppConfig; use OC\AppFramework\Bootstrap\Coordinator; use OC\ServerNotAvailableException; @@ -47,14 +16,15 @@ use OCP\App\Events\AppDisableEvent; use OCP\App\Events\AppEnableEvent; use OCP\App\IAppManager; use OCP\App\ManagerEvent; -use OCP\EventDispatcher\IEventDispatcher; use OCP\Collaboration\AutoComplete\IManager as IAutoCompleteManager; use OCP\Collaboration\Collaborators\ISearch as ICollaboratorSearch; use OCP\Diagnostics\IEventLogger; +use OCP\EventDispatcher\IEventDispatcher; use OCP\ICacheFactory; use OCP\IConfig; use OCP\IGroup; use OCP\IGroupManager; +use OCP\IURLGenerator; use OCP\IUser; use OCP\IUserSession; use OCP\Settings\IManager as ISettingsManager; @@ -73,14 +43,6 @@ class AppManager implements IAppManager { 'prevent_group_restriction', ]; - private IUserSession $userSession; - private IConfig $config; - private AppConfig $appConfig; - private IGroupManager $groupManager; - private ICacheFactory $memCacheFactory; - private IEventDispatcher $dispatcher; - private LoggerInterface $logger; - /** @var string[] $appId => $enabled */ private array $installedAppsCache = []; @@ -103,20 +65,57 @@ class AppManager implements IAppManager { /** @var array<string, true> */ private array $loadedApps = []; - public function __construct(IUserSession $userSession, - IConfig $config, - AppConfig $appConfig, - IGroupManager $groupManager, - ICacheFactory $memCacheFactory, - IEventDispatcher $dispatcher, - LoggerInterface $logger) { - $this->userSession = $userSession; - $this->config = $config; - $this->appConfig = $appConfig; - $this->groupManager = $groupManager; - $this->memCacheFactory = $memCacheFactory; - $this->dispatcher = $dispatcher; - $this->logger = $logger; + private ?AppConfig $appConfig = null; + private ?IURLGenerator $urlGenerator = null; + + /** + * Be extremely careful when injecting classes here. The AppManager is used by the installer, + * so it needs to work before installation. See how AppConfig and IURLGenerator are injected for reference + */ + public function __construct( + private IUserSession $userSession, + private IConfig $config, + private IGroupManager $groupManager, + private ICacheFactory $memCacheFactory, + private IEventDispatcher $dispatcher, + private LoggerInterface $logger, + ) { + } + + public function getAppIcon(string $appId, bool $dark = false): ?string { + $possibleIcons = $dark ? [$appId . '-dark.svg', 'app-dark.svg'] : [$appId . '.svg', 'app.svg']; + $icon = null; + foreach ($possibleIcons as $iconName) { + try { + $icon = $this->getUrlGenerator()->imagePath($appId, $iconName); + break; + } catch (\RuntimeException $e) { + // ignore + } + } + return $icon; + } + + private function getAppConfig(): AppConfig { + if ($this->appConfig !== null) { + return $this->appConfig; + } + if (!$this->config->getSystemValueBool('installed', false)) { + throw new \Exception('Nextcloud is not installed yet, AppConfig is not available'); + } + $this->appConfig = \OCP\Server::get(AppConfig::class); + return $this->appConfig; + } + + private function getUrlGenerator(): IURLGenerator { + if ($this->urlGenerator !== null) { + return $this->urlGenerator; + } + if (!$this->config->getSystemValueBool('installed', false)) { + throw new \Exception('Nextcloud is not installed yet, AppConfig is not available'); + } + $this->urlGenerator = \OCP\Server::get(IURLGenerator::class); + return $this->urlGenerator; } /** @@ -124,7 +123,7 @@ class AppManager implements IAppManager { */ private function getInstalledAppsValues(): array { if (!$this->installedAppsCache) { - $values = $this->appConfig->getValues(false, 'enabled'); + $values = $this->getAppConfig()->getValues(false, 'enabled'); $alwaysEnabledApps = $this->getAlwaysEnabledApps(); foreach ($alwaysEnabledApps as $appId) { @@ -249,7 +248,7 @@ class AppManager implements IAppManager { private function getAppTypes(string $app): array { //load the cache if (count($this->appTypes) === 0) { - $this->appTypes = $this->appConfig->getValues(false, 'types') ?: []; + $this->appTypes = $this->getAppConfig()->getValues(false, 'types') ?: []; } if (isset($this->appTypes[$app])) { @@ -288,7 +287,7 @@ class AppManager implements IAppManager { * Check if an app is enabled for user * * @param string $appId - * @param \OCP\IUser $user (optional) if not defined, the currently logged in user will be used + * @param \OCP\IUser|null $user (optional) if not defined, the currently logged in user will be used * @return bool */ public function isEnabledForUser($appId, $user = null) { @@ -537,7 +536,7 @@ class AppManager implements IAppManager { } $this->installedAppsCache[$appId] = 'yes'; - $this->appConfig->setValue($appId, 'enabled', 'yes'); + $this->getAppConfig()->setValue($appId, 'enabled', 'yes'); $this->dispatcher->dispatchTyped(new AppEnableEvent($appId)); $this->dispatcher->dispatch(ManagerEvent::EVENT_APP_ENABLE, new ManagerEvent( ManagerEvent::EVENT_APP_ENABLE, $appId @@ -591,7 +590,7 @@ class AppManager implements IAppManager { }, $groups); $this->installedAppsCache[$appId] = json_encode($groupIds); - $this->appConfig->setValue($appId, 'enabled', json_encode($groupIds)); + $this->getAppConfig()->setValue($appId, 'enabled', json_encode($groupIds)); $this->dispatcher->dispatchTyped(new AppEnableEvent($appId, $groupIds)); $this->dispatcher->dispatch(ManagerEvent::EVENT_APP_ENABLE_FOR_GROUPS, new ManagerEvent( ManagerEvent::EVENT_APP_ENABLE_FOR_GROUPS, $appId, $groups @@ -612,7 +611,7 @@ class AppManager implements IAppManager { } if ($automaticDisabled) { - $previousSetting = $this->appConfig->getValue($appId, 'enabled', 'yes'); + $previousSetting = $this->getAppConfig()->getValue($appId, 'enabled', 'yes'); if ($previousSetting !== 'yes' && $previousSetting !== 'no') { $previousSetting = json_decode($previousSetting, true); } @@ -620,7 +619,7 @@ class AppManager implements IAppManager { } unset($this->installedAppsCache[$appId]); - $this->appConfig->setValue($appId, 'enabled', 'no'); + $this->getAppConfig()->setValue($appId, 'enabled', 'no'); // run uninstall steps $appData = $this->getAppInfo($appId); @@ -685,7 +684,7 @@ class AppManager implements IAppManager { $apps = $this->getInstalledApps(); foreach ($apps as $appId) { $appInfo = $this->getAppInfo($appId); - $appDbVersion = $this->appConfig->getValue($appId, 'installed_version'); + $appDbVersion = $this->getAppConfig()->getValue($appId, 'installed_version'); if ($appDbVersion && isset($appInfo['version']) && version_compare($appInfo['version'], $appDbVersion, '>') @@ -701,10 +700,7 @@ class AppManager implements IAppManager { /** * Returns the app information from "appinfo/info.xml". * - * @param string $appId app id - * - * @param bool $path - * @param null $lang + * @param string|null $lang * @return array|null app info */ public function getAppInfo(string $appId, bool $path = false, $lang = null) { @@ -816,22 +812,39 @@ class AppManager implements IAppManager { /** * @inheritdoc */ - public function getDefaultEnabledApps():array { + public function getDefaultEnabledApps(): array { $this->loadShippedJson(); return $this->defaultEnabled; } - public function getDefaultAppForUser(?IUser $user = null): string { + public function getDefaultAppForUser(?IUser $user = null, bool $withFallbacks = true): string { // Set fallback to always-enabled files app - $appId = 'files'; - $defaultApps = explode(',', $this->config->getSystemValueString('defaultapp', 'dashboard,files')); + $appId = $withFallbacks ? 'files' : ''; + $defaultApps = explode(',', $this->config->getSystemValueString('defaultapp', '')); + $defaultApps = array_filter($defaultApps); $user ??= $this->userSession->getUser(); if ($user !== null) { $userDefaultApps = explode(',', $this->config->getUserValue($user->getUID(), 'core', 'defaultapp')); $defaultApps = array_filter(array_merge($userDefaultApps, $defaultApps)); + if (empty($defaultApps) && $withFallbacks) { + /* Fallback on user defined apporder */ + $customOrders = json_decode($this->config->getUserValue($user->getUID(), 'core', 'apporder', '[]'), true, flags:JSON_THROW_ON_ERROR); + if (!empty($customOrders)) { + // filter only entries with app key (when added using closures or NavigationManager::add the app is not guranteed to be set) + $customOrders = array_filter($customOrders, fn ($entry) => isset($entry['app'])); + // sort apps by order + usort($customOrders, fn ($a, $b) => $a['order'] - $b['order']); + // set default apps to sorted apps + $defaultApps = array_map(fn ($entry) => $entry['app'], $customOrders); + } + } + } + + if (empty($defaultApps) && $withFallbacks) { + $defaultApps = ['dashboard','files']; } // Find the first app that is enabled for the current user @@ -845,4 +858,19 @@ class AppManager implements IAppManager { return $appId; } + + public function getDefaultApps(): array { + return explode(',', $this->config->getSystemValueString('defaultapp', 'dashboard,files')); + } + + public function setDefaultApps(array $defaultApps): void { + foreach ($defaultApps as $app) { + if (!$this->isInstalled($app)) { + $this->logger->debug('Can not set not installed app as default app', ['missing_app' => $app]); + throw new InvalidArgumentException('App is not installed'); + } + } + + $this->config->setSystemValue('defaultapp', join(',', $defaultApps)); + } } diff --git a/lib/private/App/AppStore/Bundles/Bundle.php b/lib/private/App/AppStore/Bundles/Bundle.php index dfc93fdfaa2..62f09b82f79 100644 --- a/lib/private/App/AppStore/Bundles/Bundle.php +++ b/lib/private/App/AppStore/Bundles/Bundle.php @@ -1,39 +1,19 @@ <?php /** - * @copyright Copyright (c) 2017 Lukas Reschke <lukas@statuscode.ch> - * - * @author Christoph Wurst <christoph@winzerhof-wurst.at> - * @author Lukas Reschke <lukas@statuscode.ch> - * - * @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/>. - * + * SPDX-FileCopyrightText: 2017 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later */ namespace OC\App\AppStore\Bundles; use OCP\IL10N; abstract class Bundle { - /** @var IL10N */ - protected $l10n; - /** * @param IL10N $l10n */ - public function __construct(IL10N $l10n) { - $this->l10n = $l10n; + public function __construct( + protected IL10N $l10n, + ) { } /** diff --git a/lib/private/App/AppStore/Bundles/BundleFetcher.php b/lib/private/App/AppStore/Bundles/BundleFetcher.php index 0d2bb61495f..01325699e2c 100644 --- a/lib/private/App/AppStore/Bundles/BundleFetcher.php +++ b/lib/private/App/AppStore/Bundles/BundleFetcher.php @@ -1,36 +1,16 @@ <?php /** - * @copyright Copyright (c) 2017 Lukas Reschke <lukas@statuscode.ch> - * - * @author Arthur Schiwon <blizzz@arthur-schiwon.de> - * @author Christoph Wurst <christoph@winzerhof-wurst.at> - * @author Lukas Reschke <lukas@statuscode.ch> - * - * @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/>. - * + * SPDX-FileCopyrightText: 2017 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later */ namespace OC\App\AppStore\Bundles; use OCP\IL10N; class BundleFetcher { - private IL10N $l10n; - - public function __construct(IL10N $l10n) { - $this->l10n = $l10n; + public function __construct( + private IL10N $l10n, + ) { } /** @@ -43,6 +23,7 @@ class BundleFetcher { new GroupwareBundle($this->l10n), new SocialSharingBundle($this->l10n), new EducationBundle($this->l10n), + new PublicSectorBundle($this->l10n), ]; } diff --git a/lib/private/App/AppStore/Bundles/EducationBundle.php b/lib/private/App/AppStore/Bundles/EducationBundle.php index 58ffd4f83b8..a08d707b021 100644 --- a/lib/private/App/AppStore/Bundles/EducationBundle.php +++ b/lib/private/App/AppStore/Bundles/EducationBundle.php @@ -1,25 +1,7 @@ <?php /** - * @copyright Copyright (c) 2017 Lukas Reschke <lukas@statuscode.ch> - * - * @author Lukas Reschke <lukas@statuscode.ch> - * @author Morris Jobke <hey@morrisjobke.de> - * - * @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/>. - * + * SPDX-FileCopyrightText: 2017 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later */ namespace OC\App\AppStore\Bundles; diff --git a/lib/private/App/AppStore/Bundles/EnterpriseBundle.php b/lib/private/App/AppStore/Bundles/EnterpriseBundle.php index bb64ec391ff..88a9ec60a00 100644 --- a/lib/private/App/AppStore/Bundles/EnterpriseBundle.php +++ b/lib/private/App/AppStore/Bundles/EnterpriseBundle.php @@ -1,25 +1,7 @@ <?php /** - * @copyright Copyright (c) 2017 Lukas Reschke <lukas@statuscode.ch> - * - * @author Joas Schilling <coding@schilljs.com> - * @author Lukas Reschke <lukas@statuscode.ch> - * - * @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/>. - * + * SPDX-FileCopyrightText: 2017 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later */ namespace OC\App\AppStore\Bundles; diff --git a/lib/private/App/AppStore/Bundles/GroupwareBundle.php b/lib/private/App/AppStore/Bundles/GroupwareBundle.php index 3a46ada52ad..7c7b74ff53d 100644 --- a/lib/private/App/AppStore/Bundles/GroupwareBundle.php +++ b/lib/private/App/AppStore/Bundles/GroupwareBundle.php @@ -1,26 +1,7 @@ <?php /** - * @copyright Copyright (c) 2017 Lukas Reschke <lukas@statuscode.ch> - * - * @author Bjoern Schiessle <bjoern@schiessle.org> - * @author Lukas Reschke <lukas@statuscode.ch> - * @author Morris Jobke <hey@morrisjobke.de> - * - * @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/>. - * + * SPDX-FileCopyrightText: 2017 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later */ namespace OC\App\AppStore\Bundles; diff --git a/lib/private/App/AppStore/Bundles/HubBundle.php b/lib/private/App/AppStore/Bundles/HubBundle.php index d5d236a1855..354e01e25c2 100644 --- a/lib/private/App/AppStore/Bundles/HubBundle.php +++ b/lib/private/App/AppStore/Bundles/HubBundle.php @@ -3,26 +3,8 @@ declare(strict_types=1); /** - * @copyright Copyright (c) 2020 Arthur Schiwon <blizzz@arthur-schiwon.de> - * - * @author Arthur Schiwon <blizzz@arthur-schiwon.de> - * @author Julius Härtl <jus@bitgrid.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/>. - * + * SPDX-FileCopyrightText: 2020 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later */ namespace OC\App\AppStore\Bundles; diff --git a/lib/private/App/AppStore/Bundles/PublicSectorBundle.php b/lib/private/App/AppStore/Bundles/PublicSectorBundle.php new file mode 100644 index 00000000000..edb86fb5ca7 --- /dev/null +++ b/lib/private/App/AppStore/Bundles/PublicSectorBundle.php @@ -0,0 +1,34 @@ +<?php +/** + * SPDX-FileCopyrightText: 2024 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later + */ +namespace OC\App\AppStore\Bundles; + +class PublicSectorBundle extends Bundle { + /** + * {@inheritDoc} + */ + public function getName(): string { + return $this->l10n->t('Public sector bundle'); + } + + /** + * {@inheritDoc} + */ + public function getAppIdentifiers(): array { + + return [ + 'files_confidential', + 'forms', + 'collectives', + 'files_antivirus', + 'twofactor_nextcloud_notification', + 'tables', + 'richdocuments', + 'admin_audit', + 'files_retention', + ]; + } + +} diff --git a/lib/private/App/AppStore/Bundles/SocialSharingBundle.php b/lib/private/App/AppStore/Bundles/SocialSharingBundle.php index f823792745b..7f925688ca3 100644 --- a/lib/private/App/AppStore/Bundles/SocialSharingBundle.php +++ b/lib/private/App/AppStore/Bundles/SocialSharingBundle.php @@ -1,25 +1,7 @@ <?php /** - * @copyright Copyright (c) 2017 Lukas Reschke <lukas@statuscode.ch> - * - * @author Lukas Reschke <lukas@statuscode.ch> - * @author Morris Jobke <hey@morrisjobke.de> - * - * @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/>. - * + * SPDX-FileCopyrightText: 2017 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later */ namespace OC\App\AppStore\Bundles; diff --git a/lib/private/App/AppStore/Fetcher/AppDiscoverFetcher.php b/lib/private/App/AppStore/Fetcher/AppDiscoverFetcher.php new file mode 100644 index 00000000000..1174c7d240b --- /dev/null +++ b/lib/private/App/AppStore/Fetcher/AppDiscoverFetcher.php @@ -0,0 +1,99 @@ +<?php +/** + * SPDX-FileCopyrightText: 2024 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later + */ + +namespace OC\App\AppStore\Fetcher; + +use DateTimeImmutable; +use OC\App\CompareVersion; +use OC\Files\AppData\Factory; +use OCP\AppFramework\Utility\ITimeFactory; +use OCP\Http\Client\IClientService; +use OCP\IConfig; +use OCP\Support\Subscription\IRegistry; +use Psr\Log\LoggerInterface; + +class AppDiscoverFetcher extends Fetcher { + + public const INVALIDATE_AFTER_SECONDS = 86400; + + public function __construct( + Factory $appDataFactory, + IClientService $clientService, + ITimeFactory $timeFactory, + IConfig $config, + LoggerInterface $logger, + IRegistry $registry, + private CompareVersion $compareVersion, + ) { + parent::__construct( + $appDataFactory, + $clientService, + $timeFactory, + $config, + $logger, + $registry + ); + + $this->fileName = 'discover.json'; + $this->endpointName = 'discover.json'; + } + + /** + * Get the app discover section entries + * + * @param bool $allowUnstable Include also upcoming entries + */ + public function get($allowUnstable = false) { + $entries = parent::get(false); + $now = new DateTimeImmutable(); + + return array_filter($entries, function (array $entry) use ($now, $allowUnstable) { + // Always remove expired entries + if (isset($entry['expiryDate'])) { + try { + $expiryDate = new DateTimeImmutable($entry['expiryDate']); + if ($expiryDate < $now) { + return false; + } + } catch (\Throwable $e) { + // Invalid expiryDate format + return false; + } + } + + // If not include upcoming entries, check for upcoming dates and remove those entries + if (!$allowUnstable && isset($entry['date'])) { + try { + $date = new DateTimeImmutable($entry['date']); + if ($date > $now) { + return false; + } + } catch (\Throwable $e) { + // Invalid date format + return false; + } + } + // Otherwise the entry is not time limited and should stay + return true; + }); + } + + public function getETag(): string|null { + $rootFolder = $this->appData->getFolder('/'); + + try { + $file = $rootFolder->getFile($this->fileName); + $jsonBlob = json_decode($file->getContent(), true); + + if (is_array($jsonBlob) && isset($jsonBlob['ETag'])) { + return (string)$jsonBlob['ETag']; + } + } catch (\Throwable $e) { + // ignore + } + return null; + } +} diff --git a/lib/private/App/AppStore/Fetcher/AppFetcher.php b/lib/private/App/AppStore/Fetcher/AppFetcher.php index 47bdece372d..508a5dae8f7 100644 --- a/lib/private/App/AppStore/Fetcher/AppFetcher.php +++ b/lib/private/App/AppStore/Fetcher/AppFetcher.php @@ -1,31 +1,7 @@ <?php /** - * @copyright Copyright (c) 2016 Lukas Reschke <lukas@statuscode.ch> - * - * @author Christoph Wurst <christoph@winzerhof-wurst.at> - * @author Georg Ehrke <oc.list@georgehrke.com> - * @author Jakub Onderka <ahoj@jakubonderka.cz> - * @author Joas Schilling <coding@schilljs.com> - * @author John Molakvoæ <skjnldsv@protonmail.com> - * @author Lukas Reschke <lukas@statuscode.ch> - * @author Morris Jobke <hey@morrisjobke.de> - * @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/>. - * + * SPDX-FileCopyrightText: 2016 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later */ namespace OC\App\AppStore\Fetcher; @@ -39,22 +15,17 @@ use OCP\Support\Subscription\IRegistry; use Psr\Log\LoggerInterface; class AppFetcher extends Fetcher { - /** @var CompareVersion */ - private $compareVersion; - - /** @var IRegistry */ - protected $registry; - /** @var bool */ private $ignoreMaxVersion; public function __construct(Factory $appDataFactory, - IClientService $clientService, - ITimeFactory $timeFactory, - IConfig $config, - CompareVersion $compareVersion, - LoggerInterface $logger, - IRegistry $registry) { + IClientService $clientService, + ITimeFactory $timeFactory, + IConfig $config, + private CompareVersion $compareVersion, + LoggerInterface $logger, + protected IRegistry $registry, + ) { parent::__construct( $appDataFactory, $clientService, @@ -64,9 +35,6 @@ class AppFetcher extends Fetcher { $registry ); - $this->compareVersion = $compareVersion; - $this->registry = $registry; - $this->fileName = 'apps.json'; $this->endpointName = 'apps.json'; $this->ignoreMaxVersion = true; diff --git a/lib/private/App/AppStore/Fetcher/CategoryFetcher.php b/lib/private/App/AppStore/Fetcher/CategoryFetcher.php index afe051e6281..d72f8fa7e24 100644 --- a/lib/private/App/AppStore/Fetcher/CategoryFetcher.php +++ b/lib/private/App/AppStore/Fetcher/CategoryFetcher.php @@ -1,28 +1,7 @@ <?php /** - * @copyright Copyright (c) 2016 Lukas Reschke <lukas@statuscode.ch> - * - * @author Georg Ehrke <oc.list@georgehrke.com> - * @author Joas Schilling <coding@schilljs.com> - * @author Lukas Reschke <lukas@statuscode.ch> - * @author Morris Jobke <hey@morrisjobke.de> - * @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/>. - * + * SPDX-FileCopyrightText: 2016 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later */ namespace OC\App\AppStore\Fetcher; @@ -34,12 +13,14 @@ use OCP\Support\Subscription\IRegistry; use Psr\Log\LoggerInterface; class CategoryFetcher extends Fetcher { - public function __construct(Factory $appDataFactory, - IClientService $clientService, - ITimeFactory $timeFactory, - IConfig $config, - LoggerInterface $logger, - IRegistry $registry) { + public function __construct( + Factory $appDataFactory, + IClientService $clientService, + ITimeFactory $timeFactory, + IConfig $config, + LoggerInterface $logger, + IRegistry $registry, + ) { parent::__construct( $appDataFactory, $clientService, diff --git a/lib/private/App/AppStore/Fetcher/Fetcher.php b/lib/private/App/AppStore/Fetcher/Fetcher.php index 095b026cb44..ad76befc5fa 100644 --- a/lib/private/App/AppStore/Fetcher/Fetcher.php +++ b/lib/private/App/AppStore/Fetcher/Fetcher.php @@ -1,32 +1,7 @@ <?php /** - * @copyright Copyright (c) 2016 Lukas Reschke <lukas@statuscode.ch> - * - * @author Daniel Kesselberg <mail@danielkesselberg.de> - * @author Georg Ehrke <oc.list@georgehrke.com> - * @author Joas Schilling <coding@schilljs.com> - * @author John Molakvoæ <skjnldsv@protonmail.com> - * @author Julius Härtl <jus@bitgrid.net> - * @author Lukas Reschke <lukas@statuscode.ch> - * @author Morris Jobke <hey@morrisjobke.de> - * @author Roeland Jago Douma <roeland@famdouma.nl> - * @author Steffen Lindner <mail@steffen-lindner.de> - * - * @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/>. - * + * SPDX-FileCopyrightText: 2016 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later */ namespace OC\App\AppStore\Fetcher; @@ -44,19 +19,10 @@ use Psr\Log\LoggerInterface; abstract class Fetcher { public const INVALIDATE_AFTER_SECONDS = 3600; public const RETRY_AFTER_FAILURE_SECONDS = 300; + public const APP_STORE_URL = 'https://apps.nextcloud.com/api/v1'; /** @var IAppData */ protected $appData; - /** @var IClientService */ - protected $clientService; - /** @var ITimeFactory */ - protected $timeFactory; - /** @var IConfig */ - protected $config; - /** @var LoggerInterface */ - protected $logger; - /** @var IRegistry */ - protected $registry; /** @var string */ protected $fileName; @@ -67,18 +33,15 @@ abstract class Fetcher { /** @var ?string */ protected $channel = null; - public function __construct(Factory $appDataFactory, - IClientService $clientService, - ITimeFactory $timeFactory, - IConfig $config, - LoggerInterface $logger, - IRegistry $registry) { + public function __construct( + Factory $appDataFactory, + protected IClientService $clientService, + protected ITimeFactory $timeFactory, + protected IConfig $config, + protected LoggerInterface $logger, + protected IRegistry $registry, + ) { $this->appData = $appDataFactory->get('appstore'); - $this->clientService = $clientService; - $this->timeFactory = $timeFactory; - $this->config = $config; - $this->logger = $logger; - $this->registry = $registry; } /** @@ -109,10 +72,13 @@ abstract class Fetcher { ]; } - // If we have a valid subscription key, send it to the appstore - $subscriptionKey = $this->config->getAppValue('support', 'subscription_key'); - if ($this->registry->delegateHasValidSubscription() && $subscriptionKey) { - $options['headers']['X-NC-Subscription-Key'] = $subscriptionKey; + if ($this->config->getSystemValueString('appstoreurl', self::APP_STORE_URL) === self::APP_STORE_URL) { + // If we have a valid subscription key, send it to the appstore + $subscriptionKey = $this->config->getAppValue('support', 'subscription_key'); + if ($this->registry->delegateHasValidSubscription() && $subscriptionKey) { + $options['headers'] ??= []; + $options['headers']['X-NC-Subscription-Key'] = $subscriptionKey; + } } $client = $this->clientService->newClient(); @@ -142,7 +108,7 @@ abstract class Fetcher { } /** - * Returns the array with the categories on the appstore server + * Returns the array with the entries on the appstore server * * @param bool [$allowUnstable] Allow unstable releases * @return array @@ -150,8 +116,9 @@ abstract class Fetcher { public function get($allowUnstable = false) { $appstoreenabled = $this->config->getSystemValueBool('appstoreenabled', true); $internetavailable = $this->config->getSystemValueBool('has_internet_connection', true); + $isDefaultAppStore = $this->config->getSystemValueString('appstoreurl', self::APP_STORE_URL) === self::APP_STORE_URL; - if (!$appstoreenabled || !$internetavailable) { + if (!$appstoreenabled || (!$internetavailable && $isDefaultAppStore)) { return []; } @@ -189,7 +156,7 @@ abstract class Fetcher { try { $responseJson = $this->fetch($ETag, $content, $allowUnstable); - if (empty($responseJson)) { + if (empty($responseJson) || empty($responseJson['data'])) { return []; } diff --git a/lib/private/App/AppStore/Version/Version.php b/lib/private/App/AppStore/Version/Version.php index d41ca1770f0..b7e679d250a 100644 --- a/lib/private/App/AppStore/Version/Version.php +++ b/lib/private/App/AppStore/Version/Version.php @@ -1,40 +1,19 @@ <?php /** - * @copyright Copyright (c) 2016 Lukas Reschke <lukas@statuscode.ch> - * - * @author Lukas Reschke <lukas@statuscode.ch> - * - * @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/>. - * + * SPDX-FileCopyrightText: 2016 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later */ namespace OC\App\AppStore\Version; class Version { - /** @var string */ - private $minVersion; - /** @var string */ - private $maxVersion; - /** * @param string $minVersion * @param string $maxVersion */ - public function __construct($minVersion, $maxVersion) { - $this->minVersion = $minVersion; - $this->maxVersion = $maxVersion; + public function __construct( + private string $minVersion, + private string $maxVersion, + ) { } /** diff --git a/lib/private/App/AppStore/Version/VersionParser.php b/lib/private/App/AppStore/Version/VersionParser.php index 2b88399b9fd..59715c8d385 100644 --- a/lib/private/App/AppStore/Version/VersionParser.php +++ b/lib/private/App/AppStore/Version/VersionParser.php @@ -1,26 +1,7 @@ <?php /** - * @copyright Copyright (c) 2016 Lukas Reschke <lukas@statuscode.ch> - * - * @author Christoph Wurst <christoph@winzerhof-wurst.at> - * @author Lukas Reschke <lukas@statuscode.ch> - * @author Morris Jobke <hey@morrisjobke.de> - * - * @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/>. - * + * SPDX-FileCopyrightText: 2016 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later */ namespace OC\App\AppStore\Version; @@ -54,9 +35,9 @@ class VersionParser { // Count the amount of =, if it is one then it's either maximum or minimum // version. If it is two then it is maximum and minimum. $versionElements = explode(' ', $versionSpec); - $firstVersion = isset($versionElements[0]) ? $versionElements[0] : ''; + $firstVersion = $versionElements[0] ?? ''; $firstVersionNumber = substr($firstVersion, 2); - $secondVersion = isset($versionElements[1]) ? $versionElements[1] : ''; + $secondVersion = $versionElements[1] ?? ''; $secondVersionNumber = substr($secondVersion, 2); switch (count($versionElements)) { diff --git a/lib/private/App/CompareVersion.php b/lib/private/App/CompareVersion.php index a349c7aa6f2..7edc3e565b1 100644 --- a/lib/private/App/CompareVersion.php +++ b/lib/private/App/CompareVersion.php @@ -3,25 +3,8 @@ declare(strict_types=1); /** - * @copyright 2018 Christoph Wurst <christoph@winzerhof-wurst.at> - * - * @author Christoph Wurst <christoph@winzerhof-wurst.at> - * - * @license GNU AGPL version 3 or any later version - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as - * published by the Free Software Foundation, either version 3 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - * + * SPDX-FileCopyrightText: 2018 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later */ namespace OC\App; diff --git a/lib/private/App/DependencyAnalyzer.php b/lib/private/App/DependencyAnalyzer.php index 3bdc551ea5a..d963c74de79 100644 --- a/lib/private/App/DependencyAnalyzer.php +++ b/lib/private/App/DependencyAnalyzer.php @@ -1,42 +1,14 @@ <?php /** - * @copyright Copyright (c) 2016, ownCloud, Inc. - * @copyright Copyright (c) 2016, Lukas Reschke <lukas@statuscode.ch> - * - * @author Bernhard Posselt <dev@bernhard-posselt.com> - * @author Christoph Wurst <christoph@winzerhof-wurst.at> - * @author Joas Schilling <coding@schilljs.com> - * @author Julius Härtl <jus@bitgrid.net> - * @author Lukas Reschke <lukas@statuscode.ch> - * @author Morris Jobke <hey@morrisjobke.de> - * @author Roeland Jago Douma <roeland@famdouma.nl> - * @author Stefan Weil <sw@weilnetz.de> - * @author Thomas Müller <thomas.mueller@tmit.eu> - * - * @license AGPL-3.0 - * - * This code is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License, version 3, - * as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License, version 3, - * along with this program. If not, see <http://www.gnu.org/licenses/> - * + * SPDX-FileCopyrightText: 2016 Nextcloud GmbH and Nextcloud contributors + * SPDX-FileCopyrightText: 2016 ownCloud, Inc. + * SPDX-License-Identifier: AGPL-3.0-only */ namespace OC\App; use OCP\IL10N; class DependencyAnalyzer { - /** @var Platform */ - private $platform; - /** @var \OCP\IL10N */ - private $l; /** @var array */ private $appInfo; @@ -44,9 +16,10 @@ class DependencyAnalyzer { * @param Platform $platform * @param \OCP\IL10N $l */ - public function __construct(Platform $platform, IL10N $l) { - $this->platform = $platform; - $this->l = $l; + public function __construct( + private Platform $platform, + private IL10N $l, + ) { } /** diff --git a/lib/private/App/InfoParser.php b/lib/private/App/InfoParser.php index 79d051fd2a1..d29b1d6596d 100644 --- a/lib/private/App/InfoParser.php +++ b/lib/private/App/InfoParser.php @@ -1,47 +1,21 @@ <?php /** - * @copyright Copyright (c) 2016, ownCloud, Inc. - * @copyright Copyright (c) 2016, Lukas Reschke <lukas@statuscode.ch> - * - * @author Andreas Fischer <bantu@owncloud.com> - * @author Arthur Schiwon <blizzz@arthur-schiwon.de> - * @author Christoph Wurst <christoph@winzerhof-wurst.at> - * @author Daniel Kesselberg <mail@danielkesselberg.de> - * @author Joas Schilling <coding@schilljs.com> - * @author Lukas Reschke <lukas@statuscode.ch> - * @author Roeland Jago Douma <roeland@famdouma.nl> - * @author Thomas Müller <thomas.mueller@tmit.eu> - * - * @license AGPL-3.0 - * - * This code is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License, version 3, - * as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License, version 3, - * along with this program. If not, see <http://www.gnu.org/licenses/> - * + * SPDX-FileCopyrightText: 2016 Nextcloud GmbH and Nextcloud contributors + * SPDX-FileCopyrightText: 2016 ownCloud, Inc. + * SPDX-License-Identifier: AGPL-3.0-only */ namespace OC\App; use OCP\ICache; -use function libxml_disable_entity_loader; use function simplexml_load_string; class InfoParser { - /** @var \OCP\ICache|null */ - private $cache; - /** * @param ICache|null $cache */ - public function __construct(ICache $cache = null) { - $this->cache = $cache; + public function __construct( + private ?ICache $cache = null, + ) { } /** @@ -61,13 +35,7 @@ class InfoParser { } libxml_use_internal_errors(true); - if ((PHP_VERSION_ID < 80000)) { - $loadEntities = libxml_disable_entity_loader(false); - $xml = simplexml_load_string(file_get_contents($file)); - libxml_disable_entity_loader($loadEntities); - } else { - $xml = simplexml_load_string(file_get_contents($file)); - } + $xml = simplexml_load_string(file_get_contents($file)); if ($xml === false) { libxml_clear_errors(); @@ -272,7 +240,7 @@ class InfoParser { } else { $array[$element] = $data; } - // Just a value + // Just a value } else { if ($totalElement > 1) { $array[$element][] = $this->xmlToArray($node); diff --git a/lib/private/App/Platform.php b/lib/private/App/Platform.php index 1cab740bebb..c2c059220c8 100644 --- a/lib/private/App/Platform.php +++ b/lib/private/App/Platform.php @@ -1,32 +1,13 @@ <?php /** - * @copyright Copyright (c) 2016, ownCloud, Inc. - * - * @author Bernhard Posselt <dev@bernhard-posselt.com> - * @author Christoph Wurst <christoph@winzerhof-wurst.at> - * @author Julius Härtl <jus@bitgrid.net> - * @author Morris Jobke <hey@morrisjobke.de> - * @author Thomas Müller <thomas.mueller@tmit.eu> - * - * @license AGPL-3.0 - * - * This code is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License, version 3, - * as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License, version 3, - * along with this program. If not, see <http://www.gnu.org/licenses/> - * + * SPDX-FileCopyrightText: 2016-2024 Nextcloud GmbH and Nextcloud contributors + * SPDX-FileCopyrightText: 2016 ownCloud, Inc. + * SPDX-License-Identifier: AGPL-3.0-only */ namespace OC\App; -use OCP\IConfig; use OCP\IBinaryFinder; +use OCP\IConfig; /** * Class Platform @@ -36,10 +17,9 @@ use OCP\IBinaryFinder; * @package OC\App */ class Platform { - private IConfig $config; - - public function __construct(IConfig $config) { - $this->config = $config; + public function __construct( + private IConfig $config, + ) { } public function getPhpVersion(): string { diff --git a/lib/private/App/PlatformRepository.php b/lib/private/App/PlatformRepository.php index 9b94c0b07bc..6d9a9cd011a 100644 --- a/lib/private/App/PlatformRepository.php +++ b/lib/private/App/PlatformRepository.php @@ -1,24 +1,8 @@ <?php /** - * @copyright Copyright (c) 2016, ownCloud, Inc. - * - * @author Christoph Wurst <christoph@winzerhof-wurst.at> - * @author Thomas Müller <thomas.mueller@tmit.eu> - * - * @license AGPL-3.0 - * - * This code is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License, version 3, - * as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License, version 3, - * along with this program. If not, see <http://www.gnu.org/licenses/> - * + * SPDX-FileCopyrightText: 2016-2024 Nextcloud GmbH and Nextcloud contributors + * SPDX-FileCopyrightText: 2016 ownCloud, Inc. + * SPDX-License-Identifier: AGPL-3.0-only */ namespace OC\App; @@ -154,7 +138,7 @@ class PlatformRepository { */ public function normalizeVersion(string $version, ?string $fullVersion = null): string { $version = trim($version); - if (null === $fullVersion) { + if ($fullVersion === null) { $fullVersion = $version; } // ignore aliases and just assume the alias is required instead of the source @@ -165,7 +149,7 @@ class PlatformRepository { if (preg_match('{^(?:dev-)?(?:master|trunk|default)$}i', $version)) { return '9999999-dev'; } - if ('dev-' === strtolower(substr($version, 0, 4))) { + if (strtolower(substr($version, 0, 4)) === 'dev-') { return 'dev-' . substr($version, 4); } // match classical versioning @@ -188,7 +172,7 @@ class PlatformRepository { // add version modifiers if a version was matched if (isset($index)) { if (!empty($matches[$index])) { - if ('stable' === $matches[$index]) { + if ($matches[$index] === 'stable') { return $version; } $version .= '-' . $this->expandStability($matches[$index]) . (!empty($matches[$index + 1]) ? $matches[$index + 1] : ''); diff --git a/lib/private/AppConfig.php b/lib/private/AppConfig.php index 84f0d5b9e5a..d046557e42c 100644 --- a/lib/private/AppConfig.php +++ b/lib/private/AppConfig.php @@ -1,383 +1,1356 @@ <?php + +declare(strict_types=1); /** - * @copyright Copyright (c) 2017, Joas Schilling <coding@schilljs.com> - * @copyright Copyright (c) 2016, ownCloud, Inc. - * - * @author Arthur Schiwon <blizzz@arthur-schiwon.de> - * @author Bart Visscher <bartv@thisnet.nl> - * @author Christoph Wurst <christoph@winzerhof-wurst.at> - * @author Jakob Sack <mail@jakobsack.de> - * @author Joas Schilling <coding@schilljs.com> - * @author Jörn Friedrich Dreyer <jfd@butonic.de> - * @author michaelletzgus <michaelletzgus@users.noreply.github.com> - * @author Morris Jobke <hey@morrisjobke.de> - * @author Robin Appelman <robin@icewind.nl> - * @author Robin McCorkell <robin@mccorkell.me.uk> - * @author Roeland Jago Douma <roeland@famdouma.nl> - * - * @license AGPL-3.0 - * - * This code is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License, version 3, - * as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License, version 3, - * along with this program. If not, see <http://www.gnu.org/licenses/> - * + * SPDX-FileCopyrightText: 2017 Nextcloud GmbH and Nextcloud contributors + * SPDX-FileCopyrightText: 2016 ownCloud, Inc. + * SPDX-License-Identifier: AGPL-3.0-only */ + namespace OC; -use OC\DB\Connection; -use OC\DB\OracleConnection; +use InvalidArgumentException; +use JsonException; +use OCP\DB\Exception as DBException; use OCP\DB\QueryBuilder\IQueryBuilder; +use OCP\Exceptions\AppConfigIncorrectTypeException; +use OCP\Exceptions\AppConfigTypeConflictException; +use OCP\Exceptions\AppConfigUnknownKeyException; use OCP\IAppConfig; use OCP\IConfig; +use OCP\IDBConnection; +use OCP\Security\ICrypto; +use Psr\Log\LoggerInterface; /** * This class provides an easy way for apps to store config values in the * database. + * + * **Note:** since 29.0.0, it supports **lazy loading** + * + * ### What is lazy loading ? + * In order to avoid loading useless config values into memory for each request, + * only non-lazy values are now loaded. + * + * Once a value that is lazy is requested, all lazy values will be loaded. + * + * Similarly, some methods from this class are marked with a warning about ignoring + * lazy loading. Use them wisely and only on parts of the code that are called + * during specific requests or actions to avoid loading the lazy values all the time. + * + * @since 7.0.0 + * @since 29.0.0 - Supporting types and lazy loading */ class AppConfig implements IAppConfig { - /** @var array[] */ - protected $sensitiveValues = [ - 'circles' => [ - '/^key_pairs$/', - '/^local_gskey$/', - ], - 'external' => [ - '/^sites$/', - ], - 'integration_discourse' => [ - '/^private_key$/', - '/^public_key$/', - ], - 'integration_dropbox' => [ - '/^client_id$/', - '/^client_secret$/', - ], - 'integration_github' => [ - '/^client_id$/', - '/^client_secret$/', - ], - 'integration_gitlab' => [ - '/^client_id$/', - '/^client_secret$/', - '/^oauth_instance_url$/', - ], - 'integration_google' => [ - '/^client_id$/', - '/^client_secret$/', - ], - 'integration_jira' => [ - '/^client_id$/', - '/^client_secret$/', - '/^forced_instance_url$/', - ], - 'integration_onedrive' => [ - '/^client_id$/', - '/^client_secret$/', - ], - 'integration_openproject' => [ - '/^client_id$/', - '/^client_secret$/', - '/^oauth_instance_url$/', - ], - 'integration_reddit' => [ - '/^client_id$/', - '/^client_secret$/', - ], - 'integration_suitecrm' => [ - '/^client_id$/', - '/^client_secret$/', - '/^oauth_instance_url$/', - ], - 'integration_twitter' => [ - '/^consumer_key$/', - '/^consumer_secret$/', - '/^followed_user$/', - ], - 'integration_zammad' => [ - '/^client_id$/', - '/^client_secret$/', - '/^oauth_instance_url$/', - ], - 'notify_push' => [ - '/^cookie$/', - ], - 'spreed' => [ - '/^bridge_bot_password$/', - '/^hosted-signaling-server-(.*)$/', - '/^recording_servers$/', - '/^signaling_servers$/', - '/^signaling_ticket_secret$/', - '/^signaling_token_privkey_(.*)$/', - '/^signaling_token_pubkey_(.*)$/', - '/^sip_bridge_dialin_info$/', - '/^sip_bridge_shared_secret$/', - '/^stun_servers$/', - '/^turn_servers$/', - '/^turn_server_secret$/', - ], - 'support' => [ - '/^last_response$/', - '/^potential_subscription_key$/', - '/^subscription_key$/', - ], - 'theming' => [ - '/^imprintUrl$/', - '/^privacyUrl$/', - '/^slogan$/', - '/^url$/', - ], - 'user_ldap' => [ - '/^(s..)?ldap_agent_password$/', - ], - 'user_saml' => [ - '/^idp-x509cert$/', - ], - ]; - - /** @var Connection */ - protected $conn; - - /** @var array[] */ - private $cache = []; - - /** @var bool */ - private $configLoaded = false; - - /** - * @param Connection $conn - */ - public function __construct(Connection $conn) { - $this->conn = $conn; + private const APP_MAX_LENGTH = 32; + private const KEY_MAX_LENGTH = 64; + private const ENCRYPTION_PREFIX = '$AppConfigEncryption$'; + private const ENCRYPTION_PREFIX_LENGTH = 21; // strlen(self::ENCRYPTION_PREFIX) + + /** @var array<string, array<string, mixed>> ['app_id' => ['config_key' => 'config_value']] */ + private array $fastCache = []; // cache for normal config keys + /** @var array<string, array<string, mixed>> ['app_id' => ['config_key' => 'config_value']] */ + private array $lazyCache = []; // cache for lazy config keys + /** @var array<string, array<string, int>> ['app_id' => ['config_key' => bitflag]] */ + private array $valueTypes = []; // type for all config values + private bool $fastLoaded = false; + private bool $lazyLoaded = false; + + /** + * $migrationCompleted is only needed to manage the previous structure + * of the database during the upgrading process to nc29. + * + * only when upgrading from a version prior 28.0.2 + * + * @TODO: remove this value in Nextcloud 30+ + */ + private bool $migrationCompleted = true; + + public function __construct( + protected IDBConnection $connection, + protected LoggerInterface $logger, + protected ICrypto $crypto, + ) { } /** - * @param string $app - * @return array + * @inheritDoc + * + * @return string[] list of app ids + * @since 7.0.0 + */ + public function getApps(): array { + $this->loadConfigAll(); + $apps = array_merge(array_keys($this->fastCache), array_keys($this->lazyCache)); + sort($apps); + + return array_values(array_unique($apps)); + } + + /** + * @inheritDoc + * + * @param string $app id of the app + * + * @return string[] list of stored config keys + * @since 29.0.0 + */ + public function getKeys(string $app): array { + $this->assertParams($app); + $this->loadConfigAll(); + $keys = array_merge(array_keys($this->fastCache[$app] ?? []), array_keys($this->lazyCache[$app] ?? [])); + sort($keys); + + return array_values(array_unique($keys)); + } + + /** + * @inheritDoc + * + * @param string $app id of the app + * @param string $key config key + * @param bool|null $lazy TRUE to search within lazy loaded config, NULL to search within all config + * + * @return bool TRUE if key exists + * @since 7.0.0 + * @since 29.0.0 Added the $lazy argument */ - private function getAppValues($app) { - $this->loadConfigValues(); + public function hasKey(string $app, string $key, ?bool $lazy = false): bool { + $this->assertParams($app, $key); + $this->loadConfig($lazy); - if (isset($this->cache[$app])) { - return $this->cache[$app]; + if ($lazy === null) { + $appCache = $this->getAllValues($app); + return isset($appCache[$key]); } - return []; + if ($lazy) { + return isset($this->lazyCache[$app][$key]); + } + + return isset($this->fastCache[$app][$key]); } /** - * Get all apps using the config + * @param string $app id of the app + * @param string $key config key + * @param bool|null $lazy TRUE to search within lazy loaded config, NULL to search within all config * - * @return string[] an array of app ids + * @return bool + * @throws AppConfigUnknownKeyException if config key is not known + * @since 29.0.0 + */ + public function isSensitive(string $app, string $key, ?bool $lazy = false): bool { + $this->assertParams($app, $key); + $this->loadConfig($lazy); + + if (!isset($this->valueTypes[$app][$key])) { + throw new AppConfigUnknownKeyException('unknown config key'); + } + + return $this->isTyped(self::VALUE_SENSITIVE, $this->valueTypes[$app][$key]); + } + + /** + * @inheritDoc * - * This function returns a list of all apps that have at least one - * entry in the appconfig table. + * @param string $app if of the app + * @param string $key config key + * + * @return bool TRUE if config is lazy loaded + * @throws AppConfigUnknownKeyException if config key is not known + * @see IAppConfig for details about lazy loading + * @since 29.0.0 */ - public function getApps() { - $this->loadConfigValues(); + public function isLazy(string $app, string $key): bool { + // there is a huge probability the non-lazy config are already loaded + if ($this->hasKey($app, $key, false)) { + return false; + } - return $this->getSortedKeys($this->cache); + // key not found, we search in the lazy config + if ($this->hasKey($app, $key, true)) { + return true; + } + + throw new AppConfigUnknownKeyException('unknown config key'); } + /** - * Get the available keys for an app + * @inheritDoc * - * @param string $app the app we are looking for - * @return array an array of key names + * @param string $app id of the app + * @param string $prefix config keys prefix to search + * @param bool $filtered TRUE to hide sensitive config values. Value are replaced by {@see IConfig::SENSITIVE_VALUE} * - * This function gets all keys of an app. Please note that the values are - * not returned. + * @return array<string, string|int|float|bool|array> [configKey => configValue] + * @since 29.0.0 */ - public function getKeys($app) { - $this->loadConfigValues(); + public function getAllValues(string $app, string $prefix = '', bool $filtered = false): array { + $this->assertParams($app, $prefix); + // if we want to filter values, we need to get sensitivity + $this->loadConfigAll(); + // array_merge() will remove numeric keys (here config keys), so addition arrays instead + $values = $this->formatAppValues($app, ($this->fastCache[$app] ?? []) + ($this->lazyCache[$app] ?? [])); + $values = array_filter( + $values, + function (string $key) use ($prefix): bool { + return str_starts_with($key, $prefix); // filter values based on $prefix + }, ARRAY_FILTER_USE_KEY + ); - if (isset($this->cache[$app])) { - return $this->getSortedKeys($this->cache[$app]); + if (!$filtered) { + return $values; } - return []; + /** + * Using the old (deprecated) list of sensitive values. + */ + foreach ($this->getSensitiveKeys($app) as $sensitiveKeyExp) { + $sensitiveKeys = preg_grep($sensitiveKeyExp, array_keys($values)); + foreach ($sensitiveKeys as $sensitiveKey) { + $this->valueTypes[$app][$sensitiveKey] = ($this->valueTypes[$app][$sensitiveKey] ?? 0) | self::VALUE_SENSITIVE; + } + } + + $result = []; + foreach ($values as $key => $value) { + $result[$key] = $this->isTyped(self::VALUE_SENSITIVE, $this->valueTypes[$app][$key] ?? 0) ? IConfig::SENSITIVE_VALUE : $value; + } + + return $result; } - public function getSortedKeys($data) { - $keys = array_keys($data); - sort($keys); - return $keys; + /** + * @inheritDoc + * + * @param string $key config key + * @param bool $lazy search within lazy loaded config + * @param int|null $typedAs enforce type for the returned values ({@see self::VALUE_STRING} and others) + * + * @return array<string, string|int|float|bool|array> [appId => configValue] + * @since 29.0.0 + */ + public function searchValues(string $key, bool $lazy = false, ?int $typedAs = null): array { + $this->assertParams('', $key, true); + $this->loadConfig($lazy); + + /** @var array<array-key, array<array-key, mixed>> $cache */ + if ($lazy) { + $cache = $this->lazyCache; + } else { + $cache = $this->fastCache; + } + + $values = []; + foreach (array_keys($cache) as $app) { + if (isset($cache[$app][$key])) { + $values[$app] = $this->convertTypedValue($cache[$app][$key], $typedAs ?? $this->getValueType((string)$app, $key, $lazy)); + } + } + + return $values; } + /** - * Gets the config value + * Get the config value as string. + * If the value does not exist the given default will be returned. + * + * Set lazy to `null` to ignore it and get the value from either source. + * + * **WARNING:** Method is internal and **SHOULD** not be used, as it is better to get the value with a type. + * + * @param string $app id of the app + * @param string $key config key + * @param string $default config value + * @param null|bool $lazy get config as lazy loaded or not. can be NULL * - * @param string $app app - * @param string $key key - * @param string $default = null, default value if the key does not exist * @return string the value or $default + * @internal + * @since 29.0.0 + * @see IAppConfig for explanation about lazy loading + * @see getValueString() + * @see getValueInt() + * @see getValueFloat() + * @see getValueBool() + * @see getValueArray() + */ + public function getValueMixed( + string $app, + string $key, + string $default = '', + ?bool $lazy = false + ): string { + try { + $lazy = ($lazy === null) ? $this->isLazy($app, $key) : $lazy; + } catch (AppConfigUnknownKeyException $e) { + return $default; + } + + return $this->getTypedValue( + $app, + $key, + $default, + $lazy, + self::VALUE_MIXED + ); + } + + /** + * @inheritDoc * - * This function gets a value from the appconfig table. If the key does - * not exist the default value will be returned + * @param string $app id of the app + * @param string $key config key + * @param string $default default value + * @param bool $lazy search within lazy loaded config + * + * @return string stored config value or $default if not set in database + * @throws InvalidArgumentException if one of the argument format is invalid + * @throws AppConfigTypeConflictException in case of conflict with the value type set in database + * @since 29.0.0 + * @see IAppConfig for explanation about lazy loading */ - public function getValue($app, $key, $default = null) { - $this->loadConfigValues(); + public function getValueString( + string $app, + string $key, + string $default = '', + bool $lazy = false + ): string { + return $this->getTypedValue($app, $key, $default, $lazy, self::VALUE_STRING); + } + + /** + * @inheritDoc + * + * @param string $app id of the app + * @param string $key config key + * @param int $default default value + * @param bool $lazy search within lazy loaded config + * + * @return int stored config value or $default if not set in database + * @throws InvalidArgumentException if one of the argument format is invalid + * @throws AppConfigTypeConflictException in case of conflict with the value type set in database + * @since 29.0.0 + * @see IAppConfig for explanation about lazy loading + */ + public function getValueInt( + string $app, + string $key, + int $default = 0, + bool $lazy = false + ): int { + return (int)$this->getTypedValue($app, $key, (string)$default, $lazy, self::VALUE_INT); + } + + /** + * @inheritDoc + * + * @param string $app id of the app + * @param string $key config key + * @param float $default default value + * @param bool $lazy search within lazy loaded config + * + * @return float stored config value or $default if not set in database + * @throws InvalidArgumentException if one of the argument format is invalid + * @throws AppConfigTypeConflictException in case of conflict with the value type set in database + * @since 29.0.0 + * @see IAppConfig for explanation about lazy loading + */ + public function getValueFloat(string $app, string $key, float $default = 0, bool $lazy = false): float { + return (float)$this->getTypedValue($app, $key, (string)$default, $lazy, self::VALUE_FLOAT); + } + + /** + * @inheritDoc + * + * @param string $app id of the app + * @param string $key config key + * @param bool $default default value + * @param bool $lazy search within lazy loaded config + * + * @return bool stored config value or $default if not set in database + * @throws InvalidArgumentException if one of the argument format is invalid + * @throws AppConfigTypeConflictException in case of conflict with the value type set in database + * @since 29.0.0 + * @see IAppConfig for explanation about lazy loading + */ + public function getValueBool(string $app, string $key, bool $default = false, bool $lazy = false): bool { + $b = strtolower($this->getTypedValue($app, $key, $default ? 'true' : 'false', $lazy, self::VALUE_BOOL)); + return in_array($b, ['1', 'true', 'yes', 'on']); + } + + /** + * @inheritDoc + * + * @param string $app id of the app + * @param string $key config key + * @param array $default default value + * @param bool $lazy search within lazy loaded config + * + * @return array stored config value or $default if not set in database + * @throws InvalidArgumentException if one of the argument format is invalid + * @throws AppConfigTypeConflictException in case of conflict with the value type set in database + * @since 29.0.0 + * @see IAppConfig for explanation about lazy loading + */ + public function getValueArray( + string $app, + string $key, + array $default = [], + bool $lazy = false + ): array { + try { + $defaultJson = json_encode($default, JSON_THROW_ON_ERROR); + $value = json_decode($this->getTypedValue($app, $key, $defaultJson, $lazy, self::VALUE_ARRAY), true, flags: JSON_THROW_ON_ERROR); - if ($this->hasKey($app, $key)) { - return $this->cache[$app][$key]; + return is_array($value) ? $value : []; + } catch (JsonException) { + return []; } + } - return $default; + /** + * @param string $app id of the app + * @param string $key config key + * @param string $default default value + * @param bool $lazy search within lazy loaded config + * @param int $type value type {@see VALUE_STRING} {@see VALUE_INT}{@see VALUE_FLOAT} {@see VALUE_BOOL} {@see VALUE_ARRAY} + * + * @return string + * @throws AppConfigTypeConflictException if type from database is not VALUE_MIXED and different from the requested one + * @throws InvalidArgumentException + */ + private function getTypedValue( + string $app, + string $key, + string $default, + bool $lazy, + int $type + ): string { + $this->assertParams($app, $key, valueType: $type); + $this->loadConfig($lazy); + + /** + * We ignore check if mixed type is requested. + * If type of stored value is set as mixed, we don't filter. + * If type of stored value is defined, we compare with the one requested. + */ + $knownType = $this->valueTypes[$app][$key] ?? 0; + if (!$this->isTyped(self::VALUE_MIXED, $type) + && $knownType > 0 + && !$this->isTyped(self::VALUE_MIXED, $knownType) + && !$this->isTyped($type, $knownType)) { + $this->logger->warning('conflict with value type from database', ['app' => $app, 'key' => $key, 'type' => $type, 'knownType' => $knownType]); + throw new AppConfigTypeConflictException('conflict with value type from database'); + } + + /** + * - the pair $app/$key cannot exist in both array, + * - we should still return an existing non-lazy value even if current method + * is called with $lazy is true + * + * This way, lazyCache will be empty until the load for lazy config value is requested. + */ + if (isset($this->lazyCache[$app][$key])) { + $value = $this->lazyCache[$app][$key]; + } elseif (isset($this->fastCache[$app][$key])) { + $value = $this->fastCache[$app][$key]; + } else { + return $default; + } + + $sensitive = $this->isTyped(self::VALUE_SENSITIVE, $knownType); + if ($sensitive && str_starts_with($value, self::ENCRYPTION_PREFIX)) { + // Only decrypt values that are stored encrypted + $value = $this->crypto->decrypt(substr($value, self::ENCRYPTION_PREFIX_LENGTH)); + } + + return $value; } /** - * check if a key is set in the appconfig + * @inheritDoc * - * @param string $app - * @param string $key - * @return bool + * @param string $app id of the app + * @param string $key config key + * + * @return int type of the value + * @throws AppConfigUnknownKeyException if config key is not known + * @since 29.0.0 + * @see VALUE_STRING + * @see VALUE_INT + * @see VALUE_FLOAT + * @see VALUE_BOOL + * @see VALUE_ARRAY + */ + public function getValueType(string $app, string $key, ?bool $lazy = null): int { + $this->assertParams($app, $key); + $this->loadConfig($lazy); + + if (!isset($this->valueTypes[$app][$key])) { + throw new AppConfigUnknownKeyException('unknown config key'); + } + + $type = $this->valueTypes[$app][$key]; + $type &= ~self::VALUE_SENSITIVE; + return $type; + } + + + /** + * Store a config key and its value in database as VALUE_MIXED + * + * **WARNING:** Method is internal and **MUST** not be used as it is best to set a real value type + * + * @param string $app id of the app + * @param string $key config key + * @param string $value config value + * @param bool $lazy set config as lazy loaded + * @param bool $sensitive if TRUE value will be hidden when listing config values. + * + * @return bool TRUE if value was different, therefor updated in database + * @throws AppConfigTypeConflictException if type from database is not VALUE_MIXED + * @internal + * @since 29.0.0 + * @see IAppConfig for explanation about lazy loading + * @see setValueString() + * @see setValueInt() + * @see setValueFloat() + * @see setValueBool() + * @see setValueArray() */ - public function hasKey($app, $key) { - $this->loadConfigValues(); + public function setValueMixed( + string $app, + string $key, + string $value, + bool $lazy = false, + bool $sensitive = false + ): bool { + return $this->setTypedValue( + $app, + $key, + $value, + $lazy, + self::VALUE_MIXED | ($sensitive ? self::VALUE_SENSITIVE : 0) + ); + } - return isset($this->cache[$app][$key]); + + /** + * @inheritDoc + * + * @param string $app id of the app + * @param string $key config key + * @param string $value config value + * @param bool $lazy set config as lazy loaded + * @param bool $sensitive if TRUE value will be hidden when listing config values. + * + * @return bool TRUE if value was different, therefor updated in database + * @throws AppConfigTypeConflictException if type from database is not VALUE_MIXED and different from the requested one + * @since 29.0.0 + * @see IAppConfig for explanation about lazy loading + */ + public function setValueString( + string $app, + string $key, + string $value, + bool $lazy = false, + bool $sensitive = false + ): bool { + return $this->setTypedValue( + $app, + $key, + $value, + $lazy, + self::VALUE_STRING | ($sensitive ? self::VALUE_SENSITIVE : 0) + ); } /** - * Sets a value. If the key did not exist before it will be created. + * @inheritDoc * - * @param string $app app - * @param string $key key - * @param string|float|int $value value - * @return bool True if the value was inserted or updated, false if the value was the same + * @param string $app id of the app + * @param string $key config key + * @param int $value config value + * @param bool $lazy set config as lazy loaded + * @param bool $sensitive if TRUE value will be hidden when listing config values. + * + * @return bool TRUE if value was different, therefor updated in database + * @throws AppConfigTypeConflictException if type from database is not VALUE_MIXED and different from the requested one + * @since 29.0.0 + * @see IAppConfig for explanation about lazy loading */ - public function setValue($app, $key, $value) { - if (!$this->hasKey($app, $key)) { - $inserted = (bool) $this->conn->insertIfNotExist('*PREFIX*appconfig', [ - 'appid' => $app, - 'configkey' => $key, - 'configvalue' => $value, - ], [ - 'appid', - 'configkey', - ]); - - if ($inserted) { - if (!isset($this->cache[$app])) { - $this->cache[$app] = []; + public function setValueInt( + string $app, + string $key, + int $value, + bool $lazy = false, + bool $sensitive = false + ): bool { + if ($value > 2000000000) { + $this->logger->debug('You are trying to store an integer value around/above 2,147,483,647. This is a reminder that reaching this theoretical limit on 32 bits system will throw an exception.'); + } + + return $this->setTypedValue( + $app, + $key, + (string)$value, + $lazy, + self::VALUE_INT | ($sensitive ? self::VALUE_SENSITIVE : 0) + ); + } + + /** + * @inheritDoc + * + * @param string $app id of the app + * @param string $key config key + * @param float $value config value + * @param bool $lazy set config as lazy loaded + * @param bool $sensitive if TRUE value will be hidden when listing config values. + * + * @return bool TRUE if value was different, therefor updated in database + * @throws AppConfigTypeConflictException if type from database is not VALUE_MIXED and different from the requested one + * @since 29.0.0 + * @see IAppConfig for explanation about lazy loading + */ + public function setValueFloat( + string $app, + string $key, + float $value, + bool $lazy = false, + bool $sensitive = false + ): bool { + return $this->setTypedValue( + $app, + $key, + (string)$value, + $lazy, + self::VALUE_FLOAT | ($sensitive ? self::VALUE_SENSITIVE : 0) + ); + } + + /** + * @inheritDoc + * + * @param string $app id of the app + * @param string $key config key + * @param bool $value config value + * @param bool $lazy set config as lazy loaded + * + * @return bool TRUE if value was different, therefor updated in database + * @throws AppConfigTypeConflictException if type from database is not VALUE_MIXED and different from the requested one + * @since 29.0.0 + * @see IAppConfig for explanation about lazy loading + */ + public function setValueBool( + string $app, + string $key, + bool $value, + bool $lazy = false + ): bool { + return $this->setTypedValue( + $app, + $key, + ($value) ? '1' : '0', + $lazy, + self::VALUE_BOOL + ); + } + + /** + * @inheritDoc + * + * @param string $app id of the app + * @param string $key config key + * @param array $value config value + * @param bool $lazy set config as lazy loaded + * @param bool $sensitive if TRUE value will be hidden when listing config values. + * + * @return bool TRUE if value was different, therefor updated in database + * @throws AppConfigTypeConflictException if type from database is not VALUE_MIXED and different from the requested one + * @throws JsonException + * @since 29.0.0 + * @see IAppConfig for explanation about lazy loading + */ + public function setValueArray( + string $app, + string $key, + array $value, + bool $lazy = false, + bool $sensitive = false + ): bool { + try { + return $this->setTypedValue( + $app, + $key, + json_encode($value, JSON_THROW_ON_ERROR), + $lazy, + self::VALUE_ARRAY | ($sensitive ? self::VALUE_SENSITIVE : 0) + ); + } catch (JsonException $e) { + $this->logger->warning('could not setValueArray', ['app' => $app, 'key' => $key, 'exception' => $e]); + throw $e; + } + } + + /** + * Store a config key and its value in database + * + * If config key is already known with the exact same config value and same sensitive/lazy status, the + * database is not updated. If config value was previously stored as sensitive, status will not be + * altered. + * + * @param string $app id of the app + * @param string $key config key + * @param string $value config value + * @param bool $lazy config set as lazy loaded + * @param int $type value type {@see VALUE_STRING} {@see VALUE_INT} {@see VALUE_FLOAT} {@see VALUE_BOOL} {@see VALUE_ARRAY} + * + * @return bool TRUE if value was updated in database + * @throws AppConfigTypeConflictException if type from database is not VALUE_MIXED and different from the requested one + * @see IAppConfig for explanation about lazy loading + */ + private function setTypedValue( + string $app, + string $key, + string $value, + bool $lazy, + int $type + ): bool { + $this->assertParams($app, $key); + $this->loadConfig($lazy); + + $sensitive = $this->isTyped(self::VALUE_SENSITIVE, $type); + $inserted = $refreshCache = false; + + $origValue = $value; + if ($sensitive || ($this->hasKey($app, $key, $lazy) && $this->isSensitive($app, $key, $lazy))) { + $value = self::ENCRYPTION_PREFIX . $this->crypto->encrypt($value); + } + + if ($this->hasKey($app, $key, $lazy)) { + /** + * no update if key is already known with set lazy status and value is + * not different, unless sensitivity is switched from false to true. + */ + if ($origValue === $this->getTypedValue($app, $key, $value, $lazy, $type) + && (!$sensitive || $this->isSensitive($app, $key, $lazy))) { + return false; + } + } else { + /** + * if key is not known yet, we try to insert. + * It might fail if the key exists with a different lazy flag. + */ + try { + $insert = $this->connection->getQueryBuilder(); + $insert->insert('appconfig') + ->setValue('appid', $insert->createNamedParameter($app)) + ->setValue('lazy', $insert->createNamedParameter(($lazy) ? 1 : 0, IQueryBuilder::PARAM_INT)) + ->setValue('type', $insert->createNamedParameter($type, IQueryBuilder::PARAM_INT)) + ->setValue('configkey', $insert->createNamedParameter($key)) + ->setValue('configvalue', $insert->createNamedParameter($value)); + $insert->executeStatement(); + $inserted = true; + } catch (DBException $e) { + if ($e->getReason() !== DBException::REASON_UNIQUE_CONSTRAINT_VIOLATION) { + throw $e; // TODO: throw exception or just log and returns false !? + } + } + } + + /** + * We cannot insert a new row, meaning we need to update an already existing one + */ + if (!$inserted) { + $currType = $this->valueTypes[$app][$key] ?? 0; + if ($currType === 0) { // this might happen when switching lazy loading status + $this->loadConfigAll(); + $currType = $this->valueTypes[$app][$key] ?? 0; + } + + /** + * This should only happen during the upgrade process from 28 to 29. + * We only log a warning and set it to VALUE_MIXED. + */ + if ($currType === 0) { + $this->logger->warning('Value type is set to zero (0) in database. This is fine only during the upgrade process from 28 to 29.', ['app' => $app, 'key' => $key]); + $currType = self::VALUE_MIXED; + } + + /** + * we only accept a different type from the one stored in database + * if the one stored in database is not-defined (VALUE_MIXED) + */ + if (!$this->isTyped(self::VALUE_MIXED, $currType) && + ($type | self::VALUE_SENSITIVE) !== ($currType | self::VALUE_SENSITIVE)) { + try { + $currType = $this->convertTypeToString($currType); + $type = $this->convertTypeToString($type); + } catch (AppConfigIncorrectTypeException) { + // can be ignored, this was just needed for a better exception message. } + throw new AppConfigTypeConflictException('conflict between new type (' . $type . ') and old type (' . $currType . ')'); + } + + // we fix $type if the stored value, or the new value as it might be changed, is set as sensitive + if ($sensitive || $this->isTyped(self::VALUE_SENSITIVE, $currType)) { + $type |= self::VALUE_SENSITIVE; + } + + if ($lazy !== $this->isLazy($app, $key)) { + $refreshCache = true; + } + + $update = $this->connection->getQueryBuilder(); + $update->update('appconfig') + ->set('configvalue', $update->createNamedParameter($value)) + ->set('lazy', $update->createNamedParameter(($lazy) ? 1 : 0, IQueryBuilder::PARAM_INT)) + ->set('type', $update->createNamedParameter($type, IQueryBuilder::PARAM_INT)) + ->where($update->expr()->eq('appid', $update->createNamedParameter($app))) + ->andWhere($update->expr()->eq('configkey', $update->createNamedParameter($key))); + + $update->executeStatement(); + } + + if ($refreshCache) { + $this->clearCache(); + return true; + } + + // update local cache + if ($lazy) { + $this->lazyCache[$app][$key] = $value; + } else { + $this->fastCache[$app][$key] = $value; + } + $this->valueTypes[$app][$key] = $type; - $this->cache[$app][$key] = $value; - return true; + return true; + } + + /** + * Change the type of config value. + * + * **WARNING:** Method is internal and **MUST** not be used as it may break things. + * + * @param string $app id of the app + * @param string $key config key + * @param int $type value type {@see VALUE_STRING} {@see VALUE_INT} {@see VALUE_FLOAT} {@see VALUE_BOOL} {@see VALUE_ARRAY} + * + * @return bool TRUE if database update were necessary + * @throws AppConfigUnknownKeyException if $key is now known in database + * @throws AppConfigIncorrectTypeException if $type is not valid + * @internal + * @since 29.0.0 + */ + public function updateType(string $app, string $key, int $type = self::VALUE_MIXED): bool { + $this->assertParams($app, $key); + $this->loadConfigAll(); + $lazy = $this->isLazy($app, $key); + + // type can only be one type + if (!in_array($type, [self::VALUE_MIXED, self::VALUE_STRING, self::VALUE_INT, self::VALUE_FLOAT, self::VALUE_BOOL, self::VALUE_ARRAY])) { + throw new AppConfigIncorrectTypeException('Unknown value type'); + } + + $currType = $this->valueTypes[$app][$key]; + if (($type | self::VALUE_SENSITIVE) === ($currType | self::VALUE_SENSITIVE)) { + return false; + } + + // we complete with sensitive flag if the stored value is set as sensitive + if ($this->isTyped(self::VALUE_SENSITIVE, $currType)) { + $type = $type | self::VALUE_SENSITIVE; + } + + $update = $this->connection->getQueryBuilder(); + $update->update('appconfig') + ->set('type', $update->createNamedParameter($type, IQueryBuilder::PARAM_INT)) + ->where($update->expr()->eq('appid', $update->createNamedParameter($app))) + ->andWhere($update->expr()->eq('configkey', $update->createNamedParameter($key))); + $update->executeStatement(); + $this->valueTypes[$app][$key] = $type; + + return true; + } + + + /** + * @inheritDoc + * + * @param string $app id of the app + * @param string $key config key + * @param bool $sensitive TRUE to set as sensitive, FALSE to unset + * + * @return bool TRUE if entry was found in database and an update was necessary + * @since 29.0.0 + */ + public function updateSensitive(string $app, string $key, bool $sensitive): bool { + $this->assertParams($app, $key); + $this->loadConfigAll(); + + try { + if ($sensitive === $this->isSensitive($app, $key, null)) { + return false; } + } catch (AppConfigUnknownKeyException $e) { + return false; + } + + $lazy = $this->isLazy($app, $key); + if ($lazy) { + $cache = $this->lazyCache; + } else { + $cache = $this->fastCache; } - $sql = $this->conn->getQueryBuilder(); - $sql->update('appconfig') - ->set('configvalue', $sql->createNamedParameter($value)) - ->where($sql->expr()->eq('appid', $sql->createNamedParameter($app))) - ->andWhere($sql->expr()->eq('configkey', $sql->createNamedParameter($key))); + if (!isset($cache[$app][$key])) { + throw new AppConfigUnknownKeyException('unknown config key'); + } - /* - * Only limit to the existing value for non-Oracle DBs: - * http://docs.oracle.com/cd/E11882_01/server.112/e26088/conditions002.htm#i1033286 - * > Large objects (LOBs) are not supported in comparison conditions. + /** + * type returned by getValueType() is already cleaned from sensitive flag + * we just need to update it based on $sensitive and store it in database */ - if (!($this->conn instanceof OracleConnection)) { - /* - * Only update the value when it is not the same - * Note that NULL requires some special handling. Since comparing - * against null can have special results. - */ + $type = $this->getValueType($app, $key); + $value = $cache[$app][$key]; + if ($sensitive) { + $type |= self::VALUE_SENSITIVE; + $value = self::ENCRYPTION_PREFIX . $this->crypto->encrypt($value); + } else { + $value = $this->crypto->decrypt(substr($value, self::ENCRYPTION_PREFIX_LENGTH)); + } + + $update = $this->connection->getQueryBuilder(); + $update->update('appconfig') + ->set('type', $update->createNamedParameter($type, IQueryBuilder::PARAM_INT)) + ->set('configvalue', $update->createNamedParameter($value)) + ->where($update->expr()->eq('appid', $update->createNamedParameter($app))) + ->andWhere($update->expr()->eq('configkey', $update->createNamedParameter($key))); + $update->executeStatement(); + + $this->valueTypes[$app][$key] = $type; + + return true; + } + + /** + * @inheritDoc + * + * @param string $app id of the app + * @param string $key config key + * @param bool $lazy TRUE to set as lazy loaded, FALSE to unset + * + * @return bool TRUE if entry was found in database and an update was necessary + * @since 29.0.0 + */ + public function updateLazy(string $app, string $key, bool $lazy): bool { + $this->assertParams($app, $key); + $this->loadConfigAll(); + + try { + if ($lazy === $this->isLazy($app, $key)) { + return false; + } + } catch (AppConfigUnknownKeyException $e) { + return false; + } + + $update = $this->connection->getQueryBuilder(); + $update->update('appconfig') + ->set('lazy', $update->createNamedParameter($lazy ? 1 : 0, IQueryBuilder::PARAM_INT)) + ->where($update->expr()->eq('appid', $update->createNamedParameter($app))) + ->andWhere($update->expr()->eq('configkey', $update->createNamedParameter($key))); + $update->executeStatement(); + + // At this point, it is a lot safer to clean cache + $this->clearCache(); + + return true; + } + + /** + * @inheritDoc + * + * @param string $app id of the app + * @param string $key config key + * + * @return array + * @throws AppConfigUnknownKeyException if config key is not known in database + * @since 29.0.0 + */ + public function getDetails(string $app, string $key): array { + $this->assertParams($app, $key); + $this->loadConfigAll(); + $lazy = $this->isLazy($app, $key); + + if ($lazy) { + $cache = $this->lazyCache; + } else { + $cache = $this->fastCache; + } + + $type = $this->getValueType($app, $key); + try { + $typeString = $this->convertTypeToString($type); + } catch (AppConfigIncorrectTypeException $e) { + $this->logger->warning('type stored in database is not correct', ['exception' => $e, 'type' => $type]); + $typeString = (string)$type; + } + + if (!isset($cache[$app][$key])) { + throw new AppConfigUnknownKeyException('unknown config key'); + } + + $value = $cache[$app][$key]; + $sensitive = $this->isSensitive($app, $key, null); + if ($sensitive && str_starts_with($value, self::ENCRYPTION_PREFIX)) { + $value = $this->crypto->decrypt(substr($value, self::ENCRYPTION_PREFIX_LENGTH)); + } + + return [ + 'app' => $app, + 'key' => $key, + 'value' => $value, + 'type' => $type, + 'lazy' => $lazy, + 'typeString' => $typeString, + 'sensitive' => $sensitive + ]; + } + + /** + * @param string $type + * + * @return int + * @throws AppConfigIncorrectTypeException + * @since 29.0.0 + */ + public function convertTypeToInt(string $type): int { + return match (strtolower($type)) { + 'mixed' => IAppConfig::VALUE_MIXED, + 'string' => IAppConfig::VALUE_STRING, + 'integer' => IAppConfig::VALUE_INT, + 'float' => IAppConfig::VALUE_FLOAT, + 'boolean' => IAppConfig::VALUE_BOOL, + 'array' => IAppConfig::VALUE_ARRAY, + default => throw new AppConfigIncorrectTypeException('Unknown type ' . $type) + }; + } + + /** + * @param int $type + * + * @return string + * @throws AppConfigIncorrectTypeException + * @since 29.0.0 + */ + public function convertTypeToString(int $type): string { + $type &= ~self::VALUE_SENSITIVE; + + return match ($type) { + IAppConfig::VALUE_MIXED => 'mixed', + IAppConfig::VALUE_STRING => 'string', + IAppConfig::VALUE_INT => 'integer', + IAppConfig::VALUE_FLOAT => 'float', + IAppConfig::VALUE_BOOL => 'boolean', + IAppConfig::VALUE_ARRAY => 'array', + default => throw new AppConfigIncorrectTypeException('Unknown numeric type ' . $type) + }; + } + + /** + * @inheritDoc + * + * @param string $app id of the app + * @param string $key config key + * + * @since 29.0.0 + */ + public function deleteKey(string $app, string $key): void { + $this->assertParams($app, $key); + $qb = $this->connection->getQueryBuilder(); + $qb->delete('appconfig') + ->where($qb->expr()->eq('appid', $qb->createNamedParameter($app))) + ->andWhere($qb->expr()->eq('configkey', $qb->createNamedParameter($key))); + $qb->executeStatement(); + + unset($this->lazyCache[$app][$key]); + unset($this->fastCache[$app][$key]); + } + + /** + * @inheritDoc + * + * @param string $app id of the app + * + * @since 29.0.0 + */ + public function deleteApp(string $app): void { + $this->assertParams($app); + $qb = $this->connection->getQueryBuilder(); + $qb->delete('appconfig') + ->where($qb->expr()->eq('appid', $qb->createNamedParameter($app))); + $qb->executeStatement(); + + $this->clearCache(); + } + + /** + * @inheritDoc + * + * @param bool $reload set to TRUE to refill cache instantly after clearing it + * + * @since 29.0.0 + */ + public function clearCache(bool $reload = false): void { + $this->lazyLoaded = $this->fastLoaded = false; + $this->lazyCache = $this->fastCache = $this->valueTypes = []; + + if (!$reload) { + return; + } + + $this->loadConfigAll(); + } + + + /** + * For debug purpose. + * Returns the cached data. + * + * @return array + * @since 29.0.0 + * @internal + */ + public function statusCache(): array { + return [ + 'fastLoaded' => $this->fastLoaded, + 'fastCache' => $this->fastCache, + 'lazyLoaded' => $this->lazyLoaded, + 'lazyCache' => $this->lazyCache, + ]; + } - if ($value === null) { - $sql->andWhere( - $sql->expr()->isNotNull('configvalue') - ); + /** + * @param int $needle bitflag to search + * @param int $type known value + * + * @return bool TRUE if bitflag $needle is set in $type + */ + private function isTyped(int $needle, int $type): bool { + return (($needle & $type) !== 0); + } + + /** + * Confirm the string set for app and key fit the database description + * + * @param string $app assert $app fit in database + * @param string $configKey assert config key fit in database + * @param bool $allowEmptyApp $app can be empty string + * @param int $valueType assert value type is only one type + * + * @throws InvalidArgumentException + */ + private function assertParams(string $app = '', string $configKey = '', bool $allowEmptyApp = false, int $valueType = -1): void { + if (!$allowEmptyApp && $app === '') { + throw new InvalidArgumentException('app cannot be an empty string'); + } + if (strlen($app) > self::APP_MAX_LENGTH) { + throw new InvalidArgumentException( + 'Value (' . $app . ') for app is too long (' . self::APP_MAX_LENGTH . ')' + ); + } + if (strlen($configKey) > self::KEY_MAX_LENGTH) { + throw new InvalidArgumentException('Value (' . $configKey . ') for key is too long (' . self::KEY_MAX_LENGTH . ')'); + } + if ($valueType > -1) { + $valueType &= ~self::VALUE_SENSITIVE; + if (!in_array($valueType, [self::VALUE_MIXED, self::VALUE_STRING, self::VALUE_INT, self::VALUE_FLOAT, self::VALUE_BOOL, self::VALUE_ARRAY])) { + throw new InvalidArgumentException('Unknown value type'); + } + } + } + + private function loadConfigAll(): void { + $this->loadConfig(null); + } + + /** + * Load normal config or config set as lazy loaded + * + * @param bool|null $lazy set to TRUE to load config set as lazy loaded, set to NULL to load all config + */ + private function loadConfig(?bool $lazy = false): void { + if ($this->isLoaded($lazy)) { + return; + } + + if (($lazy ?? true) !== false) { // if lazy is null or true, we debug log + $this->logger->debug('The loading of lazy AppConfig values have been requested', ['exception' => new \RuntimeException('ignorable exception')]); + } + + $qb = $this->connection->getQueryBuilder(); + $qb->from('appconfig'); + + /** + * The use of $this->>migrationCompleted is only needed to manage the + * database during the upgrading process to nc29. + */ + if (!$this->migrationCompleted) { + $qb->select('appid', 'configkey', 'configvalue'); + } else { + // we only need value from lazy when loadConfig does not specify it + $qb->select('appid', 'configkey', 'configvalue', 'type'); + + if ($lazy !== null) { + $qb->where($qb->expr()->eq('lazy', $qb->createNamedParameter($lazy ? 1 : 0, IQueryBuilder::PARAM_INT))); } else { - $sql->andWhere( - $sql->expr()->orX( - $sql->expr()->isNull('configvalue'), - $sql->expr()->neq('configvalue', $sql->createNamedParameter($value), IQueryBuilder::PARAM_STR) - ) - ); + $qb->addSelect('lazy'); } } - $changedRow = (bool) $sql->execute(); + try { + $result = $qb->executeQuery(); + } catch (DBException $e) { + /** + * in case of issue with field name, it means that migration is not completed. + * Falling back to a request without select on lazy. + * This whole try/catch and the migrationCompleted variable can be removed in NC30. + */ + if ($e->getReason() !== DBException::REASON_INVALID_FIELD_NAME) { + throw $e; + } + + $this->migrationCompleted = false; + $this->loadConfig($lazy); - $this->cache[$app][$key] = $value; + return; + } - return $changedRow; + $rows = $result->fetchAll(); + foreach ($rows as $row) { + // most of the time, 'lazy' is not in the select because its value is already known + if (($row['lazy'] ?? ($lazy ?? 0) ? 1 : 0) === 1) { + $this->lazyCache[$row['appid']][$row['configkey']] = $row['configvalue'] ?? ''; + } else { + $this->fastCache[$row['appid']][$row['configkey']] = $row['configvalue'] ?? ''; + } + $this->valueTypes[$row['appid']][$row['configkey']] = (int)($row['type'] ?? 0); + } + $result->closeCursor(); + $this->setAsLoaded($lazy); } /** - * Deletes a key + * if $lazy is: + * - false: will returns true if fast config is loaded + * - true : will returns true if lazy config is loaded + * - null : will returns true if both config are loaded * - * @param string $app app - * @param string $key key - * @return boolean + * @param bool $lazy + * + * @return bool */ - public function deleteKey($app, $key) { - $this->loadConfigValues(); + private function isLoaded(?bool $lazy): bool { + if ($lazy === null) { + return $this->lazyLoaded && $this->fastLoaded; + } + + return $lazy ? $this->lazyLoaded : $this->fastLoaded; + } - $sql = $this->conn->getQueryBuilder(); - $sql->delete('appconfig') - ->where($sql->expr()->eq('appid', $sql->createParameter('app'))) - ->andWhere($sql->expr()->eq('configkey', $sql->createParameter('configkey'))) - ->setParameter('app', $app) - ->setParameter('configkey', $key); - $sql->execute(); + /** + * if $lazy is: + * - false: set fast config as loaded + * - true : set lazy config as loaded + * - null : set both config as loaded + * + * @param bool $lazy + */ + private function setAsLoaded(?bool $lazy): void { + if ($lazy === null) { + $this->fastLoaded = true; + $this->lazyLoaded = true; + + return; + } - unset($this->cache[$app][$key]); - return false; + if ($lazy) { + $this->lazyLoaded = true; + } else { + $this->fastLoaded = true; + } } /** - * Remove app from appconfig + * Gets the config value * * @param string $app app - * @return boolean + * @param string $key key + * @param string $default = null, default value if the key does not exist + * + * @return string the value or $default + * @deprecated - use getValue*() * - * Removes all keys in appconfig belonging to the app. + * This function gets a value from the appconfig table. If the key does + * not exist the default value will be returned */ - public function deleteApp($app) { - $this->loadConfigValues(); + public function getValue($app, $key, $default = null) { + $this->loadConfig(); - $sql = $this->conn->getQueryBuilder(); - $sql->delete('appconfig') - ->where($sql->expr()->eq('appid', $sql->createParameter('app'))) - ->setParameter('app', $app); - $sql->execute(); + return $this->fastCache[$app][$key] ?? $default; + } - unset($this->cache[$app]); - return false; + /** + * Sets a value. If the key did not exist before it will be created. + * + * @param string $app app + * @param string $key key + * @param string|float|int $value value + * + * @return bool True if the value was inserted or updated, false if the value was the same + * @throws AppConfigTypeConflictException + * @throws AppConfigUnknownKeyException + * @deprecated + */ + public function setValue($app, $key, $value) { + /** + * TODO: would it be overkill, or decently improve performance, to catch + * call to this method with $key='enabled' and 'hide' config value related + * to $app when the app is disabled (by modifying entry in database: lazy=lazy+2) + * or enabled (lazy=lazy-2) + * + * this solution would remove the loading of config values from disabled app + * unless calling the method {@see loadConfigAll()} + */ + return $this->setTypedValue($app, $key, (string)$value, false, self::VALUE_MIXED); } + /** * get multiple values, either the app or key can be used as wildcard by setting it to false * * @param string|false $app * @param string|false $key + * * @return array|false + * @deprecated 29.0.0 use {@see getAllValues()} */ public function getValues($app, $key) { if (($app !== false) === ($key !== false)) { return false; } - if ($key === false) { - return $this->getAppValues($app); + $key = ($key === false) ? '' : $key; + if (!$app) { + return $this->searchValues($key, false, self::VALUE_MIXED); } else { - $appIds = $this->getApps(); - $values = array_map(function ($appId) use ($key) { - return isset($this->cache[$appId][$key]) ? $this->cache[$appId][$key] : null; - }, $appIds); - $result = array_combine($appIds, $values); - - return array_filter($result); + return $this->getAllValues($app, $key); } } @@ -385,58 +1358,184 @@ class AppConfig implements IAppConfig { * get all values of the app or and filters out sensitive data * * @param string $app + * * @return array + * @deprecated 29.0.0 use {@see getAllValues()} */ public function getFilteredValues($app) { - $values = $this->getValues($app, false); + return $this->getAllValues($app, filtered: true); + } - if (isset($this->sensitiveValues[$app])) { - foreach ($this->sensitiveValues[$app] as $sensitiveKeyExp) { - $sensitiveKeys = preg_grep($sensitiveKeyExp, array_keys($values)); - foreach ($sensitiveKeys as $sensitiveKey) { - $values[$sensitiveKey] = IConfig::SENSITIVE_VALUE; - } + + /** + * **Warning:** avoid default NULL value for $lazy as this will + * load all lazy values from the database + * + * @param string $app + * @param array<string, string> $values ['key' => 'value'] + * @param bool|null $lazy + * + * @return array<string, string|int|float|bool|array> + */ + private function formatAppValues(string $app, array $values, ?bool $lazy = null): array { + foreach($values as $key => $value) { + try { + $type = $this->getValueType($app, $key, $lazy); + } catch (AppConfigUnknownKeyException $e) { + continue; } + + $values[$key] = $this->convertTypedValue($value, $type); } return $values; } /** - * Load all the app config values + * convert string value to the expected type + * + * @param string $value + * @param int $type + * + * @return string|int|float|bool|array */ - protected function loadConfigValues() { - if ($this->configLoaded) { - return; + private function convertTypedValue(string $value, int $type): string|int|float|bool|array { + switch ($type) { + case self::VALUE_INT: + return (int)$value; + case self::VALUE_FLOAT: + return (float)$value; + case self::VALUE_BOOL: + return in_array(strtolower($value), ['1', 'true', 'yes', 'on']); + case self::VALUE_ARRAY: + try { + return json_decode($value, true, flags: JSON_THROW_ON_ERROR); + } catch (JsonException $e) { + // ignoreable + } + break; } + return $value; + } - $this->cache = []; - - $sql = $this->conn->getQueryBuilder(); - $sql->select('*') - ->from('appconfig'); - $result = $sql->execute(); - - // we are going to store the result in memory anyway - $rows = $result->fetchAll(); - foreach ($rows as $row) { - if (!isset($this->cache[$row['appid']])) { - $this->cache[(string)$row['appid']] = []; - } - - $this->cache[(string)$row['appid']][(string)$row['configkey']] = (string)$row['configvalue']; - } - $result->closeCursor(); + /** + * @param string $app + * + * @return string[] + * @deprecated data sensitivity should be set when calling setValue*() + */ + private function getSensitiveKeys(string $app): array { + $sensitiveValues = [ + 'circles' => [ + '/^key_pairs$/', + '/^local_gskey$/', + ], + 'external' => [ + '/^sites$/', + ], + 'integration_discourse' => [ + '/^private_key$/', + '/^public_key$/', + ], + 'integration_dropbox' => [ + '/^client_id$/', + '/^client_secret$/', + ], + 'integration_github' => [ + '/^client_id$/', + '/^client_secret$/', + ], + 'integration_gitlab' => [ + '/^client_id$/', + '/^client_secret$/', + '/^oauth_instance_url$/', + ], + 'integration_google' => [ + '/^client_id$/', + '/^client_secret$/', + ], + 'integration_jira' => [ + '/^client_id$/', + '/^client_secret$/', + '/^forced_instance_url$/', + ], + 'integration_onedrive' => [ + '/^client_id$/', + '/^client_secret$/', + ], + 'integration_openproject' => [ + '/^client_id$/', + '/^client_secret$/', + '/^oauth_instance_url$/', + ], + 'integration_reddit' => [ + '/^client_id$/', + '/^client_secret$/', + ], + 'integration_suitecrm' => [ + '/^client_id$/', + '/^client_secret$/', + '/^oauth_instance_url$/', + ], + 'integration_twitter' => [ + '/^consumer_key$/', + '/^consumer_secret$/', + '/^followed_user$/', + ], + 'integration_zammad' => [ + '/^client_id$/', + '/^client_secret$/', + '/^oauth_instance_url$/', + ], + 'notify_push' => [ + '/^cookie$/', + ], + 'serverinfo' => [ + '/^token$/', + ], + 'spreed' => [ + '/^bridge_bot_password$/', + '/^hosted-signaling-server-(.*)$/', + '/^recording_servers$/', + '/^signaling_servers$/', + '/^signaling_ticket_secret$/', + '/^signaling_token_privkey_(.*)$/', + '/^signaling_token_pubkey_(.*)$/', + '/^sip_bridge_dialin_info$/', + '/^sip_bridge_shared_secret$/', + '/^stun_servers$/', + '/^turn_servers$/', + '/^turn_server_secret$/', + ], + 'support' => [ + '/^last_response$/', + '/^potential_subscription_key$/', + '/^subscription_key$/', + ], + 'theming' => [ + '/^imprintUrl$/', + '/^privacyUrl$/', + '/^slogan$/', + '/^url$/', + ], + 'user_ldap' => [ + '/^(s..)?ldap_agent_password$/', + ], + 'user_saml' => [ + '/^idp-x509cert$/', + ], + ]; - $this->configLoaded = true; + return $sensitiveValues[$app] ?? []; } - /** * Clear all the cached app config values * New cache will be generated next time a config value is retrieved + * + * @deprecated use {@see clearCache()} */ public function clearCachedConfig(): void { - $this->configLoaded = false; + $this->clearCache(); } } diff --git a/lib/private/AppFramework/App.php b/lib/private/AppFramework/App.php index ffd77da888e..9f9fb32dbcb 100644 --- a/lib/private/AppFramework/App.php +++ b/lib/private/AppFramework/App.php @@ -1,49 +1,26 @@ <?php declare(strict_types=1); - /** - * @copyright Copyright (c) 2016, ownCloud, Inc. - * - * @author Bernhard Posselt <dev@bernhard-posselt.com> - * @author Christoph Wurst <christoph@winzerhof-wurst.at> - * @author Joas Schilling <coding@schilljs.com> - * @author Lukas Reschke <lukas@statuscode.ch> - * @author Morris Jobke <hey@morrisjobke.de> - * @author Robin Appelman <robin@icewind.nl> - * @author Roeland Jago Douma <roeland@famdouma.nl> - * @author Thomas Müller <thomas.mueller@tmit.eu> - * - * @license AGPL-3.0 - * - * This code is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License, version 3, - * as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License, version 3, - * along with this program. If not, see <http://www.gnu.org/licenses/> - * + * SPDX-FileCopyrightText: 2016-2024 Nextcloud GmbH and Nextcloud contributors + * SPDX-FileCopyrightText: 2016 ownCloud, Inc. + * SPDX-License-Identifier: AGPL-3.0-only */ namespace OC\AppFramework; use OC\AppFramework\DependencyInjection\DIContainer; use OC\AppFramework\Http\Dispatcher; use OC\AppFramework\Http\Request; -use OCP\App\IAppManager; -use OCP\Profiler\IProfiler; use OC\Profiler\RoutingDataCollector; -use OCP\AppFramework\QueryException; +use OCP\App\IAppManager; use OCP\AppFramework\Http; use OCP\AppFramework\Http\ICallbackResponse; use OCP\AppFramework\Http\IOutput; +use OCP\AppFramework\QueryException; use OCP\Diagnostics\IEventLogger; use OCP\HintException; use OCP\IRequest; +use OCP\Profiler\IProfiler; /** * Entry point for every request in your app. You can consider this as your @@ -116,7 +93,7 @@ class App { * @param array $urlParams list of URL parameters (optional) * @throws HintException */ - public static function main(string $controllerName, string $methodName, DIContainer $container, array $urlParams = null) { + public static function main(string $controllerName, string $methodName, DIContainer $container, ?array $urlParams = null) { /** @var IProfiler $profiler */ $profiler = $container->get(IProfiler::class); $eventLogger = $container->get(IEventLogger::class); @@ -257,7 +234,7 @@ class App { * @param DIContainer $container an instance of a pimple container. */ public static function part(string $controllerName, string $methodName, array $urlParams, - DIContainer $container) { + DIContainer $container) { $container['urlParams'] = $urlParams; $controller = $container[$controllerName]; diff --git a/lib/private/AppFramework/Bootstrap/ARegistration.php b/lib/private/AppFramework/Bootstrap/ARegistration.php index 797d7d2317b..37984667727 100644 --- a/lib/private/AppFramework/Bootstrap/ARegistration.php +++ b/lib/private/AppFramework/Bootstrap/ARegistration.php @@ -3,25 +3,8 @@ declare(strict_types=1); /** - * @copyright 2021 Christoph Wurst <christoph@winzerhof-wurst.at> - * - * @author Christoph Wurst <christoph@winzerhof-wurst.at> - * - * @license GNU AGPL version 3 or any later version - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as - * published by the Free Software Foundation, either version 3 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - * + * SPDX-FileCopyrightText: 2021 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later */ namespace OC\AppFramework\Bootstrap; diff --git a/lib/private/AppFramework/Bootstrap/BootContext.php b/lib/private/AppFramework/Bootstrap/BootContext.php index 8c37d00a419..b3da08adb07 100644 --- a/lib/private/AppFramework/Bootstrap/BootContext.php +++ b/lib/private/AppFramework/Bootstrap/BootContext.php @@ -3,26 +3,8 @@ declare(strict_types=1); /** - * @copyright 2020 Christoph Wurst <christoph@winzerhof-wurst.at> - * - * @author Christoph Wurst <christoph@winzerhof-wurst.at> - * @author Morris Jobke <hey@morrisjobke.de> - * - * @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/>. - * + * SPDX-FileCopyrightText: 2020 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later */ namespace OC\AppFramework\Bootstrap; diff --git a/lib/private/AppFramework/Bootstrap/Coordinator.php b/lib/private/AppFramework/Bootstrap/Coordinator.php index f41b734a25b..4e78450fa04 100644 --- a/lib/private/AppFramework/Bootstrap/Coordinator.php +++ b/lib/private/AppFramework/Bootstrap/Coordinator.php @@ -3,67 +3,30 @@ declare(strict_types=1); /** - * @copyright 2020 Christoph Wurst <christoph@winzerhof-wurst.at> - * - * @author Christoph Wurst <christoph@winzerhof-wurst.at> - * @author Joas Schilling <coding@schilljs.com> - * @author Julius Härtl <jus@bitgrid.net> - * @author Morris Jobke <hey@morrisjobke.de> - * @author Robin Appelman <robin@icewind.nl> - * - * @license GNU AGPL version 3 or any later version - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as - * published by the Free Software Foundation, either version 3 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - * + * SPDX-FileCopyrightText: 2020 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later */ namespace OC\AppFramework\Bootstrap; -use OCP\Diagnostics\IEventLogger; -use function class_exists; -use function class_implements; -use function in_array; -use OC_App; use OC\Support\CrashReport\Registry; +use OC_App; +use OCP\App\AppPathNotFoundException; +use OCP\App\IAppManager; use OCP\AppFramework\App; use OCP\AppFramework\Bootstrap\IBootstrap; use OCP\AppFramework\QueryException; use OCP\Dashboard\IManager; +use OCP\Diagnostics\IEventLogger; use OCP\EventDispatcher\IEventDispatcher; use OCP\IServerContainer; use Psr\Log\LoggerInterface; use Throwable; +use function class_exists; +use function class_implements; +use function in_array; class Coordinator { - /** @var IServerContainer */ - private $serverContainer; - - /** @var Registry */ - private $registry; - - /** @var IManager */ - private $dashboardManager; - - /** @var IEventDispatcher */ - private $eventDispatcher; - - /** @var IEventLogger */ - private $eventLogger; - - /** @var LoggerInterface */ - private $logger; - /** @var RegistrationContext|null */ private $registrationContext; @@ -71,19 +34,14 @@ class Coordinator { private $bootedApps = []; public function __construct( - IServerContainer $container, - Registry $registry, - IManager $dashboardManager, - IEventDispatcher $eventListener, - IEventLogger $eventLogger, - LoggerInterface $logger + private IServerContainer $serverContainer, + private Registry $registry, + private IManager $dashboardManager, + private IEventDispatcher $eventDispatcher, + private IEventLogger $eventLogger, + private IAppManager $appManager, + private LoggerInterface $logger, ) { - $this->serverContainer = $container; - $this->registry = $registry; - $this->dashboardManager = $dashboardManager; - $this->eventDispatcher = $eventListener; - $this->eventLogger = $eventLogger; - $this->logger = $logger; } public function runInitialRegistration(): void { @@ -108,11 +66,10 @@ class Coordinator { $this->eventLogger->start("bootstrap:register_app:$appId:autoloader", "Setup autoloader for $appId"); /* * First, we have to enable the app's autoloader - * - * @todo use $this->appManager->getAppPath($appId) here */ - $path = OC_App::getAppPath($appId); - if ($path === false) { + try { + $path = $this->appManager->getAppPath($appId); + } catch (AppPathNotFoundException) { // Ignore continue; } diff --git a/lib/private/AppFramework/Bootstrap/EventListenerRegistration.php b/lib/private/AppFramework/Bootstrap/EventListenerRegistration.php index 2ad410be26f..92955fc4123 100644 --- a/lib/private/AppFramework/Bootstrap/EventListenerRegistration.php +++ b/lib/private/AppFramework/Bootstrap/EventListenerRegistration.php @@ -3,25 +3,8 @@ declare(strict_types=1); /** - * @copyright 2021 Christoph Wurst <christoph@winzerhof-wurst.at> - * - * @author Christoph Wurst <christoph@winzerhof-wurst.at> - * - * @license GNU AGPL version 3 or any later version - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as - * published by the Free Software Foundation, either version 3 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - * + * SPDX-FileCopyrightText: 2021 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later */ namespace OC\AppFramework\Bootstrap; @@ -37,9 +20,9 @@ class EventListenerRegistration extends ServiceRegistration { private $priority; public function __construct(string $appId, - string $event, - string $service, - int $priority) { + string $event, + string $service, + int $priority) { parent::__construct($appId, $service); $this->event = $event; $this->priority = $priority; diff --git a/lib/private/AppFramework/Bootstrap/FunctionInjector.php b/lib/private/AppFramework/Bootstrap/FunctionInjector.php index 2f95906ab4c..973fc13aa2c 100644 --- a/lib/private/AppFramework/Bootstrap/FunctionInjector.php +++ b/lib/private/AppFramework/Bootstrap/FunctionInjector.php @@ -3,25 +3,8 @@ declare(strict_types=1); /** - * @copyright 2020 Christoph Wurst <christoph@winzerhof-wurst.at> - * - * @author Christoph Wurst <christoph@winzerhof-wurst.at> - * - * @license GNU AGPL version 3 or any later version - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as - * published by the Free Software Foundation, either version 3 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - * + * SPDX-FileCopyrightText: 2020 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later */ namespace OC\AppFramework\Bootstrap; diff --git a/lib/private/AppFramework/Bootstrap/MiddlewareRegistration.php b/lib/private/AppFramework/Bootstrap/MiddlewareRegistration.php index da226b311e0..d2ad6bbf0f6 100644 --- a/lib/private/AppFramework/Bootstrap/MiddlewareRegistration.php +++ b/lib/private/AppFramework/Bootstrap/MiddlewareRegistration.php @@ -2,25 +2,9 @@ declare(strict_types=1); -/* - * @copyright 2023 Christoph Wurst <christoph@winzerhof-wurst.at> - * - * @author 2023 Christoph Wurst <christoph@winzerhof-wurst.at> - * - * @license GNU AGPL version 3 or any later version - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as - * published by the Free Software Foundation, either version 3 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. +/** + * SPDX-FileCopyrightText: 2023 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later */ namespace OC\AppFramework\Bootstrap; diff --git a/lib/private/AppFramework/Bootstrap/ParameterRegistration.php b/lib/private/AppFramework/Bootstrap/ParameterRegistration.php index b501a757abd..cc9a4875e9a 100644 --- a/lib/private/AppFramework/Bootstrap/ParameterRegistration.php +++ b/lib/private/AppFramework/Bootstrap/ParameterRegistration.php @@ -3,25 +3,8 @@ declare(strict_types=1); /** - * @copyright 2021 Christoph Wurst <christoph@winzerhof-wurst.at> - * - * @author Christoph Wurst <christoph@winzerhof-wurst.at> - * - * @license GNU AGPL version 3 or any later version - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as - * published by the Free Software Foundation, either version 3 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - * + * SPDX-FileCopyrightText: 2021 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later */ namespace OC\AppFramework\Bootstrap; @@ -36,8 +19,8 @@ final class ParameterRegistration extends ARegistration { private $value; public function __construct(string $appId, - string $name, - $value) { + string $name, + $value) { parent::__construct($appId); $this->name = $name; $this->value = $value; diff --git a/lib/private/AppFramework/Bootstrap/PreviewProviderRegistration.php b/lib/private/AppFramework/Bootstrap/PreviewProviderRegistration.php index 36c5cae7db3..7ecc4aac7f2 100644 --- a/lib/private/AppFramework/Bootstrap/PreviewProviderRegistration.php +++ b/lib/private/AppFramework/Bootstrap/PreviewProviderRegistration.php @@ -2,25 +2,9 @@ declare(strict_types=1); -/* - * @copyright 2021 Christoph Wurst <christoph@winzerhof-wurst.at> - * - * @author 2021 Christoph Wurst <christoph@winzerhof-wurst.at> - * - * @license GNU AGPL version 3 or any later version - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as - * published by the Free Software Foundation, either version 3 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. +/** + * SPDX-FileCopyrightText: 2021 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later */ namespace OC\AppFramework\Bootstrap; @@ -34,8 +18,8 @@ class PreviewProviderRegistration extends ServiceRegistration { private $mimeTypeRegex; public function __construct(string $appId, - string $service, - string $mimeTypeRegex) { + string $service, + string $mimeTypeRegex) { parent::__construct($appId, $service); $this->mimeTypeRegex = $mimeTypeRegex; } diff --git a/lib/private/AppFramework/Bootstrap/RegistrationContext.php b/lib/private/AppFramework/Bootstrap/RegistrationContext.php index 5aea2a7a744..df03d59ebfa 100644 --- a/lib/private/AppFramework/Bootstrap/RegistrationContext.php +++ b/lib/private/AppFramework/Bootstrap/RegistrationContext.php @@ -3,42 +3,13 @@ declare(strict_types=1); /** - * @copyright 2020 Christoph Wurst <christoph@winzerhof-wurst.at> - * - * @author Christoph Wurst <christoph@winzerhof-wurst.at> - * @author Joas Schilling <coding@schilljs.com> - * @author Julius Härtl <jus@bitgrid.net> - * @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/>. - * + * SPDX-FileCopyrightText: 2020 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later */ namespace OC\AppFramework\Bootstrap; use Closure; -use OCP\Calendar\Resource\IBackend as IResourceBackend; -use OCP\Calendar\Room\IBackend as IRoomBackend; -use OCP\Collaboration\Reference\IReferenceProvider; -use OCP\TextProcessing\IProvider as ITextProcessingProvider; -use OCP\SpeechToText\ISpeechToTextProvider; -use OCP\Talk\ITalkBackend; -use OCP\Translation\ITranslationProvider; -use RuntimeException; -use function array_shift; use OC\Support\CrashReport\Registry; use OCP\AppFramework\App; use OCP\AppFramework\Bootstrap\IRegistrationContext; @@ -46,7 +17,10 @@ use OCP\AppFramework\Middleware; use OCP\AppFramework\Services\InitialStateProvider; use OCP\Authentication\IAlternativeLogin; use OCP\Calendar\ICalendarProvider; +use OCP\Calendar\Resource\IBackend as IResourceBackend; +use OCP\Calendar\Room\IBackend as IRoomBackend; use OCP\Capabilities\ICapability; +use OCP\Collaboration\Reference\IReferenceProvider; use OCP\Dashboard\IManager; use OCP\Dashboard\IWidget; use OCP\EventDispatcher\IEventDispatcher; @@ -55,11 +29,20 @@ use OCP\Http\WellKnown\IHandler; use OCP\Notification\INotifier; use OCP\Profile\ILinkAction; use OCP\Search\IProvider; +use OCP\Settings\IDeclarativeSettingsForm; +use OCP\SetupCheck\ISetupCheck; use OCP\Share\IPublicShareTemplateProvider; +use OCP\SpeechToText\ISpeechToTextProvider; use OCP\Support\CrashReport\IReporter; +use OCP\Talk\ITalkBackend; +use OCP\Teams\ITeamResourceProvider; +use OCP\TextProcessing\IProvider as ITextProcessingProvider; +use OCP\Translation\ITranslationProvider; use OCP\UserMigration\IMigrator as IUserMigrator; use Psr\Log\LoggerInterface; +use RuntimeException; use Throwable; +use function array_shift; class RegistrationContext { /** @var ServiceRegistration<ICapability>[] */ @@ -137,8 +120,8 @@ class RegistrationContext { /** @var ServiceRegistration<IReferenceProvider>[] */ private array $referenceProviders = []; - - + /** @var ServiceRegistration<\OCP\TextToImage\IProvider>[] */ + private $textToImageProviders = []; /** @var ParameterRegistration[] */ private $sensitiveMethods = []; @@ -146,11 +129,25 @@ class RegistrationContext { /** @var ServiceRegistration<IPublicShareTemplateProvider>[] */ private $publicShareTemplateProviders = []; - /** @var LoggerInterface */ - private $logger; + private LoggerInterface $logger; + + /** @var ServiceRegistration<ISetupCheck>[] */ + private array $setupChecks = []; /** @var PreviewProviderRegistration[] */ - private $previewProviders = []; + private array $previewProviders = []; + + /** @var ServiceRegistration<IDeclarativeSettingsForm>[] */ + private array $declarativeSettings = []; + + /** @var ServiceRegistration<ITeamResourceProvider>[] */ + private array $teamResourceProviders = []; + + /** @var ServiceRegistration<\OCP\TaskProcessing\IProvider>[] */ + private array $taskProcessingProviders = []; + + /** @var ServiceRegistration<\OCP\TaskProcessing\ITaskType>[] */ + private array $taskProcessingTaskTypes = []; public function __construct(LoggerInterface $logger) { $this->logger = $logger; @@ -273,6 +270,13 @@ class RegistrationContext { ); } + public function registerTextToImageProvider(string $providerClass): void { + $this->context->registerTextToImageProvider( + $this->appId, + $providerClass + ); + } + public function registerTemplateProvider(string $providerClass): void { $this->context->registerTemplateProvider( $this->appId, @@ -344,6 +348,13 @@ class RegistrationContext { ); } + public function registerTeamResourceProvider(string $class) : void { + $this->context->registerTeamResourceProvider( + $this->appId, + $class + ); + } + public function registerCalendarRoomBackend(string $class): void { $this->context->registerCalendarRoomBackend( $this->appId, @@ -372,6 +383,34 @@ class RegistrationContext { $class ); } + + public function registerSetupCheck(string $setupCheckClass): void { + $this->context->registerSetupCheck( + $this->appId, + $setupCheckClass + ); + } + + public function registerDeclarativeSettings(string $declarativeSettingsClass): void { + $this->context->registerDeclarativeSettings( + $this->appId, + $declarativeSettingsClass + ); + } + + public function registerTaskProcessingProvider(string $taskProcessingProviderClass): void { + $this->context->registerTaskProcessingProvider( + $this->appId, + $taskProcessingProviderClass + ); + } + + public function registerTaskProcessingTaskType(string $taskProcessingTaskTypeClass): void { + $this->context->registerTaskProcessingTaskType( + $this->appId, + $taskProcessingTaskTypeClass + ); + } }; } @@ -383,14 +422,14 @@ class RegistrationContext { } /** - * @psalm-param class-string<IReporter> $capability + * @psalm-param class-string<IReporter> $reporterClass */ public function registerCrashReporter(string $appId, string $reporterClass): void { $this->crashReporters[] = new ServiceRegistration($appId, $reporterClass); } /** - * @psalm-param class-string<IWidget> $capability + * @psalm-param class-string<IWidget> $panelClass */ public function registerDashboardPanel(string $appId, string $panelClass): void { $this->dashboardPanels[] = new ServiceRegistration($appId, $panelClass); @@ -443,6 +482,10 @@ class RegistrationContext { $this->textProcessingProviders[] = new ServiceRegistration($appId, $class); } + public function registerTextToImageProvider(string $appId, string $class): void { + $this->textToImageProviders[] = new ServiceRegistration($appId, $class); + } + public function registerTemplateProvider(string $appId, string $class): void { $this->templateProviders[] = new ServiceRegistration($appId, $class); } @@ -508,6 +551,16 @@ class RegistrationContext { } /** + * @psalm-param class-string<ITeamResourceProvider> $class + */ + public function registerTeamResourceProvider(string $appId, string $class) { + $this->teamResourceProviders[] = new ServiceRegistration( + $appId, + $class + ); + } + + /** * @psalm-param class-string<IUserMigrator> $migratorClass */ public function registerUserMigrator(string $appId, string $migratorClass): void { @@ -524,6 +577,34 @@ class RegistrationContext { } /** + * @psalm-param class-string<ISetupCheck> $setupCheckClass + */ + public function registerSetupCheck(string $appId, string $setupCheckClass): void { + $this->setupChecks[] = new ServiceRegistration($appId, $setupCheckClass); + } + + /** + * @psalm-param class-string<IDeclarativeSettingsForm> $declarativeSettingsClass + */ + public function registerDeclarativeSettings(string $appId, string $declarativeSettingsClass): void { + $this->declarativeSettings[] = new ServiceRegistration($appId, $declarativeSettingsClass); + } + + /** + * @psalm-param class-string<\OCP\TaskProcessing\IProvider> $declarativeSettingsClass + */ + public function registerTaskProcessingProvider(string $appId, string $taskProcessingProviderClass): void { + $this->taskProcessingProviders[] = new ServiceRegistration($appId, $taskProcessingProviderClass); + } + + /** + * @psalm-param class-string<\OCP\TaskProcessing\ITaskType> $declarativeSettingsClass + */ + public function registerTaskProcessingTaskType(string $appId, string $taskProcessingTaskTypeClass) { + $this->taskProcessingTaskTypes[] = new ServiceRegistration($appId, $taskProcessingTaskTypeClass); + } + + /** * @param App[] $apps */ public function delegateCapabilityRegistrations(array $apps): void { @@ -565,9 +646,6 @@ class RegistrationContext { } } - /** - * @param App[] $apps - */ public function delegateDashboardPanelRegistrations(IManager $dashboardManager): void { while (($panel = array_shift($this->dashboardPanels)) !== null) { try { @@ -729,6 +807,13 @@ class RegistrationContext { } /** + * @return ServiceRegistration<\OCP\TextToImage\IProvider>[] + */ + public function getTextToImageProviders(): array { + return $this->textToImageProviders; + } + + /** * @return ServiceRegistration<ICustomTemplateProvider>[] */ public function getTemplateProviders(): array { @@ -828,4 +913,39 @@ class RegistrationContext { public function getPublicShareTemplateProviders(): array { return $this->publicShareTemplateProviders; } + + /** + * @return ServiceRegistration<ISetupCheck>[] + */ + public function getSetupChecks(): array { + return $this->setupChecks; + } + + /** + * @return ServiceRegistration<ITeamResourceProvider>[] + */ + public function getTeamResourceProviders(): array { + return $this->teamResourceProviders; + } + + /** + * @return ServiceRegistration<IDeclarativeSettingsForm>[] + */ + public function getDeclarativeSettings(): array { + return $this->declarativeSettings; + } + + /** + * @return ServiceRegistration<\OCP\TaskProcessing\IProvider>[] + */ + public function getTaskProcessingProviders(): array { + return $this->taskProcessingProviders; + } + + /** + * @return ServiceRegistration<\OCP\TaskProcessing\ITaskType>[] + */ + public function getTaskProcessingTaskTypes(): array { + return $this->taskProcessingTaskTypes; + } } diff --git a/lib/private/AppFramework/Bootstrap/ServiceAliasRegistration.php b/lib/private/AppFramework/Bootstrap/ServiceAliasRegistration.php index e2b115e0353..aa3e38e4c46 100644 --- a/lib/private/AppFramework/Bootstrap/ServiceAliasRegistration.php +++ b/lib/private/AppFramework/Bootstrap/ServiceAliasRegistration.php @@ -3,25 +3,8 @@ declare(strict_types=1); /** - * @copyright 2021 Christoph Wurst <christoph@winzerhof-wurst.at> - * - * @author Christoph Wurst <christoph@winzerhof-wurst.at> - * - * @license GNU AGPL version 3 or any later version - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as - * published by the Free Software Foundation, either version 3 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - * + * SPDX-FileCopyrightText: 2021 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later */ namespace OC\AppFramework\Bootstrap; @@ -46,8 +29,8 @@ class ServiceAliasRegistration extends ARegistration { * @paslm-param string|class-string $target */ public function __construct(string $appId, - string $alias, - string $target) { + string $alias, + string $target) { parent::__construct($appId); $this->alias = $alias; $this->target = $target; diff --git a/lib/private/AppFramework/Bootstrap/ServiceFactoryRegistration.php b/lib/private/AppFramework/Bootstrap/ServiceFactoryRegistration.php index b6658e55239..63e73410b5a 100644 --- a/lib/private/AppFramework/Bootstrap/ServiceFactoryRegistration.php +++ b/lib/private/AppFramework/Bootstrap/ServiceFactoryRegistration.php @@ -3,25 +3,8 @@ declare(strict_types=1); /** - * @copyright 2021 Christoph Wurst <christoph@winzerhof-wurst.at> - * - * @author Christoph Wurst <christoph@winzerhof-wurst.at> - * - * @license GNU AGPL version 3 or any later version - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as - * published by the Free Software Foundation, either version 3 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - * + * SPDX-FileCopyrightText: 2021 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later */ namespace OC\AppFramework\Bootstrap; @@ -45,9 +28,9 @@ class ServiceFactoryRegistration extends ARegistration { private $shared; public function __construct(string $appId, - string $alias, - callable $target, - bool $shared) { + string $alias, + callable $target, + bool $shared) { parent::__construct($appId); $this->name = $alias; $this->factory = $target; diff --git a/lib/private/AppFramework/Bootstrap/ServiceRegistration.php b/lib/private/AppFramework/Bootstrap/ServiceRegistration.php index 6c89cdf9d71..6eda5e0196f 100644 --- a/lib/private/AppFramework/Bootstrap/ServiceRegistration.php +++ b/lib/private/AppFramework/Bootstrap/ServiceRegistration.php @@ -3,25 +3,8 @@ declare(strict_types=1); /** - * @copyright 2021 Christoph Wurst <christoph@winzerhof-wurst.at> - * - * @author Christoph Wurst <christoph@winzerhof-wurst.at> - * - * @license GNU AGPL version 3 or any later version - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as - * published by the Free Software Foundation, either version 3 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - * + * SPDX-FileCopyrightText: 2021 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later */ namespace OC\AppFramework\Bootstrap; diff --git a/lib/private/AppFramework/DependencyInjection/DIContainer.php b/lib/private/AppFramework/DependencyInjection/DIContainer.php index a012d1e8ea6..9e3119d262a 100644 --- a/lib/private/AppFramework/DependencyInjection/DIContainer.php +++ b/lib/private/AppFramework/DependencyInjection/DIContainer.php @@ -1,35 +1,8 @@ <?php /** - * @copyright Copyright (c) 2016, ownCloud, Inc. - * - * @author Arthur Schiwon <blizzz@arthur-schiwon.de> - * @author Bernhard Posselt <dev@bernhard-posselt.com> - * @author Bjoern Schiessle <bjoern@schiessle.org> - * @author Christoph Wurst <christoph@winzerhof-wurst.at> - * @author Joas Schilling <coding@schilljs.com> - * @author Jörn Friedrich Dreyer <jfd@butonic.de> - * @author Lukas Reschke <lukas@statuscode.ch> - * @author Morris Jobke <hey@morrisjobke.de> - * @author Robin McCorkell <robin@mccorkell.me.uk> - * @author Roeland Jago Douma <roeland@famdouma.nl> - * @author Sebastian Wessalowski <sebastian@wessalowski.org> - * @author Thomas Müller <thomas.mueller@tmit.eu> - * @author Thomas Tanghus <thomas@tanghus.net> - * - * @license AGPL-3.0 - * - * This code is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License, version 3, - * as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License, version 3, - * along with this program. If not, see <http://www.gnu.org/licenses/> - * + * SPDX-FileCopyrightText: 2016-2024 Nextcloud GmbH and Nextcloud contributors + * SPDX-FileCopyrightText: 2016 ownCloud, Inc. + * SPDX-License-Identifier: AGPL-3.0-only */ namespace OC\AppFramework\DependencyInjection; @@ -96,7 +69,7 @@ class DIContainer extends SimpleContainer implements IAppContainer { * @param array $urlParams * @param ServerContainer|null $server */ - public function __construct(string $appName, array $urlParams = [], ServerContainer $server = null) { + public function __construct(string $appName, array $urlParams = [], ?ServerContainer $server = null) { parent::__construct(); $this->appName = $appName; $this['appName'] = $appName; @@ -173,7 +146,7 @@ class DIContainer extends SimpleContainer implements IAppContainer { }); $this->registerService('OC_Defaults', function (ContainerInterface $c) { - return $c->get(IServerContainer::class)->getThemingDefaults(); + return $c->get(IServerContainer::class)->get('ThemingDefaults'); }); $this->registerService('Protocol', function (ContainerInterface $c) { @@ -302,7 +275,8 @@ class DIContainer extends SimpleContainer implements IAppContainer { $c->get(IRequest::class), $c->get(IUserSession::class), $c->get(IControllerMethodReflector::class), - $c->get(OC\Security\RateLimiting\Limiter::class) + $c->get(OC\Security\RateLimiting\Limiter::class), + $c->get(ISession::class) ) ); $dispatcher->registerMiddleware( @@ -345,6 +319,7 @@ class DIContainer extends SimpleContainer implements IAppContainer { $this->registerService(IAppConfig::class, function (ContainerInterface $c) { return new OC\AppFramework\Services\AppConfig( $c->get(IConfig::class), + $c->get(\OCP\IAppConfig::class), $c->get('AppName') ); }); @@ -404,33 +379,6 @@ class DIContainer extends SimpleContainer implements IAppContainer { } /** - * @deprecated use the ILogger instead - * @param string $message - * @param string $level - * @return mixed - */ - public function log($message, $level) { - switch ($level) { - case 'debug': - $level = ILogger::DEBUG; - break; - case 'info': - $level = ILogger::INFO; - break; - case 'warn': - $level = ILogger::WARN; - break; - case 'fatal': - $level = ILogger::FATAL; - break; - default: - $level = ILogger::ERROR; - break; - } - \OCP\Util::writeLog($this->getAppName(), $message, $level); - } - - /** * Register a capability * * @param string $serviceName e.g. 'OCA\Files\Capabilities' diff --git a/lib/private/AppFramework/Http.php b/lib/private/AppFramework/Http.php index 251c231fe65..3c3e19692bf 100644 --- a/lib/private/AppFramework/Http.php +++ b/lib/private/AppFramework/Http.php @@ -1,31 +1,9 @@ <?php + /** - * @copyright Copyright (c) 2016, ownCloud, Inc. - * - * @author Christoph Wurst <christoph@winzerhof-wurst.at> - * @author Jörn Friedrich Dreyer <jfd@butonic.de> - * @author Julius Härtl <jus@bitgrid.net> - * @author Lukas Reschke <lukas@statuscode.ch> - * @author Morris Jobke <hey@morrisjobke.de> - * @author Robin McCorkell <robin@mccorkell.me.uk> - * @author Roeland Jago Douma <roeland@famdouma.nl> - * @author Thomas Müller <thomas.mueller@tmit.eu> - * @author Thomas Tanghus <thomas@tanghus.net> - * - * @license AGPL-3.0 - * - * This code is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License, version 3, - * as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License, version 3, - * along with this program. If not, see <http://www.gnu.org/licenses/> - * + * SPDX-FileCopyrightText: 2016-2024 Nextcloud GmbH and Nextcloud contributors + * SPDX-FileCopyrightText: 2016 ownCloud, Inc. + * SPDX-License-Identifier: AGPL-3.0-only */ namespace OC\AppFramework; diff --git a/lib/private/AppFramework/Http/Dispatcher.php b/lib/private/AppFramework/Http/Dispatcher.php index 13b391eb287..bbb68972a41 100644 --- a/lib/private/AppFramework/Http/Dispatcher.php +++ b/lib/private/AppFramework/Http/Dispatcher.php @@ -1,34 +1,10 @@ <?php declare(strict_types=1); - /** - * @copyright Copyright (c) 2016, ownCloud, Inc. - * - * @author Bernhard Posselt <dev@bernhard-posselt.com> - * @author Christoph Wurst <christoph@winzerhof-wurst.at> - * @author Joas Schilling <coding@schilljs.com> - * @author Julius Härtl <jus@bitgrid.net> - * @author Lukas Reschke <lukas@statuscode.ch> - * @author Morris Jobke <hey@morrisjobke.de> - * @author Roeland Jago Douma <roeland@famdouma.nl> - * @author Thomas Müller <thomas.mueller@tmit.eu> - * @author Thomas Tanghus <thomas@tanghus.net> - * - * @license AGPL-3.0 - * - * This code is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License, version 3, - * as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License, version 3, - * along with this program. If not, see <http://www.gnu.org/licenses/> - * + * SPDX-FileCopyrightText: 2016-2024 Nextcloud GmbH and Nextcloud contributors + * SPDX-FileCopyrightText: 2016 ownCloud, Inc. + * SPDX-License-Identifier: AGPL-3.0-only */ namespace OC\AppFramework\Http; @@ -38,6 +14,7 @@ use OC\AppFramework\Utility\ControllerMethodReflector; use OC\DB\ConnectionAdapter; use OCP\AppFramework\Controller; use OCP\AppFramework\Http\DataResponse; +use OCP\AppFramework\Http\ParameterOutOfRangeException; use OCP\AppFramework\Http\Response; use OCP\Diagnostics\IEventLogger; use OCP\IConfig; @@ -88,14 +65,14 @@ class Dispatcher { * @param IEventLogger $eventLogger */ public function __construct(Http $protocol, - MiddlewareDispatcher $middlewareDispatcher, - ControllerMethodReflector $reflector, - IRequest $request, - IConfig $config, - ConnectionAdapter $connection, - LoggerInterface $logger, - IEventLogger $eventLogger, - ContainerInterface $appContainer) { + MiddlewareDispatcher $middlewareDispatcher, + ControllerMethodReflector $reflector, + IRequest $request, + IConfig $config, + ConnectionAdapter $connection, + LoggerInterface $logger, + IEventLogger $eventLogger, + ContainerInterface $appContainer) { $this->protocol = $protocol; $this->middlewareDispatcher = $middlewareDispatcher; $this->reflector = $reflector; @@ -197,7 +174,7 @@ class Dispatcher { private function executeController(Controller $controller, string $methodName): Response { $arguments = []; - // valid types that will be casted + // valid types that will be cast $types = ['int', 'integer', 'bool', 'boolean', 'float', 'double']; foreach ($this->reflector->getParameters() as $param => $default) { @@ -219,6 +196,7 @@ class Dispatcher { $value = false; } elseif ($value !== null && \in_array($type, $types, true)) { settype($value, $type); + $this->ensureParameterValueSatisfiesRange($param, $value); } elseif ($value === null && $type !== null && $this->appContainer->has($type)) { $value = $this->appContainer->get($type); } @@ -250,4 +228,22 @@ class Dispatcher { return $response; } + + /** + * @psalm-param mixed $value + * @throws ParameterOutOfRangeException + */ + private function ensureParameterValueSatisfiesRange(string $param, $value): void { + $rangeInfo = $this->reflector->getRange($param); + if ($rangeInfo) { + if ($value < $rangeInfo['min'] || $value > $rangeInfo['max']) { + throw new ParameterOutOfRangeException( + $param, + $value, + $rangeInfo['min'], + $rangeInfo['max'], + ); + } + } + } } diff --git a/lib/private/AppFramework/Http/Output.php b/lib/private/AppFramework/Http/Output.php index 963e01456e0..ff0ef1c15ca 100644 --- a/lib/private/AppFramework/Http/Output.php +++ b/lib/private/AppFramework/Http/Output.php @@ -1,28 +1,9 @@ <?php + /** - * @copyright Copyright (c) 2016, ownCloud, Inc. - * - * @author Bernhard Posselt <dev@bernhard-posselt.com> - * @author Christoph Wurst <christoph@winzerhof-wurst.at> - * @author Lukas Reschke <lukas@statuscode.ch> - * @author Robin Appelman <robin@icewind.nl> - * @author Roeland Jago Douma <roeland@famdouma.nl> - * @author Stefan Weil <sw@weilnetz.de> - * - * @license AGPL-3.0 - * - * This code is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License, version 3, - * as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License, version 3, - * along with this program. If not, see <http://www.gnu.org/licenses/> - * + * SPDX-FileCopyrightText: 2016-2024 Nextcloud GmbH and Nextcloud contributors + * SPDX-FileCopyrightText: 2016 ownCloud, Inc. + * SPDX-License-Identifier: AGPL-3.0-only */ namespace OC\AppFramework\Http; diff --git a/lib/private/AppFramework/Http/Request.php b/lib/private/AppFramework/Http/Request.php index 408e88583a0..0bd430545d4 100644 --- a/lib/private/AppFramework/Http/Request.php +++ b/lib/private/AppFramework/Http/Request.php @@ -1,47 +1,10 @@ <?php declare(strict_types=1); - /** - * @copyright Copyright (c) 2016, ownCloud, Inc. - * - * @author b108@volgograd "b108@volgograd" - * @author Bart Visscher <bartv@thisnet.nl> - * @author Bernhard Posselt <dev@bernhard-posselt.com> - * @author Christoph Wurst <christoph@winzerhof-wurst.at> - * @author Daniel Kesselberg <mail@danielkesselberg.de> - * @author Georg Ehrke <oc.list@georgehrke.com> - * @author J0WI <J0WI@users.noreply.github.com> - * @author Joas Schilling <coding@schilljs.com> - * @author Juan Pablo Villafáñez <jvillafanez@solidgear.es> - * @author Julius Härtl <jus@bitgrid.net> - * @author Lukas Reschke <lukas@statuscode.ch> - * @author Mitar <mitar.git@tnode.com> - * @author Morris Jobke <hey@morrisjobke.de> - * @author Oliver Wegner <void1976@gmail.com> - * @author Robin Appelman <robin@icewind.nl> - * @author Robin McCorkell <robin@mccorkell.me.uk> - * @author Roeland Jago Douma <roeland@famdouma.nl> - * @author Thomas Müller <thomas.mueller@tmit.eu> - * @author Thomas Tanghus <thomas@tanghus.net> - * @author Vincent Petry <vincent@nextcloud.com> - * @author Simon Leiner <simon@leiner.me> - * @author Stanimir Bozhilov <stanimir@audriga.com> - * - * @license AGPL-3.0 - * - * This code is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License, version 3, - * as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License, version 3, - * along with this program. If not, see <http://www.gnu.org/licenses/> - * + * SPDX-FileCopyrightText: 2016-2024 Nextcloud GmbH and Nextcloud contributors + * SPDX-FileCopyrightText: 2016 ownCloud, Inc. + * SPDX-License-Identifier: AGPL-3.0-only */ namespace OC\AppFramework\Http; @@ -57,17 +20,18 @@ use Symfony\Component\HttpFoundation\IpUtils; * Class for accessing variables in the request. * This class provides an immutable object with request variables. * - * @property mixed[] cookies - * @property mixed[] env - * @property mixed[] files - * @property string method - * @property mixed[] parameters - * @property mixed[] server + * @property mixed[] $cookies + * @property mixed[] $env + * @property mixed[] $files + * @property string $method + * @property mixed[] $parameters + * @property mixed[] $server + * @template-implements \ArrayAccess<string,mixed> */ class Request implements \ArrayAccess, \Countable, IRequest { public const USER_AGENT_IE = '/(MSIE)|(Trident)/'; // Microsoft Edge User Agent from https://msdn.microsoft.com/en-us/library/hh869301(v=vs.85).aspx - public const USER_AGENT_MS_EDGE = '/^Mozilla\/5\.0 \([^)]+\) AppleWebKit\/[0-9.]+ \(KHTML, like Gecko\) Chrome\/[0-9.]+ (Mobile Safari|Safari)\/[0-9.]+ Edge\/[0-9.]+$/'; + public const USER_AGENT_MS_EDGE = '/^Mozilla\/5\.0 \([^)]+\) AppleWebKit\/[0-9.]+ \(KHTML, like Gecko\) Chrome\/[0-9.]+ (Mobile Safari|Safari)\/[0-9.]+ Edge?\/[0-9.]+$/'; // Firefox User Agent from https://developer.mozilla.org/en-US/docs/Web/HTTP/Gecko_user_agent_string_reference public const USER_AGENT_FIREFOX = '/^Mozilla\/5\.0 \([^)]+\) Gecko\/[0-9.]+ Firefox\/[0-9.]+$/'; // Chrome User Agent from https://developer.chrome.com/multidevice/user-agent @@ -118,10 +82,10 @@ class Request implements \ArrayAccess, \Countable, IRequest { * @see https://www.php.net/manual/en/reserved.variables.php */ public function __construct(array $vars, - IRequestId $requestId, - IConfig $config, - CsrfTokenManager $csrfTokenManager = null, - string $stream = 'php://input') { + IRequestId $requestId, + IConfig $config, + ?CsrfTokenManager $csrfTokenManager = null, + string $stream = 'php://input') { $this->inputStream = $stream; $this->items['params'] = []; $this->requestId = $requestId; @@ -193,9 +157,7 @@ class Request implements \ArrayAccess, \Countable, IRequest { */ #[\ReturnTypeWillChange] public function offsetGet($offset) { - return isset($this->items['parameters'][$offset]) - ? $this->items['parameters'][$offset] - : null; + return $this->items['parameters'][$offset] ?? null; } /** @@ -255,9 +217,7 @@ class Request implements \ArrayAccess, \Countable, IRequest { case 'cookies': case 'urlParams': case 'method': - return isset($this->items[$name]) - ? $this->items[$name] - : null; + return $this->items[$name] ?? null; case 'parameters': case 'params': if ($this->isPutStreamContent()) { @@ -435,8 +395,8 @@ class Request implements \ArrayAccess, \Countable, IRequest { $this->items['post'] = $params; } } - // Handle application/x-www-form-urlencoded for methods other than GET - // or post correctly + // Handle application/x-www-form-urlencoded for methods other than GET + // or post correctly } elseif ($this->method !== 'GET' && $this->method !== 'POST' && str_contains($this->getHeader('Content-Type'), 'application/x-www-form-urlencoded')) { @@ -577,7 +537,14 @@ class Request implements \ArrayAccess, \Countable, IRequest { * @return boolean true if $remoteAddress matches any entry in $trustedProxies, false otherwise */ protected function isTrustedProxy($trustedProxies, $remoteAddress) { - return IpUtils::checkIp($remoteAddress, $trustedProxies); + try { + return IpUtils::checkIp($remoteAddress, $trustedProxies); + } catch (\Throwable) { + // We can not log to our log here as the logger is using `getRemoteAddress` which uses the function, so we would have a cyclic dependency + // Reaching this line means `trustedProxies` is in invalid format. + error_log('Nextcloud trustedProxies has malformed entries'); + return false; + } } /** @@ -597,14 +564,25 @@ class Request implements \ArrayAccess, \Countable, IRequest { // only have one default, so we cannot ship an insecure product out of the box ]); - foreach ($forwardedForHeaders as $header) { + // Read the x-forwarded-for headers and values in reverse order as per + // https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/X-Forwarded-For#selecting_an_ip_address + foreach (array_reverse($forwardedForHeaders) as $header) { if (isset($this->server[$header])) { - foreach (explode(',', $this->server[$header]) as $IP) { + foreach (array_reverse(explode(',', $this->server[$header])) as $IP) { $IP = trim($IP); + $colons = substr_count($IP, ':'); + if ($colons > 1) { + // Extract IP from string with brackets and optional port + if (preg_match('/^\[(.+?)\](?::\d+)?$/', $IP, $matches) && isset($matches[1])) { + $IP = $matches[1]; + } + } elseif ($colons === 1) { + // IPv4 with port + $IP = substr($IP, 0, strpos($IP, ':')); + } - // remove brackets from IPv6 addresses - if (str_starts_with($IP, '[') && str_ends_with($IP, ']')) { - $IP = substr($IP, 1, -1); + if ($this->isTrustedProxy($trustedProxies, $IP)) { + continue; } if (filter_var($IP, FILTER_VALIDATE_IP) !== false) { @@ -620,14 +598,12 @@ class Request implements \ArrayAccess, \Countable, IRequest { /** * Check overwrite condition - * @param string $type * @return bool */ - private function isOverwriteCondition(string $type = ''): bool { + private function isOverwriteCondition(): bool { $regex = '/' . $this->config->getSystemValueString('overwritecondaddr', '') . '/'; $remoteAddr = isset($this->server['REMOTE_ADDR']) ? $this->server['REMOTE_ADDR'] : ''; - return $regex === '//' || preg_match($regex, $remoteAddr) === 1 - || $type !== 'protocol'; + return $regex === '//' || preg_match($regex, $remoteAddr) === 1; } /** @@ -637,7 +613,7 @@ class Request implements \ArrayAccess, \Countable, IRequest { */ public function getServerProtocol(): string { if ($this->config->getSystemValueString('overwriteprotocol') !== '' - && $this->isOverwriteCondition('protocol')) { + && $this->isOverwriteCondition()) { return $this->config->getSystemValueString('overwriteprotocol'); } diff --git a/lib/private/AppFramework/Http/RequestId.php b/lib/private/AppFramework/Http/RequestId.php index 70032873a75..c3a99c93591 100644 --- a/lib/private/AppFramework/Http/RequestId.php +++ b/lib/private/AppFramework/Http/RequestId.php @@ -2,24 +2,8 @@ declare(strict_types=1); /** - * @copyright Copyright (c) 2022, Joas Schilling <coding@schilljs.com> - * - * @author Joas Schilling <coding@schilljs.com> - * - * @license AGPL-3.0 - * - * This code is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License, version 3, - * as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License, version 3, - * along with this program. If not, see <http://www.gnu.org/licenses/> - * + * SPDX-FileCopyrightText: 2022 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-only */ namespace OC\AppFramework\Http; @@ -31,7 +15,7 @@ class RequestId implements IRequestId { protected string $requestId; public function __construct(string $uniqueId, - ISecureRandom $secureRandom) { + ISecureRandom $secureRandom) { $this->requestId = $uniqueId; $this->secureRandom = $secureRandom; } diff --git a/lib/private/AppFramework/Logger.php b/lib/private/AppFramework/Logger.php index cd70cead7e9..4ae4e6cae34 100644 --- a/lib/private/AppFramework/Logger.php +++ b/lib/private/AppFramework/Logger.php @@ -3,26 +3,8 @@ declare(strict_types=1); /** - * @copyright Copyright (c) 2018, Roeland Jago Douma <roeland@famdouma.nl> - * - * @author Christoph Wurst <christoph@winzerhof-wurst.at> - * @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/>. - * + * SPDX-FileCopyrightText: 2018 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later */ namespace OC\AppFramework; diff --git a/lib/private/AppFramework/Middleware/AdditionalScriptsMiddleware.php b/lib/private/AppFramework/Middleware/AdditionalScriptsMiddleware.php index 64b1c7d2fcd..4f1c69b104f 100644 --- a/lib/private/AppFramework/Middleware/AdditionalScriptsMiddleware.php +++ b/lib/private/AppFramework/Middleware/AdditionalScriptsMiddleware.php @@ -3,27 +3,8 @@ declare(strict_types=1); /** - * @copyright Copyright (c) 2019, Roeland Jago Douma <roeland@famdouma.nl> - * - * @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/>. - * + * SPDX-FileCopyrightText: 2019 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later */ namespace OC\AppFramework\Middleware; diff --git a/lib/private/AppFramework/Middleware/CompressionMiddleware.php b/lib/private/AppFramework/Middleware/CompressionMiddleware.php index b1b89832b00..8bc56beb62e 100644 --- a/lib/private/AppFramework/Middleware/CompressionMiddleware.php +++ b/lib/private/AppFramework/Middleware/CompressionMiddleware.php @@ -3,26 +3,8 @@ declare(strict_types=1); /** - * @copyright Copyright (c) 2020, Roeland Jago Douma <roeland@famdouma.nl> - * - * @author Morris Jobke <hey@morrisjobke.de> - * @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/>. - * + * SPDX-FileCopyrightText: 2020 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later */ namespace OC\AppFramework\Middleware; diff --git a/lib/private/AppFramework/Middleware/MiddlewareDispatcher.php b/lib/private/AppFramework/Middleware/MiddlewareDispatcher.php index 35eb0098eed..2b5acc8b75f 100644 --- a/lib/private/AppFramework/Middleware/MiddlewareDispatcher.php +++ b/lib/private/AppFramework/Middleware/MiddlewareDispatcher.php @@ -1,33 +1,10 @@ <?php declare(strict_types=1); - /** - * @copyright Copyright (c) 2016, ownCloud, Inc. - * - * @author Christoph Wurst <christoph@winzerhof-wurst.at> - * @author Jörn Friedrich Dreyer <jfd@butonic.de> - * @author Lukas Reschke <lukas@statuscode.ch> - * @author Morris Jobke <hey@morrisjobke.de> - * @author Roeland Jago Douma <roeland@famdouma.nl> - * @author Stefan Weil <sw@weilnetz.de> - * @author Thomas Müller <thomas.mueller@tmit.eu> - * @author Thomas Tanghus <thomas@tanghus.net> - * - * @license AGPL-3.0 - * - * This code is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License, version 3, - * as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License, version 3, - * along with this program. If not, see <http://www.gnu.org/licenses/> - * + * SPDX-FileCopyrightText: 2016-2024 Nextcloud GmbH and Nextcloud contributors + * SPDX-FileCopyrightText: 2016 ownCloud, Inc. + * SPDX-License-Identifier: AGPL-3.0-only */ namespace OC\AppFramework\Middleware; @@ -40,15 +17,15 @@ use OCP\AppFramework\Middleware; */ class MiddlewareDispatcher { /** - * @var array array containing all the middlewares + * @var Middleware[] array containing all the middlewares */ - private $middlewares; + private array $middlewares; /** * @var int counter which tells us what middleware was executed once an * exception occurs */ - private $middlewareCounter; + private int $middlewareCounter; /** @@ -64,14 +41,14 @@ class MiddlewareDispatcher { * Adds a new middleware * @param Middleware $middleWare the middleware which will be added */ - public function registerMiddleware(Middleware $middleWare) { + public function registerMiddleware(Middleware $middleWare): void { $this->middlewares[] = $middleWare; } /** * returns an array with all middleware elements - * @return array the middlewares + * @return Middleware[] the middlewares */ public function getMiddlewares(): array { return $this->middlewares; @@ -86,7 +63,7 @@ class MiddlewareDispatcher { * @param string $methodName the name of the method that will be called on * the controller */ - public function beforeController(Controller $controller, string $methodName) { + public function beforeController(Controller $controller, string $methodName): void { // we need to count so that we know which middlewares we have to ask in // case there is an exception $middlewareCount = \count($this->middlewares); diff --git a/lib/private/AppFramework/Middleware/NotModifiedMiddleware.php b/lib/private/AppFramework/Middleware/NotModifiedMiddleware.php index 4ebdf10f403..17b423164f6 100644 --- a/lib/private/AppFramework/Middleware/NotModifiedMiddleware.php +++ b/lib/private/AppFramework/Middleware/NotModifiedMiddleware.php @@ -3,25 +3,8 @@ declare(strict_types=1); /** - * @copyright Copyright (c) 2020, Roeland Jago Douma <roeland@famdouma.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/>. - * + * SPDX-FileCopyrightText: 2020 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later */ namespace OC\AppFramework\Middleware; diff --git a/lib/private/AppFramework/Middleware/OCSMiddleware.php b/lib/private/AppFramework/Middleware/OCSMiddleware.php index 955b15f80c8..46612bf0d29 100644 --- a/lib/private/AppFramework/Middleware/OCSMiddleware.php +++ b/lib/private/AppFramework/Middleware/OCSMiddleware.php @@ -1,27 +1,7 @@ <?php /** - * @copyright Copyright (c) 2016 Roeland Jago Douma <roeland@famdouma.nl> - * - * @author Christoph Wurst <christoph@winzerhof-wurst.at> - * @author Joas Schilling <coding@schilljs.com> - * @author Lukas Reschke <lukas@statuscode.ch> - * @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/>. - * + * SPDX-FileCopyrightText: 2016 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later */ namespace OC\AppFramework\Middleware; diff --git a/lib/private/AppFramework/Middleware/PublicShare/Exceptions/NeedAuthenticationException.php b/lib/private/AppFramework/Middleware/PublicShare/Exceptions/NeedAuthenticationException.php index 8b568f06dd4..c80d06c90ae 100644 --- a/lib/private/AppFramework/Middleware/PublicShare/Exceptions/NeedAuthenticationException.php +++ b/lib/private/AppFramework/Middleware/PublicShare/Exceptions/NeedAuthenticationException.php @@ -1,25 +1,7 @@ <?php /** - * @copyright Copyright (c) 2016 Roeland Jago Douma <roeland@famdouma.nl> - * - * @author Christoph Wurst <christoph@winzerhof-wurst.at> - * @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/>. - * + * SPDX-FileCopyrightText: 2016 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later */ namespace OC\AppFramework\Middleware\PublicShare\Exceptions; diff --git a/lib/private/AppFramework/Middleware/PublicShare/PublicShareMiddleware.php b/lib/private/AppFramework/Middleware/PublicShare/PublicShareMiddleware.php index 7acb579938b..41cba1aacd3 100644 --- a/lib/private/AppFramework/Middleware/PublicShare/PublicShareMiddleware.php +++ b/lib/private/AppFramework/Middleware/PublicShare/PublicShareMiddleware.php @@ -1,25 +1,7 @@ <?php /** - * @copyright Copyright (c) 2016 Roeland Jago Douma <roeland@famdouma.nl> - * - * @author Christoph Wurst <christoph@winzerhof-wurst.at> - * @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/>. - * + * SPDX-FileCopyrightText: 2016 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later */ namespace OC\AppFramework\Middleware\PublicShare; diff --git a/lib/private/AppFramework/Middleware/Security/BruteForceMiddleware.php b/lib/private/AppFramework/Middleware/Security/BruteForceMiddleware.php index 574e86a9ca2..4b4425517e0 100644 --- a/lib/private/AppFramework/Middleware/Security/BruteForceMiddleware.php +++ b/lib/private/AppFramework/Middleware/Security/BruteForceMiddleware.php @@ -3,28 +3,8 @@ declare(strict_types=1); /** - * @copyright Copyright (c) 2023 Joas Schilling <coding@schilljs.com> - * @copyright Copyright (c) 2017 Lukas Reschke <lukas@statuscode.ch> - * - * @author Christoph Wurst <christoph@winzerhof-wurst.at> - * @author Joas Schilling <coding@schilljs.com> - * @author Lukas Reschke <lukas@statuscode.ch> - * - * @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/>. - * + * SPDX-FileCopyrightText: 2017 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later */ namespace OC\AppFramework\Middleware\Security; diff --git a/lib/private/AppFramework/Middleware/Security/CORSMiddleware.php b/lib/private/AppFramework/Middleware/Security/CORSMiddleware.php index 8bdacf550b6..7b617b22e3c 100644 --- a/lib/private/AppFramework/Middleware/Security/CORSMiddleware.php +++ b/lib/private/AppFramework/Middleware/Security/CORSMiddleware.php @@ -1,28 +1,9 @@ <?php + /** - * @copyright Copyright (c) 2016, ownCloud, Inc. - * - * @author Bernhard Posselt <dev@bernhard-posselt.com> - * @author Christoph Wurst <christoph@winzerhof-wurst.at> - * @author korelstar <korelstar@users.noreply.github.com> - * @author Lukas Reschke <lukas@statuscode.ch> - * @author Morris Jobke <hey@morrisjobke.de> - * @author Stefan Weil <sw@weilnetz.de> - * - * @license AGPL-3.0 - * - * This code is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License, version 3, - * as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License, version 3, - * along with this program. If not, see <http://www.gnu.org/licenses/> - * + * SPDX-FileCopyrightText: 2016-2024 Nextcloud GmbH and Nextcloud contributors + * SPDX-FileCopyrightText: 2016 ownCloud, Inc. + * SPDX-License-Identifier: AGPL-3.0-only */ namespace OC\AppFramework\Middleware\Security; @@ -38,6 +19,7 @@ use OCP\AppFramework\Http\JSONResponse; use OCP\AppFramework\Http\Response; use OCP\AppFramework\Middleware; use OCP\IRequest; +use OCP\ISession; use OCP\Security\Bruteforce\IThrottler; use ReflectionMethod; @@ -58,9 +40,9 @@ class CORSMiddleware extends Middleware { private $throttler; public function __construct(IRequest $request, - ControllerMethodReflector $reflector, - Session $session, - IThrottler $throttler) { + ControllerMethodReflector $reflector, + Session $session, + IThrottler $throttler) { $this->request = $request; $this->reflector = $reflector; $this->session = $session; @@ -91,6 +73,10 @@ class CORSMiddleware extends Middleware { if ($this->request->passesCSRFCheck()) { return; } + // Skip CORS check for requests with AppAPI auth. + if ($this->session->getSession() instanceof ISession && $this->session->getSession()->get('app_api') === true) { + return; + } $this->session->logout(); try { if ($user === null || $pass === null || !$this->session->logClientIn($user, $pass, $this->request, $this->throttler)) { diff --git a/lib/private/AppFramework/Middleware/Security/CSPMiddleware.php b/lib/private/AppFramework/Middleware/Security/CSPMiddleware.php index ae0dc1f134e..2115c07c0fc 100644 --- a/lib/private/AppFramework/Middleware/Security/CSPMiddleware.php +++ b/lib/private/AppFramework/Middleware/Security/CSPMiddleware.php @@ -3,26 +3,8 @@ declare(strict_types=1); /** - * @copyright Copyright (c) 2019, Roeland Jago Douma <roeland@famdouma.nl> - * - * @author Christoph Wurst <christoph@winzerhof-wurst.at> - * @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/>. - * + * SPDX-FileCopyrightText: 2019 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later */ namespace OC\AppFramework\Middleware\Security; @@ -44,8 +26,8 @@ class CSPMiddleware extends Middleware { private $csrfTokenManager; public function __construct(ContentSecurityPolicyManager $policyManager, - ContentSecurityPolicyNonceManager $cspNonceManager, - CsrfTokenManager $csrfTokenManager) { + ContentSecurityPolicyNonceManager $cspNonceManager, + CsrfTokenManager $csrfTokenManager) { $this->contentSecurityPolicyManager = $policyManager; $this->cspNonceManager = $cspNonceManager; $this->csrfTokenManager = $csrfTokenManager; diff --git a/lib/private/AppFramework/Middleware/Security/Exceptions/AppNotEnabledException.php b/lib/private/AppFramework/Middleware/Security/Exceptions/AppNotEnabledException.php index 2b9c5a2280b..646a5240bfc 100644 --- a/lib/private/AppFramework/Middleware/Security/Exceptions/AppNotEnabledException.php +++ b/lib/private/AppFramework/Middleware/Security/Exceptions/AppNotEnabledException.php @@ -1,26 +1,8 @@ <?php /** - * @copyright Copyright (c) 2016, ownCloud, Inc. - * - * @author Lukas Reschke <lukas@statuscode.ch> - * @author Morris Jobke <hey@morrisjobke.de> - * @author Roeland Jago Douma <roeland@famdouma.nl> - * @author Thomas Müller <thomas.mueller@tmit.eu> - * - * @license AGPL-3.0 - * - * This code is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License, version 3, - * as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License, version 3, - * along with this program. If not, see <http://www.gnu.org/licenses/> - * + * SPDX-FileCopyrightText: 2016-2024 Nextcloud GmbH and Nextcloud contributors + * SPDX-FileCopyrightText: 2016 ownCloud, Inc. + * SPDX-License-Identifier: AGPL-3.0-only */ namespace OC\AppFramework\Middleware\Security\Exceptions; diff --git a/lib/private/AppFramework/Middleware/Security/Exceptions/CrossSiteRequestForgeryException.php b/lib/private/AppFramework/Middleware/Security/Exceptions/CrossSiteRequestForgeryException.php index b30ebe58abd..0c6a28134ca 100644 --- a/lib/private/AppFramework/Middleware/Security/Exceptions/CrossSiteRequestForgeryException.php +++ b/lib/private/AppFramework/Middleware/Security/Exceptions/CrossSiteRequestForgeryException.php @@ -1,26 +1,9 @@ <?php + /** - * @copyright Copyright (c) 2016, ownCloud, Inc. - * - * @author Lukas Reschke <lukas@statuscode.ch> - * @author Morris Jobke <hey@morrisjobke.de> - * @author Roeland Jago Douma <roeland@famdouma.nl> - * @author Thomas Müller <thomas.mueller@tmit.eu> - * - * @license AGPL-3.0 - * - * This code is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License, version 3, - * as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License, version 3, - * along with this program. If not, see <http://www.gnu.org/licenses/> - * + * SPDX-FileCopyrightText: 2016-2024 Nextcloud GmbH and Nextcloud contributors + * SPDX-FileCopyrightText: 2016 ownCloud, Inc. + * SPDX-License-Identifier: AGPL-3.0-only */ namespace OC\AppFramework\Middleware\Security\Exceptions; diff --git a/lib/private/AppFramework/Middleware/Security/Exceptions/LaxSameSiteCookieFailedException.php b/lib/private/AppFramework/Middleware/Security/Exceptions/LaxSameSiteCookieFailedException.php index bbb8e746127..91f1dba874d 100644 --- a/lib/private/AppFramework/Middleware/Security/Exceptions/LaxSameSiteCookieFailedException.php +++ b/lib/private/AppFramework/Middleware/Security/Exceptions/LaxSameSiteCookieFailedException.php @@ -1,24 +1,7 @@ <?php /** - * @copyright 2017, Roeland Jago Douma <roeland@famdouma.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/>. - * + * SPDX-FileCopyrightText: 2017 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later */ namespace OC\AppFramework\Middleware\Security\Exceptions; diff --git a/lib/private/AppFramework/Middleware/Security/Exceptions/NotAdminException.php b/lib/private/AppFramework/Middleware/Security/Exceptions/NotAdminException.php index 1f1b9d6e501..6252c914ac1 100644 --- a/lib/private/AppFramework/Middleware/Security/Exceptions/NotAdminException.php +++ b/lib/private/AppFramework/Middleware/Security/Exceptions/NotAdminException.php @@ -1,30 +1,10 @@ <?php declare(strict_types=1); - /** - * @copyright Copyright (c) 2016, ownCloud, Inc. - * - * @author Joas Schilling <coding@schilljs.com> - * @author Lukas Reschke <lukas@statuscode.ch> - * @author Morris Jobke <hey@morrisjobke.de> - * @author Roeland Jago Douma <roeland@famdouma.nl> - * @author Thomas Müller <thomas.mueller@tmit.eu> - * - * @license AGPL-3.0 - * - * This code is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License, version 3, - * as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License, version 3, - * along with this program. If not, see <http://www.gnu.org/licenses/> - * + * SPDX-FileCopyrightText: 2016-2024 Nextcloud GmbH and Nextcloud contributors + * SPDX-FileCopyrightText: 2016 ownCloud, Inc. + * SPDX-License-Identifier: AGPL-3.0-only */ namespace OC\AppFramework\Middleware\Security\Exceptions; diff --git a/lib/private/AppFramework/Middleware/Security/Exceptions/NotConfirmedException.php b/lib/private/AppFramework/Middleware/Security/Exceptions/NotConfirmedException.php index b466e9a4c7a..7e950f2c976 100644 --- a/lib/private/AppFramework/Middleware/Security/Exceptions/NotConfirmedException.php +++ b/lib/private/AppFramework/Middleware/Security/Exceptions/NotConfirmedException.php @@ -1,24 +1,7 @@ <?php /** - * @copyright Copyright (c) 2016 Joas Schilling <coding@schilljs.com> - * - * @author Joas Schilling <coding@schilljs.com> - * - * @license GNU AGPL version 3 or any later version - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as - * published by the Free Software Foundation, either version 3 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - * + * SPDX-FileCopyrightText: 2016 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later */ namespace OC\AppFramework\Middleware\Security\Exceptions; diff --git a/lib/private/AppFramework/Middleware/Security/Exceptions/NotLoggedInException.php b/lib/private/AppFramework/Middleware/Security/Exceptions/NotLoggedInException.php index a0287c20435..e5a7853a64b 100644 --- a/lib/private/AppFramework/Middleware/Security/Exceptions/NotLoggedInException.php +++ b/lib/private/AppFramework/Middleware/Security/Exceptions/NotLoggedInException.php @@ -1,26 +1,9 @@ <?php + /** - * @copyright Copyright (c) 2016, ownCloud, Inc. - * - * @author Lukas Reschke <lukas@statuscode.ch> - * @author Morris Jobke <hey@morrisjobke.de> - * @author Roeland Jago Douma <roeland@famdouma.nl> - * @author Thomas Müller <thomas.mueller@tmit.eu> - * - * @license AGPL-3.0 - * - * This code is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License, version 3, - * as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License, version 3, - * along with this program. If not, see <http://www.gnu.org/licenses/> - * + * SPDX-FileCopyrightText: 2016-2024 Nextcloud GmbH and Nextcloud contributors + * SPDX-FileCopyrightText: 2016 ownCloud, Inc. + * SPDX-License-Identifier: AGPL-3.0-only */ namespace OC\AppFramework\Middleware\Security\Exceptions; diff --git a/lib/private/AppFramework/Middleware/Security/Exceptions/ReloadExecutionException.php b/lib/private/AppFramework/Middleware/Security/Exceptions/ReloadExecutionException.php index 3c65d5f5a88..d12ee96292e 100644 --- a/lib/private/AppFramework/Middleware/Security/Exceptions/ReloadExecutionException.php +++ b/lib/private/AppFramework/Middleware/Security/Exceptions/ReloadExecutionException.php @@ -3,25 +3,8 @@ declare(strict_types=1); /** - * @copyright Copyright (c) 2019, Roeland Jago Douma <roeland@famdouma.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/>. - * + * SPDX-FileCopyrightText: 2019 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later */ namespace OC\AppFramework\Middleware\Security\Exceptions; diff --git a/lib/private/AppFramework/Middleware/Security/Exceptions/SecurityException.php b/lib/private/AppFramework/Middleware/Security/Exceptions/SecurityException.php index 3232980b7e5..c8d70ad4f2b 100644 --- a/lib/private/AppFramework/Middleware/Security/Exceptions/SecurityException.php +++ b/lib/private/AppFramework/Middleware/Security/Exceptions/SecurityException.php @@ -1,26 +1,10 @@ <?php + +declare(strict_types=1); /** - * @copyright Copyright (c) 2016, ownCloud, Inc. - * - * @author Christoph Wurst <christoph@winzerhof-wurst.at> - * @author Lukas Reschke <lukas@statuscode.ch> - * @author Morris Jobke <hey@morrisjobke.de> - * @author Thomas Müller <thomas.mueller@tmit.eu> - * - * @license AGPL-3.0 - * - * This code is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License, version 3, - * as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License, version 3, - * along with this program. If not, see <http://www.gnu.org/licenses/> - * + * SPDX-FileCopyrightText: 2016-2024 Nextcloud GmbH and Nextcloud contributors + * SPDX-FileCopyrightText: 2016 ownCloud, Inc. + * SPDX-License-Identifier: AGPL-3.0-only */ namespace OC\AppFramework\Middleware\Security\Exceptions; diff --git a/lib/private/AppFramework/Middleware/Security/Exceptions/StrictCookieMissingException.php b/lib/private/AppFramework/Middleware/Security/Exceptions/StrictCookieMissingException.php index 28092331a22..8ae20ab4e70 100644 --- a/lib/private/AppFramework/Middleware/Security/Exceptions/StrictCookieMissingException.php +++ b/lib/private/AppFramework/Middleware/Security/Exceptions/StrictCookieMissingException.php @@ -3,25 +3,8 @@ declare(strict_types=1); /** - * @copyright Copyright (c) 2016 Lukas Reschke <lukas@statuscode.ch> - * - * @author Lukas Reschke <lukas@statuscode.ch> - * - * @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/>. - * + * SPDX-FileCopyrightText: 2016 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later */ namespace OC\AppFramework\Middleware\Security\Exceptions; diff --git a/lib/private/AppFramework/Middleware/Security/FeaturePolicyMiddleware.php b/lib/private/AppFramework/Middleware/Security/FeaturePolicyMiddleware.php index 418d4185184..921630e6326 100644 --- a/lib/private/AppFramework/Middleware/Security/FeaturePolicyMiddleware.php +++ b/lib/private/AppFramework/Middleware/Security/FeaturePolicyMiddleware.php @@ -3,25 +3,8 @@ declare(strict_types=1); /** - * @copyright Copyright (c) 2019, Roeland Jago Douma <roeland@famdouma.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/>. - * + * SPDX-FileCopyrightText: 2019 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later */ namespace OC\AppFramework\Middleware\Security; diff --git a/lib/private/AppFramework/Middleware/Security/PasswordConfirmationMiddleware.php b/lib/private/AppFramework/Middleware/Security/PasswordConfirmationMiddleware.php index a72a7a40016..b95a69dc3e2 100644 --- a/lib/private/AppFramework/Middleware/Security/PasswordConfirmationMiddleware.php +++ b/lib/private/AppFramework/Middleware/Security/PasswordConfirmationMiddleware.php @@ -1,25 +1,7 @@ <?php /** - * @copyright 2018, Roeland Jago Douma <roeland@famdouma.nl> - * - * @author Bjoern Schiessle <bjoern@schiessle.org> - * @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/>. - * + * SPDX-FileCopyrightText: 2018 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later */ namespace OC\AppFramework\Middleware\Security; @@ -55,9 +37,9 @@ class PasswordConfirmationMiddleware extends Middleware { * @param ITimeFactory $timeFactory */ public function __construct(ControllerMethodReflector $reflector, - ISession $session, - IUserSession $userSession, - ITimeFactory $timeFactory) { + ISession $session, + IUserSession $userSession, + ITimeFactory $timeFactory) { $this->reflector = $reflector; $this->session = $session; $this->userSession = $userSession; diff --git a/lib/private/AppFramework/Middleware/Security/RateLimitingMiddleware.php b/lib/private/AppFramework/Middleware/Security/RateLimitingMiddleware.php index 6f84a0c94d0..d593bf5019f 100644 --- a/lib/private/AppFramework/Middleware/Security/RateLimitingMiddleware.php +++ b/lib/private/AppFramework/Middleware/Security/RateLimitingMiddleware.php @@ -3,28 +3,8 @@ declare(strict_types=1); /** - * @copyright Copyright (c) 2023 Joas Schilling <coding@schilljs.com> - * @copyright Copyright (c) 2017 Lukas Reschke <lukas@statuscode.ch> - * - * @author Christoph Wurst <christoph@winzerhof-wurst.at> - * @author Joas Schilling <coding@schilljs.com> - * @author Lukas Reschke <lukas@statuscode.ch> - * - * @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/>. - * + * SPDX-FileCopyrightText: 2017 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later */ namespace OC\AppFramework\Middleware\Security; @@ -40,6 +20,7 @@ use OCP\AppFramework\Http\Response; use OCP\AppFramework\Http\TemplateResponse; use OCP\AppFramework\Middleware; use OCP\IRequest; +use OCP\ISession; use OCP\IUserSession; use ReflectionMethod; @@ -70,6 +51,7 @@ class RateLimitingMiddleware extends Middleware { protected IUserSession $userSession, protected ControllerMethodReflector $reflector, protected Limiter $limiter, + protected ISession $session, ) { } @@ -81,6 +63,11 @@ class RateLimitingMiddleware extends Middleware { parent::beforeController($controller, $methodName); $rateLimitIdentifier = get_class($controller) . '::' . $methodName; + if ($this->session->exists('app_api_system')) { + // Bypass rate limiting for app_api + return; + } + if ($this->userSession->isLoggedIn()) { $rateLimit = $this->readLimitFromAnnotationOrAttribute($controller, $methodName, 'UserRateThrottle', UserRateLimit::class); diff --git a/lib/private/AppFramework/Middleware/Security/ReloadExecutionMiddleware.php b/lib/private/AppFramework/Middleware/Security/ReloadExecutionMiddleware.php index a6a07538345..e770fa4cbff 100644 --- a/lib/private/AppFramework/Middleware/Security/ReloadExecutionMiddleware.php +++ b/lib/private/AppFramework/Middleware/Security/ReloadExecutionMiddleware.php @@ -3,25 +3,8 @@ declare(strict_types=1); /** - * @copyright Copyright (c) 2019, Roeland Jago Douma <roeland@famdouma.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/>. - * + * SPDX-FileCopyrightText: 2019 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later */ namespace OC\AppFramework\Middleware\Security; diff --git a/lib/private/AppFramework/Middleware/Security/SameSiteCookieMiddleware.php b/lib/private/AppFramework/Middleware/Security/SameSiteCookieMiddleware.php index e6d35dc66f2..e0bb96f132b 100644 --- a/lib/private/AppFramework/Middleware/Security/SameSiteCookieMiddleware.php +++ b/lib/private/AppFramework/Middleware/Security/SameSiteCookieMiddleware.php @@ -1,25 +1,7 @@ <?php /** - * @copyright 2017, Roeland Jago Douma <roeland@famdouma.nl> - * - * @author Christoph Wurst <christoph@winzerhof-wurst.at> - * @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/>. - * + * SPDX-FileCopyrightText: 2017 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later */ namespace OC\AppFramework\Middleware\Security; @@ -38,7 +20,7 @@ class SameSiteCookieMiddleware extends Middleware { private $reflector; public function __construct(Request $request, - ControllerMethodReflector $reflector) { + ControllerMethodReflector $reflector) { $this->request = $request; $this->reflector = $reflector; } diff --git a/lib/private/AppFramework/Middleware/Security/SecurityMiddleware.php b/lib/private/AppFramework/Middleware/Security/SecurityMiddleware.php index db6c7a02c77..a38ad610fc6 100644 --- a/lib/private/AppFramework/Middleware/Security/SecurityMiddleware.php +++ b/lib/private/AppFramework/Middleware/Security/SecurityMiddleware.php @@ -1,40 +1,11 @@ <?php declare(strict_types=1); - /** - * @copyright Copyright (c) 2016, ownCloud, Inc. - * - * @author Bernhard Posselt <dev@bernhard-posselt.com> - * @author Bjoern Schiessle <bjoern@schiessle.org> - * @author Christoph Wurst <christoph@winzerhof-wurst.at> - * @author Daniel Kesselberg <mail@danielkesselberg.de> - * @author Holger Hees <holger.hees@gmail.com> - * @author Joas Schilling <coding@schilljs.com> - * @author Julien Veyssier <eneiluj@posteo.net> - * @author Lukas Reschke <lukas@statuscode.ch> - * @author Morris Jobke <hey@morrisjobke.de> - * @author Roeland Jago Douma <roeland@famdouma.nl> - * @author Stefan Weil <sw@weilnetz.de> - * @author Thomas Müller <thomas.mueller@tmit.eu> - * @author Thomas Tanghus <thomas@tanghus.net> - * - * @license AGPL-3.0 - * - * This code is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License, version 3, - * as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License, version 3, - * along with this program. If not, see <http://www.gnu.org/licenses/> - * + * SPDX-FileCopyrightText: 2016-2024 Nextcloud GmbH and Nextcloud contributors + * SPDX-FileCopyrightText: 2016 ownCloud, Inc. + * SPDX-License-Identifier: AGPL-3.0-only */ - namespace OC\AppFramework\Middleware\Security; use OC\AppFramework\Middleware\Security\Exceptions\AppNotEnabledException; @@ -104,18 +75,18 @@ class SecurityMiddleware extends Middleware { private $userSession; public function __construct(IRequest $request, - ControllerMethodReflector $reflector, - INavigationManager $navigationManager, - IURLGenerator $urlGenerator, - LoggerInterface $logger, - string $appName, - bool $isLoggedIn, - bool $isAdminUser, - bool $isSubAdmin, - IAppManager $appManager, - IL10N $l10n, - AuthorizedGroupMapper $mapper, - IUserSession $userSession + ControllerMethodReflector $reflector, + INavigationManager $navigationManager, + IURLGenerator $urlGenerator, + LoggerInterface $logger, + string $appName, + bool $isLoggedIn, + bool $isAdminUser, + bool $isSubAdmin, + IAppManager $appManager, + IL10N $l10n, + AuthorizedGroupMapper $mapper, + IUserSession $userSession ) { $this->navigationManager = $navigationManager; $this->request = $request; @@ -180,20 +151,20 @@ class SecurityMiddleware extends Middleware { } } if (!$authorized) { - throw new NotAdminException($this->l10n->t('Logged in user must be an admin, a sub admin or gotten special right to access this setting')); + throw new NotAdminException($this->l10n->t('Logged in account must be an admin, a sub admin or gotten special right to access this setting')); } } if ($this->hasAnnotationOrAttribute($reflectionMethod, 'SubAdminRequired', SubAdminRequired::class) && !$this->isSubAdmin && !$this->isAdminUser && !$authorized) { - throw new NotAdminException($this->l10n->t('Logged in user must be an admin or sub admin')); + throw new NotAdminException($this->l10n->t('Logged in account must be an admin or sub admin')); } if (!$this->hasAnnotationOrAttribute($reflectionMethod, 'SubAdminRequired', SubAdminRequired::class) && !$this->hasAnnotationOrAttribute($reflectionMethod, 'NoAdminRequired', NoAdminRequired::class) && !$this->isAdminUser && !$authorized) { - throw new NotAdminException($this->l10n->t('Logged in user must be an admin')); + throw new NotAdminException($this->l10n->t('Logged in account must be an admin')); } } diff --git a/lib/private/AppFramework/Middleware/SessionMiddleware.php b/lib/private/AppFramework/Middleware/SessionMiddleware.php index 39f85915901..b7b0fb118c2 100644 --- a/lib/private/AppFramework/Middleware/SessionMiddleware.php +++ b/lib/private/AppFramework/Middleware/SessionMiddleware.php @@ -1,30 +1,10 @@ <?php declare(strict_types=1); - /** - * @copyright Copyright (c) 2016, ownCloud, Inc. - * - * @author Christoph Wurst <christoph@winzerhof-wurst.at> - * @author Lukas Reschke <lukas@statuscode.ch> - * @author Morris Jobke <hey@morrisjobke.de> - * @author Roeland Jago Douma <roeland@famdouma.nl> - * @author Thomas Müller <thomas.mueller@tmit.eu> - * - * @license AGPL-3.0 - * - * This code is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License, version 3, - * as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License, version 3, - * along with this program. If not, see <http://www.gnu.org/licenses/> - * + * SPDX-FileCopyrightText: 2016-2024 Nextcloud GmbH and Nextcloud contributors + * SPDX-FileCopyrightText: 2016 ownCloud, Inc. + * SPDX-License-Identifier: AGPL-3.0-only */ namespace OC\AppFramework\Middleware; @@ -44,7 +24,7 @@ class SessionMiddleware extends Middleware { private $session; public function __construct(ControllerMethodReflector $reflector, - ISession $session) { + ISession $session) { $this->reflector = $reflector; $this->session = $session; } diff --git a/lib/private/AppFramework/OCS/BaseResponse.php b/lib/private/AppFramework/OCS/BaseResponse.php index 123b73d302c..2e685de856b 100644 --- a/lib/private/AppFramework/OCS/BaseResponse.php +++ b/lib/private/AppFramework/OCS/BaseResponse.php @@ -1,29 +1,7 @@ <?php /** - * @copyright 2016 Roeland Jago Douma <roeland@famdouma.nl> - * - * @author Christoph Wurst <christoph@winzerhof-wurst.at> - * @author Daniel Kesselberg <mail@danielkesselberg.de> - * @author Joas Schilling <coding@schilljs.com> - * @author Lukas Reschke <lukas@statuscode.ch> - * @author Roeland Jago Douma <roeland@famdouma.nl> - * @author Kate Döen <kate.doeen@nextcloud.com> - * - * @license GNU AGPL version 3 or any later version - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as - * published by the Free Software Foundation, either version 3 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - * + * SPDX-FileCopyrightText: 2016 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later */ namespace OC\AppFramework\OCS; @@ -64,10 +42,10 @@ abstract class BaseResponse extends Response { * @param int|null $itemsPerPage */ public function __construct(DataResponse $dataResponse, - $format = 'xml', - $statusMessage = null, - $itemsCount = null, - $itemsPerPage = null) { + $format = 'xml', + $statusMessage = null, + $itemsCount = null, + $itemsPerPage = null) { parent::__construct(); $this->format = $format; @@ -159,6 +137,10 @@ abstract class BaseResponse extends Response { $writer->startElement($k); $this->toXML($v, $writer); $writer->endElement(); + } elseif ($v instanceof \JsonSerializable) { + $writer->startElement($k); + $this->toXML($v->jsonSerialize(), $writer); + $writer->endElement(); } else { $writer->writeElement($k, $v); } diff --git a/lib/private/AppFramework/OCS/V1Response.php b/lib/private/AppFramework/OCS/V1Response.php index e6b01652789..c56aa9cf478 100644 --- a/lib/private/AppFramework/OCS/V1Response.php +++ b/lib/private/AppFramework/OCS/V1Response.php @@ -1,27 +1,7 @@ <?php /** - * @copyright 2016 Roeland Jago Douma <roeland@famdouma.nl> - * - * @author Christoph Wurst <christoph@winzerhof-wurst.at> - * @author Joas Schilling <coding@schilljs.com> - * @author Roeland Jago Douma <roeland@famdouma.nl> - * @author Kate Döen <kate.doeen@nextcloud.com> - * - * @license GNU AGPL version 3 or any later version - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as - * published by the Free Software Foundation, either version 3 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - * + * SPDX-FileCopyrightText: 2016 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later */ namespace OC\AppFramework\OCS; diff --git a/lib/private/AppFramework/OCS/V2Response.php b/lib/private/AppFramework/OCS/V2Response.php index 1e81a3c7d93..caa8302a673 100644 --- a/lib/private/AppFramework/OCS/V2Response.php +++ b/lib/private/AppFramework/OCS/V2Response.php @@ -1,26 +1,7 @@ <?php /** - * @copyright 2016 Roeland Jago Douma <roeland@famdouma.nl> - * - * @author Christoph Wurst <christoph@winzerhof-wurst.at> - * @author Roeland Jago Douma <roeland@famdouma.nl> - * @author Kate Döen <kate.doeen@nextcloud.com> - * - * @license GNU AGPL version 3 or any later version - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as - * published by the Free Software Foundation, either version 3 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - * + * SPDX-FileCopyrightText: 2016 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later */ namespace OC\AppFramework\OCS; diff --git a/lib/private/AppFramework/Routing/RouteActionHandler.php b/lib/private/AppFramework/Routing/RouteActionHandler.php index 4682d2e9e7e..ec38105a5a0 100644 --- a/lib/private/AppFramework/Routing/RouteActionHandler.php +++ b/lib/private/AppFramework/Routing/RouteActionHandler.php @@ -1,26 +1,9 @@ <?php + /** - * @copyright Copyright (c) 2016, ownCloud, Inc. - * - * @author Jörn Friedrich Dreyer <jfd@butonic.de> - * @author Morris Jobke <hey@morrisjobke.de> - * @author Roeland Jago Douma <roeland@famdouma.nl> - * @author Thomas Müller <thomas.mueller@tmit.eu> - * - * @license AGPL-3.0 - * - * This code is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License, version 3, - * as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License, version 3, - * along with this program. If not, see <http://www.gnu.org/licenses/> - * + * SPDX-FileCopyrightText: 2016-2024 Nextcloud GmbH and Nextcloud contributors + * SPDX-FileCopyrightText: 2016 ownCloud, Inc. + * SPDX-License-Identifier: AGPL-3.0-only */ namespace OC\AppFramework\Routing; diff --git a/lib/private/AppFramework/Routing/RouteConfig.php b/lib/private/AppFramework/Routing/RouteConfig.php index 6e3e49e8d99..bdeae93c7c8 100644 --- a/lib/private/AppFramework/Routing/RouteConfig.php +++ b/lib/private/AppFramework/Routing/RouteConfig.php @@ -1,32 +1,10 @@ <?php declare(strict_types=1); - /** - * @copyright Copyright (c) 2016, ownCloud, Inc. - * - * @author Christoph Wurst <christoph@winzerhof-wurst.at> - * @author Joas Schilling <coding@schilljs.com> - * @author Morris Jobke <hey@morrisjobke.de> - * @author Robin Appelman <robin@icewind.nl> - * @author Robin McCorkell <robin@mccorkell.me.uk> - * @author Roeland Jago Douma <roeland@famdouma.nl> - * @author Thomas Müller <thomas.mueller@tmit.eu> - * - * @license AGPL-3.0 - * - * This code is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License, version 3, - * as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License, version 3, - * along with this program. If not, see <http://www.gnu.org/licenses/> - * + * SPDX-FileCopyrightText: 2016-2024 Nextcloud GmbH and Nextcloud contributors + * SPDX-FileCopyrightText: 2016 ownCloud, Inc. + * SPDX-License-Identifier: AGPL-3.0-only */ namespace OC\AppFramework\Routing; @@ -136,7 +114,13 @@ class RouteConfig { $controllerName = $this->buildControllerName($controller); $actionName = $this->buildActionName($action); - $routeName = $routeNamePrefix . $this->appName . '.' . $controller . '.' . $action . $postfix; + /* + * The route name has to be lowercase, for symfony to match it correctly. + * This is required because smyfony allows mixed casing for controller names in the routes. + * To avoid breaking all the existing route names, registering and matching will only use the lowercase names. + * This is also safe on the PHP side because class and method names collide regardless of the casing. + */ + $routeName = strtolower($routeNamePrefix . $this->appName . '.' . $controller . '.' . $action . $postfix); $router = $this->router->create($routeName, $url) ->method($verb); diff --git a/lib/private/AppFramework/Routing/RouteParser.php b/lib/private/AppFramework/Routing/RouteParser.php index 1b3a6c1255a..49a50852bf3 100644 --- a/lib/private/AppFramework/Routing/RouteParser.php +++ b/lib/private/AppFramework/Routing/RouteParser.php @@ -1,26 +1,10 @@ <?php declare(strict_types=1); - /** - * @copyright Copyright (c) 2016, ownCloud, Inc. - * - * @author Roeland Jago Douma <roeland@famdouma.nl> - * - * @license AGPL-3.0 - * - * This code is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License, version 3, - * as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License, version 3, - * along with this program. If not, see <http://www.gnu.org/licenses/> - * + * SPDX-FileCopyrightText: 2016-2024 Nextcloud GmbH and Nextcloud contributors + * SPDX-FileCopyrightText: 2016 ownCloud, Inc. + * SPDX-License-Identifier: AGPL-3.0-only */ namespace OC\AppFramework\Routing; @@ -100,7 +84,13 @@ class RouteParser { $controllerName = $this->buildControllerName($controller); $actionName = $this->buildActionName($action); - $routeName = $routeNamePrefix . $appName . '.' . $controller . '.' . $action . $postfix; + /* + * The route name has to be lowercase, for symfony to match it correctly. + * This is required because smyfony allows mixed casing for controller names in the routes. + * To avoid breaking all the existing route names, registering and matching will only use the lowercase names. + * This is also safe on the PHP side because class and method names collide regardless of the casing. + */ + $routeName = strtolower($routeNamePrefix . $appName . '.' . $controller . '.' . $action . $postfix); $routeObject = new Route($url); $routeObject->method($verb); diff --git a/lib/private/AppFramework/ScopedPsrLogger.php b/lib/private/AppFramework/ScopedPsrLogger.php index 4ed91cdb6c0..2eaabdbedd7 100644 --- a/lib/private/AppFramework/ScopedPsrLogger.php +++ b/lib/private/AppFramework/ScopedPsrLogger.php @@ -3,26 +3,8 @@ declare(strict_types=1); /** - * @copyright 2020 Christoph Wurst <christoph@winzerhof-wurst.at> - * - * @author Christoph Wurst <christoph@winzerhof-wurst.at> - * @author Maxence Lange <maxence@artificial-owl.com> - * - * @license GNU AGPL version 3 or any later version - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as - * published by the Free Software Foundation, either version 3 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - * + * SPDX-FileCopyrightText: 2020 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later */ namespace OC\AppFramework; @@ -37,7 +19,7 @@ class ScopedPsrLogger implements LoggerInterface { private $appId; public function __construct(LoggerInterface $inner, - string $appId) { + string $appId) { $this->inner = $inner; $this->appId = $appId; } diff --git a/lib/private/AppFramework/Services/AppConfig.php b/lib/private/AppFramework/Services/AppConfig.php index 1fc07bc22b0..e47bbc429d0 100644 --- a/lib/private/AppFramework/Services/AppConfig.php +++ b/lib/private/AppFramework/Services/AppConfig.php @@ -3,62 +3,327 @@ declare(strict_types=1); /** - * @copyright Copyright (c) 2020, Roeland Jago Douma <roeland@famdouma.nl> - * - * @author Morris Jobke <hey@morrisjobke.de> - * @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/>. - * + * SPDX-FileCopyrightText: 2020 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later */ namespace OC\AppFramework\Services; +use InvalidArgumentException; +use JsonException; use OCP\AppFramework\Services\IAppConfig; +use OCP\Exceptions\AppConfigTypeConflictException; +use OCP\Exceptions\AppConfigUnknownKeyException; use OCP\IConfig; class AppConfig implements IAppConfig { - /** @var IConfig */ - private $config; + public function __construct( + private IConfig $config, + /** @var \OC\AppConfig */ + private \OCP\IAppConfig $appConfig, + private string $appName, + ) { + } - /** @var string */ - private $appName; + /** + * @inheritDoc + * + * @return string[] list of stored config keys + * @since 20.0.0 + */ + public function getAppKeys(): array { + return $this->appConfig->getKeys($this->appName); + } - public function __construct(IConfig $config, string $appName) { - $this->config = $config; - $this->appName = $appName; + /** + * @inheritDoc + * + * @param string $key config key + * @param bool|null $lazy TRUE to search within lazy loaded config, NULL to search within all config + * + * @return bool TRUE if key exists + * @since 29.0.0 + */ + public function hasAppKey(string $key, ?bool $lazy = false): bool { + return $this->appConfig->hasKey($this->appName, $key, $lazy); } - public function getAppKeys(): array { - return $this->config->getAppKeys($this->appName); + /** + * @param string $key config key + * @param bool|null $lazy TRUE to search within lazy loaded config, NULL to search within all config + * + * @return bool + * @throws AppConfigUnknownKeyException if config key is not known + * @since 29.0.0 + */ + public function isSensitive(string $key, ?bool $lazy = false): bool { + return $this->appConfig->isSensitive($this->appName, $key, $lazy); + } + + /** + * @inheritDoc + * + * @param string $key config key + * + * @return bool TRUE if config is lazy loaded + * @throws AppConfigUnknownKeyException if config key is not known + * @see \OCP\IAppConfig for details about lazy loading + * @since 29.0.0 + */ + public function isLazy(string $key): bool { + return $this->appConfig->isLazy($this->appName, $key); + } + + /** + * @inheritDoc + * + * @param string $key config keys prefix to search + * @param bool $filtered TRUE to hide sensitive config values. Value are replaced by {@see IConfig::SENSITIVE_VALUE} + * + * @return array<string, string|int|float|bool|array> [configKey => configValue] + * @since 29.0.0 + */ + public function getAllAppValues(string $key = '', bool $filtered = false): array { + return $this->appConfig->getAllValues($this->appName, $key, $filtered); } + /** + * @inheritDoc + * + * @param string $key the key of the value, under which will be saved + * @param string $value the value that should be stored + * @since 20.0.0 + * @deprecated 29.0.0 use {@see setAppValueString()} + */ public function setAppValue(string $key, string $value): void { - $this->config->setAppValue($this->appName, $key, $value); + /** @psalm-suppress InternalMethod */ + $this->appConfig->setValueMixed($this->appName, $key, $value); + } + + /** + * @inheritDoc + * + * @param string $key config key + * @param string $value config value + * @param bool $lazy set config as lazy loaded + * @param bool $sensitive if TRUE value will be hidden when listing config values. + * + * @return bool TRUE if value was different, therefor updated in database + * @throws AppConfigTypeConflictException if type from database is not VALUE_MIXED and different from the requested one + * @since 29.0.0 + * @see \OCP\IAppConfig for explanation about lazy loading + */ + public function setAppValueString( + string $key, + string $value, + bool $lazy = false, + bool $sensitive = false + ): bool { + return $this->appConfig->setValueString($this->appName, $key, $value, $lazy, $sensitive); + } + + /** + * @inheritDoc + * + * @param string $key config key + * @param int $value config value + * @param bool $lazy set config as lazy loaded + * @param bool $sensitive if TRUE value will be hidden when listing config values. + * + * @return bool TRUE if value was different, therefor updated in database + * @throws AppConfigTypeConflictException if type from database is not VALUE_MIXED and different from the requested one + * @since 29.0.0 + * @see \OCP\IAppConfig for explanation about lazy loading + */ + public function setAppValueInt( + string $key, + int $value, + bool $lazy = false, + bool $sensitive = false + ): bool { + return $this->appConfig->setValueInt($this->appName, $key, $value, $lazy, $sensitive); + } + + /** + * @inheritDoc + * + * @param string $key config key + * @param float $value config value + * @param bool $lazy set config as lazy loaded + * @param bool $sensitive if TRUE value will be hidden when listing config values. + * + * @return bool TRUE if value was different, therefor updated in database + * @throws AppConfigTypeConflictException if type from database is not VALUE_MIXED and different from the requested one + * @since 29.0.0 + * @see \OCP\IAppConfig for explanation about lazy loading + */ + public function setAppValueFloat( + string $key, + float $value, + bool $lazy = false, + bool $sensitive = false + ): bool { + return $this->appConfig->setValueFloat($this->appName, $key, $value, $lazy, $sensitive); } + /** + * @inheritDoc + * + * @param string $key config key + * @param bool $value config value + * @param bool $lazy set config as lazy loaded + * + * @return bool TRUE if value was different, therefor updated in database + * @throws AppConfigTypeConflictException if type from database is not VALUE_MIXED and different from the requested one + * @since 29.0.0 + * @see \OCP\IAppConfig for explanation about lazy loading + */ + public function setAppValueBool( + string $key, + bool $value, + bool $lazy = false + ): bool { + return $this->appConfig->setValueBool($this->appName, $key, $value, $lazy); + } + + /** + * @inheritDoc + * + * @param string $key config key + * @param array $value config value + * @param bool $lazy set config as lazy loaded + * @param bool $sensitive if TRUE value will be hidden when listing config values. + * + * @return bool TRUE if value was different, therefor updated in database + * @throws AppConfigTypeConflictException if type from database is not VALUE_MIXED and different from the requested one + * @throws JsonException + * @since 29.0.0 + * @see \OCP\IAppConfig for explanation about lazy loading + */ + public function setAppValueArray( + string $key, + array $value, + bool $lazy = false, + bool $sensitive = false + ): bool { + return $this->appConfig->setValueArray($this->appName, $key, $value, $lazy, $sensitive); + } + + /** + * @param string $key + * @param string $default + * + * @since 20.0.0 + * @deprecated 29.0.0 use {@see getAppValueString()} + * @return string + */ public function getAppValue(string $key, string $default = ''): string { - return $this->config->getAppValue($this->appName, $key, $default); + /** @psalm-suppress InternalMethod */ + /** @psalm-suppress UndefinedInterfaceMethod */ + return $this->appConfig->getValueMixed($this->appName, $key, $default); + } + + /** + * @inheritDoc + * + * @param string $key config key + * @param string $default default value + * @param bool $lazy search within lazy loaded config + * + * @return string stored config value or $default if not set in database + * @throws InvalidArgumentException if one of the argument format is invalid + * @throws AppConfigTypeConflictException in case of conflict with the value type set in database + * @since 29.0.0 + * @see \OCP\IAppConfig for explanation about lazy loading + */ + public function getAppValueString(string $key, string $default = '', bool $lazy = false): string { + return $this->appConfig->getValueString($this->appName, $key, $default, $lazy); + } + + /** + * @inheritDoc + * + * @param string $key config key + * @param int $default default value + * @param bool $lazy search within lazy loaded config + * + * @return int stored config value or $default if not set in database + * @throws InvalidArgumentException if one of the argument format is invalid + * @throws AppConfigTypeConflictException in case of conflict with the value type set in database + * @since 29.0.0 + * @see \OCP\IAppConfig for explanation about lazy loading + */ + public function getAppValueInt(string $key, int $default = 0, bool $lazy = false): int { + return $this->appConfig->getValueInt($this->appName, $key, $default, $lazy); + } + + /** + * @inheritDoc + * + * @param string $key config key + * @param float $default default value + * @param bool $lazy search within lazy loaded config + * + * @return float stored config value or $default if not set in database + * @throws InvalidArgumentException if one of the argument format is invalid + * @throws AppConfigTypeConflictException in case of conflict with the value type set in database + * @since 29.0.0 + * @see \OCP\IAppConfig for explanation about lazy loading + */ + public function getAppValueFloat(string $key, float $default = 0, bool $lazy = false): float { + return $this->appConfig->getValueFloat($this->appName, $key, $default, $lazy); + } + + /** + * @inheritDoc + * + * @param string $key config key + * @param bool $default default value + * @param bool $lazy search within lazy loaded config + * + * @return bool stored config value or $default if not set in database + * @throws InvalidArgumentException if one of the argument format is invalid + * @throws AppConfigTypeConflictException in case of conflict with the value type set in database + * @since 29.0.0 + * @see \OCP\IAppConfig for explanation about lazy loading + */ + public function getAppValueBool(string $key, bool $default = false, bool $lazy = false): bool { + return $this->appConfig->getValueBool($this->appName, $key, $default, $lazy); + } + + /** + * @inheritDoc + * + * @param string $key config key + * @param array $default default value + * @param bool $lazy search within lazy loaded config + * + * @return array stored config value or $default if not set in database + * @throws InvalidArgumentException if one of the argument format is invalid + * @throws AppConfigTypeConflictException in case of conflict with the value type set in database + * @since 29.0.0 + * @see \OCP\IAppConfig for explanation about lazy loading + */ + public function getAppValueArray(string $key, array $default = [], bool $lazy = false): array { + return $this->appConfig->getValueArray($this->appName, $key, $default, $lazy); } + /** + * @inheritDoc + * + * @param string $key the key of the value, under which it was saved + * @since 20.0.0 + */ public function deleteAppValue(string $key): void { - $this->config->deleteAppValue($this->appName, $key); + $this->appConfig->deleteKey($this->appName, $key); } + /** + * @inheritDoc + * + * @since 20.0.0 + */ public function deleteAppValues(): void { - $this->config->deleteAppValues($this->appName); + $this->appConfig->deleteApp($this->appName); } public function setUserValue(string $userId, string $key, string $value, ?string $preCondition = null): void { diff --git a/lib/private/AppFramework/Services/InitialState.php b/lib/private/AppFramework/Services/InitialState.php index 9a44db606ea..da225b612cf 100644 --- a/lib/private/AppFramework/Services/InitialState.php +++ b/lib/private/AppFramework/Services/InitialState.php @@ -3,25 +3,8 @@ declare(strict_types=1); /** - * @copyright Copyright (c) 2020, Roeland Jago Douma <roeland@famdouma.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/>. - * + * SPDX-FileCopyrightText: 2020 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later */ namespace OC\AppFramework\Services; diff --git a/lib/private/AppFramework/Utility/ControllerMethodReflector.php b/lib/private/AppFramework/Utility/ControllerMethodReflector.php index b76b3c33c42..9c08f58b384 100644 --- a/lib/private/AppFramework/Utility/ControllerMethodReflector.php +++ b/lib/private/AppFramework/Utility/ControllerMethodReflector.php @@ -1,35 +1,10 @@ <?php declare(strict_types=1); - /** - * @copyright Copyright (c) 2016, ownCloud, Inc. - * - * @author Bernhard Posselt <dev@bernhard-posselt.com> - * @author Bjoern Schiessle <bjoern@schiessle.org> - * @author Christoph Wurst <christoph@winzerhof-wurst.at> - * @author Daniel Kesselberg <mail@danielkesselberg.de> - * @author Joas Schilling <coding@schilljs.com> - * @author Lukas Reschke <lukas@statuscode.ch> - * @author Morris Jobke <hey@morrisjobke.de> - * @author Olivier Paroz <github@oparoz.com> - * @author Roeland Jago Douma <roeland@famdouma.nl> - * @author Thomas Müller <thomas.mueller@tmit.eu> - * - * @license AGPL-3.0 - * - * This code is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License, version 3, - * as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License, version 3, - * along with this program. If not, see <http://www.gnu.org/licenses/> - * + * SPDX-FileCopyrightText: 2016-2024 Nextcloud GmbH and Nextcloud contributors + * SPDX-FileCopyrightText: 2016 ownCloud, Inc. + * SPDX-License-Identifier: AGPL-3.0-only */ namespace OC\AppFramework\Utility; @@ -42,6 +17,7 @@ class ControllerMethodReflector implements IControllerMethodReflector { public $annotations = []; private $types = []; private $parameters = []; + private array $ranges = []; /** * @param object $object an object or classname @@ -54,26 +30,38 @@ class ControllerMethodReflector implements IControllerMethodReflector { if ($docs !== false) { // extract everything prefixed by @ and first letter uppercase preg_match_all('/^\h+\*\h+@(?P<annotation>[A-Z]\w+)((?P<parameter>.*))?$/m', $docs, $matches); - foreach ($matches['annotation'] as $key => $annontation) { - $annontation = strtolower($annontation); + foreach ($matches['annotation'] as $key => $annotation) { + $annotation = strtolower($annotation); $annotationValue = $matches['parameter'][$key]; - if (isset($annotationValue[0]) && $annotationValue[0] === '(' && $annotationValue[\strlen($annotationValue) - 1] === ')') { + if (str_starts_with($annotationValue, '(') && str_ends_with($annotationValue, ')')) { $cutString = substr($annotationValue, 1, -1); $cutString = str_replace(' ', '', $cutString); - $splittedArray = explode(',', $cutString); - foreach ($splittedArray as $annotationValues) { + $splitArray = explode(',', $cutString); + foreach ($splitArray as $annotationValues) { [$key, $value] = explode('=', $annotationValues); - $this->annotations[$annontation][$key] = $value; + $this->annotations[$annotation][$key] = $value; } continue; } - $this->annotations[$annontation] = [$annotationValue]; + $this->annotations[$annotation] = [$annotationValue]; } // extract type parameter information preg_match_all('/@param\h+(?P<type>\w+)\h+\$(?P<var>\w+)/', $docs, $matches); $this->types = array_combine($matches['var'], $matches['type']); + preg_match_all('/@psalm-param\h+(?P<type>\w+)<(?P<rangeMin>(-?\d+|min)),\h*(?P<rangeMax>(-?\d+|max))>\h+\$(?P<var>\w+)/', $docs, $matches); + foreach ($matches['var'] as $index => $varName) { + if ($matches['type'][$index] !== 'int') { + // only int ranges are possible at the moment + // @see https://psalm.dev/docs/annotating_code/type_syntax/scalar_types + continue; + } + $this->ranges[$varName] = [ + 'min' => $matches['rangeMin'][$index] === 'min' ? PHP_INT_MIN : (int)$matches['rangeMin'][$index], + 'max' => $matches['rangeMax'][$index] === 'max' ? PHP_INT_MAX : (int)$matches['rangeMax'][$index], + ]; + } } foreach ($reflection->getParameters() as $param) { @@ -106,6 +94,14 @@ class ControllerMethodReflector implements IControllerMethodReflector { return null; } + public function getRange(string $parameter): ?array { + if (array_key_exists($parameter, $this->ranges)) { + return $this->ranges[$parameter]; + } + + return null; + } + /** * @return array the arguments of the method with key => default value */ diff --git a/lib/private/AppFramework/Utility/QueryNotFoundException.php b/lib/private/AppFramework/Utility/QueryNotFoundException.php index 04faaa51a4b..4d2df14ebc8 100644 --- a/lib/private/AppFramework/Utility/QueryNotFoundException.php +++ b/lib/private/AppFramework/Utility/QueryNotFoundException.php @@ -3,25 +3,8 @@ declare(strict_types=1); /** - * @copyright Copyright (c) 2023, Ferdinand Thiessen <rpm@fthiessen.de> - * - * @author Ferdinand Thiessen <rpm@fthiessen.de> - * - * @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/>. - * + * SPDX-FileCopyrightText: 2023 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later */ namespace OC\AppFramework\Utility; diff --git a/lib/private/AppFramework/Utility/SimpleContainer.php b/lib/private/AppFramework/Utility/SimpleContainer.php index 7aa5cb83926..bf0ef36d13c 100644 --- a/lib/private/AppFramework/Utility/SimpleContainer.php +++ b/lib/private/AppFramework/Utility/SimpleContainer.php @@ -1,31 +1,9 @@ <?php + /** - * @copyright Copyright (c) 2016, ownCloud, Inc. - * - * @author Bernhard Posselt <dev@bernhard-posselt.com> - * @author Christoph Wurst <christoph@winzerhof-wurst.at> - * @author Joas Schilling <coding@schilljs.com> - * @author Lukas Reschke <lukas@statuscode.ch> - * @author Morris Jobke <hey@morrisjobke.de> - * @author Robin Appelman <robin@icewind.nl> - * @author Robin McCorkell <robin@mccorkell.me.uk> - * @author Roeland Jago Douma <roeland@famdouma.nl> - * @author Thomas Müller <thomas.mueller@tmit.eu> - * - * @license AGPL-3.0 - * - * This code is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License, version 3, - * as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License, version 3, - * along with this program. If not, see <http://www.gnu.org/licenses/> - * + * SPDX-FileCopyrightText: 2016-2024 Nextcloud GmbH and Nextcloud contributors + * SPDX-FileCopyrightText: 2016 ownCloud, Inc. + * SPDX-License-Identifier: AGPL-3.0-only */ namespace OC\AppFramework\Utility; @@ -37,8 +15,8 @@ use Pimple\Container; use Psr\Container\ContainerInterface; use ReflectionClass; use ReflectionException; -use ReflectionParameter; use ReflectionNamedType; +use ReflectionParameter; use function class_exists; /** @@ -60,7 +38,7 @@ class SimpleContainer implements ArrayAccess, ContainerInterface, IContainer { * @psalm-param S $id * @psalm-return (S is class-string<T> ? T : mixed) */ - public function get(string $id) { + public function get(string $id): mixed { return $this->query($id); } @@ -105,6 +83,11 @@ class SimpleContainer implements ArrayAccess, ContainerInterface, IContainer { try { return $this->query($resolveName); } catch (QueryException $e2) { + // Pass null if typed and nullable + if ($parameter->allowsNull() && ($parameterType instanceof ReflectionNamedType)) { + return null; + } + // don't lose the error we got while trying to query by type throw new QueryException($e->getMessage(), (int) $e->getCode(), $e); } @@ -223,8 +206,8 @@ class SimpleContainer implements ArrayAccess, ContainerInterface, IContainer { /** * @deprecated 20.0.0 use \OCP\IContainer::registerService */ - public function offsetSet($id, $service): void { - $this->container->offsetSet($id, $service); + public function offsetSet($offset, $value): void { + $this->container->offsetSet($offset, $value); } /** diff --git a/lib/private/AppFramework/Utility/TimeFactory.php b/lib/private/AppFramework/Utility/TimeFactory.php index 1e4655dd1cd..0584fd05ef9 100644 --- a/lib/private/AppFramework/Utility/TimeFactory.php +++ b/lib/private/AppFramework/Utility/TimeFactory.php @@ -1,30 +1,10 @@ <?php declare(strict_types=1); - /** - * @copyright Copyright (c) 2022, Joas Schilling <coding@schilljs.com> - * @copyright Copyright (c) 2016, ownCloud, Inc. - * - * @author Bernhard Posselt <dev@bernhard-posselt.com> - * @author Joas Schilling <coding@schilljs.com> - * @author Morris Jobke <hey@morrisjobke.de> - * @author Thomas Müller <thomas.mueller@tmit.eu> - * - * @license AGPL-3.0 - * - * This code is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License, version 3, - * as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License, version 3, - * along with this program. If not, see <http://www.gnu.org/licenses/> - * + * SPDX-FileCopyrightText: 2022 Nextcloud GmbH and Nextcloud contributors + * SPDX-FileCopyrightText: 2016 ownCloud, Inc. + * SPDX-License-Identifier: AGPL-3.0-only */ namespace OC\AppFramework\Utility; @@ -34,7 +14,7 @@ use OCP\AppFramework\Utility\ITimeFactory; * Use this to get a timestamp or DateTime object in code to remain testable * * @since 8.0.0 - * @since 26.0.0 Extends the \Psr\Clock\ClockInterface interface + * @since 27.0.0 Implements the \Psr\Clock\ClockInterface interface * @ref https://www.php-fig.org/psr/psr-20/#21-clockinterface */ class TimeFactory implements ITimeFactory { @@ -60,7 +40,7 @@ class TimeFactory implements ITimeFactory { * @since 15.0.0 * @deprecated 26.0.0 {@see ITimeFactory::now()} */ - public function getDateTime(string $time = 'now', \DateTimeZone $timezone = null): \DateTime { + public function getDateTime(string $time = 'now', ?\DateTimeZone $timezone = null): \DateTime { return new \DateTime($time, $timezone); } @@ -73,4 +53,11 @@ class TimeFactory implements ITimeFactory { return $clone; } + + public function getTimeZone(?string $timezone = null): \DateTimeZone { + if ($timezone !== null) { + return new \DateTimeZone($timezone); + } + return $this->timezone; + } } diff --git a/lib/private/AppScriptDependency.php b/lib/private/AppScriptDependency.php index 35878e85b49..380816f21ac 100644 --- a/lib/private/AppScriptDependency.php +++ b/lib/private/AppScriptDependency.php @@ -1,24 +1,7 @@ <?php /** - * @copyright Copyright (c) 2021, Jonas Meurer <jonas@freesources.org> - * - * @author Jonas Meurer <jonas@freesources.org> - * - * @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/>. - * + * SPDX-FileCopyrightText: 2021 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later */ namespace OC; diff --git a/lib/private/AppScriptSort.php b/lib/private/AppScriptSort.php index c42d02d485d..eda41e97445 100644 --- a/lib/private/AppScriptSort.php +++ b/lib/private/AppScriptSort.php @@ -1,24 +1,7 @@ <?php /** - * @copyright Copyright (c) 2021, Jonas Meurer <jonas@freesources.org> - * - * @author Jonas Meurer <jonas@freesources.org> - * - * @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/>. - * + * SPDX-FileCopyrightText: 2021 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later */ namespace OC; @@ -46,10 +29,10 @@ class AppScriptSort { * @param array $sortedScriptDeps */ private function topSortVisit( - AppScriptDependency $app, - array &$parents, - array &$scriptDeps, - array &$sortedScriptDeps): void { + AppScriptDependency $app, + array &$parents, + array &$scriptDeps, + array &$sortedScriptDeps): void { // Detect and log circular dependencies if (isset($parents[$app->getId()])) { $this->logger->error('Circular dependency in app scripts at app ' . $app->getId()); diff --git a/lib/private/Archive/Archive.php b/lib/private/Archive/Archive.php index ff6456113f8..26b940be410 100644 --- a/lib/private/Archive/Archive.php +++ b/lib/private/Archive/Archive.php @@ -1,31 +1,9 @@ <?php + /** - * @copyright Copyright (c) 2016, ownCloud, Inc. - * - * @author Arthur Schiwon <blizzz@arthur-schiwon.de> - * @author Christopher Schäpers <kondou@ts.unde.re> - * @author Christoph Wurst <christoph@winzerhof-wurst.at> - * @author Joas Schilling <coding@schilljs.com> - * @author Jörn Friedrich Dreyer <jfd@butonic.de> - * @author Lukas Reschke <lukas@statuscode.ch> - * @author Morris Jobke <hey@morrisjobke.de> - * @author Robin Appelman <robin@icewind.nl> - * @author Roeland Jago Douma <roeland@famdouma.nl> - * - * @license AGPL-3.0 - * - * This code is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License, version 3, - * as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License, version 3, - * along with this program. If not, see <http://www.gnu.org/licenses/> - * + * SPDX-FileCopyrightText: 2016-2024 Nextcloud GmbH and Nextcloud contributors + * SPDX-FileCopyrightText: 2016 ownCloud, Inc. + * SPDX-License-Identifier: AGPL-3.0-only */ namespace OC\Archive; diff --git a/lib/private/Archive/TAR.php b/lib/private/Archive/TAR.php index 9dc906384e0..e08720a75a5 100644 --- a/lib/private/Archive/TAR.php +++ b/lib/private/Archive/TAR.php @@ -1,34 +1,9 @@ <?php + /** - * @copyright Copyright (c) 2016, ownCloud, Inc. - * - * @author Bart Visscher <bartv@thisnet.nl> - * @author Christopher Schäpers <kondou@ts.unde.re> - * @author Christoph Wurst <christoph@winzerhof-wurst.at> - * @author Daniel Kesselberg <mail@danielkesselberg.de> - * @author Joas Schilling <coding@schilljs.com> - * @author Jörn Friedrich Dreyer <jfd@butonic.de> - * @author Morris Jobke <hey@morrisjobke.de> - * @author Remco Brenninkmeijer <requist1@starmail.nl> - * @author Robin Appelman <robin@icewind.nl> - * @author Robin McCorkell <robin@mccorkell.me.uk> - * @author Roeland Jago Douma <roeland@famdouma.nl> - * @author Thomas Müller <thomas.mueller@tmit.eu> - * - * @license AGPL-3.0 - * - * This code is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License, version 3, - * as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License, version 3, - * along with this program. If not, see <http://www.gnu.org/licenses/> - * + * SPDX-FileCopyrightText: 2016-2024 Nextcloud GmbH and Nextcloud contributors + * SPDX-FileCopyrightText: 2016 ownCloud, Inc. + * SPDX-License-Identifier: AGPL-3.0-only */ namespace OC\Archive; @@ -48,15 +23,9 @@ class TAR extends Archive { */ private $cachedHeaders = false; - /** - * @var \Archive_Tar - */ - private $tar = null; + private \Archive_Tar $tar; - /** - * @var string - */ - private $path; + private string $path; public function __construct(string $source) { $types = [null, 'gz', 'bz2']; @@ -137,7 +106,7 @@ class TAR extends Archive { $tmp = \OC::$server->getTempManager()->getTemporaryFolder(); $this->tar->extract($tmp); rename($tmp . $source, $tmp . $dest); - $this->tar = null; + $this->tar->_close(); unlink($this->path); $types = [null, 'gz', 'bz']; $this->tar = new \Archive_Tar($this->path, $types[self::getTarType($this->path)]); @@ -197,7 +166,7 @@ class TAR extends Archive { if ($pos = strpos($result, '/')) { $result = substr($result, 0, $pos + 1); } - if (array_search($result, $folderContent) === false) { + if (!in_array($result, $folderContent)) { $folderContent[] = $result; } } @@ -209,11 +178,16 @@ class TAR extends Archive { * get all files in the archive */ public function getFiles(): array { - if ($this->fileList) { + if ($this->fileList !== false) { return $this->fileList; } - if (!$this->cachedHeaders) { - $this->cachedHeaders = $this->tar->listContent(); + if ($this->cachedHeaders === false) { + $headers = $this->tar->listContent(); + if (is_array($headers)) { + $this->cachedHeaders = $headers; + } else { + return []; + } } $files = []; foreach ($this->cachedHeaders as $header) { @@ -230,6 +204,7 @@ class TAR extends Archive { */ public function getFile(string $path) { $string = $this->tar->extractInString($path); + /** @var ?string $string */ if (is_string($string)) { return $string; } else { @@ -269,7 +244,7 @@ class TAR extends Archive { */ public function fileExists(string $path): bool { $files = $this->getFiles(); - if ((array_search($path, $files) !== false) or (array_search($path . '/', $files) !== false)) { + if ((in_array($path, $files)) or (in_array($path . '/', $files))) { return true; } else { $folderPath = rtrim($path, '/') . '/'; @@ -300,7 +275,6 @@ class TAR extends Archive { $tmp = \OC::$server->getTempManager()->getTemporaryFolder(); $this->tar->extract($tmp); \OCP\Files::rmdirr($tmp . $path); - $this->tar = null; unlink($this->path); $this->reopen(); $this->tar->createModify([$tmp], '', $tmp); @@ -347,10 +321,7 @@ class TAR extends Archive { * reopen the archive to ensure everything is written */ private function reopen(): void { - if ($this->tar) { - $this->tar->_close(); - $this->tar = null; - } + $this->tar->_close(); $types = [null, 'gz', 'bz']; $this->tar = new \Archive_Tar($this->path, $types[self::getTarType($this->path)]); } @@ -359,7 +330,7 @@ class TAR extends Archive { * Get error object from archive_tar. */ public function getError(): ?\PEAR_Error { - if ($this->tar instanceof \Archive_Tar && $this->tar->error_object instanceof \PEAR_Error) { + if ($this->tar->error_object instanceof \PEAR_Error) { return $this->tar->error_object; } return null; diff --git a/lib/private/Archive/ZIP.php b/lib/private/Archive/ZIP.php index f98009ecc3a..52352f9505e 100644 --- a/lib/private/Archive/ZIP.php +++ b/lib/private/Archive/ZIP.php @@ -1,33 +1,9 @@ <?php + /** - * @copyright Copyright (c) 2016, ownCloud, Inc. - * - * @author Arthur Schiwon <blizzz@arthur-schiwon.de> - * @author Bart Visscher <bartv@thisnet.nl> - * @author Christopher Schäpers <kondou@ts.unde.re> - * @author Christoph Wurst <christoph@winzerhof-wurst.at> - * @author Joas Schilling <coding@schilljs.com> - * @author Jörn Friedrich Dreyer <jfd@butonic.de> - * @author Morris Jobke <hey@morrisjobke.de> - * @author Robin Appelman <robin@icewind.nl> - * @author Roeland Jago Douma <roeland@famdouma.nl> - * @author Stefan Weil <sw@weilnetz.de> - * @author Thomas Müller <thomas.mueller@tmit.eu> - * - * @license AGPL-3.0 - * - * This code is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License, version 3, - * as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License, version 3, - * along with this program. If not, see <http://www.gnu.org/licenses/> - * + * SPDX-FileCopyrightText: 2016-2024 Nextcloud GmbH and Nextcloud contributors + * SPDX-FileCopyrightText: 2016 ownCloud, Inc. + * SPDX-License-Identifier: AGPL-3.0-only */ namespace OC\Archive; diff --git a/lib/private/Authentication/Events/ARemoteWipeEvent.php b/lib/private/Authentication/Events/ARemoteWipeEvent.php index e36b7d1087b..ba1e93d26ae 100644 --- a/lib/private/Authentication/Events/ARemoteWipeEvent.php +++ b/lib/private/Authentication/Events/ARemoteWipeEvent.php @@ -3,25 +3,8 @@ declare(strict_types=1); /** - * @copyright 2019 Christoph Wurst <christoph@winzerhof-wurst.at> - * - * @author Christoph Wurst <christoph@winzerhof-wurst.at> - * - * @license GNU AGPL version 3 or any later version - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as - * published by the Free Software Foundation, either version 3 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - * + * SPDX-FileCopyrightText: 2019 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later */ namespace OC\Authentication\Events; diff --git a/lib/private/Authentication/Events/AppPasswordCreatedEvent.php b/lib/private/Authentication/Events/AppPasswordCreatedEvent.php index a90abd25026..bf502ade0cc 100644 --- a/lib/private/Authentication/Events/AppPasswordCreatedEvent.php +++ b/lib/private/Authentication/Events/AppPasswordCreatedEvent.php @@ -3,38 +3,19 @@ declare(strict_types=1); /** - * @copyright 2021 Christoph Wurst <christoph@winzerhof-wurst.at> - * - * @author Christoph Wurst <christoph@winzerhof-wurst.at> - * - * @license GNU AGPL version 3 or any later version - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as - * published by the Free Software Foundation, either version 3 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - * + * SPDX-FileCopyrightText: 2021 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later */ namespace OC\Authentication\Events; -use OC\Authentication\Token\IToken; +use OCP\Authentication\Token\IToken; use OCP\EventDispatcher\Event; class AppPasswordCreatedEvent extends Event { - /** @var IToken */ - private $token; - - public function __construct(IToken $token) { + public function __construct( + private IToken $token, + ) { parent::__construct(); - $this->token = $token; } public function getToken(): IToken { diff --git a/lib/private/Authentication/Events/LoginFailed.php b/lib/private/Authentication/Events/LoginFailed.php index ef702e40a59..23eeaef87ad 100644 --- a/lib/private/Authentication/Events/LoginFailed.php +++ b/lib/private/Authentication/Events/LoginFailed.php @@ -3,25 +3,8 @@ declare(strict_types=1); /** - * @copyright Copyright (c) 2020, Roeland Jago Douma <roeland@famdouma.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/>. - * + * SPDX-FileCopyrightText: 2020 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later */ namespace OC\Authentication\Events; diff --git a/lib/private/Authentication/Events/RemoteWipeFinished.php b/lib/private/Authentication/Events/RemoteWipeFinished.php index 10e72de096c..9704ebff3f5 100644 --- a/lib/private/Authentication/Events/RemoteWipeFinished.php +++ b/lib/private/Authentication/Events/RemoteWipeFinished.php @@ -3,25 +3,8 @@ declare(strict_types=1); /** - * @copyright 2019 Christoph Wurst <christoph@winzerhof-wurst.at> - * - * @author Christoph Wurst <christoph@winzerhof-wurst.at> - * - * @license GNU AGPL version 3 or any later version - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as - * published by the Free Software Foundation, either version 3 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - * + * SPDX-FileCopyrightText: 2019 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later */ namespace OC\Authentication\Events; diff --git a/lib/private/Authentication/Events/RemoteWipeStarted.php b/lib/private/Authentication/Events/RemoteWipeStarted.php index 273a319b1fb..ad0f72f0e09 100644 --- a/lib/private/Authentication/Events/RemoteWipeStarted.php +++ b/lib/private/Authentication/Events/RemoteWipeStarted.php @@ -3,25 +3,8 @@ declare(strict_types=1); /** - * @copyright 2019 Christoph Wurst <christoph@winzerhof-wurst.at> - * - * @author Christoph Wurst <christoph@winzerhof-wurst.at> - * - * @license GNU AGPL version 3 or any later version - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as - * published by the Free Software Foundation, either version 3 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - * + * SPDX-FileCopyrightText: 2019 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later */ namespace OC\Authentication\Events; diff --git a/lib/private/Authentication/Exceptions/ExpiredTokenException.php b/lib/private/Authentication/Exceptions/ExpiredTokenException.php index 0dc92b45920..eed2358d29d 100644 --- a/lib/private/Authentication/Exceptions/ExpiredTokenException.php +++ b/lib/private/Authentication/Exceptions/ExpiredTokenException.php @@ -3,41 +3,26 @@ declare(strict_types=1); /** - * @copyright Copyright (c) 2018 Roeland Jago Douma <roeland@famdouma.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/>. - * + * SPDX-FileCopyrightText: 2018 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later */ namespace OC\Authentication\Exceptions; use OC\Authentication\Token\IToken; -class ExpiredTokenException extends InvalidTokenException { - /** @var IToken */ - private $token; - - public function __construct(IToken $token) { - parent::__construct(); - - $this->token = $token; +/** + * @deprecated 28.0.0 use {@see \OCP\Authentication\Exceptions\ExpiredTokenException} instead + */ +class ExpiredTokenException extends \OCP\Authentication\Exceptions\ExpiredTokenException { + public function __construct( + IToken $token, + ) { + parent::__construct($token); } public function getToken(): IToken { - return $this->token; + $token = parent::getToken(); + /** @var IToken $token We know that we passed OC interface from constructor */ + return $token; } } diff --git a/lib/private/Authentication/Exceptions/InvalidProviderException.php b/lib/private/Authentication/Exceptions/InvalidProviderException.php index 86b44e4d0e2..9dbf3a7782a 100644 --- a/lib/private/Authentication/Exceptions/InvalidProviderException.php +++ b/lib/private/Authentication/Exceptions/InvalidProviderException.php @@ -3,25 +3,8 @@ declare(strict_types=1); /** - * @copyright 2018 Christoph Wurst <christoph@winzerhof-wurst.at> - * - * @author Christoph Wurst <christoph@winzerhof-wurst.at> - * - * @license GNU AGPL version 3 or any later version - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as - * published by the Free Software Foundation, either version 3 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - * + * SPDX-FileCopyrightText: 2018 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later */ namespace OC\Authentication\Exceptions; @@ -29,7 +12,7 @@ use Exception; use Throwable; class InvalidProviderException extends Exception { - public function __construct(string $providerId, Throwable $previous = null) { + public function __construct(string $providerId, ?Throwable $previous = null) { parent::__construct("The provider '$providerId' does not exist'", 0, $previous); } } diff --git a/lib/private/Authentication/Exceptions/InvalidTokenException.php b/lib/private/Authentication/Exceptions/InvalidTokenException.php index acaabff6b88..d23f767e69b 100644 --- a/lib/private/Authentication/Exceptions/InvalidTokenException.php +++ b/lib/private/Authentication/Exceptions/InvalidTokenException.php @@ -1,30 +1,15 @@ <?php declare(strict_types=1); - /** - * @copyright Copyright (c) 2016, ownCloud, Inc. - * - * @author Christoph Wurst <christoph@winzerhof-wurst.at> - * - * @license AGPL-3.0 - * - * This code is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License, version 3, - * as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License, version 3, - * along with this program. If not, see <http://www.gnu.org/licenses/> - * + * SPDX-FileCopyrightText: 2016-2024 Nextcloud GmbH and Nextcloud contributors + * SPDX-FileCopyrightText: 2016 ownCloud, Inc. + * SPDX-License-Identifier: AGPL-3.0-only */ namespace OC\Authentication\Exceptions; -use Exception; - -class InvalidTokenException extends Exception { +/** + * @deprecated 28.0.0 use OCP version instead + */ +class InvalidTokenException extends \OCP\Authentication\Exceptions\InvalidTokenException { } diff --git a/lib/private/Authentication/Exceptions/LoginRequiredException.php b/lib/private/Authentication/Exceptions/LoginRequiredException.php index cc3e8636214..d18ec4f1fbf 100644 --- a/lib/private/Authentication/Exceptions/LoginRequiredException.php +++ b/lib/private/Authentication/Exceptions/LoginRequiredException.php @@ -1,23 +1,9 @@ <?php + /** - * @copyright Copyright (c) 2016, ownCloud, Inc. - * - * @author Christoph Wurst <christoph@winzerhof-wurst.at> - * - * @license AGPL-3.0 - * - * This code is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License, version 3, - * as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License, version 3, - * along with this program. If not, see <http://www.gnu.org/licenses/> - * + * SPDX-FileCopyrightText: 2016-2024 Nextcloud GmbH and Nextcloud contributors + * SPDX-FileCopyrightText: 2016 ownCloud, Inc. + * SPDX-License-Identifier: AGPL-3.0-only */ namespace OC\Authentication\Exceptions; diff --git a/lib/private/Authentication/Exceptions/PasswordLoginForbiddenException.php b/lib/private/Authentication/Exceptions/PasswordLoginForbiddenException.php index 28829390f70..ec833a5a3d0 100644 --- a/lib/private/Authentication/Exceptions/PasswordLoginForbiddenException.php +++ b/lib/private/Authentication/Exceptions/PasswordLoginForbiddenException.php @@ -1,23 +1,9 @@ <?php + /** - * @copyright Copyright (c) 2016, ownCloud, Inc. - * - * @author Christoph Wurst <christoph@winzerhof-wurst.at> - * - * @license AGPL-3.0 - * - * This code is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License, version 3, - * as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License, version 3, - * along with this program. If not, see <http://www.gnu.org/licenses/> - * + * SPDX-FileCopyrightText: 2016-2024 Nextcloud GmbH and Nextcloud contributors + * SPDX-FileCopyrightText: 2016 ownCloud, Inc. + * SPDX-License-Identifier: AGPL-3.0-only */ namespace OC\Authentication\Exceptions; diff --git a/lib/private/Authentication/Exceptions/PasswordlessTokenException.php b/lib/private/Authentication/Exceptions/PasswordlessTokenException.php index c974cc3d6fd..a11e9a5f8d5 100644 --- a/lib/private/Authentication/Exceptions/PasswordlessTokenException.php +++ b/lib/private/Authentication/Exceptions/PasswordlessTokenException.php @@ -1,23 +1,9 @@ <?php + /** - * @copyright Copyright (c) 2016, ownCloud, Inc. - * - * @author Christoph Wurst <christoph@winzerhof-wurst.at> - * - * @license AGPL-3.0 - * - * This code is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License, version 3, - * as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License, version 3, - * along with this program. If not, see <http://www.gnu.org/licenses/> - * + * SPDX-FileCopyrightText: 2016-2024 Nextcloud GmbH and Nextcloud contributors + * SPDX-FileCopyrightText: 2016 ownCloud, Inc. + * SPDX-License-Identifier: AGPL-3.0-only */ namespace OC\Authentication\Exceptions; diff --git a/lib/private/Authentication/Exceptions/TokenPasswordExpiredException.php b/lib/private/Authentication/Exceptions/TokenPasswordExpiredException.php index 7710673ce7c..aa331f31fd0 100644 --- a/lib/private/Authentication/Exceptions/TokenPasswordExpiredException.php +++ b/lib/private/Authentication/Exceptions/TokenPasswordExpiredException.php @@ -3,25 +3,8 @@ declare(strict_types=1); /** - * @copyright Copyright (c) 2019, Roeland Jago Douma <roeland@famdouma.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/>. - * + * SPDX-FileCopyrightText: 2019 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later */ namespace OC\Authentication\Exceptions; diff --git a/lib/private/Authentication/Exceptions/TwoFactorAuthRequiredException.php b/lib/private/Authentication/Exceptions/TwoFactorAuthRequiredException.php index 9733c4547b4..8873b2c9f85 100644 --- a/lib/private/Authentication/Exceptions/TwoFactorAuthRequiredException.php +++ b/lib/private/Authentication/Exceptions/TwoFactorAuthRequiredException.php @@ -1,23 +1,9 @@ <?php + /** - * @copyright Copyright (c) 2016, ownCloud, Inc. - * - * @author Christoph Wurst <christoph@winzerhof-wurst.at> - * - * @license AGPL-3.0 - * - * This code is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License, version 3, - * as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License, version 3, - * along with this program. If not, see <http://www.gnu.org/licenses/> - * + * SPDX-FileCopyrightText: 2016-2024 Nextcloud GmbH and Nextcloud contributors + * SPDX-FileCopyrightText: 2016 ownCloud, Inc. + * SPDX-License-Identifier: AGPL-3.0-only */ namespace OC\Authentication\Exceptions; diff --git a/lib/private/Authentication/Exceptions/UserAlreadyLoggedInException.php b/lib/private/Authentication/Exceptions/UserAlreadyLoggedInException.php index 520372f3087..257f59772fc 100644 --- a/lib/private/Authentication/Exceptions/UserAlreadyLoggedInException.php +++ b/lib/private/Authentication/Exceptions/UserAlreadyLoggedInException.php @@ -1,23 +1,9 @@ <?php + /** - * @copyright Copyright (c) 2016, ownCloud, Inc. - * - * @author Christoph Wurst <christoph@winzerhof-wurst.at> - * - * @license AGPL-3.0 - * - * This code is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License, version 3, - * as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License, version 3, - * along with this program. If not, see <http://www.gnu.org/licenses/> - * + * SPDX-FileCopyrightText: 2016-2024 Nextcloud GmbH and Nextcloud contributors + * SPDX-FileCopyrightText: 2016 ownCloud, Inc. + * SPDX-License-Identifier: AGPL-3.0-only */ namespace OC\Authentication\Exceptions; diff --git a/lib/private/Authentication/Exceptions/WipeTokenException.php b/lib/private/Authentication/Exceptions/WipeTokenException.php index 1c60ab9da78..6bf0565434a 100644 --- a/lib/private/Authentication/Exceptions/WipeTokenException.php +++ b/lib/private/Authentication/Exceptions/WipeTokenException.php @@ -3,41 +3,26 @@ declare(strict_types=1); /** - * @copyright Copyright (c) 2019, Roeland Jago Douma <roeland@famdouma.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/>. - * + * SPDX-FileCopyrightText: 2019 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later */ namespace OC\Authentication\Exceptions; use OC\Authentication\Token\IToken; -class WipeTokenException extends InvalidTokenException { - /** @var IToken */ - private $token; - - public function __construct(IToken $token) { - parent::__construct(); - - $this->token = $token; +/** + * @deprecated 28.0.0 use {@see \OCP\Authentication\Exceptions\WipeTokenException} instead + */ +class WipeTokenException extends \OCP\Authentication\Exceptions\WipeTokenException { + public function __construct( + IToken $token, + ) { + parent::__construct($token); } public function getToken(): IToken { - return $this->token; + $token = parent::getToken(); + /** @var IToken $token We know that we passed OC interface from constructor */ + return $token; } } diff --git a/lib/private/Authentication/Listeners/LoginFailedListener.php b/lib/private/Authentication/Listeners/LoginFailedListener.php index d2971e07b55..0358887bb86 100644 --- a/lib/private/Authentication/Listeners/LoginFailedListener.php +++ b/lib/private/Authentication/Listeners/LoginFailedListener.php @@ -3,26 +3,8 @@ declare(strict_types=1); /** - * @copyright Copyright (c) 2020, Roeland Jago Douma <roeland@famdouma.nl> - * - * @author Christoph Wurst <christoph@winzerhof-wurst.at> - * @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/>. - * + * SPDX-FileCopyrightText: 2020 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later */ namespace OC\Authentication\Listeners; diff --git a/lib/private/Authentication/Listeners/RemoteWipeActivityListener.php b/lib/private/Authentication/Listeners/RemoteWipeActivityListener.php index edebb2a2641..457630eff27 100644 --- a/lib/private/Authentication/Listeners/RemoteWipeActivityListener.php +++ b/lib/private/Authentication/Listeners/RemoteWipeActivityListener.php @@ -3,26 +3,8 @@ declare(strict_types=1); /** - * @copyright 2019 Christoph Wurst <christoph@winzerhof-wurst.at> - * - * @author Christoph Wurst <christoph@winzerhof-wurst.at> - * @author Joas Schilling <coding@schilljs.com> - * - * @license GNU AGPL version 3 or any later version - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as - * published by the Free Software Foundation, either version 3 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - * + * SPDX-FileCopyrightText: 2019 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later */ namespace OC\Authentication\Listeners; @@ -46,7 +28,7 @@ class RemoteWipeActivityListener implements IEventListener { private $logger; public function __construct(IActvityManager $activityManager, - LoggerInterface $logger) { + LoggerInterface $logger) { $this->activityManager = $activityManager; $this->logger = $logger; } diff --git a/lib/private/Authentication/Listeners/RemoteWipeEmailListener.php b/lib/private/Authentication/Listeners/RemoteWipeEmailListener.php index cba2b183589..96878c44123 100644 --- a/lib/private/Authentication/Listeners/RemoteWipeEmailListener.php +++ b/lib/private/Authentication/Listeners/RemoteWipeEmailListener.php @@ -3,26 +3,8 @@ declare(strict_types=1); /** - * @copyright 2019 Christoph Wurst <christoph@winzerhof-wurst.at> - * - * @author Christoph Wurst <christoph@winzerhof-wurst.at> - * @author Joas Schilling <coding@schilljs.com> - * - * @license GNU AGPL version 3 or any later version - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as - * published by the Free Software Foundation, either version 3 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - * + * SPDX-FileCopyrightText: 2019 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later */ namespace OC\Authentication\Listeners; @@ -57,9 +39,9 @@ class RemoteWipeEmailListener implements IEventListener { private $logger; public function __construct(IMailer $mailer, - IUserManager $userManager, - IL10nFactory $l10nFactory, - LoggerInterface $logger) { + IUserManager $userManager, + IL10nFactory $l10nFactory, + LoggerInterface $logger) { $this->mailer = $mailer; $this->userManager = $userManager; $this->l10n = $l10nFactory->get('core'); diff --git a/lib/private/Authentication/Listeners/RemoteWipeNotificationsListener.php b/lib/private/Authentication/Listeners/RemoteWipeNotificationsListener.php index 81feab32746..d95bcd98cf9 100644 --- a/lib/private/Authentication/Listeners/RemoteWipeNotificationsListener.php +++ b/lib/private/Authentication/Listeners/RemoteWipeNotificationsListener.php @@ -3,26 +3,8 @@ declare(strict_types=1); /** - * @copyright 2019 Christoph Wurst <christoph@winzerhof-wurst.at> - * - * @author Christoph Wurst <christoph@winzerhof-wurst.at> - * @author Joas Schilling <coding@schilljs.com> - * - * @license GNU AGPL version 3 or any later version - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as - * published by the Free Software Foundation, either version 3 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - * + * SPDX-FileCopyrightText: 2019 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later */ namespace OC\Authentication\Listeners; @@ -45,7 +27,7 @@ class RemoteWipeNotificationsListener implements IEventListener { private $timeFactory; public function __construct(INotificationManager $notificationManager, - ITimeFactory $timeFactory) { + ITimeFactory $timeFactory) { $this->notificationManager = $notificationManager; $this->timeFactory = $timeFactory; } diff --git a/lib/private/Authentication/Listeners/UserDeletedFilesCleanupListener.php b/lib/private/Authentication/Listeners/UserDeletedFilesCleanupListener.php index 5e657be0763..697aea71c6d 100644 --- a/lib/private/Authentication/Listeners/UserDeletedFilesCleanupListener.php +++ b/lib/private/Authentication/Listeners/UserDeletedFilesCleanupListener.php @@ -3,25 +3,8 @@ declare(strict_types=1); /** - * @copyright Copyright (c) 2021 Robin Appelman <robin@icewind.nl> - * - * @author Robin Appelman <robin@icewind.nl> - * - * @license GNU AGPL version 3 or any later version - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as - * published by the Free Software Foundation, either version 3 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - * + * SPDX-FileCopyrightText: 2021 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later */ namespace OC\Authentication\Listeners; @@ -34,6 +17,7 @@ use OCP\Files\Storage\IStorage; use OCP\User\Events\BeforeUserDeletedEvent; use OCP\User\Events\UserDeletedEvent; +/** @template-implements IEventListener<BeforeUserDeletedEvent|UserDeletedEvent> */ class UserDeletedFilesCleanupListener implements IEventListener { /** @var array<string,IStorage> */ private $homeStorageCache = []; @@ -55,7 +39,7 @@ class UserDeletedFilesCleanupListener implements IEventListener { $userHome = $this->mountProviderCollection->getHomeMountForUser($event->getUser()); $storage = $userHome->getStorage(); if (!$storage) { - throw new \Exception("User has no home storage"); + throw new \Exception("Account has no home storage"); } // remove all wrappers, so we do the delete directly on the home storage bypassing any wrapper diff --git a/lib/private/Authentication/Listeners/UserDeletedStoreCleanupListener.php b/lib/private/Authentication/Listeners/UserDeletedStoreCleanupListener.php index e478c21239f..5f21c640780 100644 --- a/lib/private/Authentication/Listeners/UserDeletedStoreCleanupListener.php +++ b/lib/private/Authentication/Listeners/UserDeletedStoreCleanupListener.php @@ -3,25 +3,8 @@ declare(strict_types=1); /** - * @copyright 2020 Christoph Wurst <christoph@winzerhof-wurst.at> - * - * @author Christoph Wurst <christoph@winzerhof-wurst.at> - * - * @license GNU AGPL version 3 or any later version - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as - * published by the Free Software Foundation, either version 3 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - * + * SPDX-FileCopyrightText: 2020 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later */ namespace OC\Authentication\Listeners; diff --git a/lib/private/Authentication/Listeners/UserDeletedTokenCleanupListener.php b/lib/private/Authentication/Listeners/UserDeletedTokenCleanupListener.php index a09a08568d5..3631c04432c 100644 --- a/lib/private/Authentication/Listeners/UserDeletedTokenCleanupListener.php +++ b/lib/private/Authentication/Listeners/UserDeletedTokenCleanupListener.php @@ -3,26 +3,8 @@ declare(strict_types=1); /** - * @copyright 2020 Christoph Wurst <christoph@winzerhof-wurst.at> - * - * @author Christoph Wurst <christoph@winzerhof-wurst.at> - * @author Joas Schilling <coding@schilljs.com> - * - * @license GNU AGPL version 3 or any later version - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as - * published by the Free Software Foundation, either version 3 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - * + * SPDX-FileCopyrightText: 2020 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later */ namespace OC\Authentication\Listeners; @@ -44,7 +26,7 @@ class UserDeletedTokenCleanupListener implements IEventListener { private $logger; public function __construct(Manager $manager, - LoggerInterface $logger) { + LoggerInterface $logger) { $this->manager = $manager; $this->logger = $logger; } diff --git a/lib/private/Authentication/Listeners/UserDeletedWebAuthnCleanupListener.php b/lib/private/Authentication/Listeners/UserDeletedWebAuthnCleanupListener.php index 4927c3ac7f9..67f8ff7cfcd 100644 --- a/lib/private/Authentication/Listeners/UserDeletedWebAuthnCleanupListener.php +++ b/lib/private/Authentication/Listeners/UserDeletedWebAuthnCleanupListener.php @@ -3,25 +3,8 @@ declare(strict_types=1); /** - * @copyright Copyright (c) 2021 Morris Jobke <hey@morrisjobke.de> - * - * @author Morris Jobke <hey@morrisjobke.de> - * - * @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/>. - * + * SPDX-FileCopyrightText: 2021 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later */ namespace OC\Authentication\Listeners; @@ -31,6 +14,7 @@ use OCP\EventDispatcher\Event; use OCP\EventDispatcher\IEventListener; use OCP\User\Events\UserDeletedEvent; +/** @template-implements IEventListener<UserDeletedEvent> */ class UserDeletedWebAuthnCleanupListener implements IEventListener { /** @var PublicKeyCredentialMapper */ private $credentialMapper; diff --git a/lib/private/Authentication/Listeners/UserLoggedInListener.php b/lib/private/Authentication/Listeners/UserLoggedInListener.php index df030c44749..a8d4baeafa1 100644 --- a/lib/private/Authentication/Listeners/UserLoggedInListener.php +++ b/lib/private/Authentication/Listeners/UserLoggedInListener.php @@ -3,26 +3,8 @@ declare(strict_types=1); /** - * @copyright Copyright (c) 2020, Roeland Jago Douma <roeland@famdouma.nl> - * - * @author Christoph Wurst <christoph@winzerhof-wurst.at> - * @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/>. - * + * SPDX-FileCopyrightText: 2020 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later */ namespace OC\Authentication\Listeners; diff --git a/lib/private/Authentication/Login/ALoginCommand.php b/lib/private/Authentication/Login/ALoginCommand.php index 7944eac1b62..a9f51f0da9e 100644 --- a/lib/private/Authentication/Login/ALoginCommand.php +++ b/lib/private/Authentication/Login/ALoginCommand.php @@ -3,25 +3,8 @@ declare(strict_types=1); /** - * @copyright 2019 Christoph Wurst <christoph@winzerhof-wurst.at> - * - * @author Christoph Wurst <christoph@winzerhof-wurst.at> - * - * @license GNU AGPL version 3 or any later version - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as - * published by the Free Software Foundation, either version 3 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - * + * SPDX-FileCopyrightText: 2019 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later */ namespace OC\Authentication\Login; diff --git a/lib/private/Authentication/Login/Chain.php b/lib/private/Authentication/Login/Chain.php index 3c3179472c4..abd24287a6c 100644 --- a/lib/private/Authentication/Login/Chain.php +++ b/lib/private/Authentication/Login/Chain.php @@ -3,25 +3,8 @@ declare(strict_types=1); /** - * @copyright 2019 Christoph Wurst <christoph@winzerhof-wurst.at> - * - * @author Christoph Wurst <christoph@winzerhof-wurst.at> - * - * @license GNU AGPL version 3 or any later version - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as - * published by the Free Software Foundation, either version 3 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - * + * SPDX-FileCopyrightText: 2019 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later */ namespace OC\Authentication\Login; @@ -63,17 +46,17 @@ class Chain { private $finishRememberedLoginCommand; public function __construct(PreLoginHookCommand $preLoginHookCommand, - UserDisabledCheckCommand $userDisabledCheckCommand, - UidLoginCommand $uidLoginCommand, - EmailLoginCommand $emailLoginCommand, - LoggedInCheckCommand $loggedInCheckCommand, - CompleteLoginCommand $completeLoginCommand, - CreateSessionTokenCommand $createSessionTokenCommand, - ClearLostPasswordTokensCommand $clearLostPasswordTokensCommand, - UpdateLastPasswordConfirmCommand $updateLastPasswordConfirmCommand, - SetUserTimezoneCommand $setUserTimezoneCommand, - TwoFactorCommand $twoFactorCommand, - FinishRememberedLoginCommand $finishRememberedLoginCommand + UserDisabledCheckCommand $userDisabledCheckCommand, + UidLoginCommand $uidLoginCommand, + EmailLoginCommand $emailLoginCommand, + LoggedInCheckCommand $loggedInCheckCommand, + CompleteLoginCommand $completeLoginCommand, + CreateSessionTokenCommand $createSessionTokenCommand, + ClearLostPasswordTokensCommand $clearLostPasswordTokensCommand, + UpdateLastPasswordConfirmCommand $updateLastPasswordConfirmCommand, + SetUserTimezoneCommand $setUserTimezoneCommand, + TwoFactorCommand $twoFactorCommand, + FinishRememberedLoginCommand $finishRememberedLoginCommand ) { $this->preLoginHookCommand = $preLoginHookCommand; $this->userDisabledCheckCommand = $userDisabledCheckCommand; diff --git a/lib/private/Authentication/Login/ClearLostPasswordTokensCommand.php b/lib/private/Authentication/Login/ClearLostPasswordTokensCommand.php index 1643b5af1d5..40369c383ac 100644 --- a/lib/private/Authentication/Login/ClearLostPasswordTokensCommand.php +++ b/lib/private/Authentication/Login/ClearLostPasswordTokensCommand.php @@ -3,25 +3,8 @@ declare(strict_types=1); /** - * @copyright 2019 Christoph Wurst <christoph@winzerhof-wurst.at> - * - * @author Christoph Wurst <christoph@winzerhof-wurst.at> - * - * @license GNU AGPL version 3 or any later version - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as - * published by the Free Software Foundation, either version 3 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - * + * SPDX-FileCopyrightText: 2019 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later */ namespace OC\Authentication\Login; diff --git a/lib/private/Authentication/Login/CompleteLoginCommand.php b/lib/private/Authentication/Login/CompleteLoginCommand.php index 46f7f1a3fa7..ec6fdf75f40 100644 --- a/lib/private/Authentication/Login/CompleteLoginCommand.php +++ b/lib/private/Authentication/Login/CompleteLoginCommand.php @@ -3,25 +3,8 @@ declare(strict_types=1); /** - * @copyright 2019 Christoph Wurst <christoph@winzerhof-wurst.at> - * - * @author Christoph Wurst <christoph@winzerhof-wurst.at> - * - * @license GNU AGPL version 3 or any later version - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as - * published by the Free Software Foundation, either version 3 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - * + * SPDX-FileCopyrightText: 2019 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later */ namespace OC\Authentication\Login; diff --git a/lib/private/Authentication/Login/CreateSessionTokenCommand.php b/lib/private/Authentication/Login/CreateSessionTokenCommand.php index ba237dfbf20..7619ad90d93 100644 --- a/lib/private/Authentication/Login/CreateSessionTokenCommand.php +++ b/lib/private/Authentication/Login/CreateSessionTokenCommand.php @@ -3,27 +3,8 @@ declare(strict_types=1); /** - * @copyright 2019 Christoph Wurst <christoph@winzerhof-wurst.at> - * - * @author Christoph Wurst <christoph@winzerhof-wurst.at> - * @author J0WI <J0WI@users.noreply.github.com> - * @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/>. - * + * SPDX-FileCopyrightText: 2019 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later */ namespace OC\Authentication\Login; @@ -39,7 +20,7 @@ class CreateSessionTokenCommand extends ALoginCommand { private $userSession; public function __construct(IConfig $config, - Session $userSession) { + Session $userSession) { $this->config = $config; $this->userSession = $userSession; } diff --git a/lib/private/Authentication/Login/EmailLoginCommand.php b/lib/private/Authentication/Login/EmailLoginCommand.php index 315fe450697..96cb39277fd 100644 --- a/lib/private/Authentication/Login/EmailLoginCommand.php +++ b/lib/private/Authentication/Login/EmailLoginCommand.php @@ -3,25 +3,8 @@ declare(strict_types=1); /** - * @copyright 2019 Christoph Wurst <christoph@winzerhof-wurst.at> - * - * @author Christoph Wurst <christoph@winzerhof-wurst.at> - * - * @license GNU AGPL version 3 or any later version - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as - * published by the Free Software Foundation, either version 3 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - * + * SPDX-FileCopyrightText: 2019 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later */ namespace OC\Authentication\Login; diff --git a/lib/private/Authentication/Login/FinishRememberedLoginCommand.php b/lib/private/Authentication/Login/FinishRememberedLoginCommand.php index 56ea042a662..3eb1f8f1a65 100644 --- a/lib/private/Authentication/Login/FinishRememberedLoginCommand.php +++ b/lib/private/Authentication/Login/FinishRememberedLoginCommand.php @@ -3,26 +3,8 @@ declare(strict_types=1); /** - * @copyright 2019 Christoph Wurst <christoph@winzerhof-wurst.at> - * - * @author Christoph Wurst <christoph@winzerhof-wurst.at> - * @author Julius Härtl <jus@bitgrid.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/>. - * + * SPDX-FileCopyrightText: 2019 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later */ namespace OC\Authentication\Login; diff --git a/lib/private/Authentication/Login/LoggedInCheckCommand.php b/lib/private/Authentication/Login/LoggedInCheckCommand.php index dc1a4d2d883..b6b59ced6ce 100644 --- a/lib/private/Authentication/Login/LoggedInCheckCommand.php +++ b/lib/private/Authentication/Login/LoggedInCheckCommand.php @@ -3,27 +3,8 @@ declare(strict_types=1); /** - * @copyright 2019 Christoph Wurst <christoph@winzerhof-wurst.at> - * - * @author Christoph Wurst <christoph@winzerhof-wurst.at> - * @author Joas Schilling <coding@schilljs.com> - * @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/>. - * + * SPDX-FileCopyrightText: 2019 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later */ namespace OC\Authentication\Login; @@ -39,7 +20,7 @@ class LoggedInCheckCommand extends ALoginCommand { private $dispatcher; public function __construct(LoggerInterface $logger, - IEventDispatcher $dispatcher) { + IEventDispatcher $dispatcher) { $this->logger = $logger; $this->dispatcher = $dispatcher; } diff --git a/lib/private/Authentication/Login/LoginData.php b/lib/private/Authentication/Login/LoginData.php index 240a1dc6476..1ad97a9d559 100644 --- a/lib/private/Authentication/Login/LoginData.php +++ b/lib/private/Authentication/Login/LoginData.php @@ -3,26 +3,8 @@ declare(strict_types=1); /** - * @copyright 2019 Christoph Wurst <christoph@winzerhof-wurst.at> - * - * @author Christoph Wurst <christoph@winzerhof-wurst.at> - * @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/>. - * + * SPDX-FileCopyrightText: 2019 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later */ namespace OC\Authentication\Login; @@ -55,11 +37,11 @@ class LoginData { private $rememberLogin = true; public function __construct(IRequest $request, - string $username, - ?string $password, - string $redirectUrl = null, - string $timeZone = '', - string $timeZoneOffset = '') { + string $username, + ?string $password, + ?string $redirectUrl = null, + string $timeZone = '', + string $timeZoneOffset = '') { $this->request = $request; $this->username = $username; $this->password = $password; diff --git a/lib/private/Authentication/Login/LoginResult.php b/lib/private/Authentication/Login/LoginResult.php index dec012c2fc9..95e87b520e3 100644 --- a/lib/private/Authentication/Login/LoginResult.php +++ b/lib/private/Authentication/Login/LoginResult.php @@ -3,28 +3,13 @@ declare(strict_types=1); /** - * @copyright 2019 Christoph Wurst <christoph@winzerhof-wurst.at> - * - * @author Christoph Wurst <christoph@winzerhof-wurst.at> - * - * @license GNU AGPL version 3 or any later version - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as - * published by the Free Software Foundation, either version 3 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - * + * SPDX-FileCopyrightText: 2019 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later */ namespace OC\Authentication\Login; +use OC\Core\Controller\LoginController; + class LoginResult { /** @var bool */ private $success; @@ -59,7 +44,10 @@ class LoginResult { return $result; } - public static function failure(LoginData $data, string $msg = null): LoginResult { + /** + * @param LoginController::LOGIN_MSG_*|null $msg + */ + public static function failure(LoginData $data, ?string $msg = null): LoginResult { $result = new static(false, $data); if ($msg !== null) { $result->setErrorMessage($msg); diff --git a/lib/private/Authentication/Login/PreLoginHookCommand.php b/lib/private/Authentication/Login/PreLoginHookCommand.php index 21c97433f5a..d5aa174094d 100644 --- a/lib/private/Authentication/Login/PreLoginHookCommand.php +++ b/lib/private/Authentication/Login/PreLoginHookCommand.php @@ -3,26 +3,8 @@ declare(strict_types=1); /** - * @copyright 2019 Christoph Wurst <christoph@winzerhof-wurst.at> - * - * @author Christoph Wurst <christoph@winzerhof-wurst.at> - * @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/>. - * + * SPDX-FileCopyrightText: 2019 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later */ namespace OC\Authentication\Login; diff --git a/lib/private/Authentication/Login/SetUserTimezoneCommand.php b/lib/private/Authentication/Login/SetUserTimezoneCommand.php index f68fce1771e..90bc444ae7d 100644 --- a/lib/private/Authentication/Login/SetUserTimezoneCommand.php +++ b/lib/private/Authentication/Login/SetUserTimezoneCommand.php @@ -3,25 +3,8 @@ declare(strict_types=1); /** - * @copyright 2019 Christoph Wurst <christoph@winzerhof-wurst.at> - * - * @author Christoph Wurst <christoph@winzerhof-wurst.at> - * - * @license GNU AGPL version 3 or any later version - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as - * published by the Free Software Foundation, either version 3 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - * + * SPDX-FileCopyrightText: 2019 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later */ namespace OC\Authentication\Login; @@ -36,7 +19,7 @@ class SetUserTimezoneCommand extends ALoginCommand { private $session; public function __construct(IConfig $config, - ISession $session) { + ISession $session) { $this->config = $config; $this->session = $session; } diff --git a/lib/private/Authentication/Login/TwoFactorCommand.php b/lib/private/Authentication/Login/TwoFactorCommand.php index 256d88ffa81..fc5285221a2 100644 --- a/lib/private/Authentication/Login/TwoFactorCommand.php +++ b/lib/private/Authentication/Login/TwoFactorCommand.php @@ -3,35 +3,17 @@ declare(strict_types=1); /** - * @copyright 2019 Christoph Wurst <christoph@winzerhof-wurst.at> - * - * @author Christoph Wurst <christoph@winzerhof-wurst.at> - * @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/>. - * + * SPDX-FileCopyrightText: 2019 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later */ namespace OC\Authentication\Login; -use function array_pop; -use function count; use OC\Authentication\TwoFactorAuth\Manager; use OC\Authentication\TwoFactorAuth\MandatoryTwoFactor; use OCP\Authentication\TwoFactorAuth\IProvider; use OCP\IURLGenerator; +use function array_pop; +use function count; class TwoFactorCommand extends ALoginCommand { /** @var Manager */ @@ -44,8 +26,8 @@ class TwoFactorCommand extends ALoginCommand { private $urlGenerator; public function __construct(Manager $twoFactorManager, - MandatoryTwoFactor $mandatoryTwoFactor, - IURLGenerator $urlGenerator) { + MandatoryTwoFactor $mandatoryTwoFactor, + IURLGenerator $urlGenerator) { $this->twoFactorManager = $twoFactorManager; $this->mandatoryTwoFactor = $mandatoryTwoFactor; $this->urlGenerator = $urlGenerator; diff --git a/lib/private/Authentication/Login/UidLoginCommand.php b/lib/private/Authentication/Login/UidLoginCommand.php index d3216b6aad8..511b5f61e0e 100644 --- a/lib/private/Authentication/Login/UidLoginCommand.php +++ b/lib/private/Authentication/Login/UidLoginCommand.php @@ -3,25 +3,8 @@ declare(strict_types=1); /** - * @copyright 2019 Christoph Wurst <christoph@winzerhof-wurst.at> - * - * @author Christoph Wurst <christoph@winzerhof-wurst.at> - * - * @license GNU AGPL version 3 or any later version - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as - * published by the Free Software Foundation, either version 3 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - * + * SPDX-FileCopyrightText: 2019 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later */ namespace OC\Authentication\Login; diff --git a/lib/private/Authentication/Login/UpdateLastPasswordConfirmCommand.php b/lib/private/Authentication/Login/UpdateLastPasswordConfirmCommand.php index 571ea931d8a..0582239e9de 100644 --- a/lib/private/Authentication/Login/UpdateLastPasswordConfirmCommand.php +++ b/lib/private/Authentication/Login/UpdateLastPasswordConfirmCommand.php @@ -3,25 +3,8 @@ declare(strict_types=1); /** - * @copyright 2019 Christoph Wurst <christoph@winzerhof-wurst.at> - * - * @author Christoph Wurst <christoph@winzerhof-wurst.at> - * - * @license GNU AGPL version 3 or any later version - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as - * published by the Free Software Foundation, either version 3 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - * + * SPDX-FileCopyrightText: 2019 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later */ namespace OC\Authentication\Login; diff --git a/lib/private/Authentication/Login/UserDisabledCheckCommand.php b/lib/private/Authentication/Login/UserDisabledCheckCommand.php index 7cf4c7235ec..8777aa6dcea 100644 --- a/lib/private/Authentication/Login/UserDisabledCheckCommand.php +++ b/lib/private/Authentication/Login/UserDisabledCheckCommand.php @@ -3,26 +3,8 @@ declare(strict_types=1); /** - * @copyright 2019 Christoph Wurst <christoph@winzerhof-wurst.at> - * - * @author Christoph Wurst <christoph@winzerhof-wurst.at> - * @author Joas Schilling <coding@schilljs.com> - * - * @license GNU AGPL version 3 or any later version - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as - * published by the Free Software Foundation, either version 3 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - * + * SPDX-FileCopyrightText: 2019 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later */ namespace OC\Authentication\Login; @@ -38,7 +20,7 @@ class UserDisabledCheckCommand extends ALoginCommand { private $logger; public function __construct(IUserManager $userManager, - LoggerInterface $logger) { + LoggerInterface $logger) { $this->userManager = $userManager; $this->logger = $logger; } diff --git a/lib/private/Authentication/Login/WebAuthnChain.php b/lib/private/Authentication/Login/WebAuthnChain.php index f3ebc313a44..c31e39de28c 100644 --- a/lib/private/Authentication/Login/WebAuthnChain.php +++ b/lib/private/Authentication/Login/WebAuthnChain.php @@ -3,25 +3,8 @@ declare(strict_types=1); /** - * @copyright Copyright (c) 2020, Roeland Jago Douma <roeland@famdouma.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/>. - * + * SPDX-FileCopyrightText: 2020 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later */ namespace OC\Authentication\Login; @@ -57,15 +40,15 @@ class WebAuthnChain { private $webAuthnLoginCommand; public function __construct(UserDisabledCheckCommand $userDisabledCheckCommand, - WebAuthnLoginCommand $webAuthnLoginCommand, - LoggedInCheckCommand $loggedInCheckCommand, - CompleteLoginCommand $completeLoginCommand, - CreateSessionTokenCommand $createSessionTokenCommand, - ClearLostPasswordTokensCommand $clearLostPasswordTokensCommand, - UpdateLastPasswordConfirmCommand $updateLastPasswordConfirmCommand, - SetUserTimezoneCommand $setUserTimezoneCommand, - TwoFactorCommand $twoFactorCommand, - FinishRememberedLoginCommand $finishRememberedLoginCommand + WebAuthnLoginCommand $webAuthnLoginCommand, + LoggedInCheckCommand $loggedInCheckCommand, + CompleteLoginCommand $completeLoginCommand, + CreateSessionTokenCommand $createSessionTokenCommand, + ClearLostPasswordTokensCommand $clearLostPasswordTokensCommand, + UpdateLastPasswordConfirmCommand $updateLastPasswordConfirmCommand, + SetUserTimezoneCommand $setUserTimezoneCommand, + TwoFactorCommand $twoFactorCommand, + FinishRememberedLoginCommand $finishRememberedLoginCommand ) { $this->userDisabledCheckCommand = $userDisabledCheckCommand; $this->webAuthnLoginCommand = $webAuthnLoginCommand; diff --git a/lib/private/Authentication/Login/WebAuthnLoginCommand.php b/lib/private/Authentication/Login/WebAuthnLoginCommand.php index 478a579853c..8f14e5b3f6d 100644 --- a/lib/private/Authentication/Login/WebAuthnLoginCommand.php +++ b/lib/private/Authentication/Login/WebAuthnLoginCommand.php @@ -3,25 +3,8 @@ declare(strict_types=1); /** - * @copyright Copyright (c) 2020, Roeland Jago Douma <roeland@famdouma.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/>. - * + * SPDX-FileCopyrightText: 2020 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later */ namespace OC\Authentication\Login; diff --git a/lib/private/Authentication/LoginCredentials/Credentials.php b/lib/private/Authentication/LoginCredentials/Credentials.php index 69bfc2ba198..2d7ed3adfd0 100644 --- a/lib/private/Authentication/LoginCredentials/Credentials.php +++ b/lib/private/Authentication/LoginCredentials/Credentials.php @@ -1,24 +1,7 @@ <?php /** - * @copyright 2016 Christoph Wurst <christoph@winzerhof-wurst.at> - * - * @author Christoph Wurst <christoph@winzerhof-wurst.at> - * - * @license GNU AGPL version 3 or any later version - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as - * published by the Free Software Foundation, either version 3 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - * + * SPDX-FileCopyrightText: 2016 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later */ namespace OC\Authentication\LoginCredentials; diff --git a/lib/private/Authentication/LoginCredentials/Store.php b/lib/private/Authentication/LoginCredentials/Store.php index 3a09e983ee8..bd39dd11460 100644 --- a/lib/private/Authentication/LoginCredentials/Store.php +++ b/lib/private/Authentication/LoginCredentials/Store.php @@ -3,33 +3,15 @@ declare(strict_types=1); /** - * @copyright 2016 Christoph Wurst <christoph@winzerhof-wurst.at> - * - * @author Christoph Wurst <christoph@winzerhof-wurst.at> - * @author Joas Schilling <coding@schilljs.com> - * - * @license GNU AGPL version 3 or any later version - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as - * published by the Free Software Foundation, either version 3 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - * + * SPDX-FileCopyrightText: 2016 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later */ namespace OC\Authentication\LoginCredentials; -use OC\Authentication\Exceptions\InvalidTokenException; use OC\Authentication\Exceptions\PasswordlessTokenException; use OC\Authentication\Token\IProvider; use OCP\Authentication\Exceptions\CredentialsUnavailableException; +use OCP\Authentication\Exceptions\InvalidTokenException; use OCP\Authentication\LoginCredentials\ICredentials; use OCP\Authentication\LoginCredentials\IStore; use OCP\ISession; @@ -48,8 +30,8 @@ class Store implements IStore { private $tokenProvider; public function __construct(ISession $session, - LoggerInterface $logger, - IProvider $tokenProvider = null) { + LoggerInterface $logger, + ?IProvider $tokenProvider = null) { $this->session = $session; $this->logger = $logger; $this->tokenProvider = $tokenProvider; diff --git a/lib/private/Authentication/Notifications/Notifier.php b/lib/private/Authentication/Notifications/Notifier.php index 8cf5d653771..3b6c9b3e610 100644 --- a/lib/private/Authentication/Notifications/Notifier.php +++ b/lib/private/Authentication/Notifications/Notifier.php @@ -3,26 +3,8 @@ declare(strict_types=1); /** - * @copyright 2019 Christoph Wurst <christoph@winzerhof-wurst.at> - * - * @author Joas Schilling <coding@schilljs.com> - * @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/>. - * + * SPDX-FileCopyrightText: 2019 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later */ namespace OC\Authentication\Notifications; diff --git a/lib/private/Authentication/Token/INamedToken.php b/lib/private/Authentication/Token/INamedToken.php index 96a7719fb41..9a90cfc7d76 100644 --- a/lib/private/Authentication/Token/INamedToken.php +++ b/lib/private/Authentication/Token/INamedToken.php @@ -3,26 +3,8 @@ declare(strict_types=1); /** - * @copyright Copyright (c) 2019, Daniel Kesselberg (mail@danielkesselberg.de) - * - * @author Daniel Kesselberg <mail@danielkesselberg.de> - * @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/>. - * + * SPDX-FileCopyrightText: 2019 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later */ namespace OC\Authentication\Token; diff --git a/lib/private/Authentication/Token/IProvider.php b/lib/private/Authentication/Token/IProvider.php index a12d3ba34d9..dfb17301ab3 100644 --- a/lib/private/Authentication/Token/IProvider.php +++ b/lib/private/Authentication/Token/IProvider.php @@ -3,36 +3,17 @@ declare(strict_types=1); /** - * @copyright Copyright (c) 2016, ownCloud, Inc. - * - * @author Christoph Wurst <christoph@winzerhof-wurst.at> - * @author Lukas Reschke <lukas@statuscode.ch> - * @author Marcel Waldvogel <marcel.waldvogel@uni-konstanz.de> - * @author Morris Jobke <hey@morrisjobke.de> - * @author Robin Appelman <robin@icewind.nl> - * @author Roeland Jago Douma <roeland@famdouma.nl> - * - * @license AGPL-3.0 - * - * This code is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License, version 3, - * as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License, version 3, - * along with this program. If not, see <http://www.gnu.org/licenses/> - * + * SPDX-FileCopyrightText: 2016-2024 Nextcloud GmbH and Nextcloud contributors + * SPDX-FileCopyrightText: 2016 ownCloud, Inc. + * SPDX-License-Identifier: AGPL-3.0-only */ namespace OC\Authentication\Token; -use OC\Authentication\Exceptions\ExpiredTokenException; -use OC\Authentication\Exceptions\InvalidTokenException; use OC\Authentication\Exceptions\PasswordlessTokenException; -use OC\Authentication\Exceptions\WipeTokenException; +use OCP\Authentication\Exceptions\ExpiredTokenException; +use OCP\Authentication\Exceptions\InvalidTokenException; +use OCP\Authentication\Exceptions\WipeTokenException; +use OCP\Authentication\Token\IToken as OCPIToken; interface IProvider { /** @@ -45,16 +26,16 @@ interface IProvider { * @param string $name Name will be trimmed to 120 chars when longer * @param int $type token type * @param int $remember whether the session token should be used for remember-me - * @return IToken + * @return OCPIToken * @throws \RuntimeException when OpenSSL reports a problem */ public function generateToken(string $token, - string $uid, - string $loginName, - ?string $password, - string $name, - int $type = IToken::TEMPORARY_TOKEN, - int $remember = IToken::DO_NOT_REMEMBER): IToken; + string $uid, + string $loginName, + ?string $password, + string $name, + int $type = OCPIToken::TEMPORARY_TOKEN, + int $remember = OCPIToken::DO_NOT_REMEMBER): OCPIToken; /** * Get a token by token id @@ -63,9 +44,9 @@ interface IProvider { * @throws InvalidTokenException * @throws ExpiredTokenException * @throws WipeTokenException - * @return IToken + * @return OCPIToken */ - public function getToken(string $tokenId): IToken; + public function getToken(string $tokenId): OCPIToken; /** * Get a token by token id @@ -74,9 +55,9 @@ interface IProvider { * @throws InvalidTokenException * @throws ExpiredTokenException * @throws WipeTokenException - * @return IToken + * @return OCPIToken */ - public function getTokenById(int $tokenId): IToken; + public function getTokenById(int $tokenId): OCPIToken; /** * Duplicate an existing session token @@ -85,9 +66,9 @@ interface IProvider { * @param string $sessionId * @throws InvalidTokenException * @throws \RuntimeException when OpenSSL reports a problem - * @return IToken The new token + * @return OCPIToken The new token */ - public function renewSessionToken(string $oldSessionId, string $sessionId): IToken; + public function renewSessionToken(string $oldSessionId, string $sessionId): OCPIToken; /** * Invalidate (delete) the given session token @@ -117,16 +98,16 @@ interface IProvider { /** * Save the updated token * - * @param IToken $token + * @param OCPIToken $token */ - public function updateToken(IToken $token); + public function updateToken(OCPIToken $token); /** * Update token activity timestamp * - * @param IToken $token + * @param OCPIToken $token */ - public function updateTokenActivity(IToken $token); + public function updateTokenActivity(OCPIToken $token); /** * Get all tokens of a user @@ -135,49 +116,49 @@ interface IProvider { * where a high number of (session) tokens is generated * * @param string $uid - * @return IToken[] + * @return OCPIToken[] */ public function getTokenByUser(string $uid): array; /** * Get the (unencrypted) password of the given token * - * @param IToken $savedToken + * @param OCPIToken $savedToken * @param string $tokenId * @throws InvalidTokenException * @throws PasswordlessTokenException * @return string */ - public function getPassword(IToken $savedToken, string $tokenId): string; + public function getPassword(OCPIToken $savedToken, string $tokenId): string; /** * Encrypt and set the password of the given token * - * @param IToken $token + * @param OCPIToken $token * @param string $tokenId * @param string $password * @throws InvalidTokenException */ - public function setPassword(IToken $token, string $tokenId, string $password); + public function setPassword(OCPIToken $token, string $tokenId, string $password); /** * Rotate the token. Useful for for example oauth tokens * - * @param IToken $token + * @param OCPIToken $token * @param string $oldTokenId * @param string $newTokenId - * @return IToken + * @return OCPIToken * @throws \RuntimeException when OpenSSL reports a problem */ - public function rotate(IToken $token, string $oldTokenId, string $newTokenId): IToken; + public function rotate(OCPIToken $token, string $oldTokenId, string $newTokenId): OCPIToken; /** * Marks a token as having an invalid password. * - * @param IToken $token + * @param OCPIToken $token * @param string $tokenId */ - public function markPasswordInvalid(IToken $token, string $tokenId); + public function markPasswordInvalid(OCPIToken $token, string $tokenId); /** * Update all the passwords of $uid if required diff --git a/lib/private/Authentication/Token/IToken.php b/lib/private/Authentication/Token/IToken.php index 5ca4eaea843..2028a0b328c 100644 --- a/lib/private/Authentication/Token/IToken.php +++ b/lib/private/Authentication/Token/IToken.php @@ -1,134 +1,17 @@ <?php declare(strict_types=1); - /** - * @copyright Copyright (c) 2016, ownCloud, Inc. - * - * @author Christoph Wurst <christoph@winzerhof-wurst.at> - * @author Robin Appelman <robin@icewind.nl> - * @author Roeland Jago Douma <roeland@famdouma.nl> - * - * @license AGPL-3.0 - * - * This code is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License, version 3, - * as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License, version 3, - * along with this program. If not, see <http://www.gnu.org/licenses/> - * + * SPDX-FileCopyrightText: 2016-2024 Nextcloud GmbH and Nextcloud contributors + * SPDX-FileCopyrightText: 2016 ownCloud, Inc. + * SPDX-License-Identifier: AGPL-3.0-only */ namespace OC\Authentication\Token; -use JsonSerializable; - -interface IToken extends JsonSerializable { - public const TEMPORARY_TOKEN = 0; - public const PERMANENT_TOKEN = 1; - public const WIPE_TOKEN = 2; - public const DO_NOT_REMEMBER = 0; - public const REMEMBER = 1; - - /** - * Get the token ID - * - * @return int - */ - public function getId(): int; - - /** - * Get the user UID - * - * @return string - */ - public function getUID(): string; - - /** - * Get the login name used when generating the token - * - * @return string - */ - public function getLoginName(): string; - - /** - * Get the (encrypted) login password - * - * @return string|null - */ - public function getPassword(); - - /** - * Get the timestamp of the last password check - * - * @return int - */ - public function getLastCheck(): int; - - /** - * Set the timestamp of the last password check - * - * @param int $time - */ - public function setLastCheck(int $time); - - /** - * Get the authentication scope for this token - * - * @return string - */ - public function getScope(): string; +use OCP\Authentication\Token\IToken as OCPIToken; - /** - * Get the authentication scope for this token - * - * @return array - */ - public function getScopeAsArray(): array; - - /** - * Set the authentication scope for this token - * - * @param array $scope - */ - public function setScope($scope); - - /** - * Get the name of the token - * @return string - */ - public function getName(): string; - - /** - * Get the remember state of the token - * - * @return int - */ - public function getRemember(): int; - - /** - * Set the token - * - * @param string $token - */ - public function setToken(string $token); - - /** - * Set the password - * - * @param string $password - */ - public function setPassword(string $password); - - /** - * Set the expiration time of the token - * - * @param int|null $expires - */ - public function setExpires($expires); +/** + * @deprecated 28.0.0 use {@see \OCP\Authentication\Token\IToken} instead + */ +interface IToken extends OCPIToken { } diff --git a/lib/private/Authentication/Token/IWipeableToken.php b/lib/private/Authentication/Token/IWipeableToken.php index 5ba994e0d5e..fc1476785cd 100644 --- a/lib/private/Authentication/Token/IWipeableToken.php +++ b/lib/private/Authentication/Token/IWipeableToken.php @@ -3,26 +3,8 @@ declare(strict_types=1); /** - * @copyright Copyright (c) 2019, Roeland Jago Douma <roeland@famdouma.nl> - * - * @author Christoph Wurst <christoph@winzerhof-wurst.at> - * @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/>. - * + * SPDX-FileCopyrightText: 2019 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later */ namespace OC\Authentication\Token; diff --git a/lib/private/Authentication/Token/Manager.php b/lib/private/Authentication/Token/Manager.php index 6a1c7d4c1e7..37ed6083d82 100644 --- a/lib/private/Authentication/Token/Manager.php +++ b/lib/private/Authentication/Token/Manager.php @@ -1,38 +1,20 @@ <?php declare(strict_types=1); - /** - * @copyright Copyright 2018, Roeland Jago Douma <roeland@famdouma.nl> - * - * @author Christoph Wurst <christoph@winzerhof-wurst.at> - * @author Morris Jobke <hey@morrisjobke.de> - * @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/>. - * + * SPDX-FileCopyrightText: 2018 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later */ namespace OC\Authentication\Token; use Doctrine\DBAL\Exception\UniqueConstraintViolationException; -use OC\Authentication\Exceptions\ExpiredTokenException; -use OC\Authentication\Exceptions\InvalidTokenException; +use OC\Authentication\Exceptions\InvalidTokenException as OcInvalidTokenException; use OC\Authentication\Exceptions\PasswordlessTokenException; -use OC\Authentication\Exceptions\WipeTokenException; +use OCP\Authentication\Exceptions\ExpiredTokenException; +use OCP\Authentication\Exceptions\InvalidTokenException; +use OCP\Authentication\Exceptions\WipeTokenException; use OCP\Authentication\Token\IProvider as OCPIProvider; +use OCP\Authentication\Token\IToken as OCPIToken; class Manager implements IProvider, OCPIProvider { /** @var PublicKeyTokenProvider */ @@ -52,15 +34,15 @@ class Manager implements IProvider, OCPIProvider { * @param string $name Name will be trimmed to 120 chars when longer * @param int $type token type * @param int $remember whether the session token should be used for remember-me - * @return IToken + * @return OCPIToken */ public function generateToken(string $token, - string $uid, - string $loginName, - $password, - string $name, - int $type = IToken::TEMPORARY_TOKEN, - int $remember = IToken::DO_NOT_REMEMBER): IToken { + string $uid, + string $loginName, + $password, + string $name, + int $type = OCPIToken::TEMPORARY_TOKEN, + int $remember = OCPIToken::DO_NOT_REMEMBER): OCPIToken { if (mb_strlen($name) > 128) { $name = mb_substr($name, 0, 120) . '…'; } @@ -93,10 +75,10 @@ class Manager implements IProvider, OCPIProvider { /** * Save the updated token * - * @param IToken $token + * @param OCPIToken $token * @throws InvalidTokenException */ - public function updateToken(IToken $token) { + public function updateToken(OCPIToken $token) { $provider = $this->getProvider($token); $provider->updateToken($token); } @@ -105,16 +87,16 @@ class Manager implements IProvider, OCPIProvider { * Update token activity timestamp * * @throws InvalidTokenException - * @param IToken $token + * @param OCPIToken $token */ - public function updateTokenActivity(IToken $token) { + public function updateTokenActivity(OCPIToken $token) { $provider = $this->getProvider($token); $provider->updateTokenActivity($token); } /** * @param string $uid - * @return IToken[] + * @return OCPIToken[] */ public function getTokenByUser(string $uid): array { return $this->publicKeyTokenProvider->getTokenByUser($uid); @@ -126,9 +108,9 @@ class Manager implements IProvider, OCPIProvider { * @param string $tokenId * @throws InvalidTokenException * @throws \RuntimeException when OpenSSL reports a problem - * @return IToken + * @return OCPIToken */ - public function getToken(string $tokenId): IToken { + public function getToken(string $tokenId): OCPIToken { try { return $this->publicKeyTokenProvider->getToken($tokenId); } catch (WipeTokenException $e) { @@ -145,9 +127,9 @@ class Manager implements IProvider, OCPIProvider { * * @param int $tokenId * @throws InvalidTokenException - * @return IToken + * @return OCPIToken */ - public function getTokenById(int $tokenId): IToken { + public function getTokenById(int $tokenId): OCPIToken { try { return $this->publicKeyTokenProvider->getTokenById($tokenId); } catch (ExpiredTokenException $e) { @@ -163,9 +145,9 @@ class Manager implements IProvider, OCPIProvider { * @param string $oldSessionId * @param string $sessionId * @throws InvalidTokenException - * @return IToken + * @return OCPIToken */ - public function renewSessionToken(string $oldSessionId, string $sessionId): IToken { + public function renewSessionToken(string $oldSessionId, string $sessionId): OCPIToken { try { return $this->publicKeyTokenProvider->renewSessionToken($oldSessionId, $sessionId); } catch (ExpiredTokenException $e) { @@ -176,18 +158,18 @@ class Manager implements IProvider, OCPIProvider { } /** - * @param IToken $savedToken + * @param OCPIToken $savedToken * @param string $tokenId session token * @throws InvalidTokenException * @throws PasswordlessTokenException * @return string */ - public function getPassword(IToken $savedToken, string $tokenId): string { + public function getPassword(OCPIToken $savedToken, string $tokenId): string { $provider = $this->getProvider($savedToken); return $provider->getPassword($savedToken, $tokenId); } - public function setPassword(IToken $token, string $tokenId, string $password) { + public function setPassword(OCPIToken $token, string $tokenId, string $password) { $provider = $this->getProvider($token); $provider->setPassword($token, $tokenId, $password); } @@ -209,35 +191,37 @@ class Manager implements IProvider, OCPIProvider { } /** - * @param IToken $token + * @param OCPIToken $token * @param string $oldTokenId * @param string $newTokenId - * @return IToken + * @return OCPIToken * @throws InvalidTokenException * @throws \RuntimeException when OpenSSL reports a problem */ - public function rotate(IToken $token, string $oldTokenId, string $newTokenId): IToken { + public function rotate(OCPIToken $token, string $oldTokenId, string $newTokenId): OCPIToken { if ($token instanceof PublicKeyToken) { return $this->publicKeyTokenProvider->rotate($token, $oldTokenId, $newTokenId); } - throw new InvalidTokenException(); + /** @psalm-suppress DeprecatedClass We have to throw the OC version so both OC and OCP catches catch it */ + throw new OcInvalidTokenException(); } /** - * @param IToken $token + * @param OCPIToken $token * @return IProvider * @throws InvalidTokenException */ - private function getProvider(IToken $token): IProvider { + private function getProvider(OCPIToken $token): IProvider { if ($token instanceof PublicKeyToken) { return $this->publicKeyTokenProvider; } - throw new InvalidTokenException(); + /** @psalm-suppress DeprecatedClass We have to throw the OC version so both OC and OCP catches catch it */ + throw new OcInvalidTokenException(); } - public function markPasswordInvalid(IToken $token, string $tokenId) { + public function markPasswordInvalid(OCPIToken $token, string $tokenId) { $this->getProvider($token)->markPasswordInvalid($token, $tokenId); } diff --git a/lib/private/Authentication/Token/PublicKeyToken.php b/lib/private/Authentication/Token/PublicKeyToken.php index 45335e17c31..0b7a2589f3e 100644 --- a/lib/private/Authentication/Token/PublicKeyToken.php +++ b/lib/private/Authentication/Token/PublicKeyToken.php @@ -3,27 +3,8 @@ declare(strict_types=1); /** - * @copyright Copyright (c) 2016 Roeland Jago Douma <roeland@famdouma.nl> - * - * @author Christoph Wurst <christoph@winzerhof-wurst.at> - * @author Daniel Kesselberg <mail@danielkesselberg.de> - * @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/>. - * + * SPDX-FileCopyrightText: 2016 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later */ namespace OC\Authentication\Token; @@ -137,10 +118,8 @@ class PublicKeyToken extends Entity implements INamedToken, IWipeableToken { /** * Get the (encrypted) login password - * - * @return string|null */ - public function getPassword() { + public function getPassword(): ?string { return parent::getPassword(); } @@ -165,10 +144,8 @@ class PublicKeyToken extends Entity implements INamedToken, IWipeableToken { /** * Get the timestamp of the last password check - * - * @param int $time */ - public function setLastCheck(int $time) { + public function setLastCheck(int $time): void { parent::setLastCheck($time); } @@ -191,7 +168,7 @@ class PublicKeyToken extends Entity implements INamedToken, IWipeableToken { return $scope; } - public function setScope($scope) { + public function setScope(array|string|null $scope): void { if (is_array($scope)) { parent::setScope(json_encode($scope)); } else { @@ -211,15 +188,15 @@ class PublicKeyToken extends Entity implements INamedToken, IWipeableToken { return parent::getRemember(); } - public function setToken(string $token) { + public function setToken(string $token): void { parent::setToken($token); } - public function setPassword(string $password = null) { + public function setPassword(?string $password = null): void { parent::setPassword($password); } - public function setExpires($expires) { + public function setExpires($expires): void { parent::setExpires($expires); } diff --git a/lib/private/Authentication/Token/PublicKeyTokenMapper.php b/lib/private/Authentication/Token/PublicKeyTokenMapper.php index 855639dd907..0db5c4f53e7 100644 --- a/lib/private/Authentication/Token/PublicKeyTokenMapper.php +++ b/lib/private/Authentication/Token/PublicKeyTokenMapper.php @@ -3,32 +3,14 @@ declare(strict_types=1); /** - * @copyright Copyright (c) 2018 Roeland Jago Douma <roeland@famdouma.nl> - * - * @author Christoph Wurst <christoph@winzerhof-wurst.at> - * @author Daniel Kesselberg <mail@danielkesselberg.de> - * @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/>. - * + * SPDX-FileCopyrightText: 2018 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later */ namespace OC\Authentication\Token; use OCP\AppFramework\Db\DoesNotExistException; use OCP\AppFramework\Db\QBMapper; +use OCP\Authentication\Token\IToken; use OCP\DB\QueryBuilder\IQueryBuilder; use OCP\IDBConnection; @@ -42,8 +24,6 @@ class PublicKeyTokenMapper extends QBMapper { /** * Invalidate (delete) a given token - * - * @param string $token */ public function invalidate(string $token) { /* @var $qb IQueryBuilder */ @@ -150,14 +130,15 @@ class PublicKeyTokenMapper extends QBMapper { return $entities; } - public function deleteById(string $uid, int $id) { + public function getTokenByUserAndId(string $uid, int $id): ?string { /* @var $qb IQueryBuilder */ $qb = $this->db->getQueryBuilder(); - $qb->delete($this->tableName) + $qb->select('token') + ->from($this->tableName) ->where($qb->expr()->eq('id', $qb->createNamedParameter($id))) ->andWhere($qb->expr()->eq('uid', $qb->createNamedParameter($uid))) ->andWhere($qb->expr()->eq('version', $qb->createNamedParameter(PublicKeyToken::VERSION, IQueryBuilder::PARAM_INT))); - $qb->execute(); + return $qb->executeQuery()->fetchOne() ?: null; } /** diff --git a/lib/private/Authentication/Token/PublicKeyTokenProvider.php b/lib/private/Authentication/Token/PublicKeyTokenProvider.php index 3fb11611076..5363b82df69 100644 --- a/lib/private/Authentication/Token/PublicKeyTokenProvider.php +++ b/lib/private/Authentication/Token/PublicKeyTokenProvider.php @@ -1,43 +1,23 @@ <?php declare(strict_types=1); - /** - * @copyright Copyright 2018, Roeland Jago Douma <roeland@famdouma.nl> - * - * @author Christoph Wurst <christoph@winzerhof-wurst.at> - * @author Daniel Kesselberg <mail@danielkesselberg.de> - * @author Joas Schilling <coding@schilljs.com> - * @author Morris Jobke <hey@morrisjobke.de> - * @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/>. - * + * SPDX-FileCopyrightText: 2018 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later */ namespace OC\Authentication\Token; use OC\Authentication\Exceptions\ExpiredTokenException; use OC\Authentication\Exceptions\InvalidTokenException; -use OC\Authentication\Exceptions\TokenPasswordExpiredException; use OC\Authentication\Exceptions\PasswordlessTokenException; +use OC\Authentication\Exceptions\TokenPasswordExpiredException; use OC\Authentication\Exceptions\WipeTokenException; -use OCP\AppFramework\Db\TTransactional; -use OCP\Cache\CappedMemoryCache; use OCP\AppFramework\Db\DoesNotExistException; +use OCP\AppFramework\Db\TTransactional; use OCP\AppFramework\Utility\ITimeFactory; +use OCP\Authentication\Token\IToken as OCPIToken; +use OCP\ICache; +use OCP\ICacheFactory; use OCP\IConfig; use OCP\IDBConnection; use OCP\IUserManager; @@ -47,6 +27,8 @@ use Psr\Log\LoggerInterface; class PublicKeyTokenProvider implements IProvider { public const TOKEN_MIN_LENGTH = 22; + /** Token cache TTL in seconds */ + private const TOKEN_CACHE_TTL = 10; use TTransactional; @@ -67,18 +49,20 @@ class PublicKeyTokenProvider implements IProvider { /** @var ITimeFactory */ private $time; - /** @var CappedMemoryCache */ + /** @var ICache */ private $cache; - private IHasher $hasher; + /** @var IHasher */ + private $hasher; public function __construct(PublicKeyTokenMapper $mapper, - ICrypto $crypto, - IConfig $config, - IDBConnection $db, - LoggerInterface $logger, - ITimeFactory $time, - IHasher $hasher) { + ICrypto $crypto, + IConfig $config, + IDBConnection $db, + LoggerInterface $logger, + ITimeFactory $time, + IHasher $hasher, + ICacheFactory $cacheFactory) { $this->mapper = $mapper; $this->crypto = $crypto; $this->config = $config; @@ -86,7 +70,9 @@ class PublicKeyTokenProvider implements IProvider { $this->logger = $logger; $this->time = $time; - $this->cache = new CappedMemoryCache(); + $this->cache = $cacheFactory->isLocalCacheAvailable() + ? $cacheFactory->createLocal('authtoken_') + : $cacheFactory->createInMemory(); $this->hasher = $hasher; } @@ -94,12 +80,12 @@ class PublicKeyTokenProvider implements IProvider { * {@inheritDoc} */ public function generateToken(string $token, - string $uid, - string $loginName, - ?string $password, - string $name, - int $type = IToken::TEMPORARY_TOKEN, - int $remember = IToken::DO_NOT_REMEMBER): IToken { + string $uid, + string $loginName, + ?string $password, + string $name, + int $type = OCPIToken::TEMPORARY_TOKEN, + int $remember = OCPIToken::DO_NOT_REMEMBER): OCPIToken { if (strlen($token) < self::TOKEN_MIN_LENGTH) { $exception = new InvalidTokenException('Token is too short, minimum of ' . self::TOKEN_MIN_LENGTH . ' characters is required, ' . strlen($token) . ' characters given'); $this->logger->error('Invalid token provided when generating new token', ['exception' => $exception]); @@ -128,12 +114,12 @@ class PublicKeyTokenProvider implements IProvider { } // Add the token to the cache - $this->cache[$dbToken->getToken()] = $dbToken; + $this->cacheToken($dbToken); return $dbToken; } - public function getToken(string $tokenId): IToken { + public function getToken(string $tokenId): OCPIToken { /** * Token length: 72 * @see \OC\Core\Controller\ClientFlowLoginController::generateAppPassword @@ -156,57 +142,76 @@ class PublicKeyTokenProvider implements IProvider { } $tokenHash = $this->hashToken($tokenId); + if ($token = $this->getTokenFromCache($tokenHash)) { + $this->checkToken($token); + return $token; + } - if (isset($this->cache[$tokenHash])) { - if ($this->cache[$tokenHash] instanceof DoesNotExistException) { - $ex = $this->cache[$tokenHash]; - throw new InvalidTokenException("Token does not exist: " . $ex->getMessage(), 0, $ex); - } - $token = $this->cache[$tokenHash]; - } else { + try { + $token = $this->mapper->getToken($tokenHash); + $this->cacheToken($token); + } catch (DoesNotExistException $ex) { try { - $token = $this->mapper->getToken($tokenHash); - $this->cache[$token->getToken()] = $token; - } catch (DoesNotExistException $ex) { - try { - $token = $this->mapper->getToken($this->hashTokenWithEmptySecret($tokenId)); - $this->cache[$token->getToken()] = $token; - $this->rotate($token, $tokenId, $tokenId); - } catch (DoesNotExistException $ex2) { - $this->cache[$tokenHash] = $ex2; - throw new InvalidTokenException("Token does not exist: " . $ex->getMessage(), 0, $ex); - } + $token = $this->mapper->getToken($this->hashTokenWithEmptySecret($tokenId)); + $this->rotate($token, $tokenId, $tokenId); + } catch (DoesNotExistException) { + $this->cacheInvalidHash($tokenHash); + throw new InvalidTokenException("Token does not exist: " . $ex->getMessage(), 0, $ex); } } - if ((int)$token->getExpires() !== 0 && $token->getExpires() < $this->time->getTime()) { - throw new ExpiredTokenException($token); - } + $this->checkToken($token); - if ($token->getType() === IToken::WIPE_TOKEN) { - throw new WipeTokenException($token); + return $token; + } + + /** + * @throws InvalidTokenException when token doesn't exist + */ + private function getTokenFromCache(string $tokenHash): ?PublicKeyToken { + $serializedToken = $this->cache->get($tokenHash); + if ($serializedToken === false) { + throw new InvalidTokenException('Token does not exist: ' . $tokenHash); } - if ($token->getPasswordInvalid() === true) { - //The password is invalid we should throw an TokenPasswordExpiredException - throw new TokenPasswordExpiredException($token); + if ($serializedToken === null) { + return null; } - return $token; + $token = unserialize($serializedToken, [ + 'allowed_classes' => [PublicKeyToken::class], + ]); + + return $token instanceof PublicKeyToken ? $token : null; + } + + private function cacheToken(PublicKeyToken $token): void { + $this->cache->set($token->getToken(), serialize($token), self::TOKEN_CACHE_TTL); + } + + private function cacheInvalidHash(string $tokenHash): void { + // Invalid entries can be kept longer in cache since it’s unlikely to reuse them + $this->cache->set($tokenHash, false, self::TOKEN_CACHE_TTL * 2); } - public function getTokenById(int $tokenId): IToken { + public function getTokenById(int $tokenId): OCPIToken { try { $token = $this->mapper->getTokenById($tokenId); } catch (DoesNotExistException $ex) { throw new InvalidTokenException("Token with ID $tokenId does not exist: " . $ex->getMessage(), 0, $ex); } + $this->checkToken($token); + + return $token; + } + + private function checkToken($token): void { if ((int)$token->getExpires() !== 0 && $token->getExpires() < $this->time->getTime()) { throw new ExpiredTokenException($token); } - if ($token->getType() === IToken::WIPE_TOKEN) { + if ($token->getType() === OCPIToken::WIPE_TOKEN) { throw new WipeTokenException($token); } @@ -214,13 +219,9 @@ class PublicKeyTokenProvider implements IProvider { //The password is invalid we should throw an TokenPasswordExpiredException throw new TokenPasswordExpiredException($token); } - - return $token; } - public function renewSessionToken(string $oldSessionId, string $sessionId): IToken { - $this->cache->clear(); - + public function renewSessionToken(string $oldSessionId, string $sessionId): OCPIToken { return $this->atomic(function () use ($oldSessionId, $sessionId) { $token = $this->getToken($oldSessionId); @@ -239,10 +240,12 @@ class PublicKeyTokenProvider implements IProvider { $token->getLoginName(), $password, $token->getName(), - IToken::TEMPORARY_TOKEN, + OCPIToken::TEMPORARY_TOKEN, $token->getRemember() ); + $this->cacheToken($newToken); + $this->cacheInvalidHash($token->getToken()); $this->mapper->delete($token); return $newToken; @@ -250,47 +253,44 @@ class PublicKeyTokenProvider implements IProvider { } public function invalidateToken(string $token) { - $this->cache->clear(); - + $tokenHash = $this->hashToken($token); $this->mapper->invalidate($this->hashToken($token)); $this->mapper->invalidate($this->hashTokenWithEmptySecret($token)); + $this->cacheInvalidHash($tokenHash); } public function invalidateTokenById(string $uid, int $id) { - $this->cache->clear(); + $token = $this->mapper->getTokenById($id); + if ($token->getUID() !== $uid) { + return; + } + $this->mapper->invalidate($token->getToken()); + $this->cacheInvalidHash($token->getToken()); - $this->mapper->deleteById($uid, $id); } public function invalidateOldTokens() { - $this->cache->clear(); - $olderThan = $this->time->getTime() - $this->config->getSystemValueInt('session_lifetime', 60 * 60 * 24); $this->logger->debug('Invalidating session tokens older than ' . date('c', $olderThan), ['app' => 'cron']); - $this->mapper->invalidateOld($olderThan, IToken::DO_NOT_REMEMBER); + $this->mapper->invalidateOld($olderThan, OCPIToken::DO_NOT_REMEMBER); $rememberThreshold = $this->time->getTime() - $this->config->getSystemValueInt('remember_login_cookie_lifetime', 60 * 60 * 24 * 15); $this->logger->debug('Invalidating remembered session tokens older than ' . date('c', $rememberThreshold), ['app' => 'cron']); - $this->mapper->invalidateOld($rememberThreshold, IToken::REMEMBER); + $this->mapper->invalidateOld($rememberThreshold, OCPIToken::REMEMBER); } public function invalidateLastUsedBefore(string $uid, int $before): void { - $this->cache->clear(); - $this->mapper->invalidateLastUsedBefore($uid, $before); } - public function updateToken(IToken $token) { - $this->cache->clear(); - + public function updateToken(OCPIToken $token) { if (!($token instanceof PublicKeyToken)) { throw new InvalidTokenException("Invalid token type"); } $this->mapper->update($token); + $this->cacheToken($token); } - public function updateTokenActivity(IToken $token) { - $this->cache->clear(); - + public function updateTokenActivity(OCPIToken $token) { if (!($token instanceof PublicKeyToken)) { throw new InvalidTokenException("Invalid token type"); } @@ -303,6 +303,7 @@ class PublicKeyTokenProvider implements IProvider { if ($token->getLastActivity() < ($now - $activityInterval)) { $token->setLastActivity($now); $this->mapper->updateActivity($token, $now); + $this->cacheToken($token); } } @@ -310,7 +311,7 @@ class PublicKeyTokenProvider implements IProvider { return $this->mapper->getTokenByUser($uid); } - public function getPassword(IToken $savedToken, string $tokenId): string { + public function getPassword(OCPIToken $savedToken, string $tokenId): string { if (!($savedToken instanceof PublicKeyToken)) { throw new InvalidTokenException("Invalid token type"); } @@ -326,9 +327,7 @@ class PublicKeyTokenProvider implements IProvider { return $this->decryptPassword($savedToken->getPassword(), $privateKey); } - public function setPassword(IToken $token, string $tokenId, string $password) { - $this->cache->clear(); - + public function setPassword(OCPIToken $token, string $tokenId, string $password) { if (!($token instanceof PublicKeyToken)) { throw new InvalidTokenException("Invalid token type"); } @@ -353,9 +352,7 @@ class PublicKeyTokenProvider implements IProvider { return $this->hasher->hash(sha1($password) . $password); } - public function rotate(IToken $token, string $oldTokenId, string $newTokenId): IToken { - $this->cache->clear(); - + public function rotate(OCPIToken $token, string $oldTokenId, string $newTokenId): OCPIToken { if (!($token instanceof PublicKeyToken)) { throw new InvalidTokenException("Invalid token type"); } @@ -425,12 +422,12 @@ class PublicKeyTokenProvider implements IProvider { * @throws \RuntimeException when OpenSSL reports a problem */ private function newToken(string $token, - string $uid, - string $loginName, - $password, - string $name, - int $type, - int $remember): PublicKeyToken { + string $uid, + string $loginName, + $password, + string $name, + int $type, + int $remember): PublicKeyToken { $dbToken = new PublicKeyToken(); $dbToken->setUid($uid); $dbToken->setLoginName($loginName); @@ -478,20 +475,17 @@ class PublicKeyTokenProvider implements IProvider { return $dbToken; } - public function markPasswordInvalid(IToken $token, string $tokenId) { - $this->cache->clear(); - + public function markPasswordInvalid(OCPIToken $token, string $tokenId) { if (!($token instanceof PublicKeyToken)) { throw new InvalidTokenException("Invalid token type"); } $token->setPasswordInvalid(true); $this->mapper->update($token); + $this->cacheToken($token); } public function updatePasswords(string $uid, string $password) { - $this->cache->clear(); - // prevent setting an empty pw as result of pw-less-login if ($password === '' || !$this->config->getSystemValueBool('auth.storeCryptedPassword', true)) { return; diff --git a/lib/private/Authentication/Token/RemoteWipe.php b/lib/private/Authentication/Token/RemoteWipe.php index 5fd01cfbe87..43c2bd060d1 100644 --- a/lib/private/Authentication/Token/RemoteWipe.php +++ b/lib/private/Authentication/Token/RemoteWipe.php @@ -3,38 +3,19 @@ declare(strict_types=1); /** - * @copyright 2019 Christoph Wurst <christoph@winzerhof-wurst.at> - * - * @author Christoph Wurst <christoph@winzerhof-wurst.at> - * @author Joas Schilling <coding@schilljs.com> - * @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/>. - * + * SPDX-FileCopyrightText: 2019 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later */ namespace OC\Authentication\Token; -use Psr\Log\LoggerInterface; -use function array_filter; use OC\Authentication\Events\RemoteWipeFinished; use OC\Authentication\Events\RemoteWipeStarted; -use OC\Authentication\Exceptions\InvalidTokenException; -use OC\Authentication\Exceptions\WipeTokenException; +use OCP\Authentication\Exceptions\InvalidTokenException; +use OCP\Authentication\Exceptions\WipeTokenException; use OCP\EventDispatcher\IEventDispatcher; use OCP\IUser; +use Psr\Log\LoggerInterface; +use function array_filter; class RemoteWipe { /** @var IProvider */ @@ -47,8 +28,8 @@ class RemoteWipe { private $logger; public function __construct(IProvider $tokenProvider, - IEventDispatcher $eventDispatcher, - LoggerInterface $logger) { + IEventDispatcher $eventDispatcher, + LoggerInterface $logger) { $this->tokenProvider = $tokenProvider; $this->eventDispatcher = $eventDispatcher; $this->logger = $logger; diff --git a/lib/private/Authentication/Token/TokenCleanupJob.php b/lib/private/Authentication/Token/TokenCleanupJob.php index 292f8f310e8..041d2e8a5e2 100644 --- a/lib/private/Authentication/Token/TokenCleanupJob.php +++ b/lib/private/Authentication/Token/TokenCleanupJob.php @@ -1,23 +1,7 @@ <?php /** - * @copyright 2022 Thomas Citharel <nextcloud@tcit.fr> - * - * @author Thomas Citharel <nextcloud@tcit.fr> - * - * @license AGPL-3.0 - * - * This code is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License, version 3, - * as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License, version 3, - * along with this program. If not, see <http://www.gnu.org/licenses/> - * + * SPDX-FileCopyrightText: 2022 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-only */ namespace OC\Authentication\Token; diff --git a/lib/private/Authentication/TwoFactorAuth/Db/ProviderUserAssignmentDao.php b/lib/private/Authentication/TwoFactorAuth/Db/ProviderUserAssignmentDao.php index 4817c6b8de0..c84b7f1af20 100644 --- a/lib/private/Authentication/TwoFactorAuth/Db/ProviderUserAssignmentDao.php +++ b/lib/private/Authentication/TwoFactorAuth/Db/ProviderUserAssignmentDao.php @@ -3,30 +3,11 @@ declare(strict_types=1); /** - * @copyright 2018 Christoph Wurst <christoph@winzerhof-wurst.at> - * - * @author Christoph Wurst <christoph@winzerhof-wurst.at> - * - * @license GNU AGPL version 3 or any later version - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as - * published by the Free Software Foundation, either version 3 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - * + * SPDX-FileCopyrightText: 2018 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later */ namespace OC\Authentication\TwoFactorAuth\Db; -use Doctrine\DBAL\Exception\UniqueConstraintViolationException; -use OCP\DB\QueryBuilder\IQueryBuilder; use OCP\IDBConnection; use function array_map; @@ -59,7 +40,7 @@ class ProviderUserAssignmentDao { $result = $query->execute(); $providers = []; foreach ($result->fetchAll() as $row) { - $providers[(string)$row['provider_id']] = 1 === (int)$row['enabled']; + $providers[(string)$row['provider_id']] = (int)$row['enabled'] === 1; } $result->closeCursor(); @@ -70,32 +51,29 @@ class ProviderUserAssignmentDao { * Persist a new/updated (provider_id, uid, enabled) tuple */ public function persist(string $providerId, string $uid, int $enabled): void { - $qb = $this->conn->getQueryBuilder(); - - try { - // Insert a new entry - $insertQuery = $qb->insert(self::TABLE_NAME)->values([ - 'provider_id' => $qb->createNamedParameter($providerId), - 'uid' => $qb->createNamedParameter($uid), - 'enabled' => $qb->createNamedParameter($enabled, IQueryBuilder::PARAM_INT), - ]); - - $insertQuery->execute(); - } catch (UniqueConstraintViolationException $ex) { - // There is already an entry -> update it - $updateQuery = $qb->update(self::TABLE_NAME) - ->set('enabled', $qb->createNamedParameter($enabled)) - ->where($qb->expr()->eq('provider_id', $qb->createNamedParameter($providerId))) - ->andWhere($qb->expr()->eq('uid', $qb->createNamedParameter($uid))); - $updateQuery->execute(); + $conn = $this->conn; + + // Insert a new entry + if ($conn->insertIgnoreConflict(self::TABLE_NAME, [ + 'provider_id' => $providerId, + 'uid' => $uid, + 'enabled' => $enabled, + ])) { + return; } + + // There is already an entry -> update it + $qb = $conn->getQueryBuilder(); + $updateQuery = $qb->update(self::TABLE_NAME) + ->set('enabled', $qb->createNamedParameter($enabled)) + ->where($qb->expr()->eq('provider_id', $qb->createNamedParameter($providerId))) + ->andWhere($qb->expr()->eq('uid', $qb->createNamedParameter($uid))); + $updateQuery->executeStatement(); } /** * Delete all provider states of a user and return the provider IDs * - * @param string $uid - * * @return list<array{provider_id: string, uid: string, enabled: bool}> */ public function deleteByUser(string $uid): array { @@ -103,7 +81,7 @@ class ProviderUserAssignmentDao { $selectQuery = $qb1->select('*') ->from(self::TABLE_NAME) ->where($qb1->expr()->eq('uid', $qb1->createNamedParameter($uid))); - $selectResult = $selectQuery->execute(); + $selectResult = $selectQuery->executeQuery(); $rows = $selectResult->fetchAll(); $selectResult->closeCursor(); @@ -111,15 +89,15 @@ class ProviderUserAssignmentDao { $deleteQuery = $qb2 ->delete(self::TABLE_NAME) ->where($qb2->expr()->eq('uid', $qb2->createNamedParameter($uid))); - $deleteQuery->execute(); + $deleteQuery->executeStatement(); - return array_map(function (array $row) { + return array_values(array_map(function (array $row) { return [ - 'provider_id' => $row['provider_id'], - 'uid' => $row['uid'], - 'enabled' => 1 === (int) $row['enabled'], + 'provider_id' => (string)$row['provider_id'], + 'uid' => (string)$row['uid'], + 'enabled' => ((int) $row['enabled']) === 1, ]; - }, $rows); + }, $rows)); } public function deleteAll(string $providerId): void { diff --git a/lib/private/Authentication/TwoFactorAuth/EnforcementState.php b/lib/private/Authentication/TwoFactorAuth/EnforcementState.php index b95128c1e0f..e02064bc8f7 100644 --- a/lib/private/Authentication/TwoFactorAuth/EnforcementState.php +++ b/lib/private/Authentication/TwoFactorAuth/EnforcementState.php @@ -3,25 +3,8 @@ declare(strict_types=1); /** - * @copyright 2018 Christoph Wurst <christoph@winzerhof-wurst.at> - * - * @author Christoph Wurst <christoph@winzerhof-wurst.at> - * - * @license GNU AGPL version 3 or any later version - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as - * published by the Free Software Foundation, either version 3 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - * + * SPDX-FileCopyrightText: 2018 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later */ namespace OC\Authentication\TwoFactorAuth; @@ -45,8 +28,8 @@ class EnforcementState implements JsonSerializable { * @param string[] $excludedGroups */ public function __construct(bool $enforced, - array $enforcedGroups = [], - array $excludedGroups = []) { + array $enforcedGroups = [], + array $excludedGroups = []) { $this->enforced = $enforced; $this->enforcedGroups = $enforcedGroups; $this->excludedGroups = $excludedGroups; diff --git a/lib/private/Authentication/TwoFactorAuth/Manager.php b/lib/private/Authentication/TwoFactorAuth/Manager.php index ff0c33445a2..2585646c998 100644 --- a/lib/private/Authentication/TwoFactorAuth/Manager.php +++ b/lib/private/Authentication/TwoFactorAuth/Manager.php @@ -1,38 +1,19 @@ <?php declare(strict_types=1); - /** - * @copyright Copyright (c) 2016, ownCloud, Inc. - * - * @author Christoph Wurst <christoph@winzerhof-wurst.at> - * @author Joas Schilling <coding@schilljs.com> - * @author Lukas Reschke <lukas@statuscode.ch> - * @author Roeland Jago Douma <roeland@famdouma.nl> - * - * @license AGPL-3.0 - * - * This code is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License, version 3, - * as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License, version 3, - * along with this program. If not, see <http://www.gnu.org/licenses/> - * + * SPDX-FileCopyrightText: 2016-2024 Nextcloud GmbH and Nextcloud contributors + * SPDX-FileCopyrightText: 2016 ownCloud, Inc. + * SPDX-License-Identifier: AGPL-3.0-only */ namespace OC\Authentication\TwoFactorAuth; use BadMethodCallException; use Exception; -use OC\Authentication\Exceptions\InvalidTokenException; use OC\Authentication\Token\IProvider as TokenProvider; use OCP\Activity\IManager; use OCP\AppFramework\Utility\ITimeFactory; +use OCP\Authentication\Exceptions\InvalidTokenException; use OCP\Authentication\TwoFactorAuth\IActivatableAtLogin; use OCP\Authentication\TwoFactorAuth\IProvider; use OCP\Authentication\TwoFactorAuth\IRegistry; @@ -89,15 +70,15 @@ class Manager { private $userIsTwoFactorAuthenticated = []; public function __construct(ProviderLoader $providerLoader, - IRegistry $providerRegistry, - MandatoryTwoFactor $mandatoryTwoFactor, - ISession $session, - IConfig $config, - IManager $activityManager, - LoggerInterface $logger, - TokenProvider $tokenProvider, - ITimeFactory $timeFactory, - IEventDispatcher $eventDispatcher) { + IRegistry $providerRegistry, + MandatoryTwoFactor $mandatoryTwoFactor, + ISession $session, + IConfig $config, + IManager $activityManager, + LoggerInterface $logger, + TokenProvider $tokenProvider, + ITimeFactory $timeFactory, + IEventDispatcher $eventDispatcher) { $this->providerLoader = $providerLoader; $this->providerRegistry = $providerRegistry; $this->mandatoryTwoFactor = $mandatoryTwoFactor; @@ -313,13 +294,13 @@ class Manager { * @param IUser $user the currently logged in user * @return boolean */ - public function needsSecondFactor(IUser $user = null): bool { + public function needsSecondFactor(?IUser $user = null): bool { if ($user === null) { return false; } - // If we are authenticated using an app password skip all this - if ($this->session->exists('app_password')) { + // If we are authenticated using an app password or AppAPI Auth, skip all this + if ($this->session->exists('app_password') || $this->session->get('app_api') === true) { return false; } diff --git a/lib/private/Authentication/TwoFactorAuth/MandatoryTwoFactor.php b/lib/private/Authentication/TwoFactorAuth/MandatoryTwoFactor.php index 3bfbd77941b..37c9d3fc550 100644 --- a/lib/private/Authentication/TwoFactorAuth/MandatoryTwoFactor.php +++ b/lib/private/Authentication/TwoFactorAuth/MandatoryTwoFactor.php @@ -3,25 +3,8 @@ declare(strict_types=1); /** - * @copyright 2018 Christoph Wurst <christoph@winzerhof-wurst.at> - * - * @author Christoph Wurst <christoph@winzerhof-wurst.at> - * - * @license GNU AGPL version 3 or any later version - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as - * published by the Free Software Foundation, either version 3 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - * + * SPDX-FileCopyrightText: 2018 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later */ namespace OC\Authentication\TwoFactorAuth; diff --git a/lib/private/Authentication/TwoFactorAuth/ProviderLoader.php b/lib/private/Authentication/TwoFactorAuth/ProviderLoader.php index efd92f8ba30..b9a0a97bec4 100644 --- a/lib/private/Authentication/TwoFactorAuth/ProviderLoader.php +++ b/lib/private/Authentication/TwoFactorAuth/ProviderLoader.php @@ -3,26 +3,8 @@ declare(strict_types=1); /** - * @copyright 2018 Christoph Wurst <christoph@winzerhof-wurst.at> - * - * @author Christoph Wurst <christoph@winzerhof-wurst.at> - * @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/>. - * + * SPDX-FileCopyrightText: 2018 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later */ namespace OC\Authentication\TwoFactorAuth; diff --git a/lib/private/Authentication/TwoFactorAuth/ProviderManager.php b/lib/private/Authentication/TwoFactorAuth/ProviderManager.php index c7c075bdab3..5ce4c598154 100644 --- a/lib/private/Authentication/TwoFactorAuth/ProviderManager.php +++ b/lib/private/Authentication/TwoFactorAuth/ProviderManager.php @@ -3,25 +3,8 @@ declare(strict_types=1); /** - * @copyright 2018 Christoph Wurst <christoph@winzerhof-wurst.at> - * - * @author Christoph Wurst <christoph@winzerhof-wurst.at> - * - * @license GNU AGPL version 3 or any later version - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as - * published by the Free Software Foundation, either version 3 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - * + * SPDX-FileCopyrightText: 2018 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later */ namespace OC\Authentication\TwoFactorAuth; diff --git a/lib/private/Authentication/TwoFactorAuth/ProviderSet.php b/lib/private/Authentication/TwoFactorAuth/ProviderSet.php index af270fb83c8..15b82be6dec 100644 --- a/lib/private/Authentication/TwoFactorAuth/ProviderSet.php +++ b/lib/private/Authentication/TwoFactorAuth/ProviderSet.php @@ -3,31 +3,14 @@ declare(strict_types=1); /** - * @copyright 2018 Christoph Wurst <christoph@winzerhof-wurst.at> - * - * @author Christoph Wurst <christoph@winzerhof-wurst.at> - * - * @license GNU AGPL version 3 or any later version - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as - * published by the Free Software Foundation, either version 3 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - * + * SPDX-FileCopyrightText: 2018 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later */ namespace OC\Authentication\TwoFactorAuth; -use function array_filter; use OCA\TwoFactorBackupCodes\Provider\BackupCodesProvider; use OCP\Authentication\TwoFactorAuth\IProvider; +use function array_filter; /** * Contains all two-factor provider information for the two-factor login challenge diff --git a/lib/private/Authentication/TwoFactorAuth/Registry.php b/lib/private/Authentication/TwoFactorAuth/Registry.php index 482c025e144..544f60c4f97 100644 --- a/lib/private/Authentication/TwoFactorAuth/Registry.php +++ b/lib/private/Authentication/TwoFactorAuth/Registry.php @@ -3,26 +3,8 @@ declare(strict_types=1); /** - * @copyright 2018 Christoph Wurst <christoph@winzerhof-wurst.at> - * - * @author Christoph Wurst <christoph@winzerhof-wurst.at> - * @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/>. - * + * SPDX-FileCopyrightText: 2018 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later */ namespace OC\Authentication\TwoFactorAuth; @@ -45,7 +27,7 @@ class Registry implements IRegistry { private $dispatcher; public function __construct(ProviderUserAssignmentDao $assignmentDao, - IEventDispatcher $dispatcher) { + IEventDispatcher $dispatcher) { $this->assignmentDao = $assignmentDao; $this->dispatcher = $dispatcher; } diff --git a/lib/private/Authentication/WebAuthn/CredentialRepository.php b/lib/private/Authentication/WebAuthn/CredentialRepository.php index e5c3fcf1618..f32136f9594 100644 --- a/lib/private/Authentication/WebAuthn/CredentialRepository.php +++ b/lib/private/Authentication/WebAuthn/CredentialRepository.php @@ -3,25 +3,8 @@ declare(strict_types=1); /** - * @copyright Copyright (c) 2020, Roeland Jago Douma <roeland@famdouma.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/>. - * + * SPDX-FileCopyrightText: 2020 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later */ namespace OC\Authentication\WebAuthn; @@ -61,7 +44,7 @@ class CredentialRepository implements PublicKeyCredentialSourceRepository { }, $entities); } - public function saveAndReturnCredentialSource(PublicKeyCredentialSource $publicKeyCredentialSource, string $name = null): PublicKeyCredentialEntity { + public function saveAndReturnCredentialSource(PublicKeyCredentialSource $publicKeyCredentialSource, ?string $name = null): PublicKeyCredentialEntity { $oldEntity = null; try { @@ -87,7 +70,7 @@ class CredentialRepository implements PublicKeyCredentialSourceRepository { return $this->credentialMapper->insertOrUpdate($entity); } - public function saveCredentialSource(PublicKeyCredentialSource $publicKeyCredentialSource, string $name = null): void { + public function saveCredentialSource(PublicKeyCredentialSource $publicKeyCredentialSource, ?string $name = null): void { $this->saveAndReturnCredentialSource($publicKeyCredentialSource, $name); } } diff --git a/lib/private/Authentication/WebAuthn/Db/PublicKeyCredentialEntity.php b/lib/private/Authentication/WebAuthn/Db/PublicKeyCredentialEntity.php index 6f97ded483d..443a7985cae 100644 --- a/lib/private/Authentication/WebAuthn/Db/PublicKeyCredentialEntity.php +++ b/lib/private/Authentication/WebAuthn/Db/PublicKeyCredentialEntity.php @@ -3,26 +3,8 @@ declare(strict_types=1); /** - * @copyright Copyright (c) 2020, Roeland Jago Douma <roeland@famdouma.nl> - * - * @author Christoph Wurst <christoph@winzerhof-wurst.at> - * @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/>. - * + * SPDX-FileCopyrightText: 2020 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later */ namespace OC\Authentication\WebAuthn\Db; diff --git a/lib/private/Authentication/WebAuthn/Db/PublicKeyCredentialMapper.php b/lib/private/Authentication/WebAuthn/Db/PublicKeyCredentialMapper.php index 72c557eb53a..fa7304157c8 100644 --- a/lib/private/Authentication/WebAuthn/Db/PublicKeyCredentialMapper.php +++ b/lib/private/Authentication/WebAuthn/Db/PublicKeyCredentialMapper.php @@ -3,26 +3,8 @@ declare(strict_types=1); /** - * @copyright Copyright (c) 2020, Roeland Jago Douma <roeland@famdouma.nl> - * - * @author Christoph Wurst <christoph@winzerhof-wurst.at> - * @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/>. - * + * SPDX-FileCopyrightText: 2020 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later */ namespace OC\Authentication\WebAuthn\Db; diff --git a/lib/private/Authentication/WebAuthn/Manager.php b/lib/private/Authentication/WebAuthn/Manager.php index 744a3fa354a..007be245992 100644 --- a/lib/private/Authentication/WebAuthn/Manager.php +++ b/lib/private/Authentication/WebAuthn/Manager.php @@ -3,27 +3,8 @@ declare(strict_types=1); /** - * @copyright Copyright (c) 2020, Roeland Jago Douma <roeland@famdouma.nl> - * - * @author Christoph Wurst <christoph@winzerhof-wurst.at> - * @author Joas Schilling <coding@schilljs.com> - * @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/>. - * + * SPDX-FileCopyrightText: 2020 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later */ namespace OC\Authentication\WebAuthn; @@ -83,15 +64,15 @@ class Manager { public function startRegistration(IUser $user, string $serverHost): PublicKeyCredentialCreationOptions { $rpEntity = new PublicKeyCredentialRpEntity( 'Nextcloud', //Name - $this->stripPort($serverHost), //ID + $this->stripPort($serverHost), //ID null //Icon ); $userEntity = new PublicKeyCredentialUserEntity( - $user->getUID(), //Name - $user->getUID(), //ID - $user->getDisplayName() //Display name -// 'https://foo.example.co/avatar/123e4567-e89b-12d3-a456-426655440000' //Icon + $user->getUID(), // Name + $user->getUID(), // ID + $user->getDisplayName() // Display name + // 'https://foo.example.co/avatar/123e4567-e89b-12d3-a456-426655440000' //Icon ); $challenge = random_bytes(32); @@ -108,8 +89,9 @@ class Manager { $authenticatorSelectionCriteria = new AuthenticatorSelectionCriteria( null, + AuthenticatorSelectionCriteria::USER_VERIFICATION_REQUIREMENT_DISCOURAGED, + null, false, - AuthenticatorSelectionCriteria::USER_VERIFICATION_REQUIREMENT_DISCOURAGED ); return new PublicKeyCredentialCreationOptions( @@ -117,11 +99,10 @@ class Manager { $userEntity, $challenge, $publicKeyCredentialParametersList, - $timeout, - $excludedPublicKeyDescriptors, $authenticatorSelectionCriteria, PublicKeyCredentialCreationOptions::ATTESTATION_CONVEYANCE_PREFERENCE_NONE, - null + $excludedPublicKeyDescriptors, + $timeout, ); } @@ -149,7 +130,7 @@ class Manager { try { // Load the data $publicKeyCredential = $publicKeyCredentialLoader->load($data); - $response = $publicKeyCredential->getResponse(); + $response = $publicKeyCredential->response; // Check if the response is an Authenticator Attestation Response if (!$response instanceof AuthenticatorAttestationResponse) { @@ -162,7 +143,9 @@ class Manager { $publicKeyCredentialSource = $authenticatorAttestationResponseValidator->check( $response, $publicKeyCredentialCreationOptions, - $request); + $request, + ['localhost'], + ); } catch (\Throwable $exception) { throw $exception; } @@ -180,18 +163,18 @@ class Manager { $registeredPublicKeyCredentialDescriptors = array_map(function (PublicKeyCredentialEntity $entity) { $credential = $entity->toPublicKeyCredentialSource(); return new PublicKeyCredentialDescriptor( - $credential->getType(), - $credential->getPublicKeyCredentialId() + $credential->type, + $credential->publicKeyCredentialId, ); }, $this->credentialMapper->findAllForUid($uid)); // Public Key Credential Request Options return new PublicKeyCredentialRequestOptions( - random_bytes(32), // Challenge - 60000, // Timeout - $this->stripPort($serverHost), // Relying Party ID - $registeredPublicKeyCredentialDescriptors, // Registered PublicKeyCredentialDescriptor classes - AuthenticatorSelectionCriteria::USER_VERIFICATION_REQUIREMENT_DISCOURAGED + random_bytes(32), // Challenge + $this->stripPort($serverHost), // Relying Party ID + $registeredPublicKeyCredentialDescriptors, // Registered PublicKeyCredentialDescriptor classes + AuthenticatorSelectionCriteria::USER_VERIFICATION_REQUIREMENT_DISCOURAGED, + 60000, // Timeout ); } @@ -213,16 +196,15 @@ class Manager { $tokenBindingHandler, $extensionOutputCheckerHandler, $algorithmManager, - null, - $this->logger, ); + $authenticatorAssertionResponseValidator->setLogger($this->logger); try { $this->logger->debug('Loading publickey credentials from: ' . $data); // Load the data $publicKeyCredential = $publicKeyCredentialLoader->load($data); - $response = $publicKeyCredential->getResponse(); + $response = $publicKeyCredential->response; // Check if the response is an Authenticator Attestation Response if (!$response instanceof AuthenticatorAssertionResponse) { @@ -233,18 +215,17 @@ class Manager { $request = ServerRequest::fromGlobals(); $publicKeyCredentialSource = $authenticatorAssertionResponseValidator->check( - $publicKeyCredential->getRawId(), + $publicKeyCredential->rawId, $response, $publicKeyCredentialRequestOptions, $request, - $uid + $uid, + ['localhost'], ); } catch (\Throwable $e) { throw $e; } - - return true; } diff --git a/lib/private/Avatar/Avatar.php b/lib/private/Avatar/Avatar.php index 69bf9bacfcf..1ad70001f13 100644 --- a/lib/private/Avatar/Avatar.php +++ b/lib/private/Avatar/Avatar.php @@ -3,36 +3,9 @@ declare(strict_types=1); /** - * @copyright Copyright (c) 2016, ownCloud, Inc. - * @copyright 2018 John Molakvoæ <skjnldsv@protonmail.com> - * - * @author Christopher Schäpers <kondou@ts.unde.re> - * @author Christoph Wurst <christoph@winzerhof-wurst.at> - * @author Jan-Christoph Borchardt <hey@jancborchardt.net> - * @author Joas Schilling <coding@schilljs.com> - * @author John Molakvoæ <skjnldsv@protonmail.com> - * @author Julius Härtl <jus@bitgrid.net> - * @author Michael Weimann <mail@michael-weimann.eu> - * @author Morris Jobke <hey@morrisjobke.de> - * @author Robin Appelman <robin@icewind.nl> - * @author Roeland Jago Douma <roeland@famdouma.nl> - * @author Sergey Shliakhov <husband.sergey@gmail.com> - * @author Thomas Müller <thomas.mueller@tmit.eu> - * - * @license AGPL-3.0 - * - * This code is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License, version 3, - * as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License, version 3, - * along with this program. If not, see <http://www.gnu.org/licenses/> - * + * SPDX-FileCopyrightText: 2018 Nextcloud GmbH and Nextcloud contributors + * SPDX-FileCopyrightText: 2016 ownCloud, Inc. + * SPDX-License-Identifier: AGPL-3.0-only */ namespace OC\Avatar; @@ -151,6 +124,7 @@ abstract class Avatar implements IAvatar { /** * Generate png avatar with GD + * @throws \Exception when an error occurs in gd calls */ protected function generateAvatar(string $userDisplayName, int $size, bool $darkTheme): string { $text = $this->getAvatarText(); @@ -158,6 +132,9 @@ abstract class Avatar implements IAvatar { $backgroundColor = $textColor->alphaBlending(0.1, $darkTheme ? new Color(0, 0, 0) : new Color(255, 255, 255)); $im = imagecreatetruecolor($size, $size); + if ($im === false) { + throw new \Exception('Failed to create avatar image'); + } $background = imagecolorallocate( $im, $backgroundColor->red(), @@ -169,6 +146,9 @@ abstract class Avatar implements IAvatar { $textColor->green(), $textColor->blue() ); + if ($background === false || $textColor === false) { + throw new \Exception('Failed to create avatar image color'); + } imagefilledrectangle($im, 0, 0, $size, $size, $background); $font = __DIR__ . '/../../../core/fonts/NotoSans-Regular.ttf'; @@ -191,7 +171,7 @@ abstract class Avatar implements IAvatar { /** * Calculate real image ttf center * - * @param resource $image + * @param \GdImage $image * @param string $text text string * @param string $font font path * @param int $size font size diff --git a/lib/private/Avatar/AvatarManager.php b/lib/private/Avatar/AvatarManager.php index 4125c8eb0a8..f8ce4d5b656 100644 --- a/lib/private/Avatar/AvatarManager.php +++ b/lib/private/Avatar/AvatarManager.php @@ -1,37 +1,10 @@ <?php declare(strict_types=1); - /** - * @copyright Copyright (c) 2016, ownCloud, Inc. - * - * @author Arthur Schiwon <blizzz@arthur-schiwon.de> - * @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 Lukas Reschke <lukas@statuscode.ch> - * @author Michael Weimann <mail@michael-weimann.eu> - * @author Morris Jobke <hey@morrisjobke.de> - * @author Robin Appelman <robin@icewind.nl> - * @author Roeland Jago Douma <roeland@famdouma.nl> - * @author Thomas Müller <thomas.mueller@tmit.eu> - * @author Vincent Petry <vincent@nextcloud.com> - * - * @license AGPL-3.0 - * - * This code is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License, version 3, - * as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License, version 3, - * along with this program. If not, see <http://www.gnu.org/licenses/> - * + * SPDX-FileCopyrightText: 2019-2024 Nextcloud GmbH and Nextcloud contributors + * SPDX-FileCopyrightText: 2016 ownCloud, Inc. + * SPDX-License-Identifier: AGPL-3.0-only */ namespace OC\Avatar; @@ -55,71 +28,42 @@ use Psr\Log\LoggerInterface; * This class implements methods to access Avatar functionality */ class AvatarManager implements IAvatarManager { - /** @var IUserSession */ - private $userSession; - - /** @var Manager */ - private $userManager; - - /** @var IAppData */ - private $appData; - - /** @var IL10N */ - private $l; - - /** @var LoggerInterface */ - private $logger; - - /** @var IConfig */ - private $config; - - /** @var IAccountManager */ - private $accountManager; - - /** @var KnownUserService */ - private $knownUserService; - public function __construct( - IUserSession $userSession, - Manager $userManager, - IAppData $appData, - IL10N $l, - LoggerInterface $logger, - IConfig $config, - IAccountManager $accountManager, - KnownUserService $knownUserService + private IUserSession $userSession, + private Manager $userManager, + private IAppData $appData, + private IL10N $l, + private LoggerInterface $logger, + private IConfig $config, + private IAccountManager $accountManager, + private KnownUserService $knownUserService, ) { - $this->userSession = $userSession; - $this->userManager = $userManager; - $this->appData = $appData; - $this->l = $l; - $this->logger = $logger; - $this->config = $config; - $this->accountManager = $accountManager; - $this->knownUserService = $knownUserService; } /** * return a user specific instance of \OCP\IAvatar + * + * If the user is disabled a guest avatar will be returned + * * @see \OCP\IAvatar * @param string $userId the ownCloud user id - * @return \OCP\IAvatar * @throws \Exception In case the username is potentially dangerous * @throws NotFoundException In case there is no user folder yet */ - public function getAvatar(string $userId) : IAvatar { + public function getAvatar(string $userId): IAvatar { $user = $this->userManager->get($userId); if ($user === null) { throw new \Exception('user does not exist'); } + if (!$user->isEnabled()) { + return $this->getGuestAvatar($userId); + } + // sanitize userID - fixes casing issue (needed for the filesystem stuff that is done below) $userId = $user->getUID(); - $requestingUser = null; - if ($this->userSession !== null) { - $requestingUser = $this->userSession->getUser(); - } + $requestingUser = $this->userSession->getUser(); try { $folder = $this->appData->getFolder($userId); @@ -157,7 +101,7 @@ class AvatarManager implements IAvatarManager { /** * Clear generated avatars */ - public function clearCachedAvatars() { + public function clearCachedAvatars(): void { $users = $this->config->getUsersForUserValue('avatar', 'generated', 'true'); foreach ($users as $userId) { // This also bumps the avatar version leading to cache invalidation in browsers @@ -174,7 +118,7 @@ class AvatarManager implements IAvatarManager { } catch (NotPermittedException | StorageNotAvailableException $e) { $this->logger->error("Unable to delete user avatars for $userId. gnoring avatar deletion"); } catch (NoUserException $e) { - $this->logger->debug("User $userId not found. gnoring avatar deletion"); + $this->logger->debug("Account $userId not found. Ignoring avatar deletion"); } $this->config->deleteUserValue($userId, 'avatar', 'generated'); } @@ -183,7 +127,6 @@ class AvatarManager implements IAvatarManager { * Returns a GuestAvatar. * * @param string $name The guest name, e.g. "Albert". - * @return IAvatar */ public function getGuestAvatar(string $name): IAvatar { return new GuestAvatar($name, $this->logger); diff --git a/lib/private/Avatar/GuestAvatar.php b/lib/private/Avatar/GuestAvatar.php index 083deb4108f..7ae633f1260 100644 --- a/lib/private/Avatar/GuestAvatar.php +++ b/lib/private/Avatar/GuestAvatar.php @@ -3,31 +3,13 @@ declare(strict_types=1); /** - * @copyright Copyright (c) 2018, Michael Weimann <mail@michael-weimann.eu> - * - * @author Joas Schilling <coding@schilljs.com> - * @author Michael Weimann <mail@michael-weimann.eu> - * - * @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/>. - * + * SPDX-FileCopyrightText: 2018 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later */ namespace OC\Avatar; -use OCP\Files\SimpleFS\ISimpleFile; use OCP\Files\SimpleFS\InMemoryFile; +use OCP\Files\SimpleFS\ISimpleFile; use Psr\Log\LoggerInterface; /** @@ -35,18 +17,15 @@ use Psr\Log\LoggerInterface; */ class GuestAvatar extends Avatar { /** - * Holds the guest user display name. - */ - private string $userDisplayName; - - /** * GuestAvatar constructor. * * @param string $userDisplayName The guest user display name */ - public function __construct(string $userDisplayName, LoggerInterface $logger) { + public function __construct( + private string $userDisplayName, + LoggerInterface $logger, + ) { parent::__construct($logger); - $this->userDisplayName = $userDisplayName; } /** @@ -68,7 +47,6 @@ class GuestAvatar extends Avatar { * Setting avatars isn't implemented for guests. * * @param \OCP\IImage|resource|string $data - * @return void */ public function set($data): void { // unimplemented for guest user avatars diff --git a/lib/private/Avatar/PlaceholderAvatar.php b/lib/private/Avatar/PlaceholderAvatar.php index e7ca89f4d30..07c54f62713 100644 --- a/lib/private/Avatar/PlaceholderAvatar.php +++ b/lib/private/Avatar/PlaceholderAvatar.php @@ -3,26 +3,8 @@ declare(strict_types=1); /** - * @copyright Copyright (c) 2018, Michael Weimann <mail@michael-weimann.eu> - * - * @author Joas Schilling <coding@schilljs.com> - * @author Vincent Petry <vincent@nextcloud.com> - * - * @license GNU AGPL version 3 or any later version - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as - * published by the Free Software Foundation, either version 3 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - * + * SPDX-FileCopyrightText: 2018 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later */ namespace OC\Avatar; @@ -32,9 +14,7 @@ use OCP\Files\NotFoundException; use OCP\Files\NotPermittedException; use OCP\Files\SimpleFS\ISimpleFile; use OCP\Files\SimpleFS\ISimpleFolder; -use OCP\IConfig; use OCP\IImage; -use OCP\IL10N; use Psr\Log\LoggerInterface; /** @@ -44,26 +24,12 @@ use Psr\Log\LoggerInterface; * for faster retrieval, unlike the GuestAvatar. */ class PlaceholderAvatar extends Avatar { - private ISimpleFolder $folder; - private User $user; - - /** - * UserAvatar constructor. - * - * @param IConfig $config The configuration - * @param ISimpleFolder $folder The avatar files folder - * @param IL10N $l The localization helper - * @param User $user The user this class manages the avatar for - * @param LoggerInterface $logger The logger - */ public function __construct( - ISimpleFolder $folder, - $user, - LoggerInterface $logger) { + private ISimpleFolder $folder, + private User $user, + LoggerInterface $logger, + ) { parent::__construct($logger); - - $this->folder = $folder; - $this->user = $user; } /** @@ -80,7 +46,6 @@ class PlaceholderAvatar extends Avatar { * @throws \Exception if the provided file is not a jpg or png image * @throws \Exception if the provided image is not valid * @throws NotSquareException if the image is not square - * @return void */ public function set($data): void { // unimplemented for placeholder avatars @@ -102,8 +67,6 @@ class PlaceholderAvatar extends Avatar { * * If there is no avatar file yet, one is generated. * - * @param int $size - * @return ISimpleFile * @throws NotFoundException * @throws \OCP\Files\NotPermittedException * @throws \OCP\PreConditionNotMetException diff --git a/lib/private/Avatar/UserAvatar.php b/lib/private/Avatar/UserAvatar.php index 6d39d5f067d..ce604394c4e 100644 --- a/lib/private/Avatar/UserAvatar.php +++ b/lib/private/Avatar/UserAvatar.php @@ -3,29 +3,8 @@ declare(strict_types=1); /** - * @copyright Copyright (c) 2018, Michael Weimann <mail@michael-weimann.eu> - * - * @author Arthur Schiwon <blizzz@arthur-schiwon.de> - * @author Christoph Wurst <christoph@winzerhof-wurst.at> - * @author Joas Schilling <coding@schilljs.com> - * @author Michael Weimann <mail@michael-weimann.eu> - * @author Vincent Petry <vincent@nextcloud.com> - * - * @license GNU AGPL version 3 or any later version - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as - * published by the Free Software Foundation, either version 3 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - * + * SPDX-FileCopyrightText: 2018 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later */ namespace OC\Avatar; @@ -44,31 +23,14 @@ use Psr\Log\LoggerInterface; * This class represents a registered user's avatar. */ class UserAvatar extends Avatar { - private IConfig $config; - private ISimpleFolder $folder; - private IL10N $l; - private User $user; - - /** - * UserAvatar constructor. - * - * @param IConfig $config The configuration - * @param ISimpleFolder $folder The avatar files folder - * @param IL10N $l The localization helper - * @param User $user The user this class manages the avatar for - * @param LoggerInterface $logger The logger - */ public function __construct( - ISimpleFolder $folder, - IL10N $l, - User $user, + private ISimpleFolder $folder, + private IL10N $l, + private User $user, LoggerInterface $logger, - IConfig $config) { + private IConfig $config, + ) { parent::__construct($logger); - $this->folder = $folder; - $this->l = $l; - $this->user = $user; - $this->config = $config; } /** @@ -85,7 +47,6 @@ class UserAvatar extends Avatar { * @throws \Exception if the provided file is not a jpg or png image * @throws \Exception if the provided image is not valid * @throws NotSquareException if the image is not square - * @return void */ public function set($data): void { $img = $this->getAvatarImage($data); @@ -113,7 +74,6 @@ class UserAvatar extends Avatar { * Returns an image from several sources. * * @param IImage|resource|string|\GdImage $data An image object, imagedata or path to the avatar - * @return IImage */ private function getAvatarImage($data): IImage { if ($data instanceof IImage) { @@ -229,8 +189,6 @@ class UserAvatar extends Avatar { * * If there is no avatar file yet, one is generated. * - * @param int $size - * @return ISimpleFile * @throws NotFoundException * @throws \OCP\Files\NotPermittedException * @throws \OCP\PreConditionNotMetException diff --git a/lib/private/BackgroundJob/Job.php b/lib/private/BackgroundJob/Job.php deleted file mode 100644 index ffcaaf8c36d..00000000000 --- a/lib/private/BackgroundJob/Job.php +++ /dev/null @@ -1,98 +0,0 @@ -<?php -/** - * @copyright Copyright (c) 2016, ownCloud, Inc. - * - * @author Daniel Kesselberg <mail@danielkesselberg.de> - * @author Morris Jobke <hey@morrisjobke.de> - * @author Noveen Sachdeva <noveen.sachdeva@research.iiit.ac.in> - * @author Robin Appelman <robin@icewind.nl> - * @author Thomas Müller <thomas.mueller@tmit.eu> - * - * @license AGPL-3.0 - * - * This code is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License, version 3, - * as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License, version 3, - * along with this program. If not, see <http://www.gnu.org/licenses/> - * - */ -namespace OC\BackgroundJob; - -use OCP\BackgroundJob\IJob; -use OCP\BackgroundJob\IJobList; -use OCP\ILogger; - -/** - * @deprecated internal class, use \OCP\BackgroundJob\Job - */ -abstract class Job implements IJob { - /** @var int */ - protected $id; - - /** @var int */ - protected $lastRun; - - /** @var mixed */ - protected $argument; - - public function execute(IJobList $jobList, ILogger $logger = null) { - $jobList->setLastRun($this); - if ($logger === null) { - $logger = \OC::$server->getLogger(); - } - - try { - $jobStartTime = time(); - $logger->debug('Run ' . get_class($this) . ' job with ID ' . $this->getId(), ['app' => 'cron']); - $this->run($this->argument); - $timeTaken = time() - $jobStartTime; - - $logger->debug('Finished ' . get_class($this) . ' job with ID ' . $this->getId() . ' in ' . $timeTaken . ' seconds', ['app' => 'cron']); - $jobList->setExecutionTime($this, $timeTaken); - } catch (\Throwable $e) { - if ($logger) { - $logger->logException($e, [ - 'app' => 'core', - 'message' => 'Error while running background job (class: ' . get_class($this) . ', arguments: ' . print_r($this->argument, true) . ')' - ]); - } - } - } - - public function start(IJobList $jobList): void { - $this->execute($jobList); - } - - abstract protected function run($argument); - - public function setId(int $id) { - $this->id = $id; - } - - public function setLastRun(int $lastRun) { - $this->lastRun = $lastRun; - } - - public function setArgument($argument) { - $this->argument = $argument; - } - - public function getId() { - return $this->id; - } - - public function getLastRun() { - return $this->lastRun; - } - - public function getArgument() { - return $this->argument; - } -} diff --git a/lib/private/BackgroundJob/JobList.php b/lib/private/BackgroundJob/JobList.php index 36cccbd4eab..3df9be7558c 100644 --- a/lib/private/BackgroundJob/JobList.php +++ b/lib/private/BackgroundJob/JobList.php @@ -1,31 +1,9 @@ <?php + /** - * @copyright Copyright (c) 2016, ownCloud, Inc. - * - * @author Christoph Wurst <christoph@winzerhof-wurst.at> - * @author Georg Ehrke <oc.list@georgehrke.com> - * @author Joas Schilling <coding@schilljs.com> - * @author Jörn Friedrich Dreyer <jfd@butonic.de> - * @author Morris Jobke <hey@morrisjobke.de> - * @author Noveen Sachdeva <noveen.sachdeva@research.iiit.ac.in> - * @author Robin Appelman <robin@icewind.nl> - * @author Robin McCorkell <robin@mccorkell.me.uk> - * @author Roeland Jago Douma <roeland@famdouma.nl> - * - * @license AGPL-3.0 - * - * This code is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License, version 3, - * as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License, version 3, - * along with this program. If not, see <http://www.gnu.org/licenses/> - * + * SPDX-FileCopyrightText: 2016-2024 Nextcloud GmbH and Nextcloud contributors + * SPDX-FileCopyrightText: 2016 ownCloud, Inc. + * SPDX-License-Identifier: AGPL-3.0-only */ namespace OC\BackgroundJob; @@ -41,6 +19,10 @@ use OCP\DB\QueryBuilder\IQueryBuilder; use OCP\IConfig; use OCP\IDBConnection; use Psr\Log\LoggerInterface; +use function get_class; +use function json_encode; +use function md5; +use function strlen; class JobList implements IJobList { protected IDBConnection $connection; @@ -55,11 +37,10 @@ class JobList implements IJobList { $this->logger = $logger; } - /** - * @param IJob|class-string<IJob> $job - * @param mixed $argument - */ - public function add($job, $argument = null): void { + public function add($job, $argument = null, ?int $firstCheck = null): void { + if ($firstCheck === null) { + $firstCheck = $this->timeFactory->getTime(); + } if ($job instanceof IJob) { $class = get_class($job); } else { @@ -79,18 +60,23 @@ class JobList implements IJobList { 'argument' => $query->createNamedParameter($argumentJson), 'argument_hash' => $query->createNamedParameter(md5($argumentJson)), 'last_run' => $query->createNamedParameter(0, IQueryBuilder::PARAM_INT), - 'last_checked' => $query->createNamedParameter($this->timeFactory->getTime(), IQueryBuilder::PARAM_INT), + 'last_checked' => $query->createNamedParameter($firstCheck, IQueryBuilder::PARAM_INT), ]); } else { $query->update('jobs') ->set('reserved_at', $query->expr()->literal(0, IQueryBuilder::PARAM_INT)) - ->set('last_checked', $query->createNamedParameter($this->timeFactory->getTime(), IQueryBuilder::PARAM_INT)) + ->set('last_checked', $query->createNamedParameter($firstCheck, IQueryBuilder::PARAM_INT)) + ->set('last_run', $query->createNamedParameter(0, IQueryBuilder::PARAM_INT)) ->where($query->expr()->eq('class', $query->createNamedParameter($class))) ->andWhere($query->expr()->eq('argument_hash', $query->createNamedParameter(md5($argumentJson)))); } $query->executeStatement(); } + public function scheduleAfter(string $job, int $runAfter, $argument = null): void { + $this->add($job, $argument, $runAfter); + } + /** * @param IJob|string $job * @param mixed $argument @@ -203,10 +189,9 @@ class JobList implements IJobList { } /** - * Get the next job in the list - * @return ?IJob the next job to run. Beware that this object may be a singleton and may be modified by the next call to buildJob. + * @inheritDoc */ - public function getNext(bool $onlyTimeSensitive = false): ?IJob { + public function getNext(bool $onlyTimeSensitive = false, ?array $jobClasses = null): ?IJob { $query = $this->connection->getQueryBuilder(); $query->select('*') ->from('jobs') @@ -219,6 +204,14 @@ class JobList implements IJobList { $query->andWhere($query->expr()->eq('time_sensitive', $query->createNamedParameter(IJob::TIME_SENSITIVE, IQueryBuilder::PARAM_INT))); } + if ($jobClasses !== null && count($jobClasses) > 0) { + $orClasses = $query->expr()->orx(); + foreach ($jobClasses as $jobClass) { + $orClasses->add($query->expr()->eq('class', $query->createNamedParameter($jobClass, IQueryBuilder::PARAM_STR))); + } + $query->andWhere($orClasses); + } + $result = $query->executeQuery(); $row = $result->fetch(); $result->closeCursor(); @@ -253,7 +246,7 @@ class JobList implements IJobList { if ($count === 0) { // Background job already executed elsewhere, try again. - return $this->getNext($onlyTimeSensitive); + return $this->getNext($onlyTimeSensitive, $jobClasses); } if ($job === null) { @@ -266,7 +259,7 @@ class JobList implements IJobList { $reset->executeStatement(); // Background job from disabled app, try again. - return $this->getNext($onlyTimeSensitive); + return $this->getNext($onlyTimeSensitive, $jobClasses); } return $job; @@ -384,6 +377,7 @@ class JobList implements IJobList { $query = $this->connection->getQueryBuilder(); $query->update('jobs') ->set('execution_duration', $query->createNamedParameter($timeTaken, IQueryBuilder::PARAM_INT)) + ->set('reserved_at', $query->createNamedParameter(0, IQueryBuilder::PARAM_INT)) ->where($query->expr()->eq('id', $query->createNamedParameter($job->getId(), IQueryBuilder::PARAM_INT))); $query->executeStatement(); } @@ -406,7 +400,7 @@ class JobList implements IJobList { $query = $this->connection->getQueryBuilder(); $query->select('*') ->from('jobs') - ->where($query->expr()->neq('reserved_at', $query->createNamedParameter(0, IQueryBuilder::PARAM_INT))) + ->where($query->expr()->gt('reserved_at', $query->createNamedParameter($this->timeFactory->getTime() - 6 * 3600, IQueryBuilder::PARAM_INT))) ->setMaxResults(1); if ($className !== null) { @@ -423,4 +417,26 @@ class JobList implements IJobList { return false; } } + + public function countByClass(): array { + $query = $this->connection->getQueryBuilder(); + $query->select('class') + ->selectAlias($query->func()->count('id'), 'count') + ->from('jobs') + ->orderBy('count') + ->groupBy('class'); + + $result = $query->executeQuery(); + + $jobs = []; + + while (($row = $result->fetch()) !== false) { + /** + * @var array{count:int, class:class-string} $row + */ + $jobs[] = $row; + } + + return $jobs; + } } diff --git a/lib/private/BackgroundJob/QueuedJob.php b/lib/private/BackgroundJob/QueuedJob.php deleted file mode 100644 index 28d86481e62..00000000000 --- a/lib/private/BackgroundJob/QueuedJob.php +++ /dev/null @@ -1,49 +0,0 @@ -<?php -/** - * @copyright Copyright (c) 2016, ownCloud, Inc. - * - * @author Christoph Wurst <christoph@winzerhof-wurst.at> - * @author Lukas Reschke <lukas@statuscode.ch> - * @author Morris Jobke <hey@morrisjobke.de> - * @author Robin Appelman <robin@icewind.nl> - * - * @license AGPL-3.0 - * - * This code is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License, version 3, - * as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License, version 3, - * along with this program. If not, see <http://www.gnu.org/licenses/> - * - */ -namespace OC\BackgroundJob; - -use OCP\ILogger; - -/** - * Class QueuedJob - * - * create a background job that is to be executed once - * - * @package OC\BackgroundJob - * - * @deprecated internal class, use \OCP\BackgroundJob\QueuedJob - */ -abstract class QueuedJob extends Job { - /** - * run the job, then remove it from the joblist - * - * @param JobList $jobList - * @param ILogger|null $logger - */ - public function execute($jobList, ILogger $logger = null) { - $jobList->remove($this, $this->argument); - parent::execute($jobList, $logger); - } -} diff --git a/lib/private/BackgroundJob/TimedJob.php b/lib/private/BackgroundJob/TimedJob.php deleted file mode 100644 index 0f0951e1aec..00000000000 --- a/lib/private/BackgroundJob/TimedJob.php +++ /dev/null @@ -1,63 +0,0 @@ -<?php -/** - * @copyright Copyright (c) 2016, ownCloud, Inc. - * - * @author Christoph Wurst <christoph@winzerhof-wurst.at> - * @author Daniel Kesselberg <mail@danielkesselberg.de> - * @author Lukas Reschke <lukas@statuscode.ch> - * @author Morris Jobke <hey@morrisjobke.de> - * @author Robin Appelman <robin@icewind.nl> - * - * @license AGPL-3.0 - * - * This code is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License, version 3, - * as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License, version 3, - * along with this program. If not, see <http://www.gnu.org/licenses/> - * - */ -namespace OC\BackgroundJob; - -use OCP\BackgroundJob\IJobList; -use OCP\ILogger; - -/** - * Class QueuedJob - * - * create a background job that is to be executed at an interval - * - * @package OC\BackgroundJob - * - * @deprecated internal class, use \OCP\BackgroundJob\TimedJob - */ -abstract class TimedJob extends Job { - protected $interval = 0; - - /** - * set the interval for the job - * - * @param int $interval - */ - public function setInterval($interval) { - $this->interval = $interval; - } - - /** - * run the job if - * - * @param IJobList $jobList - * @param ILogger|null $logger - */ - public function execute($jobList, ILogger $logger = null) { - if ((time() - $this->lastRun) > $this->interval) { - parent::execute($jobList, $logger); - } - } -} diff --git a/lib/private/BinaryFinder.php b/lib/private/BinaryFinder.php index a7ef55237db..f7ac7a5195c 100644 --- a/lib/private/BinaryFinder.php +++ b/lib/private/BinaryFinder.php @@ -2,29 +2,15 @@ declare(strict_types = 1); /** - * @copyright 2022 Carl Schwan <carl@carlschwan.eu> - * @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/>. - * + * SPDX-FileCopyrightText: 2022 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later */ namespace OC; +use OCP\IBinaryFinder; use OCP\ICache; use OCP\ICacheFactory; -use OCP\IBinaryFinder; use Symfony\Component\Process\ExecutableFinder; /** diff --git a/lib/private/Blurhash/Listener/GenerateBlurhashMetadata.php b/lib/private/Blurhash/Listener/GenerateBlurhashMetadata.php new file mode 100644 index 00000000000..61ace449453 --- /dev/null +++ b/lib/private/Blurhash/Listener/GenerateBlurhashMetadata.php @@ -0,0 +1,153 @@ +<?php + +declare(strict_types=1); +/** + * SPDX-FileCopyrightText: 2024 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later + */ + +namespace OC\Blurhash\Listener; + +use GdImage; +use kornrunner\Blurhash\Blurhash; +use OC\Files\Node\File; +use OCP\EventDispatcher\Event; +use OCP\EventDispatcher\IEventDispatcher; +use OCP\EventDispatcher\IEventListener; +use OCP\Files\GenericFileException; +use OCP\Files\NotFoundException; +use OCP\Files\NotPermittedException; +use OCP\FilesMetadata\AMetadataEvent; +use OCP\FilesMetadata\Event\MetadataBackgroundEvent; +use OCP\FilesMetadata\Event\MetadataLiveEvent; +use OCP\IPreview; +use OCP\Lock\LockedException; + +/** + * Generate a Blurhash string as metadata when image file is uploaded/edited. + * + * @template-implements IEventListener<AMetadataEvent> + */ +class GenerateBlurhashMetadata implements IEventListener { + private const RESIZE_BOXSIZE = 300; + + private const COMPONENTS_X = 4; + private const COMPONENTS_Y = 3; + + public function __construct( + private IPreview $preview, + ) { + } + + /** + * @throws NotPermittedException + * @throws GenericFileException + * @throws LockedException + */ + public function handle(Event $event): void { + if (!($event instanceof MetadataLiveEvent) + && !($event instanceof MetadataBackgroundEvent)) { + return; + } + + $file = $event->getNode(); + if (!($file instanceof File)) { + return; + } + + $currentEtag = $file->getEtag(); + $metadata = $event->getMetadata(); + if ($metadata->getEtag('blurhash') === $currentEtag) { + return; + } + + // too heavy to run on the live thread, request a rerun as a background job + if ($event instanceof MetadataLiveEvent) { + $event->requestBackgroundJob(); + return; + } + + $image = false; + try { + // using preview image to generate the blurhash + $preview = $this->preview->getPreview($file, 256, 256); + $image = @imagecreatefromstring($preview->getContent()); + } catch (NotFoundException $e) { + // https://github.com/nextcloud/server/blob/9d70fd3e64b60a316a03fb2b237891380c310c58/lib/private/legacy/OC_Image.php#L668 + // The preview system can fail on huge picture, in that case we use our own image resizer. + if (str_starts_with($file->getMimetype(), 'image/')) { + $image = $this->resizedImageFromFile($file); + } + } + + if ($image === false) { + return; + } + + $metadata->setString('blurhash', $this->generateBlurHash($image)) + ->setEtag('blurhash', $currentEtag); + } + + /** + * @param File $file + * + * @return GdImage|false + * @throws GenericFileException + * @throws NotPermittedException + * @throws LockedException + */ + private function resizedImageFromFile(File $file): GdImage|false { + $image = @imagecreatefromstring($file->getContent()); + if ($image === false) { + return false; + } + + $currX = imagesx($image); + $currY = imagesy($image); + + if ($currX > $currY) { + $newX = self::RESIZE_BOXSIZE; + $newY = intval($currY * $newX / $currX); + } else { + $newY = self::RESIZE_BOXSIZE; + $newX = intval($currX * $newY / $currY); + } + + $newImage = imagescale($image, $newX, $newY); + return ($newImage !== false) ? $newImage : $image; + } + + /** + * @param GdImage $image + * + * @return string + */ + public function generateBlurHash(GdImage $image): string { + $width = imagesx($image); + $height = imagesy($image); + + $pixels = []; + for ($y = 0; $y < $height; ++$y) { + $row = []; + for ($x = 0; $x < $width; ++$x) { + $index = imagecolorat($image, $x, $y); + $colors = imagecolorsforindex($image, $index); + $row[] = [$colors['red'], $colors['green'], $colors['blue']]; + } + + $pixels[] = $row; + } + + return Blurhash::encode($pixels, self::COMPONENTS_X, self::COMPONENTS_Y); + } + + /** + * @param IEventDispatcher $eventDispatcher + * + * @return void + */ + public static function loadListeners(IEventDispatcher $eventDispatcher): void { + $eventDispatcher->addServiceListener(MetadataLiveEvent::class, self::class); + $eventDispatcher->addServiceListener(MetadataBackgroundEvent::class, self::class); + } +} diff --git a/lib/private/Broadcast/Events/BroadcastEvent.php b/lib/private/Broadcast/Events/BroadcastEvent.php index de950ac9371..d6c2cf49904 100644 --- a/lib/private/Broadcast/Events/BroadcastEvent.php +++ b/lib/private/Broadcast/Events/BroadcastEvent.php @@ -3,25 +3,8 @@ declare(strict_types=1); /** - * @copyright 2019 Christoph Wurst <christoph@winzerhof-wurst.at> - * - * @author Christoph Wurst <christoph@winzerhof-wurst.at> - * - * @license GNU AGPL version 3 or any later version - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as - * published by the Free Software Foundation, either version 3 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - * + * SPDX-FileCopyrightText: 2019 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later */ namespace OC\Broadcast\Events; diff --git a/lib/private/Cache/CappedMemoryCache.php b/lib/private/Cache/CappedMemoryCache.php index 31e8ef3e720..2c2eab3209a 100644 --- a/lib/private/Cache/CappedMemoryCache.php +++ b/lib/private/Cache/CappedMemoryCache.php @@ -1,23 +1,9 @@ <?php + /** - * @copyright Copyright (c) 2016, ownCloud, Inc. - * - * @author Robin Appelman <robin@icewind.nl> - * - * @license AGPL-3.0 - * - * This code is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License, version 3, - * as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License, version 3, - * along with this program. If not, see <http://www.gnu.org/licenses/> - * + * SPDX-FileCopyrightText: 2016-2024 Nextcloud GmbH and Nextcloud contributors + * SPDX-FileCopyrightText: 2016 ownCloud, Inc. + * SPDX-License-Identifier: AGPL-3.0-only */ namespace OC\Cache; diff --git a/lib/private/Cache/File.php b/lib/private/Cache/File.php index 72fc95a802b..33fa1c00e98 100644 --- a/lib/private/Cache/File.php +++ b/lib/private/Cache/File.php @@ -1,31 +1,9 @@ <?php + /** - * @copyright Copyright (c) 2016, ownCloud, Inc. - * - * @author Arthur Schiwon <blizzz@arthur-schiwon.de> - * @author Christoph Wurst <christoph@winzerhof-wurst.at> - * @author Joas Schilling <coding@schilljs.com> - * @author Lukas Reschke <lukas@statuscode.ch> - * @author Robin Appelman <robin@icewind.nl> - * @author Roeland Jago Douma <roeland@famdouma.nl> - * @author Sebastian Wessalowski <sebastian@wessalowski.org> - * @author Thomas Müller <thomas.mueller@tmit.eu> - * @author Vincent Petry <vincent@nextcloud.com> - * - * @license AGPL-3.0 - * - * This code is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License, version 3, - * as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License, version 3, - * along with this program. If not, see <http://www.gnu.org/licenses/> - * + * SPDX-FileCopyrightText: 2016-2024 Nextcloud GmbH and Nextcloud contributors + * SPDX-FileCopyrightText: 2016 ownCloud, Inc. + * SPDX-License-Identifier: AGPL-3.0-only */ namespace OC\Cache; @@ -60,7 +38,7 @@ class File implements ICache { $this->storage = new View('/' . $user->getUID() . '/cache'); return $this->storage; } else { - \OC::$server->get(LoggerInterface::class)->error('Can\'t get cache storage, user not logged in', ['app' => 'core']); + \OCP\Server::get(LoggerInterface::class)->error('Can\'t get cache storage, user not logged in', ['app' => 'core']); throw new \OC\ForbiddenException('Can\t get cache storage, user not logged in'); } } @@ -192,11 +170,11 @@ class File implements ICache { } } catch (\OCP\Lock\LockedException $e) { // ignore locked chunks - \OC::$server->getLogger()->debug('Could not cleanup locked chunk "' . $file . '"', ['app' => 'core']); + \OCP\Server::get(LoggerInterface::class)->debug('Could not cleanup locked chunk "' . $file . '"', ['app' => 'core']); } catch (\OCP\Files\ForbiddenException $e) { - \OC::$server->getLogger()->debug('Could not cleanup forbidden chunk "' . $file . '"', ['app' => 'core']); + \OCP\Server::get(LoggerInterface::class)->debug('Could not cleanup forbidden chunk "' . $file . '"', ['app' => 'core']); } catch (\OCP\Files\LockNotAcquiredException $e) { - \OC::$server->getLogger()->debug('Could not cleanup locked chunk "' . $file . '"', ['app' => 'core']); + \OCP\Server::get(LoggerInterface::class)->debug('Could not cleanup locked chunk "' . $file . '"', ['app' => 'core']); } } } diff --git a/lib/private/Calendar/CalendarQuery.php b/lib/private/Calendar/CalendarQuery.php index 3d37d9dc467..4eb4a4cd636 100644 --- a/lib/private/Calendar/CalendarQuery.php +++ b/lib/private/Calendar/CalendarQuery.php @@ -3,25 +3,8 @@ declare(strict_types=1); /** - * @copyright 2021 Anna Larch <anna.larch@gmx.net> - * - * @author Anna Larch <anna.larch@gmx.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/>. - * + * SPDX-FileCopyrightText: 2021 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later */ namespace OC\Calendar; diff --git a/lib/private/Calendar/Manager.php b/lib/private/Calendar/Manager.php index dc4801c69ce..7ae577c9d7f 100644 --- a/lib/private/Calendar/Manager.php +++ b/lib/private/Calendar/Manager.php @@ -3,27 +3,8 @@ declare(strict_types=1); /** - * @copyright 2017, Georg Ehrke <oc.list@georgehrke.com> - * - * @author Christoph Wurst <christoph@winzerhof-wurst.at> - * @author Georg Ehrke <oc.list@georgehrke.com> - * @author Anna Larch <anna.larch@gmx.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/>. - * + * SPDX-FileCopyrightText: 2017 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later */ namespace OC\Calendar; diff --git a/lib/private/Calendar/Resource/Manager.php b/lib/private/Calendar/Resource/Manager.php index 88e733f3f24..870409c9ba5 100644 --- a/lib/private/Calendar/Resource/Manager.php +++ b/lib/private/Calendar/Resource/Manager.php @@ -3,27 +3,8 @@ declare(strict_types=1); /** - * @copyright 2018, Georg Ehrke <oc.list@georgehrke.com> - * - * @author Christoph Wurst <christoph@winzerhof-wurst.at> - * @author Georg Ehrke <oc.list@georgehrke.com> - * @author Joas Schilling <coding@schilljs.com> - * - * @license GNU AGPL version 3 or any later version - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as - * published by the Free Software Foundation, either version 3 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - * + * SPDX-FileCopyrightText: 2018 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later */ namespace OC\Calendar\Resource; diff --git a/lib/private/Calendar/Room/Manager.php b/lib/private/Calendar/Room/Manager.php index d7ecdfd8b36..e5a60765919 100644 --- a/lib/private/Calendar/Room/Manager.php +++ b/lib/private/Calendar/Room/Manager.php @@ -3,27 +3,8 @@ declare(strict_types=1); /** - * @copyright 2018, Georg Ehrke <oc.list@georgehrke.com> - * - * @author Christoph Wurst <christoph@winzerhof-wurst.at> - * @author Georg Ehrke <oc.list@georgehrke.com> - * @author Joas Schilling <coding@schilljs.com> - * - * @license GNU AGPL version 3 or any later version - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as - * published by the Free Software Foundation, either version 3 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - * + * SPDX-FileCopyrightText: 2018 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later */ namespace OC\Calendar\Room; diff --git a/lib/private/CapabilitiesManager.php b/lib/private/CapabilitiesManager.php index 7885a98869d..bb84ebb65e8 100644 --- a/lib/private/CapabilitiesManager.php +++ b/lib/private/CapabilitiesManager.php @@ -1,37 +1,17 @@ <?php declare(strict_types=1); - /** - * @copyright Copyright (c) 2016, ownCloud, Inc. - * - * @author Christoph Wurst <christoph@winzerhof-wurst.at> - * @author Joas Schilling <coding@schilljs.com> - * @author Julius Härtl <jus@bitgrid.net> - * @author Morris Jobke <hey@morrisjobke.de> - * @author Roeland Jago Douma <roeland@famdouma.nl> - * - * @license AGPL-3.0 - * - * This code is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License, version 3, - * as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License, version 3, - * along with this program. If not, see <http://www.gnu.org/licenses/> - * + * SPDX-FileCopyrightText: 2016-2024 Nextcloud GmbH and Nextcloud contributors + * SPDX-FileCopyrightText: 2016 ownCloud, Inc. + * SPDX-License-Identifier: AGPL-3.0-only */ namespace OC; use OCP\AppFramework\QueryException; use OCP\Capabilities\ICapability; -use OCP\Capabilities\IPublicCapability; use OCP\Capabilities\IInitialStateExcludedCapability; +use OCP\Capabilities\IPublicCapability; use Psr\Log\LoggerInterface; class CapabilitiesManager { diff --git a/lib/private/Collaboration/AutoComplete/Manager.php b/lib/private/Collaboration/AutoComplete/Manager.php index cab15baf535..d7298d9deef 100644 --- a/lib/private/Collaboration/AutoComplete/Manager.php +++ b/lib/private/Collaboration/AutoComplete/Manager.php @@ -1,25 +1,7 @@ <?php /** - * @copyright Copyright (c) 2017 Arthur Schiwon <blizzz@arthur-schiwon.de> - * - * @author Arthur Schiwon <blizzz@arthur-schiwon.de> - * @author Christoph Wurst <christoph@winzerhof-wurst.at> - * - * @license GNU AGPL version 3 or any later version - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as - * published by the Free Software Foundation, either version 3 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - * + * SPDX-FileCopyrightText: 2017 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later */ namespace OC\Collaboration\AutoComplete; @@ -29,47 +11,46 @@ use OCP\IServerContainer; class Manager implements IManager { /** @var string[] */ - protected $sorters = []; + protected array $sorters = []; /** @var ISorter[] */ - protected $sorterInstances = []; - /** @var IServerContainer */ - private $c; + protected array $sorterInstances = []; - public function __construct(IServerContainer $container) { - $this->c = $container; + public function __construct( + private IServerContainer $container, + ) { } - public function runSorters(array $sorters, array &$sortArray, array $context) { + public function runSorters(array $sorters, array &$sortArray, array $context): void { $sorterInstances = $this->getSorters(); while ($sorter = array_shift($sorters)) { if (isset($sorterInstances[$sorter])) { $sorterInstances[$sorter]->sort($sortArray, $context); } else { - $this->c->getLogger()->warning('No sorter for ID "{id}", skipping', [ + $this->container->getLogger()->warning('No sorter for ID "{id}", skipping', [ 'app' => 'core', 'id' => $sorter ]); } } } - public function registerSorter($className) { + public function registerSorter($className): void { $this->sorters[] = $className; } - protected function getSorters() { + protected function getSorters(): array { if (count($this->sorterInstances) === 0) { foreach ($this->sorters as $sorter) { /** @var ISorter $instance */ - $instance = $this->c->resolve($sorter); + $instance = $this->container->resolve($sorter); if (!$instance instanceof ISorter) { - $this->c->getLogger()->notice('Skipping sorter which is not an instance of ISorter. Class name: {class}', + $this->container->getLogger()->notice('Skipping sorter which is not an instance of ISorter. Class name: {class}', ['app' => 'core', 'class' => $sorter]); continue; } $sorterId = trim($instance->getId()); if (trim($sorterId) === '') { - $this->c->getLogger()->notice('Skipping sorter with empty ID. Class name: {class}', + $this->container->getLogger()->notice('Skipping sorter with empty ID. Class name: {class}', ['app' => 'core', 'class' => $sorter]); continue; } diff --git a/lib/private/Collaboration/Collaborators/GroupPlugin.php b/lib/private/Collaboration/Collaborators/GroupPlugin.php index 75e52c19e0b..a7b84b72199 100644 --- a/lib/private/Collaboration/Collaborators/GroupPlugin.php +++ b/lib/private/Collaboration/Collaborators/GroupPlugin.php @@ -1,29 +1,7 @@ <?php /** - * @copyright Copyright (c) 2017 Arthur Schiwon <blizzz@arthur-schiwon.de> - * - * @author Arthur Schiwon <blizzz@arthur-schiwon.de> - * @author Christoph Wurst <christoph@winzerhof-wurst.at> - * @author Joas Schilling <coding@schilljs.com> - * @author Julius Härtl <jus@bitgrid.net> - * @author Morris Jobke <hey@morrisjobke.de> - * @author Robin Appelman <robin@icewind.nl> - * - * @license GNU AGPL version 3 or any later version - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as - * published by the Free Software Foundation, either version 3 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - * + * SPDX-FileCopyrightText: 2017 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later */ namespace OC\Collaboration\Collaborators; @@ -37,34 +15,31 @@ use OCP\IUserSession; use OCP\Share\IShare; class GroupPlugin implements ISearchPlugin { - /** @var bool */ - protected $shareeEnumeration; - /** @var bool */ - protected $shareWithGroupOnly; - /** @var bool */ - protected $shareeEnumerationInGroupOnly; - /** @var bool */ - protected $groupSharingDisabled; - - /** @var IGroupManager */ - private $groupManager; - /** @var IConfig */ - private $config; - /** @var IUserSession */ - private $userSession; - - public function __construct(IConfig $config, IGroupManager $groupManager, IUserSession $userSession) { - $this->groupManager = $groupManager; - $this->config = $config; - $this->userSession = $userSession; + protected bool $shareeEnumeration; + protected bool $shareWithGroupOnly; + + protected bool $shareeEnumerationInGroupOnly; + + protected bool $groupSharingDisabled; + + public function __construct( + private IConfig $config, + private IGroupManager $groupManager, + private IUserSession $userSession, + private mixed $shareWithGroupOnlyExcludeGroupsList = [], + ) { $this->shareeEnumeration = $this->config->getAppValue('core', 'shareapi_allow_share_dialog_user_enumeration', 'yes') === 'yes'; $this->shareWithGroupOnly = $this->config->getAppValue('core', 'shareapi_only_share_with_group_members', 'no') === 'yes'; $this->shareeEnumerationInGroupOnly = $this->shareeEnumeration && $this->config->getAppValue('core', 'shareapi_restrict_user_enumeration_to_group', 'no') === 'yes'; $this->groupSharingDisabled = $this->config->getAppValue('core', 'shareapi_allow_group_sharing', 'yes') === 'no'; + + if ($this->shareWithGroupOnly) { + $this->shareWithGroupOnlyExcludeGroupsList = json_decode($this->config->getAppValue('core', 'shareapi_only_share_with_group_members_exclude_group_list', ''), true) ?? []; + } } - public function search($search, $limit, $offset, ISearchResult $searchResult) { + public function search($search, $limit, $offset, ISearchResult $searchResult): bool { if ($this->groupSharingDisabled) { return false; } @@ -89,6 +64,9 @@ class GroupPlugin implements ISearchPlugin { return $group->getGID(); }, $userGroups); $groupIds = array_intersect($groupIds, $userGroups); + + // ShareWithGroupOnly filtering + $groupIds = array_diff($groupIds, $this->shareWithGroupOnlyExcludeGroupsList); } $lowerSearch = strtolower($search); diff --git a/lib/private/Collaboration/Collaborators/LookupPlugin.php b/lib/private/Collaboration/Collaborators/LookupPlugin.php index 86ac70ab970..3cc4e93a486 100644 --- a/lib/private/Collaboration/Collaborators/LookupPlugin.php +++ b/lib/private/Collaboration/Collaborators/LookupPlugin.php @@ -1,29 +1,7 @@ <?php /** - * @copyright Copyright (c) 2017 Arthur Schiwon <blizzz@arthur-schiwon.de> - * - * @author Arthur Schiwon <blizzz@arthur-schiwon.de> - * @author Bjoern Schiessle <bjoern@schiessle.org> - * @author Christoph Wurst <christoph@winzerhof-wurst.at> - * @author J0WI <J0WI@users.noreply.github.com> - * @author Joas Schilling <coding@schilljs.com> - * @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/>. - * + * SPDX-FileCopyrightText: 2017 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later */ namespace OC\Collaboration\Collaborators; @@ -38,31 +16,21 @@ use OCP\Share\IShare; use Psr\Log\LoggerInterface; class LookupPlugin implements ISearchPlugin { - /** @var IConfig */ - private $config; - /** @var IClientService */ - private $clientService; /** @var string remote part of the current user's cloud id */ - private $currentUserRemote; - /** @var ICloudIdManager */ - private $cloudIdManager; - /** @var LoggerInterface */ - private $logger; + private string $currentUserRemote; - public function __construct(IConfig $config, - IClientService $clientService, - IUserSession $userSession, - ICloudIdManager $cloudIdManager, - LoggerInterface $logger) { - $this->config = $config; - $this->clientService = $clientService; - $this->cloudIdManager = $cloudIdManager; + public function __construct( + private IConfig $config, + private IClientService $clientService, + IUserSession $userSession, + private ICloudIdManager $cloudIdManager, + private LoggerInterface $logger, + ) { $currentUserCloudId = $userSession->getUser()->getCloudId(); $this->currentUserRemote = $cloudIdManager->resolveCloudId($currentUserCloudId)->getRemote(); - $this->logger = $logger; } - public function search($search, $limit, $offset, ISearchResult $searchResult) { + public function search($search, $limit, $offset, ISearchResult $searchResult): bool { $isGlobalScaleEnabled = $this->config->getSystemValueBool('gs.enabled', false); $isLookupServerEnabled = $this->config->getAppValue('files_sharing', 'lookupServerEnabled', 'yes') === 'yes'; $hasInternetConnection = $this->config->getSystemValueBool('has_internet_connection', true); @@ -103,7 +71,7 @@ class LookupPlugin implements ISearchPlugin { if ($this->currentUserRemote === $remote) { continue; } - $name = isset($lookup['name']['value']) ? $lookup['name']['value'] : ''; + $name = $lookup['name']['value'] ?? ''; $label = empty($name) ? $lookup['federationId'] : $name . ' (' . $lookup['federationId'] . ')'; $result[] = [ 'label' => $label, diff --git a/lib/private/Collaboration/Collaborators/MailPlugin.php b/lib/private/Collaboration/Collaborators/MailPlugin.php index aa317ec1720..134970518bc 100644 --- a/lib/private/Collaboration/Collaborators/MailPlugin.php +++ b/lib/private/Collaboration/Collaborators/MailPlugin.php @@ -1,28 +1,7 @@ <?php /** - * @copyright Copyright (c) 2017 Arthur Schiwon <blizzz@arthur-schiwon.de> - * - * @author Arthur Schiwon <blizzz@arthur-schiwon.de> - * @author Christoph Wurst <christoph@winzerhof-wurst.at> - * @author Joas Schilling <coding@schilljs.com> - * @author Julius Härtl <jus@bitgrid.net> - * @author Tobia De Koninck <tobia@ledfan.be> - * - * @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/>. - * + * SPDX-FileCopyrightText: 2017 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later */ namespace OC\Collaboration\Collaborators; @@ -37,66 +16,48 @@ use OCP\IConfig; use OCP\IGroupManager; use OCP\IUser; use OCP\IUserSession; -use OCP\Share\IShare; use OCP\Mail\IMailer; +use OCP\Share\IShare; class MailPlugin implements ISearchPlugin { - /* @var bool */ - protected $shareWithGroupOnly; - /* @var bool */ - protected $shareeEnumeration; - /* @var bool */ - protected $shareeEnumerationInGroupOnly; - /* @var bool */ - protected $shareeEnumerationPhone; - /* @var bool */ - protected $shareeEnumerationFullMatch; - /* @var bool */ - protected $shareeEnumerationFullMatchEmail; + protected bool $shareWithGroupOnly; + + protected bool $shareeEnumeration; + + protected bool $shareeEnumerationInGroupOnly; - /** @var IManager */ - private $contactsManager; - /** @var ICloudIdManager */ - private $cloudIdManager; - /** @var IConfig */ - private $config; + protected bool $shareeEnumerationPhone; - /** @var IGroupManager */ - private $groupManager; - /** @var KnownUserService */ - private $knownUserService; - /** @var IUserSession */ - private $userSession; - /** @var IMailer */ - private $mailer; + protected bool $shareeEnumerationFullMatch; - public function __construct(IManager $contactsManager, - ICloudIdManager $cloudIdManager, - IConfig $config, - IGroupManager $groupManager, - KnownUserService $knownUserService, - IUserSession $userSession, - IMailer $mailer) { - $this->contactsManager = $contactsManager; - $this->cloudIdManager = $cloudIdManager; - $this->config = $config; - $this->groupManager = $groupManager; - $this->knownUserService = $knownUserService; - $this->userSession = $userSession; - $this->mailer = $mailer; + protected bool $shareeEnumerationFullMatchEmail; + public function __construct( + private IManager $contactsManager, + private ICloudIdManager $cloudIdManager, + private IConfig $config, + private IGroupManager $groupManager, + private KnownUserService $knownUserService, + private IUserSession $userSession, + private IMailer $mailer, + private mixed $shareWithGroupOnlyExcludeGroupsList = [], + ) { $this->shareeEnumeration = $this->config->getAppValue('core', 'shareapi_allow_share_dialog_user_enumeration', 'yes') === 'yes'; $this->shareWithGroupOnly = $this->config->getAppValue('core', 'shareapi_only_share_with_group_members', 'no') === 'yes'; $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'; + + if ($this->shareWithGroupOnly) { + $this->shareWithGroupOnlyExcludeGroupsList = json_decode($this->config->getAppValue('core', 'shareapi_only_share_with_group_members_exclude_group_list', ''), true) ?? []; + } } /** * {@inheritdoc} */ - public function search($search, $limit, $offset, ISearchResult $searchResult) { + public function search($search, $limit, $offset, ISearchResult $searchResult): bool { if ($this->shareeEnumerationFullMatch && !$this->shareeEnumerationFullMatchEmail) { return false; } @@ -120,8 +81,8 @@ class MailPlugin implements ISearchPlugin { [ 'limit' => $limit, 'offset' => $offset, - 'enumeration' => (bool) $this->shareeEnumeration, - 'fullmatch' => (bool) $this->shareeEnumerationFullMatch, + 'enumeration' => $this->shareeEnumeration, + 'fullmatch' => $this->shareeEnumerationFullMatch, ] ); $lowerSearch = strtolower($search); @@ -150,6 +111,10 @@ class MailPlugin implements ISearchPlugin { * Check if the user may share with the user associated with the e-mail of the just found contact */ $userGroups = $this->groupManager->getUserGroupIds($this->userSession->getUser()); + + // ShareWithGroupOnly filtering + $userGroups = array_diff($userGroups, $this->shareWithGroupOnlyExcludeGroupsList); + $found = false; foreach ($userGroups as $userGroup) { if ($this->groupManager->isInGroup($contact['UID'], $userGroup)) { @@ -163,7 +128,7 @@ class MailPlugin implements ISearchPlugin { } if ($exactEmailMatch && $this->shareeEnumerationFullMatch) { try { - $cloud = $this->cloudIdManager->resolveCloudId($contact['CLOUD'][0]); + $cloud = $this->cloudIdManager->resolveCloudId($contact['CLOUD'][0] ?? ''); } catch (\InvalidArgumentException $e) { continue; } @@ -188,7 +153,7 @@ class MailPlugin implements ISearchPlugin { if ($this->shareeEnumeration) { try { - $cloud = $this->cloudIdManager->resolveCloudId($contact['CLOUD'][0]); + $cloud = $this->cloudIdManager->resolveCloudId($contact['CLOUD'][0] ?? ''); } catch (\InvalidArgumentException $e) { continue; } @@ -286,6 +251,6 @@ class MailPlugin implements ISearchPlugin { public function isCurrentUser(ICloudId $cloud): bool { $currentUser = $this->userSession->getUser(); - return $currentUser instanceof IUser ? $currentUser->getUID() === $cloud->getUser() : false; + return $currentUser instanceof IUser && $currentUser->getUID() === $cloud->getUser(); } } diff --git a/lib/private/Collaboration/Collaborators/RemoteGroupPlugin.php b/lib/private/Collaboration/Collaborators/RemoteGroupPlugin.php index 413799e52c6..89d5c4e4f79 100644 --- a/lib/private/Collaboration/Collaborators/RemoteGroupPlugin.php +++ b/lib/private/Collaboration/Collaborators/RemoteGroupPlugin.php @@ -1,26 +1,7 @@ <?php /** - * @copyright Copyright (c) 2017 Arthur Schiwon <blizzz@arthur-schiwon.de> - * - * @author Bjoern Schiessle <bjoern@schiessle.org> - * @author Joas Schilling <coding@schilljs.com> - * @author John Molakvoæ <skjnldsv@protonmail.com> - * - * @license GNU AGPL version 3 or any later version - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as - * published by the Free Software Foundation, either version 3 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - * + * SPDX-FileCopyrightText: 2017 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later */ namespace OC\Collaboration\Collaborators; @@ -33,14 +14,12 @@ use OCP\Share; use OCP\Share\IShare; class RemoteGroupPlugin implements ISearchPlugin { - protected $shareeEnumeration; + private bool $enabled = false; - /** @var ICloudIdManager */ - private $cloudIdManager; - /** @var bool */ - private $enabled = false; - - public function __construct(ICloudFederationProviderManager $cloudFederationProviderManager, ICloudIdManager $cloudIdManager) { + public function __construct( + ICloudFederationProviderManager $cloudFederationProviderManager, + private ICloudIdManager $cloudIdManager, + ) { try { $fileSharingProvider = $cloudFederationProviderManager->getCloudFederationProvider('file'); $supportedShareTypes = $fileSharingProvider->getSupportedShareTypes(); @@ -50,10 +29,9 @@ class RemoteGroupPlugin implements ISearchPlugin { } catch (\Exception $e) { // do nothing, just don't enable federated group shares } - $this->cloudIdManager = $cloudIdManager; } - public function search($search, $limit, $offset, ISearchResult $searchResult) { + public function search($search, $limit, $offset, ISearchResult $searchResult): bool { $result = ['wide' => [], 'exact' => []]; $resultType = new SearchResultType('remote_groups'); @@ -83,7 +61,7 @@ class RemoteGroupPlugin implements ISearchPlugin { * @return array [user, remoteURL] * @throws \InvalidArgumentException */ - public function splitGroupRemote($address) { + public function splitGroupRemote($address): array { try { $cloudId = $this->cloudIdManager->resolveCloudId($address); return [$cloudId->getUser(), $cloudId->getRemote()]; diff --git a/lib/private/Collaboration/Collaborators/RemotePlugin.php b/lib/private/Collaboration/Collaborators/RemotePlugin.php index 7d7a013a38c..e46b71eb710 100644 --- a/lib/private/Collaboration/Collaborators/RemotePlugin.php +++ b/lib/private/Collaboration/Collaborators/RemotePlugin.php @@ -1,28 +1,7 @@ <?php /** - * @copyright Copyright (c) 2017 Arthur Schiwon <blizzz@arthur-schiwon.de> - * - * @author Arthur Schiwon <blizzz@arthur-schiwon.de> - * @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> - * - * @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/>. - * + * SPDX-FileCopyrightText: 2017 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later */ namespace OC\Collaboration\Collaborators; @@ -37,32 +16,22 @@ use OCP\IUserSession; use OCP\Share\IShare; class RemotePlugin implements ISearchPlugin { - protected $shareeEnumeration; + protected bool $shareeEnumeration; - /** @var IManager */ - private $contactsManager; - /** @var ICloudIdManager */ - private $cloudIdManager; - /** @var IConfig */ - private $config; - /** @var IUserManager */ - private $userManager; - /** @var string */ - private $userId = ''; + private string $userId; - public function __construct(IManager $contactsManager, ICloudIdManager $cloudIdManager, IConfig $config, IUserManager $userManager, IUserSession $userSession) { - $this->contactsManager = $contactsManager; - $this->cloudIdManager = $cloudIdManager; - $this->config = $config; - $this->userManager = $userManager; - $user = $userSession->getUser(); - if ($user !== null) { - $this->userId = $user->getUID(); - } + public function __construct( + private IManager $contactsManager, + private ICloudIdManager $cloudIdManager, + private IConfig $config, + private IUserManager $userManager, + IUserSession $userSession, + ) { + $this->userId = $userSession->getUser()?->getUID() ?? ''; $this->shareeEnumeration = $this->config->getAppValue('core', 'shareapi_allow_share_dialog_user_enumeration', 'yes') === 'yes'; } - public function search($search, $limit, $offset, ISearchResult $searchResult) { + public function search($search, $limit, $offset, ISearchResult $searchResult): bool { $result = ['wide' => [], 'exact' => []]; $resultType = new SearchResultType('remotes'); @@ -185,7 +154,7 @@ class RemotePlugin implements ISearchPlugin { * @return array [user, remoteURL] * @throws \InvalidArgumentException */ - public function splitUserRemote($address) { + public function splitUserRemote(string $address): array { try { $cloudId = $this->cloudIdManager->resolveCloudId($address); return [$cloudId->getUser(), $cloudId->getRemote()]; diff --git a/lib/private/Collaboration/Collaborators/Search.php b/lib/private/Collaboration/Collaborators/Search.php index 8d99ed42fcd..78b57b52400 100644 --- a/lib/private/Collaboration/Collaborators/Search.php +++ b/lib/private/Collaboration/Collaborators/Search.php @@ -1,29 +1,7 @@ <?php /** - * @copyright Copyright (c) 2017 Arthur Schiwon <blizzz@arthur-schiwon.de> - * - * @author Arthur Schiwon <blizzz@arthur-schiwon.de> - * @author Christoph Wurst <christoph@winzerhof-wurst.at> - * @author Joas Schilling <coding@schilljs.com> - * @author Morris Jobke <hey@morrisjobke.de> - * @author onehappycat <one.happy.cat@gmx.com> - * @author Robin Appelman <robin@icewind.nl> - * - * @license GNU AGPL version 3 or any later version - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as - * published by the Free Software Foundation, either version 3 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - * + * SPDX-FileCopyrightText: 2017 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later */ namespace OC\Collaboration\Collaborators; @@ -35,32 +13,28 @@ use OCP\IContainer; use OCP\Share; class Search implements ISearch { - /** @var IContainer */ - private $c; + protected array $pluginList = []; - protected $pluginList = []; - - public function __construct(IContainer $c) { - $this->c = $c; + public function __construct( + private IContainer $container, + ) { } /** * @param string $search - * @param array $shareTypes * @param bool $lookup * @param int|null $limit * @param int|null $offset - * @return array * @throws \OCP\AppFramework\QueryException */ - public function search($search, array $shareTypes, $lookup, $limit, $offset) { + public function search($search, array $shareTypes, $lookup, $limit, $offset): array { $hasMoreResults = false; // Trim leading and trailing whitespace characters, e.g. when query is copy-pasted $search = trim($search); /** @var ISearchResult $searchResult */ - $searchResult = $this->c->resolve(SearchResult::class); + $searchResult = $this->container->resolve(SearchResult::class); foreach ($shareTypes as $type) { if (!isset($this->pluginList[$type])) { @@ -68,14 +42,14 @@ class Search implements ISearch { } foreach ($this->pluginList[$type] as $plugin) { /** @var ISearchPlugin $searchPlugin */ - $searchPlugin = $this->c->resolve($plugin); + $searchPlugin = $this->container->resolve($plugin); $hasMoreResults = $searchPlugin->search($search, $limit, $offset, $searchResult) || $hasMoreResults; } } // Get from lookup server, not a separate share type if ($lookup) { - $searchPlugin = $this->c->resolve(LookupPlugin::class); + $searchPlugin = $this->container->resolve(LookupPlugin::class); $hasMoreResults = $searchPlugin->search($search, $limit, $offset, $searchResult) || $hasMoreResults; } @@ -105,7 +79,7 @@ class Search implements ISearch { return [$searchResult->asArray(), $hasMoreResults]; } - public function registerPlugin(array $pluginInfo) { + public function registerPlugin(array $pluginInfo): void { $shareType = constant(Share::class . '::' . $pluginInfo['shareType']); if ($shareType === null) { throw new \InvalidArgumentException('Provided ShareType is invalid'); diff --git a/lib/private/Collaboration/Collaborators/SearchResult.php b/lib/private/Collaboration/Collaborators/SearchResult.php index 76d78c9c231..73c0fed41e0 100644 --- a/lib/private/Collaboration/Collaborators/SearchResult.php +++ b/lib/private/Collaboration/Collaborators/SearchResult.php @@ -1,26 +1,7 @@ <?php /** - * @copyright Copyright (c) 2017 Arthur Schiwon <blizzz@arthur-schiwon.de> - * - * @author Arthur Schiwon <blizzz@arthur-schiwon.de> - * @author Christoph Wurst <christoph@winzerhof-wurst.at> - * @author Joas Schilling <coding@schilljs.com> - * - * @license GNU AGPL version 3 or any later version - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as - * published by the Free Software Foundation, either version 3 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - * + * SPDX-FileCopyrightText: 2017 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later */ namespace OC\Collaboration\Collaborators; @@ -28,13 +9,13 @@ use OCP\Collaboration\Collaborators\ISearchResult; use OCP\Collaboration\Collaborators\SearchResultType; class SearchResult implements ISearchResult { - protected $result = [ + protected array $result = [ 'exact' => [], ]; - protected $exactIdMatches = []; + protected array $exactIdMatches = []; - public function addResultSet(SearchResultType $type, array $matches, array $exactMatches = null) { + public function addResultSet(SearchResultType $type, array $matches, ?array $exactMatches = null): void { $type = $type->getLabel(); if (!isset($this->result[$type])) { $this->result[$type] = []; @@ -47,15 +28,15 @@ class SearchResult implements ISearchResult { } } - public function markExactIdMatch(SearchResultType $type) { + public function markExactIdMatch(SearchResultType $type): void { $this->exactIdMatches[$type->getLabel()] = 1; } - public function hasExactIdMatch(SearchResultType $type) { + public function hasExactIdMatch(SearchResultType $type): bool { return isset($this->exactIdMatches[$type->getLabel()]); } - public function hasResult(SearchResultType $type, $collaboratorId) { + public function hasResult(SearchResultType $type, $collaboratorId): bool { $type = $type->getLabel(); if (!isset($this->result[$type])) { return false; @@ -73,11 +54,11 @@ class SearchResult implements ISearchResult { return false; } - public function asArray() { + public function asArray(): array { return $this->result; } - public function unsetResult(SearchResultType $type) { + public function unsetResult(SearchResultType $type): void { $type = $type->getLabel(); $this->result[$type] = []; if (isset($this->result['exact'][$type])) { diff --git a/lib/private/Collaboration/Collaborators/UserPlugin.php b/lib/private/Collaboration/Collaborators/UserPlugin.php index 9beecdaa6cb..b4cb77ad5b8 100644 --- a/lib/private/Collaboration/Collaborators/UserPlugin.php +++ b/lib/private/Collaboration/Collaborators/UserPlugin.php @@ -1,33 +1,7 @@ <?php /** - * @copyright Copyright (c) 2017 Arthur Schiwon <blizzz@arthur-schiwon.de> - * - * @author Arthur Schiwon <blizzz@arthur-schiwon.de> - * @author Christoph Wurst <christoph@winzerhof-wurst.at> - * @author Georg Ehrke <oc.list@georgehrke.com> - * @author Joas Schilling <coding@schilljs.com> - * @author John Molakvoæ <skjnldsv@protonmail.com> - * @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 Thomas Citharel <nextcloud@tcit.fr> - * - * @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/>. - * + * SPDX-FileCopyrightText: 2017 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later */ namespace OC\Collaboration\Collaborators; @@ -44,50 +18,31 @@ use OCP\Share\IShare; use OCP\UserStatus\IManager as IUserStatusManager; class UserPlugin implements ISearchPlugin { - /* @var bool */ - protected $shareWithGroupOnly; - /* @var bool */ - protected $shareeEnumeration; - /* @var bool */ - protected $shareeEnumerationInGroupOnly; - /* @var bool */ - protected $shareeEnumerationPhone; - /* @var bool */ - protected $shareeEnumerationFullMatch; - /* @var bool */ - protected $shareeEnumerationFullMatchUserId; - /* @var bool */ - protected $shareeEnumerationFullMatchEmail; - /* @var bool */ - protected $shareeEnumerationFullMatchIgnoreSecondDisplayName; - - /** @var IConfig */ - private $config; - /** @var IGroupManager */ - private $groupManager; - /** @var IUserSession */ - private $userSession; - /** @var IUserManager */ - private $userManager; - /** @var KnownUserService */ - private $knownUserService; - /** @var IUserStatusManager */ - private $userStatusManager; - - public function __construct(IConfig $config, - IUserManager $userManager, - IGroupManager $groupManager, - IUserSession $userSession, - KnownUserService $knownUserService, - IUserStatusManager $userStatusManager) { - $this->config = $config; - - $this->groupManager = $groupManager; - $this->userSession = $userSession; - $this->userManager = $userManager; - $this->knownUserService = $knownUserService; - $this->userStatusManager = $userStatusManager; + protected bool $shareWithGroupOnly; + protected bool $shareeEnumeration; + + protected bool $shareeEnumerationInGroupOnly; + + protected bool $shareeEnumerationPhone; + + protected bool $shareeEnumerationFullMatch; + + protected bool $shareeEnumerationFullMatchUserId; + + protected bool $shareeEnumerationFullMatchEmail; + + protected bool $shareeEnumerationFullMatchIgnoreSecondDisplayName; + + public function __construct( + private IConfig $config, + private IUserManager $userManager, + private IGroupManager $groupManager, + private IUserSession $userSession, + private KnownUserService $knownUserService, + private IUserStatusManager $userStatusManager, + private mixed $shareWithGroupOnlyExcludeGroupsList = [], + ) { $this->shareWithGroupOnly = $this->config->getAppValue('core', 'shareapi_only_share_with_group_members', 'no') === 'yes'; $this->shareeEnumeration = $this->config->getAppValue('core', 'shareapi_allow_share_dialog_user_enumeration', 'yes') === 'yes'; $this->shareeEnumerationInGroupOnly = $this->shareeEnumeration && $this->config->getAppValue('core', 'shareapi_restrict_user_enumeration_to_group', 'no') === 'yes'; @@ -96,15 +51,23 @@ class UserPlugin implements ISearchPlugin { $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_dn', 'no') === 'yes'; + + if ($this->shareWithGroupOnly) { + $this->shareWithGroupOnlyExcludeGroupsList = json_decode($this->config->getAppValue('core', 'shareapi_only_share_with_group_members_exclude_group_list', ''), true) ?? []; + } } - public function search($search, $limit, $offset, ISearchResult $searchResult) { + public function search($search, $limit, $offset, ISearchResult $searchResult): bool { $result = ['wide' => [], 'exact' => []]; $users = []; $hasMoreResults = false; $currentUserId = $this->userSession->getUser()->getUID(); $currentUserGroups = $this->groupManager->getUserGroupIds($this->userSession->getUser()); + + // ShareWithGroupOnly filtering + $currentUserGroups = array_diff($currentUserGroups, $this->shareWithGroupOnlyExcludeGroupsList); + if ($this->shareWithGroupOnly || $this->shareeEnumerationInGroupOnly) { // Search in all the groups this user is part of foreach ($currentUserGroups as $userGroupId) { @@ -282,8 +245,6 @@ class UserPlugin implements ISearchPlugin { } } - - $type = new SearchResultType('users'); $searchResult->addResultSet($type, $result['wide'], $result['exact']); if (count($result['exact'])) { @@ -293,7 +254,7 @@ class UserPlugin implements ISearchPlugin { return $hasMoreResults; } - public function takeOutCurrentUser(array &$users) { + public function takeOutCurrentUser(array &$users): void { $currentUser = $this->userSession->getUser(); if (!is_null($currentUser)) { if (isset($users[$currentUser->getUID()])) { diff --git a/lib/private/Collaboration/Reference/File/FileReferenceEventListener.php b/lib/private/Collaboration/Reference/File/FileReferenceEventListener.php index 1dbe8e3bc35..e468ad4eb4c 100644 --- a/lib/private/Collaboration/Reference/File/FileReferenceEventListener.php +++ b/lib/private/Collaboration/Reference/File/FileReferenceEventListener.php @@ -2,24 +2,8 @@ declare(strict_types=1); /** - * @copyright Copyright (c) 2022 Julius Härtl <jus@bitgrid.net> - * - * @author Julius Härtl <jus@bitgrid.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/>. + * SPDX-FileCopyrightText: 2022 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later */ namespace OC\Collaboration\Reference\File; @@ -36,10 +20,9 @@ use OCP\Share\Events\ShareDeletedEvent; /** @template-implements IEventListener<Event|NodeDeletedEvent|ShareDeletedEvent|ShareCreatedEvent> */ class FileReferenceEventListener implements IEventListener { - private IReferenceManager $manager; - - public function __construct(IReferenceManager $manager) { - $this->manager = $manager; + public function __construct( + private IReferenceManager $manager, + ) { } public static function register(IEventDispatcher $eventDispatcher): void { diff --git a/lib/private/Collaboration/Reference/File/FileReferenceProvider.php b/lib/private/Collaboration/Reference/File/FileReferenceProvider.php index d423a830495..3cb174d9607 100644 --- a/lib/private/Collaboration/Reference/File/FileReferenceProvider.php +++ b/lib/private/Collaboration/Reference/File/FileReferenceProvider.php @@ -2,24 +2,8 @@ declare(strict_types=1); /** - * @copyright Copyright (c) 2022 Julius Härtl <jus@bitgrid.net> - * - * @author Julius Härtl <jus@bitgrid.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/>. + * SPDX-FileCopyrightText: 2022 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later */ namespace OC\Collaboration\Reference\File; @@ -31,7 +15,6 @@ use OCP\Collaboration\Reference\Reference; use OCP\Files\IMimeTypeDetector; use OCP\Files\InvalidPathException; use OCP\Files\IRootFolder; -use OCP\Files\Node; use OCP\Files\NotFoundException; use OCP\Files\NotPermittedException; use OCP\IL10N; @@ -41,26 +24,18 @@ use OCP\IUserSession; use OCP\L10N\IFactory; class FileReferenceProvider extends ADiscoverableReferenceProvider { - private IURLGenerator $urlGenerator; - private IRootFolder $rootFolder; private ?string $userId; - private IPreview $previewManager; - private IMimeTypeDetector $mimeTypeDetector; private IL10N $l10n; public function __construct( - IURLGenerator $urlGenerator, - IRootFolder $rootFolder, + private IURLGenerator $urlGenerator, + private IRootFolder $rootFolder, IUserSession $userSession, - IMimeTypeDetector $mimeTypeDetector, - IPreview $previewManager, - IFactory $l10n + private IMimeTypeDetector $mimeTypeDetector, + private IPreview $previewManager, + IFactory $l10n, ) { - $this->urlGenerator = $urlGenerator; - $this->rootFolder = $rootFolder; - $this->userId = $userSession->getUser() ? $userSession->getUser()->getUID() : null; - $this->previewManager = $previewManager; - $this->mimeTypeDetector = $mimeTypeDetector; + $this->userId = $userSession->getUser()?->getUID(); $this->l10n = $l10n->get('files'); } @@ -129,15 +104,12 @@ class FileReferenceProvider extends ADiscoverableReferenceProvider { try { $userFolder = $this->rootFolder->getUserFolder($this->userId); - $files = $userFolder->getById($fileId); + $file = $userFolder->getFirstNodeById($fileId); - if (empty($files)) { + if (!$file) { throw new NotFoundException(); } - /** @var Node $file */ - $file = array_shift($files); - $reference->setTitle($file->getName()); $reference->setDescription($file->getMimetype()); $reference->setUrl($this->urlGenerator->getAbsoluteURL('/index.php/f/' . $fileId)); diff --git a/lib/private/Collaboration/Reference/LinkReferenceProvider.php b/lib/private/Collaboration/Reference/LinkReferenceProvider.php index dbdab75abcb..5af23bf633d 100644 --- a/lib/private/Collaboration/Reference/LinkReferenceProvider.php +++ b/lib/private/Collaboration/Reference/LinkReferenceProvider.php @@ -2,190 +2,14 @@ declare(strict_types=1); /** - * @copyright Copyright (c) 2022 Julius Härtl <jus@bitgrid.net> - * - * @author Julius Härtl <jus@bitgrid.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/>. + * SPDX-FileCopyrightText: 2022 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later */ namespace OC\Collaboration\Reference; -use Fusonic\OpenGraph\Consumer; -use GuzzleHttp\Exception\GuzzleException; -use GuzzleHttp\Psr7\LimitStream; -use GuzzleHttp\Psr7\Utils; -use OC\Security\RateLimiting\Exception\RateLimitExceededException; -use OC\Security\RateLimiting\Limiter; -use OC\SystemConfig; -use OCP\Collaboration\Reference\IReference; -use OCP\Collaboration\Reference\IReferenceProvider; -use OCP\Collaboration\Reference\Reference; -use OCP\Files\AppData\IAppDataFactory; -use OCP\Files\NotFoundException; -use OCP\Http\Client\IClientService; -use OCP\IRequest; -use OCP\IURLGenerator; -use OCP\IUserSession; -use Psr\Log\LoggerInterface; +use OCP\Collaboration\Reference\LinkReferenceProvider as OCPLinkReferenceProvider; -class LinkReferenceProvider implements IReferenceProvider { - public const MAX_PREVIEW_SIZE = 1024 * 1024; - - public const ALLOWED_CONTENT_TYPES = [ - 'image/png', - 'image/jpg', - 'image/jpeg', - 'image/gif', - 'image/svg+xml', - 'image/webp' - ]; - - private IClientService $clientService; - private LoggerInterface $logger; - private SystemConfig $systemConfig; - private IAppDataFactory $appDataFactory; - private IURLGenerator $urlGenerator; - private Limiter $limiter; - private IUserSession $userSession; - private IRequest $request; - - public function __construct(IClientService $clientService, LoggerInterface $logger, SystemConfig $systemConfig, IAppDataFactory $appDataFactory, IURLGenerator $urlGenerator, Limiter $limiter, IUserSession $userSession, IRequest $request) { - $this->clientService = $clientService; - $this->logger = $logger; - $this->systemConfig = $systemConfig; - $this->appDataFactory = $appDataFactory; - $this->urlGenerator = $urlGenerator; - $this->limiter = $limiter; - $this->userSession = $userSession; - $this->request = $request; - } - - public function matchReference(string $referenceText): bool { - if ($this->systemConfig->getValue('reference_opengraph', true) !== true) { - return false; - } - - return (bool)preg_match(IURLGenerator::URL_REGEX, $referenceText); - } - - public function resolveReference(string $referenceText): ?IReference { - if ($this->matchReference($referenceText)) { - $reference = new Reference($referenceText); - $this->fetchReference($reference); - return $reference; - } - - return null; - } - - private function fetchReference(Reference $reference): void { - try { - $user = $this->userSession->getUser(); - if ($user) { - $this->limiter->registerUserRequest('opengraph', 10, 120, $user); - } else { - $this->limiter->registerAnonRequest('opengraph', 10, 120, $this->request->getRemoteAddress()); - } - } catch (RateLimitExceededException $e) { - return; - } - - $client = $this->clientService->newClient(); - try { - $headResponse = $client->head($reference->getId(), [ 'timeout' => 10 ]); - } catch (\Exception $e) { - $this->logger->debug('Failed to perform HEAD request to get target metadata', ['exception' => $e]); - return; - } - $linkContentLength = $headResponse->getHeader('Content-Length'); - if (is_numeric($linkContentLength) && (int) $linkContentLength > 5 * 1024 * 1024) { - $this->logger->debug('Skip resolving links pointing to content length > 5 MB'); - return; - } - $linkContentType = $headResponse->getHeader('Content-Type'); - $expectedContentType = 'text/html'; - $suffixedExpectedContentType = $expectedContentType . ';'; - $startsWithSuffixed = substr($linkContentType, 0, strlen($suffixedExpectedContentType)) === $suffixedExpectedContentType; - // check the header begins with the expected content type - if ($linkContentType !== $expectedContentType && !$startsWithSuffixed) { - $this->logger->debug('Skip resolving links pointing to content type that is not "text/html"'); - return; - } - try { - $response = $client->get($reference->getId(), [ 'timeout' => 10 ]); - } catch (\Exception $e) { - $this->logger->debug('Failed to fetch link for obtaining open graph data', ['exception' => $e]); - return; - } - - $responseBody = (string)$response->getBody(); - - // OpenGraph handling - $consumer = new Consumer(); - $consumer->useFallbackMode = true; - $object = $consumer->loadHtml($responseBody); - - $reference->setUrl($reference->getId()); - - if ($object->title) { - $reference->setTitle($object->title); - } - - if ($object->description) { - $reference->setDescription($object->description); - } - - if ($object->images) { - try { - $host = parse_url($object->images[0]->url, PHP_URL_HOST); - if ($host === false || $host === null) { - $this->logger->warning('Could not detect host of open graph image URI for ' . $reference->getId()); - } else { - $appData = $this->appDataFactory->get('core'); - try { - $folder = $appData->getFolder('opengraph'); - } catch (NotFoundException $e) { - $folder = $appData->newFolder('opengraph'); - } - $response = $client->get($object->images[0]->url, ['timeout' => 10]); - $contentType = $response->getHeader('Content-Type'); - $contentLength = $response->getHeader('Content-Length'); - - if (in_array($contentType, self::ALLOWED_CONTENT_TYPES, true) && $contentLength < self::MAX_PREVIEW_SIZE) { - $stream = Utils::streamFor($response->getBody()); - $bodyStream = new LimitStream($stream, self::MAX_PREVIEW_SIZE, 0); - $reference->setImageContentType($contentType); - $folder->newFile(md5($reference->getId()), $bodyStream->getContents()); - $reference->setImageUrl($this->urlGenerator->linkToRouteAbsolute('core.Reference.preview', ['referenceId' => md5($reference->getId())])); - } - } - } catch (GuzzleException $e) { - $this->logger->info('Failed to fetch and store the open graph image for ' . $reference->getId(), ['exception' => $e]); - } catch (\Throwable $e) { - $this->logger->error('Failed to fetch and store the open graph image for ' . $reference->getId(), ['exception' => $e]); - } - } - } - - public function getCachePrefix(string $referenceId): string { - return $referenceId; - } - - public function getCacheKey(string $referenceId): ?string { - return null; - } +/** @deprecated 29.0.0 Use OCP\Collaboration\Reference\LinkReferenceProvider instead */ +class LinkReferenceProvider extends OCPLinkReferenceProvider { } diff --git a/lib/private/Collaboration/Reference/ReferenceManager.php b/lib/private/Collaboration/Reference/ReferenceManager.php index 2897410f5d6..208c50a074b 100644 --- a/lib/private/Collaboration/Reference/ReferenceManager.php +++ b/lib/private/Collaboration/Reference/ReferenceManager.php @@ -2,24 +2,8 @@ declare(strict_types=1); /** - * @copyright Copyright (c) 2022 Julius Härtl <jus@bitgrid.net> - * - * @author Julius Härtl <jus@bitgrid.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/>. + * SPDX-FileCopyrightText: 2022 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later */ namespace OC\Collaboration\Reference; @@ -46,33 +30,22 @@ class ReferenceManager implements IReferenceManager { /** @var IReferenceProvider[]|null */ private ?array $providers = null; private ICache $cache; - private Coordinator $coordinator; - private ContainerInterface $container; - private LinkReferenceProvider $linkReferenceProvider; - private LoggerInterface $logger; - private IConfig $config; - private IUserSession $userSession; - - public function __construct(LinkReferenceProvider $linkReferenceProvider, - ICacheFactory $cacheFactory, - Coordinator $coordinator, - ContainerInterface $container, - LoggerInterface $logger, - IConfig $config, - IUserSession $userSession) { - $this->linkReferenceProvider = $linkReferenceProvider; + + public function __construct( + private LinkReferenceProvider $linkReferenceProvider, + ICacheFactory $cacheFactory, + private Coordinator $coordinator, + private ContainerInterface $container, + private LoggerInterface $logger, + private IConfig $config, + private IUserSession $userSession, + ) { $this->cache = $cacheFactory->createDistributed('reference'); - $this->coordinator = $coordinator; - $this->container = $container; - $this->logger = $logger; - $this->config = $config; - $this->userSession = $userSession; } /** * Extract a list of URLs from a text * - * @param string $text * @return string[] */ public function extractReferences(string $text): array { @@ -85,9 +58,6 @@ class ReferenceManager implements IReferenceManager { /** * Try to get a cached reference object from a reference string - * - * @param string $referenceId - * @return IReference|null */ public function getReferenceFromCache(string $referenceId): ?IReference { $matchedProvider = $this->getMatchedProvider($referenceId); @@ -102,9 +72,6 @@ class ReferenceManager implements IReferenceManager { /** * Try to get a cached reference object from a full cache key - * - * @param string $cacheKey - * @return IReference|null */ public function getReferenceByCacheKey(string $cacheKey): ?IReference { $cached = $this->cache->get($cacheKey); @@ -118,9 +85,6 @@ class ReferenceManager implements IReferenceManager { /** * Get a reference object from a reference string with a matching provider * Use a cached reference if possible - * - * @param string $referenceId - * @return IReference|null */ public function resolveReference(string $referenceId): ?IReference { $matchedProvider = $this->getMatchedProvider($referenceId); @@ -137,6 +101,11 @@ class ReferenceManager implements IReferenceManager { $reference = $matchedProvider->resolveReference($referenceId); if ($reference) { + $cachePrefix = $matchedProvider->getCachePrefix($referenceId); + if ($cachePrefix !== '') { + // If a prefix is used we set an additional key to know when we need to delete by prefix during invalidateCache() + $this->cache->set('hasPrefix-' . md5($cachePrefix), true, self::CACHE_TTL); + } $this->cache->set($cacheKey, Reference::toCache($reference), self::CACHE_TTL); return $reference; } @@ -148,7 +117,6 @@ class ReferenceManager implements IReferenceManager { * Try to match a reference string with all the registered providers * Fallback to the link reference provider (using OpenGraph) * - * @param string $referenceId * @return IReferenceProvider|null the first matching provider */ private function getMatchedProvider(string $referenceId): ?IReferenceProvider { @@ -169,10 +137,6 @@ class ReferenceManager implements IReferenceManager { /** * Get a hashed full cache key from a key and prefix given by a provider - * - * @param IReferenceProvider $provider - * @param string $referenceId - * @return string */ private function getFullCacheKey(IReferenceProvider $provider, string $referenceId): string { $cacheKey = $provider->getCacheKey($referenceId); @@ -183,14 +147,14 @@ class ReferenceManager implements IReferenceManager { /** * Remove a specific cache entry from its key+prefix - * - * @param string $cachePrefix - * @param string|null $cacheKey - * @return void */ public function invalidateCache(string $cachePrefix, ?string $cacheKey = null): void { if ($cacheKey === null) { - $this->cache->clear(md5($cachePrefix)); + // clear might be a heavy operation, so we only do it if there have actually been keys set + if ($this->cache->remove('hasPrefix-' . md5($cachePrefix))) { + $this->cache->clear(md5($cachePrefix)); + } + return; } diff --git a/lib/private/Collaboration/Reference/RenderReferenceEventListener.php b/lib/private/Collaboration/Reference/RenderReferenceEventListener.php index dc2c5612666..9e6192314cb 100644 --- a/lib/private/Collaboration/Reference/RenderReferenceEventListener.php +++ b/lib/private/Collaboration/Reference/RenderReferenceEventListener.php @@ -2,24 +2,8 @@ declare(strict_types=1); /** - * @copyright Copyright (c) 2022 Julien Veyssier <eneiluj@posteo.net> - * - * @author Julien Veyssier <eneiluj@posteo.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/>. + * SPDX-FileCopyrightText: 2022 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later */ namespace OC\Collaboration\Reference; @@ -34,12 +18,10 @@ use OCP\IInitialStateService; /** @template-implements IEventListener<Event|RenderReferenceEvent> */ class RenderReferenceEventListener implements IEventListener { - private IReferenceManager $manager; - private IInitialStateService $initialStateService; - - public function __construct(IReferenceManager $manager, IInitialStateService $initialStateService) { - $this->manager = $manager; - $this->initialStateService = $initialStateService; + public function __construct( + private IReferenceManager $manager, + private IInitialStateService $initialStateService, + ) { } public static function register(IEventDispatcher $eventDispatcher): void { diff --git a/lib/private/Collaboration/Resources/Collection.php b/lib/private/Collaboration/Resources/Collection.php index e34c38a80cd..2c5cc28ef28 100644 --- a/lib/private/Collaboration/Resources/Collection.php +++ b/lib/private/Collaboration/Resources/Collection.php @@ -3,27 +3,8 @@ declare(strict_types=1); /** - * @copyright Copyright (c) 2018 Joas Schilling <coding@schilljs.com> - * - * @author Christoph Wurst <christoph@winzerhof-wurst.at> - * @author Joas Schilling <coding@schilljs.com> - * @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/>. - * + * SPDX-FileCopyrightText: 2018 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later */ namespace OC\Collaboration\Resources; @@ -37,46 +18,21 @@ use OCP\IDBConnection; use OCP\IUser; class Collection implements ICollection { - /** @var Manager */ - protected $manager; - - /** @var IDBConnection */ - protected $connection; - - /** @var int */ - protected $id; - - /** @var string */ - protected $name; - - /** @var IUser|null */ - protected $userForAccess; - - /** @var bool|null */ - protected $access; - /** @var IResource[] */ - protected $resources; + protected array $resources = []; public function __construct( - IManager $manager, - IDBConnection $connection, - int $id, - string $name, - ?IUser $userForAccess = null, - ?bool $access = null + /** @var Manager $manager */ + protected IManager $manager, + protected IDBConnection $connection, + protected int $id, + protected string $name, + protected ?IUser $userForAccess = null, + protected ?bool $access = null ) { - $this->manager = $manager; - $this->connection = $connection; - $this->id = $id; - $this->name = $name; - $this->userForAccess = $userForAccess; - $this->access = $access; - $this->resources = []; } /** - * @return int * @since 16.0.0 */ public function getId(): int { @@ -84,7 +40,6 @@ class Collection implements ICollection { } /** - * @return string * @since 16.0.0 */ public function getName(): string { @@ -92,7 +47,6 @@ class Collection implements ICollection { } /** - * @param string $name * @since 16.0.0 */ public function setName(string $name): void { @@ -120,7 +74,6 @@ class Collection implements ICollection { /** * Adds a resource to a collection * - * @param IResource $resource * @throws ResourceException when the resource is already part of the collection * @since 16.0.0 */ @@ -153,7 +106,6 @@ class Collection implements ICollection { /** * Removes a resource from a collection * - * @param IResource $resource * @since 16.0.0 */ public function removeResource(IResource $resource): void { @@ -178,8 +130,6 @@ class Collection implements ICollection { /** * Can a user/guest access the collection * - * @param IUser|null $user - * @return bool * @since 16.0.0 */ public function canAccess(?IUser $user): bool { diff --git a/lib/private/Collaboration/Resources/Listener.php b/lib/private/Collaboration/Resources/Listener.php index 4330f3570bc..dfdde24d78e 100644 --- a/lib/private/Collaboration/Resources/Listener.php +++ b/lib/private/Collaboration/Resources/Listener.php @@ -3,26 +3,8 @@ declare(strict_types=1); /** - * @copyright Copyright (c) 2019 Joas Schilling <coding@schilljs.com> - * - * @author Christoph Wurst <christoph@winzerhof-wurst.at> - * @author Joas Schilling <coding@schilljs.com> - * - * @license GNU AGPL version 3 or any later version - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as - * published by the Free Software Foundation, either version 3 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - * + * SPDX-FileCopyrightText: 2019 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later */ namespace OC\Collaboration\Resources; diff --git a/lib/private/Collaboration/Resources/Manager.php b/lib/private/Collaboration/Resources/Manager.php index fc8804e69b4..6c9b77d41c5 100644 --- a/lib/private/Collaboration/Resources/Manager.php +++ b/lib/private/Collaboration/Resources/Manager.php @@ -3,27 +3,8 @@ declare(strict_types=1); /** - * @copyright Copyright (c) 2018 Joas Schilling <coding@schilljs.com> - * - * @author Daniel Kesselberg <mail@danielkesselberg.de> - * @author Joas Schilling <coding@schilljs.com> - * @author Julius Härtl <jus@bitgrid.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/>. - * + * SPDX-FileCopyrightText: 2018 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later */ namespace OC\Collaboration\Resources; @@ -45,26 +26,17 @@ class Manager implements IManager { public const TABLE_RESOURCES = 'collres_resources'; public const TABLE_ACCESS_CACHE = 'collres_accesscache'; - /** @var IDBConnection */ - protected $connection; - /** @var IProviderManager */ - protected $providerManager; - /** @var LoggerInterface */ - protected $logger; - /** @var string[] */ - protected $providers = []; - + protected array $providers = []; - public function __construct(IDBConnection $connection, IProviderManager $providerManager, LoggerInterface $logger) { - $this->connection = $connection; - $this->providerManager = $providerManager; - $this->logger = $logger; + public function __construct( + protected IDBConnection $connection, + protected IProviderManager $providerManager, + protected LoggerInterface $logger, + ) { } /** - * @param int $id - * @return ICollection * @throws CollectionException when the collection could not be found * @since 16.0.0 */ @@ -85,9 +57,6 @@ class Manager implements IManager { } /** - * @param int $id - * @param IUser|null $user - * @return ICollection * @throws CollectionException when the collection could not be found * @since 16.0.0 */ @@ -122,10 +91,6 @@ class Manager implements IManager { } /** - * @param IUser $user - * @param string $filter - * @param int $limit - * @param int $start * @return ICollection[] * @since 16.0.0 */ @@ -173,8 +138,6 @@ class Manager implements IManager { } /** - * @param string $name - * @return ICollection * @since 16.0.0 */ public function newCollection(string $name): ICollection { @@ -189,9 +152,6 @@ class Manager implements IManager { } /** - * @param string $type - * @param string $id - * @return IResource * @since 16.0.0 */ public function createResource(string $type, string $id): IResource { @@ -199,10 +159,6 @@ class Manager implements IManager { } /** - * @param string $type - * @param string $id - * @param IUser|null $user - * @return IResource * @throws ResourceException * @since 16.0.0 */ @@ -239,8 +195,6 @@ class Manager implements IManager { } /** - * @param ICollection $collection - * @param IUser|null $user * @return IResource[] * @since 16.0.0 */ @@ -274,8 +228,6 @@ class Manager implements IManager { /** * Get the rich object data of a resource * - * @param IResource $resource - * @return array * @since 16.0.0 */ public function getResourceRichObject(IResource $resource): array { @@ -294,9 +246,6 @@ class Manager implements IManager { /** * Can a user/guest access the collection * - * @param IResource $resource - * @param IUser|null $user - * @return bool * @since 16.0.0 */ public function canAccessResource(IResource $resource, ?IUser $user): bool { @@ -325,9 +274,6 @@ class Manager implements IManager { /** * Can a user/guest access the collection * - * @param ICollection $collection - * @param IUser|null $user - * @return bool * @since 16.0.0 */ public function canAccessCollection(ICollection $collection, ?IUser $user): bool { @@ -505,9 +451,6 @@ class Manager implements IManager { $query->execute(); } - /** - * @param string $provider - */ public function registerResourceProvider(string $provider): void { $this->logger->debug('\OC\Collaboration\Resources\Manager::registerResourceProvider is deprecated', ['provider' => $provider]); $this->providerManager->registerResourceProvider($provider); @@ -516,7 +459,6 @@ class Manager implements IManager { /** * Get the resource type of the provider * - * @return string * @since 16.0.0 */ public function getType(): string { diff --git a/lib/private/Collaboration/Resources/ProviderManager.php b/lib/private/Collaboration/Resources/ProviderManager.php index 4f5ed53b162..0ce4ae7155a 100644 --- a/lib/private/Collaboration/Resources/ProviderManager.php +++ b/lib/private/Collaboration/Resources/ProviderManager.php @@ -3,26 +3,8 @@ declare(strict_types=1); /** - * @copyright Copyright (c) 2019 Daniel Kesselberg <mail@danielkesselberg.de> - * - * @author Daniel Kesselberg <mail@danielkesselberg.de> - * @author Joas Schilling <coding@schilljs.com> - * - * @license GNU AGPL version 3 or any later version - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as - * published by the Free Software Foundation, either version 3 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - * + * SPDX-FileCopyrightText: 2019 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later */ namespace OC\Collaboration\Resources; @@ -34,20 +16,15 @@ use Psr\Log\LoggerInterface; class ProviderManager implements IProviderManager { /** @var string[] */ - protected $providers = []; + protected array $providers = []; /** @var IProvider[] */ - protected $providerInstances = []; + protected array $providerInstances = []; - /** @var IServerContainer */ - protected $serverContainer; - - /** @var LoggerInterface */ - protected $logger; - - public function __construct(IServerContainer $serverContainer, LoggerInterface $logger) { - $this->serverContainer = $serverContainer; - $this->logger = $logger; + public function __construct( + protected IServerContainer $serverContainer, + protected LoggerInterface $logger, + ) { } public function getResourceProviders(): array { diff --git a/lib/private/Collaboration/Resources/Resource.php b/lib/private/Collaboration/Resources/Resource.php index b5e0215cb39..ae011e319de 100644 --- a/lib/private/Collaboration/Resources/Resource.php +++ b/lib/private/Collaboration/Resources/Resource.php @@ -3,26 +3,8 @@ declare(strict_types=1); /** - * @copyright Copyright (c) 2018 Joas Schilling <coding@schilljs.com> - * - * @author Joas Schilling <coding@schilljs.com> - * @author Julius Härtl <jus@bitgrid.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/>. - * + * SPDX-FileCopyrightText: 2018 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later */ namespace OC\Collaboration\Resources; @@ -33,45 +15,19 @@ use OCP\IDBConnection; use OCP\IUser; class Resource implements IResource { - /** @var IManager */ - protected $manager; - - /** @var IDBConnection */ - protected $connection; - - /** @var string */ - protected $type; - - /** @var string */ - protected $id; - - /** @var IUser|null */ - protected $userForAccess; - - /** @var bool|null */ - protected $access; - - /** @var array|null */ - protected $data; + protected ?array $data = null; public function __construct( - IManager $manager, - IDBConnection $connection, - string $type, - string $id, - ?IUser $userForAccess = null, - ?bool $access = null + protected IManager $manager, + protected IDBConnection $connection, + protected string $type, + protected string $id, + protected ?IUser $userForAccess = null, + protected ?bool $access = null ) { - $this->manager = $manager; - $this->connection = $connection; - $this->type = $type; - $this->id = $id; - $this->userForAccess = $userForAccess; - $this->access = $access; } /** - * @return string * @since 16.0.0 */ public function getType(): string { @@ -79,7 +35,6 @@ class Resource implements IResource { } /** - * @return string * @since 16.0.0 */ public function getId(): string { @@ -87,7 +42,6 @@ class Resource implements IResource { } /** - * @return array * @since 16.0.0 */ public function getRichObject(): array { @@ -101,8 +55,6 @@ class Resource implements IResource { /** * Can a user/guest access the resource * - * @param IUser|null $user - * @return bool * @since 16.0.0 */ public function canAccess(?IUser $user): bool { diff --git a/lib/private/Color.php b/lib/private/Color.php index 91b9b63c009..e96d6fd23bd 100644 --- a/lib/private/Color.php +++ b/lib/private/Color.php @@ -1,25 +1,7 @@ <?php /** - * @copyright Copyright (c) 2016, John Molakvoæ <skjnldsv@protonmail.com> - * - * @author Christoph Wurst <christoph@winzerhof-wurst.at> - * @author Morris Jobke <hey@morrisjobke.de> - * - * @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/>. - * + * SPDX-FileCopyrightText: 2016 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later */ namespace OC; diff --git a/lib/private/Command/AsyncBus.php b/lib/private/Command/AsyncBus.php index ec6fbc91f68..fb7860c9dd8 100644 --- a/lib/private/Command/AsyncBus.php +++ b/lib/private/Command/AsyncBus.php @@ -1,23 +1,9 @@ <?php + /** - * @copyright Copyright (c) 2016, ownCloud, Inc. - * - * @author Robin Appelman <robin@icewind.nl> - * - * @license AGPL-3.0 - * - * This code is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License, version 3, - * as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License, version 3, - * along with this program. If not, see <http://www.gnu.org/licenses/> - * + * SPDX-FileCopyrightText: 2016-2024 Nextcloud GmbH and Nextcloud contributors + * SPDX-FileCopyrightText: 2016 ownCloud, Inc. + * SPDX-License-Identifier: AGPL-3.0-only */ namespace OC\Command; @@ -82,7 +68,7 @@ abstract class AsyncBus implements IBus { private function canRunAsync($command) { $traits = $this->getTraits($command); foreach ($traits as $trait) { - if (array_search($trait, $this->syncTraits) !== false) { + if (in_array($trait, $this->syncTraits)) { return false; } } diff --git a/lib/private/Command/CallableJob.php b/lib/private/Command/CallableJob.php index 8bb3c76c9af..8744977a81e 100644 --- a/lib/private/Command/CallableJob.php +++ b/lib/private/Command/CallableJob.php @@ -1,27 +1,13 @@ <?php + /** - * @copyright Copyright (c) 2016, ownCloud, Inc. - * - * @author Robin Appelman <robin@icewind.nl> - * - * @license AGPL-3.0 - * - * This code is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License, version 3, - * as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License, version 3, - * along with this program. If not, see <http://www.gnu.org/licenses/> - * + * SPDX-FileCopyrightText: 2016-2024 Nextcloud GmbH and Nextcloud contributors + * SPDX-FileCopyrightText: 2016 ownCloud, Inc. + * SPDX-License-Identifier: AGPL-3.0-only */ namespace OC\Command; -use OC\BackgroundJob\QueuedJob; +use OCP\BackgroundJob\QueuedJob; class CallableJob extends QueuedJob { protected function run($serializedCallable) { diff --git a/lib/private/Command/ClosureJob.php b/lib/private/Command/ClosureJob.php index 5639852e4db..58fe9696437 100644 --- a/lib/private/Command/ClosureJob.php +++ b/lib/private/Command/ClosureJob.php @@ -1,34 +1,18 @@ <?php + /** - * @copyright Copyright (c) 2016, ownCloud, Inc. - * - * @author Robin Appelman <robin@icewind.nl> - * @author Roeland Jago Douma <roeland@famdouma.nl> - * - * @license AGPL-3.0 - * - * This code is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License, version 3, - * as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License, version 3, - * along with this program. If not, see <http://www.gnu.org/licenses/> - * + * SPDX-FileCopyrightText: 2016-2024 Nextcloud GmbH and Nextcloud contributors + * SPDX-FileCopyrightText: 2016 ownCloud, Inc. + * SPDX-License-Identifier: AGPL-3.0-only */ namespace OC\Command; -use OC\BackgroundJob\QueuedJob; use Laravel\SerializableClosure\SerializableClosure as LaravelClosure; -use Opis\Closure\SerializableClosure as OpisClosure; +use OCP\BackgroundJob\QueuedJob; class ClosureJob extends QueuedJob { - protected function run($serializedCallable) { - $callable = unserialize($serializedCallable, [LaravelClosure::class, OpisClosure::class]); + protected function run($argument) { + $callable = unserialize($argument, [LaravelClosure::class]); $callable = $callable->getClosure(); if (is_callable($callable)) { $callable(); diff --git a/lib/private/Command/CommandJob.php b/lib/private/Command/CommandJob.php index 5b267162c81..41ca3a9cb40 100644 --- a/lib/private/Command/CommandJob.php +++ b/lib/private/Command/CommandJob.php @@ -1,36 +1,21 @@ <?php + /** - * @copyright Copyright (c) 2016, ownCloud, Inc. - * - * @author Robin Appelman <robin@icewind.nl> - * @author Roeland Jago Douma <roeland@famdouma.nl> - * - * @license AGPL-3.0 - * - * This code is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License, version 3, - * as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License, version 3, - * along with this program. If not, see <http://www.gnu.org/licenses/> - * + * SPDX-FileCopyrightText: 2016-2024 Nextcloud GmbH and Nextcloud contributors + * SPDX-FileCopyrightText: 2016 ownCloud, Inc. + * SPDX-License-Identifier: AGPL-3.0-only */ namespace OC\Command; -use OC\BackgroundJob\QueuedJob; +use OCP\BackgroundJob\QueuedJob; use OCP\Command\ICommand; /** * Wrap a command in the background job interface */ class CommandJob extends QueuedJob { - protected function run($serializedCommand) { - $command = unserialize($serializedCommand); + protected function run($argument) { + $command = unserialize($argument); if ($command instanceof ICommand) { $command->handle(); } else { diff --git a/lib/private/Command/CronBus.php b/lib/private/Command/CronBus.php index 8749ad0bff5..1ff9bb7099a 100644 --- a/lib/private/Command/CronBus.php +++ b/lib/private/Command/CronBus.php @@ -1,56 +1,30 @@ <?php /** - * @copyright Copyright (c) 2017 Robin Appelman <robin@icewind.nl> - * - * @author Christoph Wurst <christoph@winzerhof-wurst.at> - * @author Morris Jobke <hey@morrisjobke.de> - * @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/>. - * + * SPDX-FileCopyrightText: 2017 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later */ namespace OC\Command; -use OCP\Command\ICommand; use Laravel\SerializableClosure\SerializableClosure; +use OCP\BackgroundJob\IJob; +use OCP\BackgroundJob\IJobList; +use OCP\Command\ICommand; class CronBus extends AsyncBus { - /** - * @var \OCP\BackgroundJob\IJobList - */ - private $jobList; - - - /** - * @param \OCP\BackgroundJob\IJobList $jobList - */ - public function __construct($jobList) { - $this->jobList = $jobList; + public function __construct( + private IJobList $jobList, + ) { } - protected function queueCommand($command) { + protected function queueCommand($command): void { $this->jobList->add($this->getJobClass($command), $this->serializeCommand($command)); } /** - * @param \OCP\Command\ICommand | callable $command - * @return string + * @param ICommand|callable $command + * @return class-string<IJob> */ - private function getJobClass($command) { + private function getJobClass($command): string { if ($command instanceof \Closure) { return ClosureJob::class; } elseif (is_callable($command)) { @@ -63,10 +37,10 @@ class CronBus extends AsyncBus { } /** - * @param \OCP\Command\ICommand | callable $command + * @param ICommand|callable $command * @return string */ - private function serializeCommand($command) { + private function serializeCommand($command): string { if ($command instanceof \Closure) { return serialize(new SerializableClosure($command)); } elseif (is_callable($command) or $command instanceof ICommand) { diff --git a/lib/private/Command/FileAccess.php b/lib/private/Command/FileAccess.php index 584e61696c3..1af1591492d 100644 --- a/lib/private/Command/FileAccess.php +++ b/lib/private/Command/FileAccess.php @@ -1,24 +1,9 @@ <?php + /** - * @copyright Copyright (c) 2016, ownCloud, Inc. - * - * @author Christoph Wurst <christoph@winzerhof-wurst.at> - * @author Robin Appelman <robin@icewind.nl> - * - * @license AGPL-3.0 - * - * This code is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License, version 3, - * as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License, version 3, - * along with this program. If not, see <http://www.gnu.org/licenses/> - * + * SPDX-FileCopyrightText: 2016-2024 Nextcloud GmbH and Nextcloud contributors + * SPDX-FileCopyrightText: 2016 ownCloud, Inc. + * SPDX-License-Identifier: AGPL-3.0-only */ namespace OC\Command; diff --git a/lib/private/Command/QueueBus.php b/lib/private/Command/QueueBus.php index b2ceea744bc..ed7d43b38b6 100644 --- a/lib/private/Command/QueueBus.php +++ b/lib/private/Command/QueueBus.php @@ -1,25 +1,9 @@ <?php + /** - * @copyright Copyright (c) 2016, ownCloud, Inc. - * - * @author Christoph Wurst <christoph@winzerhof-wurst.at> - * @author Julius Härtl <jus@bitgrid.net> - * @author Robin Appelman <robin@icewind.nl> - * - * @license AGPL-3.0 - * - * This code is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License, version 3, - * as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License, version 3, - * along with this program. If not, see <http://www.gnu.org/licenses/> - * + * SPDX-FileCopyrightText: 2016-2024 Nextcloud GmbH and Nextcloud contributors + * SPDX-FileCopyrightText: 2016 ownCloud, Inc. + * SPDX-License-Identifier: AGPL-3.0-only */ namespace OC\Command; diff --git a/lib/private/Comments/Comment.php b/lib/private/Comments/Comment.php index 35e88c74438..422e29c084d 100644 --- a/lib/private/Comments/Comment.php +++ b/lib/private/Comments/Comment.php @@ -1,27 +1,9 @@ <?php + /** - * @copyright Copyright (c) 2016, ownCloud, Inc. - * - * @author Arthur Schiwon <blizzz@arthur-schiwon.de> - * @author Christoph Wurst <christoph@winzerhof-wurst.at> - * @author Joas Schilling <coding@schilljs.com> - * @author Roeland Jago Douma <roeland@famdouma.nl> - * @author Thomas Müller <thomas.mueller@tmit.eu> - * - * @license AGPL-3.0 - * - * This code is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License, version 3, - * as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License, version 3, - * along with this program. If not, see <http://www.gnu.org/licenses/> - * + * SPDX-FileCopyrightText: 2016-2024 Nextcloud GmbH and Nextcloud contributors + * SPDX-FileCopyrightText: 2016 ownCloud, Inc. + * SPDX-License-Identifier: AGPL-3.0-only */ namespace OC\Comments; @@ -42,6 +24,7 @@ class Comment implements IComment { 'objectType' => '', 'objectId' => '', 'referenceId' => null, + 'metaData' => null, 'creationDT' => null, 'latestChildDT' => null, 'reactions' => null, @@ -54,7 +37,7 @@ class Comment implements IComment { * @param array $data optional, array with keys according to column names from * the comments database scheme */ - public function __construct(array $data = null) { + public function __construct(?array $data = null) { if (is_array($data)) { $this->fromArray($data); } @@ -219,7 +202,7 @@ class Comment implements IComment { * */ public function getMentions(): array { - $ok = preg_match_all("/\B(?<![^a-z0-9_\-@\.\'\s])@(\"guest\/[a-f0-9]+\"|\"group\/[a-z0-9_\-@\.\' ]+\"|\"[a-z0-9_\-@\.\' ]+\"|[a-z0-9_\-@\.\']+)/i", $this->getMessage(), $mentions); + $ok = preg_match_all("/\B(?<![^a-z0-9_\-@\.\'\s])@(\"guest\/[a-f0-9]+\"|\"(?:federated_)?(?:group|team|user){1}\/[a-z0-9_\-@\.\' \/:]+\"|\"[a-z0-9_\-@\.\' ]+\"|[a-z0-9_\-@\.\']+)/i", $this->getMessage(), $mentions); if (!$ok || !isset($mentions[0])) { return []; } @@ -229,11 +212,21 @@ class Comment implements IComment { }); $result = []; foreach ($mentionIds as $mentionId) { + // Cut-off the @ and remove wrapping double-quotes $cleanId = trim(substr($mentionId, 1), '"'); + if (str_starts_with($cleanId, 'guest/')) { $result[] = ['type' => 'guest', 'id' => $cleanId]; + } elseif (str_starts_with($cleanId, 'federated_group/')) { + $result[] = ['type' => 'federated_group', 'id' => substr($cleanId, 16)]; } elseif (str_starts_with($cleanId, 'group/')) { $result[] = ['type' => 'group', 'id' => substr($cleanId, 6)]; + } elseif (str_starts_with($cleanId, 'federated_team/')) { + $result[] = ['type' => 'federated_team', 'id' => substr($cleanId, 15)]; + } elseif (str_starts_with($cleanId, 'team/')) { + $result[] = ['type' => 'team', 'id' => substr($cleanId, 5)]; + } elseif (str_starts_with($cleanId, 'federated_user/')) { + $result[] = ['type' => 'federated_user', 'id' => substr($cleanId, 15)]; } else { $result[] = ['type' => 'user', 'id' => $cleanId]; } @@ -403,6 +396,34 @@ class Comment implements IComment { /** * @inheritDoc */ + public function getMetaData(): ?array { + if ($this->data['metaData'] === null) { + return null; + } + + try { + $metaData = json_decode($this->data['metaData'], true, flags: JSON_THROW_ON_ERROR); + } catch (\JsonException $e) { + return null; + } + return is_array($metaData) ? $metaData : null; + } + + /** + * @inheritDoc + */ + public function setMetaData(?array $metaData): IComment { + if ($metaData === null) { + $this->data['metaData'] = null; + } else { + $this->data['metaData'] = json_encode($metaData, JSON_THROW_ON_ERROR); + } + return $this; + } + + /** + * @inheritDoc + */ public function getReactions(): array { return $this->data['reactions'] ?? []; } diff --git a/lib/private/Comments/Manager.php b/lib/private/Comments/Manager.php index af4fda277d6..e3d40f2d965 100644 --- a/lib/private/Comments/Manager.php +++ b/lib/private/Comments/Manager.php @@ -1,35 +1,14 @@ <?php + /** - * @copyright Copyright (c) 2016, ownCloud, Inc. - * - * @author Arthur Schiwon <blizzz@arthur-schiwon.de> - * @author Joas Schilling <coding@schilljs.com> - * @author John Molakvoæ <skjnldsv@protonmail.com> - * @author Morris Jobke <hey@morrisjobke.de> - * @author Robin Appelman <robin@icewind.nl> - * @author Roeland Jago Douma <roeland@famdouma.nl> - * @author Simounet <contact@simounet.net> - * @author Thomas Müller <thomas.mueller@tmit.eu> - * - * @license AGPL-3.0 - * - * This code is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License, version 3, - * as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License, version 3, - * along with this program. If not, see <http://www.gnu.org/licenses/> - * + * SPDX-FileCopyrightText: 2016-2024 Nextcloud GmbH and Nextcloud contributors + * SPDX-FileCopyrightText: 2016 ownCloud, Inc. + * SPDX-License-Identifier: AGPL-3.0-only */ namespace OC\Comments; use Doctrine\DBAL\Exception\DriverException; -use Doctrine\DBAL\Exception\InvalidFieldNameException; +use OCA\DAV\Connector\Sabre\File; use OCP\AppFramework\Utility\ITimeFactory; use OCP\Comments\CommentsEvent; use OCP\Comments\IComment; @@ -37,22 +16,19 @@ use OCP\Comments\ICommentsEventHandler; use OCP\Comments\ICommentsManager; use OCP\Comments\NotFoundException; use OCP\DB\QueryBuilder\IQueryBuilder; +use OCP\Files\FileInfo; +use OCP\Files\Folder; +use OCP\Files\IRootFolder; use OCP\IConfig; use OCP\IDBConnection; use OCP\IEmojiHelper; -use OCP\IUser; use OCP\IInitialStateService; +use OCP\IUser; use OCP\PreConditionNotMetException; use OCP\Util; use Psr\Log\LoggerInterface; class Manager implements ICommentsManager { - protected IDBConnection $dbConn; - protected LoggerInterface $logger; - protected IConfig $config; - protected ITimeFactory $timeFactory; - protected IEmojiHelper $emojiHelper; - protected IInitialStateService $initialStateService; /** @var IComment[] */ protected array $commentsCache = []; @@ -65,18 +41,15 @@ class Manager implements ICommentsManager { /** @var \Closure[] */ protected array $displayNameResolvers = []; - public function __construct(IDBConnection $dbConn, - LoggerInterface $logger, - IConfig $config, - ITimeFactory $timeFactory, - IEmojiHelper $emojiHelper, - IInitialStateService $initialStateService) { - $this->dbConn = $dbConn; - $this->logger = $logger; - $this->config = $config; - $this->timeFactory = $timeFactory; - $this->emojiHelper = $emojiHelper; - $this->initialStateService = $initialStateService; + public function __construct( + protected IDBConnection $dbConn, + protected LoggerInterface $logger, + protected IConfig $config, + protected ITimeFactory $timeFactory, + protected IEmojiHelper $emojiHelper, + protected IInitialStateService $initialStateService, + protected IRootFolder $rootFolder, + ) { } /** @@ -97,7 +70,8 @@ class Manager implements ICommentsManager { $data['expire_date'] = new \DateTime($data['expire_date']); } $data['children_count'] = (int)$data['children_count']; - $data['reference_id'] = $data['reference_id'] ?? null; + $data['reference_id'] = $data['reference_id']; + $data['meta_data'] = json_decode($data['meta_data'], true); if ($this->supportReactions()) { if ($data['reactions'] !== null) { $list = json_decode($data['reactions'], true); @@ -344,7 +318,7 @@ class Manager implements ICommentsManager { $objectId, $limit = 0, $offset = 0, - \DateTime $notOlderThan = null + ?\DateTime $notOlderThan = null ) { $comments = []; @@ -501,6 +475,22 @@ class Manager implements ICommentsManager { ) ); } + } elseif ($lastKnownCommentId > 0) { + // We didn't find the "$lastKnownComment" but we still use the ID as an offset. + // This is required as a fall-back for expired messages in talk and deleted comments in other apps. + if ($sortDirection === 'desc') { + if ($includeLastKnown) { + $query->andWhere($query->expr()->lte('id', $query->createNamedParameter($lastKnownCommentId))); + } else { + $query->andWhere($query->expr()->lt('id', $query->createNamedParameter($lastKnownCommentId))); + } + } else { + if ($includeLastKnown) { + $query->andWhere($query->expr()->gte('id', $query->createNamedParameter($lastKnownCommentId))); + } else { + $query->andWhere($query->expr()->gt('id', $query->createNamedParameter($lastKnownCommentId))); + } + } } $resultStatement = $query->execute(); @@ -520,8 +510,8 @@ class Manager implements ICommentsManager { * @param int $id the comment to look for */ protected function getLastKnownComment(string $objectType, - string $objectId, - int $id): ?IComment { + string $objectId, + int $id): ?IComment { $query = $this->dbConn->getQueryBuilder(); $query->select('*') ->from('comments') @@ -621,7 +611,7 @@ class Manager implements ICommentsManager { * @return Int * @since 9.0.0 */ - public function getNumberOfCommentsForObject($objectType, $objectId, \DateTime $notOlderThan = null, $verb = '') { + public function getNumberOfCommentsForObject($objectType, $objectId, ?\DateTime $notOlderThan = null, $verb = '') { $qb = $this->dbConn->getQueryBuilder(); $query = $qb->select($qb->func()->count('id')) ->from('comments') @@ -804,54 +794,25 @@ class Manager implements ICommentsManager { /** * Get the number of unread comments for all files in a folder * + * This is unused since 8bd39fccf411195839f2dadee085fad18ec52c23 + * * @param int $folderId * @param IUser $user * @return array [$fileId => $unreadCount] */ public function getNumberOfUnreadCommentsForFolder($folderId, IUser $user) { - $qb = $this->dbConn->getQueryBuilder(); - - $query = $qb->select('f.fileid') - ->addSelect($qb->func()->count('c.id', 'num_ids')) - ->from('filecache', 'f') - ->leftJoin('f', 'comments', 'c', $qb->expr()->andX( - $qb->expr()->eq('f.fileid', $qb->expr()->castColumn('c.object_id', IQueryBuilder::PARAM_INT)), - $qb->expr()->eq('c.object_type', $qb->createNamedParameter('files')) - )) - ->leftJoin('c', 'comments_read_markers', 'm', $qb->expr()->andX( - $qb->expr()->eq('c.object_id', 'm.object_id'), - $qb->expr()->eq('m.object_type', $qb->createNamedParameter('files')) - )) - ->where( - $qb->expr()->andX( - $qb->expr()->eq('f.parent', $qb->createNamedParameter($folderId)), - $qb->expr()->orX( - $qb->expr()->eq('c.object_type', $qb->createNamedParameter('files')), - $qb->expr()->isNull('c.object_type') - ), - $qb->expr()->orX( - $qb->expr()->eq('m.object_type', $qb->createNamedParameter('files')), - $qb->expr()->isNull('m.object_type') - ), - $qb->expr()->orX( - $qb->expr()->eq('m.user_id', $qb->createNamedParameter($user->getUID())), - $qb->expr()->isNull('m.user_id') - ), - $qb->expr()->orX( - $qb->expr()->gt('c.creation_timestamp', 'm.marker_datetime'), - $qb->expr()->isNull('m.marker_datetime') - ) - ) - )->groupBy('f.fileid'); - - $resultStatement = $query->execute(); - - $results = []; - while ($row = $resultStatement->fetch()) { - $results[$row['fileid']] = (int) $row['num_ids']; + $directory = $this->rootFolder->getFirstNodeById($folderId); + if (!$directory instanceof Folder) { + return []; } - $resultStatement->closeCursor(); - return $results; + $children = $directory->getDirectoryListing(); + $ids = array_map(fn (FileInfo $child) => (string) $child->getId(), $children); + + $ids[] = (string) $directory->getId(); + $counts = $this->getNumberOfUnreadCommentsForObjects('files', $ids, $user); + return array_filter($counts, function (int $count) { + return $count > 0; + }); } /** @@ -1134,22 +1095,6 @@ class Manager implements ICommentsManager { * @return bool */ protected function insert(IComment $comment): bool { - try { - $result = $this->insertQuery($comment, true); - } catch (InvalidFieldNameException $e) { - // The reference id field was only added in Nextcloud 19. - // In order to not cause too long waiting times on the update, - // it was decided to only add it lazy, as it is also not a critical - // feature, but only helps to have a better experience while commenting. - // So in case the reference_id field is missing, - // we simply save the comment without that field. - $result = $this->insertQuery($comment, false); - } - - return $result; - } - - protected function insertQuery(IComment $comment, bool $tryWritingReferenceId): bool { $qb = $this->dbConn->getQueryBuilder(); $values = [ @@ -1165,12 +1110,10 @@ class Manager implements ICommentsManager { 'object_type' => $qb->createNamedParameter($comment->getObjectType()), 'object_id' => $qb->createNamedParameter($comment->getObjectId()), 'expire_date' => $qb->createNamedParameter($comment->getExpireDate(), 'datetime'), + 'reference_id' => $qb->createNamedParameter($comment->getReferenceId()), + 'meta_data' => $qb->createNamedParameter(json_encode($comment->getMetaData())), ]; - if ($tryWritingReferenceId) { - $values['reference_id'] = $qb->createNamedParameter($comment->getReferenceId()); - } - $affectedRows = $qb->insert('comments') ->values($values) ->execute(); @@ -1273,12 +1216,7 @@ class Manager implements ICommentsManager { $this->sendEvent(CommentsEvent::EVENT_PRE_UPDATE, $this->get($comment->getId())); $this->uncache($comment->getId()); - try { - $result = $this->updateQuery($comment, true); - } catch (InvalidFieldNameException $e) { - // See function insert() for explanation - $result = $this->updateQuery($comment, false); - } + $result = $this->updateQuery($comment); if ($comment->getVerb() === 'reaction_deleted') { $this->deleteReaction($comment); @@ -1289,7 +1227,7 @@ class Manager implements ICommentsManager { return $result; } - protected function updateQuery(IComment $comment, bool $tryWritingReferenceId): bool { + protected function updateQuery(IComment $comment): bool { $qb = $this->dbConn->getQueryBuilder(); $qb ->update('comments') @@ -1304,14 +1242,11 @@ class Manager implements ICommentsManager { ->set('latest_child_timestamp', $qb->createNamedParameter($comment->getLatestChildDateTime(), 'datetime')) ->set('object_type', $qb->createNamedParameter($comment->getObjectType())) ->set('object_id', $qb->createNamedParameter($comment->getObjectId())) - ->set('expire_date', $qb->createNamedParameter($comment->getExpireDate(), 'datetime')); - - if ($tryWritingReferenceId) { - $qb->set('reference_id', $qb->createNamedParameter($comment->getReferenceId())); - } - - $affectedRows = $qb->where($qb->expr()->eq('id', $qb->createNamedParameter($comment->getId()))) - ->execute(); + ->set('expire_date', $qb->createNamedParameter($comment->getExpireDate(), 'datetime')) + ->set('reference_id', $qb->createNamedParameter($comment->getReferenceId())) + ->set('meta_data', $qb->createNamedParameter(json_encode($comment->getMetaData()))) + ->where($qb->expr()->eq('id', $qb->createNamedParameter($comment->getId()))); + $affectedRows = $qb->executeStatement(); if ($affectedRows === 0) { throw new NotFoundException('Comment to update does ceased to exist'); diff --git a/lib/private/Comments/ManagerFactory.php b/lib/private/Comments/ManagerFactory.php index 2b59a284b61..2436ca74c66 100644 --- a/lib/private/Comments/ManagerFactory.php +++ b/lib/private/Comments/ManagerFactory.php @@ -1,27 +1,9 @@ <?php + /** - * @copyright Copyright (c) 2016, ownCloud, Inc. - * - * @author Arthur Schiwon <blizzz@arthur-schiwon.de> - * @author Joas Schilling <coding@schilljs.com> - * @author Roeland Jago Douma <roeland@famdouma.nl> - * @author Thomas Müller <thomas.mueller@tmit.eu> - * @author Vincent Petry <vincent@nextcloud.com> - * - * @license AGPL-3.0 - * - * This code is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License, version 3, - * as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License, version 3, - * along with this program. If not, see <http://www.gnu.org/licenses/> - * + * SPDX-FileCopyrightText: 2016-2024 Nextcloud GmbH and Nextcloud contributors + * SPDX-FileCopyrightText: 2016 ownCloud, Inc. + * SPDX-License-Identifier: AGPL-3.0-only */ namespace OC\Comments; diff --git a/lib/private/Config.php b/lib/private/Config.php index 3ea822101df..ee30b8efc5e 100644 --- a/lib/private/Config.php +++ b/lib/private/Config.php @@ -1,40 +1,9 @@ <?php + /** - * @copyright Copyright (c) 2016, ownCloud, Inc. - * - * @author Adam Williamson <awilliam@redhat.com> - * @author Aldo "xoen" Giambelluca <xoen@xoen.org> - * @author Bart Visscher <bartv@thisnet.nl> - * @author Brice Maron <brice@bmaron.net> - * @author Christoph Wurst <christoph@winzerhof-wurst.at> - * @author Daniel Kesselberg <mail@danielkesselberg.de> - * @author Frank Karlitschek <frank@karlitschek.de> - * @author Jakob Sack <mail@jakobsack.de> - * @author Jan-Christoph Borchardt <hey@jancborchardt.net> - * @author Joas Schilling <coding@schilljs.com> - * @author John Molakvoæ <skjnldsv@protonmail.com> - * @author Lukas Reschke <lukas@statuscode.ch> - * @author Michael Gapczynski <GapczynskiM@gmail.com> - * @author Morris Jobke <hey@morrisjobke.de> - * @author Philipp Schaffrath <github@philipp.schaffrath.email> - * @author Robin Appelman <robin@icewind.nl> - * @author Robin McCorkell <robin@mccorkell.me.uk> - * @author Roeland Jago Douma <roeland@famdouma.nl> - * - * @license AGPL-3.0 - * - * This code is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License, version 3, - * as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License, version 3, - * along with this program. If not, see <http://www.gnu.org/licenses/> - * + * SPDX-FileCopyrightText: 2016-2024 Nextcloud GmbH and Nextcloud contributors + * SPDX-FileCopyrightText: 2016 ownCloud, Inc. + * SPDX-License-Identifier: AGPL-3.0-only */ namespace OC; @@ -153,8 +122,6 @@ class Config { * @throws HintException */ protected function set($key, $value) { - $this->checkReadOnly(); - if (!isset($this->cache[$key]) || $this->cache[$key] !== $value) { // Add change $this->cache[$key] = $value; @@ -185,8 +152,6 @@ class Config { * @throws HintException */ protected function delete($key) { - $this->checkReadOnly(); - if (isset($this->cache[$key])) { // Delete key from cache unset($this->cache[$key]); @@ -215,13 +180,24 @@ class Config { // Include file and merge config foreach ($configFiles as $file) { - $fileExistsAndIsReadable = file_exists($file) && is_readable($file); - $filePointer = $fileExistsAndIsReadable ? fopen($file, 'r') : false; - if ($file === $this->configFilePath && - $filePointer === false) { - // Opening the main config might not be possible, e.g. if the wrong - // permissions are set (likely on a new installation) - continue; + unset($CONFIG); + + // Invalidate opcache (only if the timestamp changed) + if (function_exists('opcache_invalidate')) { + opcache_invalidate($file, false); + } + + $filePointer = @fopen($file, 'r'); + if ($filePointer === false) { + // e.g. wrong permissions are set + if ($file === $this->configFilePath) { + // opening the main config file might not be possible + // (likely on a new installation) + continue; + } + + http_response_code(500); + die(sprintf('FATAL: Could not open the config file %s', $file)); } // Try to acquire a file lock @@ -229,8 +205,14 @@ class Config { throw new \Exception(sprintf('Could not acquire a shared lock on the config file %s', $file)); } - unset($CONFIG); - include $file; + try { + include $file; + } finally { + // Close the file pointer and release the lock + flock($filePointer, LOCK_UN); + fclose($filePointer); + } + if (!defined('PHPUNIT_RUN') && headers_sent()) { // syntax issues in the config file like leading spaces causing PHP to send output $errorMessage = sprintf('Config file has leading content, please remove everything before "<?php" in %s', basename($file)); @@ -242,10 +224,6 @@ class Config { if (isset($CONFIG) && is_array($CONFIG)) { $this->cache = array_merge($this->cache, $CONFIG); } - - // Close the file pointer and release the lock - flock($filePointer, LOCK_UN); - fclose($filePointer); } $this->envCache = getenv(); diff --git a/lib/private/Console/Application.php b/lib/private/Console/Application.php index 113f0507ef5..14baa528940 100644 --- a/lib/private/Console/Application.php +++ b/lib/private/Console/Application.php @@ -1,38 +1,15 @@ <?php + /** - * @copyright Copyright (c) 2016, ownCloud, Inc. - * - * @author Christoph Wurst <christoph@winzerhof-wurst.at> - * @author Joas Schilling <coding@schilljs.com> - * @author Jörn Friedrich Dreyer <jfd@butonic.de> - * @author Lukas Reschke <lukas@statuscode.ch> - * @author Michael Weimann <mail@michael-weimann.eu> - * @author Morris Jobke <hey@morrisjobke.de> - * @author Robin Appelman <robin@icewind.nl> - * @author Roeland Jago Douma <roeland@famdouma.nl> - * @author Thomas Müller <thomas.mueller@tmit.eu> - * @author Victor Dubiniuk <dubiniuk@owncloud.com> - * - * @license AGPL-3.0 - * - * This code is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License, version 3, - * as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License, version 3, - * along with this program. If not, see <http://www.gnu.org/licenses/> - * + * SPDX-FileCopyrightText: 2016-2024 Nextcloud GmbH and Nextcloud contributors + * SPDX-FileCopyrightText: 2016 ownCloud, Inc. + * SPDX-License-Identifier: AGPL-3.0-only */ namespace OC\Console; use OC\MemoryInfo; use OC\NeedsUpdateException; -use OC_App; +use OCP\App\AppPathNotFoundException; use OCP\App\IAppManager; use OCP\Console\ConsoleEvent; use OCP\EventDispatcher\IEventDispatcher; @@ -47,35 +24,21 @@ use Symfony\Component\Console\Output\ConsoleOutputInterface; use Symfony\Component\Console\Output\OutputInterface; class Application { - /** @var IConfig */ - private $config; private SymfonyApplication $application; - /** @var IEventDispatcher */ - private $dispatcher; - /** @var IRequest */ - private $request; - /** @var LoggerInterface */ - private $logger; - /** @var MemoryInfo */ - private $memoryInfo; - public function __construct(IConfig $config, - IEventDispatcher $dispatcher, - IRequest $request, - LoggerInterface $logger, - MemoryInfo $memoryInfo) { - $defaults = \OC::$server->getThemingDefaults(); - $this->config = $config; + public function __construct( + private IConfig $config, + private IEventDispatcher $dispatcher, + private IRequest $request, + private LoggerInterface $logger, + private MemoryInfo $memoryInfo, + private IAppManager $appManager, + ) { + $defaults = \OC::$server->get('ThemingDefaults'); $this->application = new SymfonyApplication($defaults->getName(), \OC_Util::getVersionString()); - $this->dispatcher = $dispatcher; - $this->request = $request; - $this->logger = $logger; - $this->memoryInfo = $memoryInfo; } /** - * @param InputInterface $input - * @param ConsoleOutputInterface $output * @throws \Exception */ public function loadCommands( @@ -118,17 +81,24 @@ class Application { } elseif ($this->config->getSystemValueBool('maintenance')) { $this->writeMaintenanceModeInfo($input, $output); } else { - OC_App::loadApps(); - $appManager = \OCP\Server::get(IAppManager::class); - foreach ($appManager->getInstalledApps() as $app) { - $appPath = \OC_App::getAppPath($app); - if ($appPath === false) { + $this->appManager->loadApps(); + foreach ($this->appManager->getInstalledApps() as $app) { + try { + $appPath = $this->appManager->getAppPath($app); + } catch (AppPathNotFoundException) { continue; } // load commands using info.xml - $info = $appManager->getAppInfo($app); + $info = $this->appManager->getAppInfo($app); if (isset($info['commands'])) { - $this->loadCommandsFromInfoXml($info['commands']); + try { + $this->loadCommandsFromInfoXml($info['commands']); + } catch (\Throwable $e) { + $output->writeln("<error>" . $e->getMessage() . "</error>"); + $this->logger->error($e->getMessage(), [ + 'exception' => $e, + ]); + } } // load from register_command.php \OC_App::registerAutoloading($app, $appPath); @@ -203,7 +173,7 @@ class Application { * @return int * @throws \Exception */ - public function run(InputInterface $input = null, OutputInterface $output = null) { + public function run(?InputInterface $input = null, ?OutputInterface $output = null) { $event = new ConsoleEvent( ConsoleEvent::EVENT_RUN, $this->request->server['argv'] diff --git a/lib/private/Console/TimestampFormatter.php b/lib/private/Console/TimestampFormatter.php index 8d74c28e94f..de0675cc5df 100644 --- a/lib/private/Console/TimestampFormatter.php +++ b/lib/private/Console/TimestampFormatter.php @@ -1,24 +1,9 @@ <?php + /** - * @copyright Copyright (c) 2016, ownCloud, Inc. - * - * @author Joas Schilling <coding@schilljs.com> - * @author Roeland Jago Douma <roeland@famdouma.nl> - * - * @license AGPL-3.0 - * - * This code is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License, version 3, - * as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License, version 3, - * along with this program. If not, see <http://www.gnu.org/licenses/> - * + * SPDX-FileCopyrightText: 2016-2024 Nextcloud GmbH and Nextcloud contributors + * SPDX-FileCopyrightText: 2016 ownCloud, Inc. + * SPDX-License-Identifier: AGPL-3.0-only */ namespace OC\Console; @@ -27,17 +12,17 @@ use Symfony\Component\Console\Formatter\OutputFormatterInterface; use Symfony\Component\Console\Formatter\OutputFormatterStyleInterface; class TimestampFormatter implements OutputFormatterInterface { - /** @var IConfig */ + /** @var ?IConfig */ protected $config; /** @var OutputFormatterInterface */ protected $formatter; /** - * @param IConfig $config + * @param ?IConfig $config * @param OutputFormatterInterface $formatter */ - public function __construct(IConfig $config, OutputFormatterInterface $formatter) { + public function __construct(?IConfig $config, OutputFormatterInterface $formatter) { $this->config = $config; $this->formatter = $formatter; } @@ -104,11 +89,16 @@ class TimestampFormatter implements OutputFormatterInterface { return $this->formatter->format($message); } - $timeZone = $this->config->getSystemValue('logtimezone', 'UTC'); - $timeZone = $timeZone !== null ? new \DateTimeZone($timeZone) : null; + if ($this->config instanceof IConfig) { + $timeZone = $this->config->getSystemValue('logtimezone', 'UTC'); + $timeZone = $timeZone !== null ? new \DateTimeZone($timeZone) : null; - $time = new \DateTime('now', $timeZone); - $timestampInfo = $time->format($this->config->getSystemValue('logdateformat', \DateTimeInterface::ATOM)); + $time = new \DateTime('now', $timeZone); + $timestampInfo = $time->format($this->config->getSystemValue('logdateformat', \DateTimeInterface::ATOM)); + } else { + $time = new \DateTime('now'); + $timestampInfo = $time->format(\DateTimeInterface::ATOM); + } return $timestampInfo . ' ' . $this->formatter->format($message); } diff --git a/lib/private/Contacts/ContactsMenu/ActionFactory.php b/lib/private/Contacts/ContactsMenu/ActionFactory.php index 739c43d0bce..71ebe575fdd 100644 --- a/lib/private/Contacts/ContactsMenu/ActionFactory.php +++ b/lib/private/Contacts/ContactsMenu/ActionFactory.php @@ -1,24 +1,7 @@ <?php /** - * @copyright 2017 Christoph Wurst <christoph@winzerhof-wurst.at> - * - * @author Christoph Wurst <christoph@winzerhof-wurst.at> - * - * @license GNU AGPL version 3 or any later version - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as - * published by the Free Software Foundation, either version 3 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - * + * SPDX-FileCopyrightText: 2017 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later */ namespace OC\Contacts\ContactsMenu; diff --git a/lib/private/Contacts/ContactsMenu/ActionProviderStore.php b/lib/private/Contacts/ContactsMenu/ActionProviderStore.php index 7ba5db4bb33..b760de03a04 100644 --- a/lib/private/Contacts/ContactsMenu/ActionProviderStore.php +++ b/lib/private/Contacts/ContactsMenu/ActionProviderStore.php @@ -3,26 +3,8 @@ declare(strict_types=1); /** - * @copyright 2017 Christoph Wurst <christoph@winzerhof-wurst.at> - * - * @author Christoph Wurst <christoph@winzerhof-wurst.at> - * @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/>. - * + * SPDX-FileCopyrightText: 2017 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later */ namespace OC\Contacts\ContactsMenu; @@ -33,6 +15,7 @@ use OC\Contacts\ContactsMenu\Providers\EMailProvider; use OC\Contacts\ContactsMenu\Providers\LocalTimeProvider; use OC\Contacts\ContactsMenu\Providers\ProfileProvider; use OCP\AppFramework\QueryException; +use OCP\Contacts\ContactsMenu\IBulkProvider; use OCP\Contacts\ContactsMenu\IProvider; use OCP\IServerContainer; use OCP\IUser; @@ -47,18 +30,26 @@ class ActionProviderStore { } /** - * @return IProvider[] + * @return list<IProvider|IBulkProvider> * @throws Exception */ public function getProviders(IUser $user): array { $appClasses = $this->getAppProviderClasses($user); $providerClasses = $this->getServerProviderClasses(); $allClasses = array_merge($providerClasses, $appClasses); + /** @var list<IProvider|IBulkProvider> $providers */ $providers = []; foreach ($allClasses as $class) { try { - $providers[] = $this->serverContainer->get($class); + $provider = $this->serverContainer->get($class); + if ($provider instanceof IProvider || $provider instanceof IBulkProvider) { + $providers[] = $provider; + } else { + $this->logger->warning('Ignoring invalid contacts menu provider', [ + 'class' => $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 e0d3515f421..0d4cc9b9b01 100644 --- a/lib/private/Contacts/ContactsMenu/Actions/LinkAction.php +++ b/lib/private/Contacts/ContactsMenu/Actions/LinkAction.php @@ -1,24 +1,7 @@ <?php /** - * @copyright 2017 Christoph Wurst <christoph@winzerhof-wurst.at> - * - * @author Christoph Wurst <christoph@winzerhof-wurst.at> - * - * @license GNU AGPL version 3 or any later version - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as - * published by the Free Software Foundation, either version 3 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - * + * SPDX-FileCopyrightText: 2017 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later */ namespace OC\Contacts\ContactsMenu\Actions; diff --git a/lib/private/Contacts/ContactsMenu/ContactsStore.php b/lib/private/Contacts/ContactsMenu/ContactsStore.php index c692b486ae4..d7cdb5efebf 100644 --- a/lib/private/Contacts/ContactsMenu/ContactsStore.php +++ b/lib/private/Contacts/ContactsMenu/ContactsStore.php @@ -1,38 +1,16 @@ <?php /** - * @copyright 2017 Christoph Wurst <christoph@winzerhof-wurst.at> - * @copyright 2017 Lukas Reschke <lukas@statuscode.ch> - * - * @author Arthur Schiwon <blizzz@arthur-schiwon.de> - * @author Christoph Wurst <christoph@winzerhof-wurst.at> - * @author Georg Ehrke <oc.list@georgehrke.com> - * @author Joas Schilling <coding@schilljs.com> - * @author Lukas Reschke <lukas@statuscode.ch> - * @author Roeland Jago Douma <roeland@famdouma.nl> - * @author Tobia De Koninck <tobia@ledfan.be> - * - * @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/>. - * + * SPDX-FileCopyrightText: 2017 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later */ namespace OC\Contacts\ContactsMenu; use OC\KnownUser\KnownUserService; use OC\Profile\ProfileManager; +use OCA\UserStatus\Db\UserStatus; +use OCA\UserStatus\Service\StatusService; use OCP\Contacts\ContactsMenu\IContactsStore; use OCP\Contacts\ContactsMenu\IEntry; use OCP\Contacts\IManager; @@ -42,10 +20,17 @@ use OCP\IURLGenerator; use OCP\IUser; use OCP\IUserManager; use OCP\L10N\IFactory as IL10NFactory; +use function array_column; +use function array_fill_keys; +use function array_filter; +use function array_key_exists; +use function array_merge; +use function count; class ContactsStore implements IContactsStore { public function __construct( private IManager $contactsManager, + private ?StatusService $userStatusService, private IConfig $config, private ProfileManager $profileManager, private IUserManager $userManager, @@ -70,15 +55,75 @@ class ContactsStore implements IContactsStore { if ($offset !== null) { $options['offset'] = $offset; } + // Status integration only works without pagination and filters + if ($offset === null && ($filter === null || $filter === '')) { + $recentStatuses = $this->userStatusService?->findAllRecentStatusChanges($limit, $offset) ?? []; + } else { + $recentStatuses = []; + } - $allContacts = $this->contactsManager->search( - $filter ?? '', - [ - 'FN', - 'EMAIL' - ], - $options - ); + // Search by status if there is no filter and statuses are available + if (!empty($recentStatuses)) { + $allContacts = array_filter(array_map(function (UserStatus $userStatus) use ($options) { + // UID is ambiguous with federation. We have to use the federated cloud ID to an exact match of + // A local user + $user = $this->userManager->get($userStatus->getUserId()); + if ($user === null) { + return null; + } + + $contact = $this->contactsManager->search( + $user->getCloudId(), + [ + 'CLOUD', + ], + array_merge( + $options, + [ + 'limit' => 1, + 'offset' => 0, + ], + ), + )[0] ?? null; + if ($contact !== null) { + $contact[Entry::PROPERTY_STATUS_MESSAGE_TIMESTAMP] = $userStatus->getStatusMessageTimestamp(); + } + return $contact; + }, $recentStatuses)); + if ($limit !== null && count($allContacts) < $limit) { + // More contacts were requested + $fromContacts = $this->contactsManager->search( + $filter ?? '', + [ + 'FN', + 'EMAIL' + ], + array_merge( + $options, + [ + 'limit' => $limit - count($allContacts), + ], + ), + ); + + // Create hash map of all status contacts + $existing = array_fill_keys(array_column($allContacts, 'URI'), null); + // Append the ones that are new + $allContacts = array_merge( + $allContacts, + array_filter($fromContacts, fn (array $contact): bool => !array_key_exists($contact['URI'], $existing)) + ); + } + } else { + $allContacts = $this->contactsManager->search( + $filter ?? '', + [ + 'FN', + 'EMAIL' + ], + $options + ); + } $userId = $user->getUID(); $contacts = array_filter($allContacts, function ($contact) use ($userId) { @@ -108,6 +153,9 @@ class ContactsStore implements IContactsStore { * 3. if the `shareapi_only_share_with_group_members` config option is * enabled it will filter all users which doesn't have a common group * with the current user. + * If enabled, the 'shareapi_only_share_with_group_members_exclude_group_list' + * config option may specify some groups excluded from the principle of + * belonging to the same group. * * @param Entry[] $entries * @return Entry[] the filtered contacts @@ -121,7 +169,7 @@ class ContactsStore implements IContactsStore { $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'; $allowEnumerationFullMatch = $this->config->getAppValue('core', 'shareapi_restrict_user_enumeration_full_match', 'yes') === 'yes'; - $excludedGroups = $this->config->getAppValue('core', 'shareapi_exclude_groups', 'no') === 'yes'; + $excludeGroups = $this->config->getAppValue('core', 'shareapi_exclude_groups', 'no'); // whether to filter out local users $skipLocal = false; @@ -130,17 +178,32 @@ class ContactsStore implements IContactsStore { $selfGroups = $this->groupManager->getUserGroupIds($self); - if ($excludedGroups) { + if ($excludeGroups && $excludeGroups !== 'no') { $excludedGroups = $this->config->getAppValue('core', 'shareapi_exclude_groups_list', ''); $decodedExcludeGroups = json_decode($excludedGroups, true); $excludeGroupsList = $decodedExcludeGroups ?? []; - if (count(array_intersect($excludeGroupsList, $selfGroups)) !== 0) { - // a group of the current user is excluded -> filter all local users + if ($excludeGroups != 'allow') { + if (count(array_intersect($excludeGroupsList, $selfGroups)) !== 0) { + // a group of the current user is excluded -> filter all local users + $skipLocal = true; + } + } else { $skipLocal = true; + if (count(array_intersect($excludeGroupsList, $selfGroups)) !== 0) { + // a group of the current user is allowed -> do not filter all local users + $skipLocal = false; + } } } + // ownGroupsOnly : some groups may be excluded + if ($ownGroupsOnly) { + $excludeGroupsFromOwnGroups = $this->config->getAppValue('core', 'shareapi_only_share_with_group_members_exclude_group_list', ''); + $excludeGroupsFromOwnGroupsList = json_decode($excludeGroupsFromOwnGroups, true) ?? []; + $selfGroups = array_diff($selfGroups, $excludeGroupsFromOwnGroupsList); + } + $selfUID = $self->getUID(); return array_values(array_filter($entries, function (IEntry $entry) use ($skipLocal, $ownGroupsOnly, $selfGroups, $selfUID, $disallowEnumeration, $restrictEnumerationGroup, $restrictEnumerationPhone, $allowEnumerationFullMatch, $filter) { @@ -265,36 +328,39 @@ class ContactsStore implements IContactsStore { private function contactArrayToEntry(array $contact): Entry { $entry = new Entry(); - if (isset($contact['UID'])) { + if (!empty($contact['UID'])) { $uid = $contact['UID']; $entry->setId($uid); + $entry->setProperty('isUser', false); + // overloaded usage so leaving as-is for now if (isset($contact['isLocalSystemBook'])) { $avatar = $this->urlGenerator->linkToRouteAbsolute('core.avatar.getAvatar', ['userId' => $uid, 'size' => 64]); - } elseif (isset($contact['FN'])) { - $avatar = $this->urlGenerator->linkToRouteAbsolute('core.GuestAvatar.getAvatar', ['guestName' => $contact['FN'], 'size' => 64]); + $entry->setProperty('isUser', true); + } elseif (!empty($contact['FN'])) { + $avatar = $this->urlGenerator->linkToRouteAbsolute('core.GuestAvatar.getAvatar', ['guestName' => str_replace('/', ' ', $contact['FN']), 'size' => 64]); } else { - $avatar = $this->urlGenerator->linkToRouteAbsolute('core.GuestAvatar.getAvatar', ['guestName' => $uid, 'size' => 64]); + $avatar = $this->urlGenerator->linkToRouteAbsolute('core.GuestAvatar.getAvatar', ['guestName' => str_replace('/', ' ', $uid), 'size' => 64]); } $entry->setAvatar($avatar); } - if (isset($contact['FN'])) { + if (!empty($contact['FN'])) { $entry->setFullName($contact['FN']); } $avatarPrefix = "VALUE=uri:"; - if (isset($contact['PHOTO']) && str_starts_with($contact['PHOTO'], $avatarPrefix)) { + if (!empty($contact['PHOTO']) && str_starts_with($contact['PHOTO'], $avatarPrefix)) { $entry->setAvatar(substr($contact['PHOTO'], strlen($avatarPrefix))); } - if (isset($contact['EMAIL'])) { + if (!empty($contact['EMAIL'])) { foreach ($contact['EMAIL'] as $email) { $entry->addEMailAddress($email); } } // Provide profile parameters for core/src/OC/contactsmenu/contact.handlebars template - if (isset($contact['UID']) && isset($contact['FN'])) { + if (!empty($contact['UID']) && !empty($contact['FN'])) { $targetUserId = $contact['UID']; $targetUser = $this->userManager->get($targetUserId); if (!empty($targetUser)) { diff --git a/lib/private/Contacts/ContactsMenu/Entry.php b/lib/private/Contacts/ContactsMenu/Entry.php index f1cb4f9c52f..d4f2dc7bf90 100644 --- a/lib/private/Contacts/ContactsMenu/Entry.php +++ b/lib/private/Contacts/ContactsMenu/Entry.php @@ -3,34 +3,19 @@ declare(strict_types=1); /** - * @copyright 2017 Christoph Wurst <christoph@winzerhof-wurst.at> - * - * @author Christoph Wurst <christoph@winzerhof-wurst.at> - * @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/>. - * + * SPDX-FileCopyrightText: 2017 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later */ namespace OC\Contacts\ContactsMenu; use OCP\Contacts\ContactsMenu\IAction; use OCP\Contacts\ContactsMenu\IEntry; +use function array_merge; class Entry implements IEntry { + public const PROPERTY_STATUS_MESSAGE_TIMESTAMP = 'statusMessageTimestamp'; + /** @var string|int|null */ private $id = null; @@ -50,6 +35,11 @@ class Entry implements IEntry { private array $properties = []; + private ?string $status = null; + private ?string $statusMessage = null; + private ?int $statusMessageTimestamp = null; + private ?string $statusIcon = null; + public function setId(string $id): void { $this->id = $id; } @@ -102,6 +92,16 @@ class Entry implements IEntry { $this->sortActions(); } + public function setStatus(string $status, + ?string $statusMessage = null, + ?int $statusMessageTimestamp = null, + ?string $icon = null): void { + $this->status = $status; + $this->statusMessage = $statusMessage; + $this->statusMessageTimestamp = $statusMessageTimestamp; + $this->statusIcon = $icon; + } + /** * @return IAction[] */ @@ -127,11 +127,15 @@ class Entry implements IEntry { }); } + public function setProperty(string $propertyName, mixed $value) { + $this->properties[$propertyName] = $value; + } + /** - * @param array $contact key-value array containing additional properties + * @param array $properties key-value array containing additional properties */ - public function setProperties(array $contact): void { - $this->properties = $contact; + public function setProperties(array $properties): void { + $this->properties = array_merge($this->properties, $properties); } public function getProperty(string $key): mixed { @@ -142,7 +146,7 @@ class Entry implements IEntry { } /** - * @return array{id: int|string|null, fullName: string, avatar: string|null, topAction: mixed, actions: array, lastMessage: '', emailAddresses: string[], profileTitle: string|null, profileUrl: string|null} + * @return array{id: int|string|null, fullName: string, avatar: string|null, topAction: mixed, actions: array, lastMessage: '', emailAddresses: string[], profileTitle: string|null, profileUrl: string|null, status: string|null, statusMessage: null|string, statusMessageTimestamp: null|int, statusIcon: null|string, isUser: bool, uid: mixed} */ public function jsonSerialize(): array { $topAction = !empty($this->actions) ? $this->actions[0]->jsonSerialize() : null; @@ -160,6 +164,20 @@ class Entry implements IEntry { 'emailAddresses' => $this->getEMailAddresses(), 'profileTitle' => $this->profileTitle, 'profileUrl' => $this->profileUrl, + 'status' => $this->status, + 'statusMessage' => $this->statusMessage, + 'statusMessageTimestamp' => $this->statusMessageTimestamp, + 'statusIcon' => $this->statusIcon, + 'isUser' => $this->getProperty('isUser') === true, + 'uid' => $this->getProperty('UID'), ]; } + + public function getStatusMessage(): ?string { + return $this->statusMessage; + } + + public function getStatusMessageTimestamp(): ?int { + return $this->statusMessageTimestamp; + } } diff --git a/lib/private/Contacts/ContactsMenu/Manager.php b/lib/private/Contacts/ContactsMenu/Manager.php index 490cf602283..65a2c4469ea 100644 --- a/lib/private/Contacts/ContactsMenu/Manager.php +++ b/lib/private/Contacts/ContactsMenu/Manager.php @@ -1,34 +1,16 @@ <?php /** - * @copyright 2017 Christoph Wurst <christoph@winzerhof-wurst.at> - * - * @author Arthur Schiwon <blizzz@arthur-schiwon.de> - * @author Christoph Wurst <christoph@winzerhof-wurst.at> - * @author Georg Ehrke <oc.list@georgehrke.com> - * @author Julius Härtl <jus@bitgrid.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/>. - * + * SPDX-FileCopyrightText: 2017 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later */ namespace OC\Contacts\ContactsMenu; use Exception; use OCP\App\IAppManager; use OCP\Constants; +use OCP\Contacts\ContactsMenu\IBulkProvider; use OCP\Contacts\ContactsMenu\IEntry; +use OCP\Contacts\ContactsMenu\IProvider; use OCP\IConfig; use OCP\IUser; @@ -80,8 +62,19 @@ class Manager { * @return IEntry[] */ private function sortEntries(array $entries): array { - usort($entries, function (IEntry $entryA, IEntry $entryB) { - return strcasecmp($entryA->getFullName(), $entryB->getFullName()); + usort($entries, function (Entry $entryA, Entry $entryB) { + $aStatusTimestamp = $entryA->getProperty(Entry::PROPERTY_STATUS_MESSAGE_TIMESTAMP); + $bStatusTimestamp = $entryB->getProperty(Entry::PROPERTY_STATUS_MESSAGE_TIMESTAMP); + if (!$aStatusTimestamp && !$bStatusTimestamp) { + return strcasecmp($entryA->getFullName(), $entryB->getFullName()); + } + if ($aStatusTimestamp === null) { + return 1; + } + if ($bStatusTimestamp === null) { + return -1; + } + return $bStatusTimestamp - $aStatusTimestamp; }); return $entries; } @@ -92,9 +85,14 @@ class Manager { */ private function processEntries(array $entries, IUser $user): void { $providers = $this->actionProviderStore->getProviders($user); - foreach ($entries as $entry) { - foreach ($providers as $provider) { - $provider->process($entry); + + foreach ($providers as $provider) { + if ($provider instanceof IBulkProvider && !($provider instanceof IProvider)) { + $provider->process($entries); + } elseif ($provider instanceof IProvider && !($provider instanceof IBulkProvider)) { + foreach ($entries as $entry) { + $provider->process($entry); + } } } } diff --git a/lib/private/Contacts/ContactsMenu/Providers/EMailProvider.php b/lib/private/Contacts/ContactsMenu/Providers/EMailProvider.php index 145c30a2ce7..c852fc90b9e 100644 --- a/lib/private/Contacts/ContactsMenu/Providers/EMailProvider.php +++ b/lib/private/Contacts/ContactsMenu/Providers/EMailProvider.php @@ -1,24 +1,7 @@ <?php /** - * @copyright 2017 Christoph Wurst <christoph@winzerhof-wurst.at> - * - * @author Christoph Wurst <christoph@winzerhof-wurst.at> - * - * @license GNU AGPL version 3 or any later version - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as - * published by the Free Software Foundation, either version 3 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - * + * SPDX-FileCopyrightText: 2017 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later */ namespace OC\Contacts\ContactsMenu\Providers; diff --git a/lib/private/Contacts/ContactsMenu/Providers/LocalTimeProvider.php b/lib/private/Contacts/ContactsMenu/Providers/LocalTimeProvider.php index 32e1280ff0f..a5d911a03b1 100644 --- a/lib/private/Contacts/ContactsMenu/Providers/LocalTimeProvider.php +++ b/lib/private/Contacts/ContactsMenu/Providers/LocalTimeProvider.php @@ -3,25 +3,8 @@ declare(strict_types=1); /** - * @copyright Copyright (c) 2023, Joas Schilling <coding@schilljs.com> - * - * @author Joas Schilling <coding@schilljs.com> - * - * @license GNU AGPL version 3 or any later version - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as - * published by the Free Software Foundation, either version 3 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - * + * SPDX-FileCopyrightText: 2023 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later */ namespace OC\Contacts\ContactsMenu\Providers; diff --git a/lib/private/Contacts/ContactsMenu/Providers/ProfileProvider.php b/lib/private/Contacts/ContactsMenu/Providers/ProfileProvider.php index 6b36b9fff0e..d099e2c620d 100644 --- a/lib/private/Contacts/ContactsMenu/Providers/ProfileProvider.php +++ b/lib/private/Contacts/ContactsMenu/Providers/ProfileProvider.php @@ -1,25 +1,8 @@ <?php /** - * @copyright 2017 Christoph Wurst <christoph@winzerhof-wurst.at> - * - * @author Christoph Wurst <christoph@winzerhof-wurst.at> - * - * @license GNU AGPL version 3 or any later version - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as - * published by the Free Software Foundation, either version 3 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - * + * SPDX-FileCopyrightText: 2017 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later */ namespace OC\Contacts\ContactsMenu\Providers; diff --git a/lib/private/ContactsManager.php b/lib/private/ContactsManager.php index c39f7c715cc..f67cb196eef 100644 --- a/lib/private/ContactsManager.php +++ b/lib/private/ContactsManager.php @@ -1,29 +1,9 @@ <?php + /** - * @copyright Copyright (c) 2016, ownCloud, Inc. - * - * @author Bart Visscher <bartv@thisnet.nl> - * @author Joas Schilling <coding@schilljs.com> - * @author John Molakvoæ <skjnldsv@protonmail.com> - * @author Morris Jobke <hey@morrisjobke.de> - * @author Thomas Citharel <nextcloud@tcit.fr> - * @author Thomas Müller <thomas.mueller@tmit.eu> - * @author Tobia De Koninck <tobia@ledfan.be> - * - * @license AGPL-3.0 - * - * This code is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License, version 3, - * as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License, version 3, - * along with this program. If not, see <http://www.gnu.org/licenses/> - * + * SPDX-FileCopyrightText: 2016-2024 Nextcloud GmbH and Nextcloud contributors + * SPDX-FileCopyrightText: 2016 ownCloud, Inc. + * SPDX-License-Identifier: AGPL-3.0-only */ namespace OC; @@ -91,20 +71,20 @@ class ContactsManager implements IManager { * This function can be used to delete the contact identified by the given id * * @param int $id the unique identifier to a contact - * @param string $address_book_key identifier of the address book in which the contact shall be deleted + * @param string $addressBookKey identifier of the address book in which the contact shall be deleted * @return bool successful or not */ - public function delete($id, $address_book_key) { - $addressBook = $this->getAddressBook($address_book_key); + public function delete($id, $addressBookKey) { + $addressBook = $this->getAddressBook($addressBookKey); if (!$addressBook) { - return null; + return false; } if ($addressBook->getPermissions() & Constants::PERMISSION_DELETE) { return $addressBook->delete($id); } - return null; + return false; } /** @@ -112,11 +92,11 @@ class ContactsManager implements IManager { * Otherwise the contact will be updated by replacing the entire data set. * * @param array $properties this array if key-value-pairs defines a contact - * @param string $address_book_key identifier of the address book in which the contact shall be created or updated - * @return array representing the contact just created or updated + * @param string $addressBookKey identifier of the address book in which the contact shall be created or updated + * @return ?array representing the contact just created or updated */ - public function createOrUpdate($properties, $address_book_key) { - $addressBook = $this->getAddressBook($address_book_key); + public function createOrUpdate($properties, $addressBookKey) { + $addressBook = $this->getAddressBook($addressBookKey); if (!$addressBook) { return null; } @@ -133,7 +113,7 @@ class ContactsManager implements IManager { * * @return bool true if enabled, false if not */ - public function isEnabled() { + public function isEnabled(): bool { return !empty($this->addressBooks) || !empty($this->addressBookLoaders); } @@ -192,11 +172,8 @@ class ContactsManager implements IManager { /** * Get (and load when needed) the address book for $key - * - * @param string $addressBookKey - * @return IAddressBook */ - protected function getAddressBook($addressBookKey) { + protected function getAddressBook(string $addressBookKey): ?IAddressBook { $this->loadAddressBooks(); if (!array_key_exists($addressBookKey, $this->addressBooks)) { return null; diff --git a/lib/private/DB/Adapter.php b/lib/private/DB/Adapter.php index acaa529c0e2..b5be14e5dc6 100644 --- a/lib/private/DB/Adapter.php +++ b/lib/private/DB/Adapter.php @@ -1,35 +1,15 @@ <?php + /** - * @copyright Copyright (c) 2016, ownCloud, Inc. - * - * @author Bart Visscher <bartv@thisnet.nl> - * @author Christoph Wurst <christoph@winzerhof-wurst.at> - * @author Joas Schilling <coding@schilljs.com> - * @author Jonny007-MKD <1-23-4-5@web.de> - * @author Morris Jobke <hey@morrisjobke.de> - * @author Ole Ostergaard <ole.c.ostergaard@gmail.com> - * @author Ole Ostergaard <ole.ostergaard@knime.com> - * @author Robin Appelman <robin@icewind.nl> - * - * @license AGPL-3.0 - * - * This code is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License, version 3, - * as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License, version 3, - * along with this program. If not, see <http://www.gnu.org/licenses/> - * + * SPDX-FileCopyrightText: 2016-2024 Nextcloud GmbH and Nextcloud contributors + * SPDX-FileCopyrightText: 2016 ownCloud, Inc. + * SPDX-License-Identifier: AGPL-3.0-only */ namespace OC\DB; use Doctrine\DBAL\Exception; use Doctrine\DBAL\Exception\UniqueConstraintViolationException; +use OC\DB\Exceptions\DbalException; /** * This handles the way we use to write queries, into something that can be @@ -99,7 +79,7 @@ class Adapter { * @throws Exception * @deprecated 15.0.0 - use unique index and "try { $db->insert() } catch (UniqueConstraintViolationException $e) {}" instead, because it is more reliable and does not have the risk for deadlocks - see https://github.com/nextcloud/server/pull/12371 */ - public function insertIfNotExist($table, $input, array $compare = null) { + public function insertIfNotExist($table, $input, ?array $compare = null) { if (empty($compare)) { $compare = array_keys($input); } @@ -142,9 +122,12 @@ class Adapter { foreach ($values as $key => $value) { $builder->setValue($key, $builder->createNamedParameter($value)); } - return $builder->execute(); - } catch (UniqueConstraintViolationException $e) { - return 0; + return $builder->executeStatement(); + } catch (DbalException $e) { + if ($e->getReason() === \OCP\DB\Exception::REASON_UNIQUE_CONSTRAINT_VIOLATION) { + return 0; + } + throw $e; } } } diff --git a/lib/private/DB/AdapterMySQL.php b/lib/private/DB/AdapterMySQL.php index 295783616b6..8e854769e1f 100644 --- a/lib/private/DB/AdapterMySQL.php +++ b/lib/private/DB/AdapterMySQL.php @@ -1,24 +1,9 @@ <?php + /** - * @copyright Copyright (c) 2016, ownCloud, Inc. - * - * @author Joas Schilling <coding@schilljs.com> - * @author Robin Appelman <robin@icewind.nl> - * - * @license AGPL-3.0 - * - * This code is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License, version 3, - * as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License, version 3, - * along with this program. If not, see <http://www.gnu.org/licenses/> - * + * SPDX-FileCopyrightText: 2016-2024 Nextcloud GmbH and Nextcloud contributors + * SPDX-FileCopyrightText: 2016 ownCloud, Inc. + * SPDX-License-Identifier: AGPL-3.0-only */ namespace OC\DB; diff --git a/lib/private/DB/AdapterOCI8.php b/lib/private/DB/AdapterOCI8.php index d8e6fabe745..0a509090bca 100644 --- a/lib/private/DB/AdapterOCI8.php +++ b/lib/private/DB/AdapterOCI8.php @@ -1,26 +1,9 @@ <?php + /** - * @copyright Copyright (c) 2016, ownCloud, Inc. - * - * @author Bart Visscher <bartv@thisnet.nl> - * @author Christoph Wurst <christoph@winzerhof-wurst.at> - * @author Robin Appelman <robin@icewind.nl> - * @author Thomas Müller <thomas.mueller@tmit.eu> - * - * @license AGPL-3.0 - * - * This code is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License, version 3, - * as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License, version 3, - * along with this program. If not, see <http://www.gnu.org/licenses/> - * + * SPDX-FileCopyrightText: 2016-2024 Nextcloud GmbH and Nextcloud contributors + * SPDX-FileCopyrightText: 2016 ownCloud, Inc. + * SPDX-License-Identifier: AGPL-3.0-only */ namespace OC\DB; diff --git a/lib/private/DB/AdapterPgSql.php b/lib/private/DB/AdapterPgSql.php index 9045aa7f879..847ea3052b7 100644 --- a/lib/private/DB/AdapterPgSql.php +++ b/lib/private/DB/AdapterPgSql.php @@ -1,28 +1,9 @@ <?php + /** - * @copyright Copyright (c) 2016, ownCloud, Inc. - * - * @author Arthur Schiwon <blizzz@arthur-schiwon.de> - * @author Bart Visscher <bartv@thisnet.nl> - * @author Christoph Wurst <christoph@winzerhof-wurst.at> - * @author Morris Jobke <hey@morrisjobke.de> - * @author Ole Ostergaard <ole.c.ostergaard@gmail.com> - * @author Ole Ostergaard <ole.ostergaard@knime.com> - * - * @license AGPL-3.0 - * - * This code is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License, version 3, - * as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License, version 3, - * along with this program. If not, see <http://www.gnu.org/licenses/> - * + * SPDX-FileCopyrightText: 2016-2024 Nextcloud GmbH and Nextcloud contributors + * SPDX-FileCopyrightText: 2016 ownCloud, Inc. + * SPDX-License-Identifier: AGPL-3.0-only */ namespace OC\DB; diff --git a/lib/private/DB/AdapterSqlite.php b/lib/private/DB/AdapterSqlite.php index 27c700bce7f..e84f62e8d80 100644 --- a/lib/private/DB/AdapterSqlite.php +++ b/lib/private/DB/AdapterSqlite.php @@ -1,28 +1,9 @@ <?php + /** - * @copyright Copyright (c) 2016, ownCloud, Inc. - * - * @author Bart Visscher <bartv@thisnet.nl> - * @author Christoph Wurst <christoph@winzerhof-wurst.at> - * @author Joas Schilling <coding@schilljs.com> - * @author Morris Jobke <hey@morrisjobke.de> - * @author Robin Appelman <robin@icewind.nl> - * @author Thomas Müller <thomas.mueller@tmit.eu> - * - * @license AGPL-3.0 - * - * This code is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License, version 3, - * as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License, version 3, - * along with this program. If not, see <http://www.gnu.org/licenses/> - * + * SPDX-FileCopyrightText: 2016-2024 Nextcloud GmbH and Nextcloud contributors + * SPDX-FileCopyrightText: 2016 ownCloud, Inc. + * SPDX-License-Identifier: AGPL-3.0-only */ namespace OC\DB; @@ -63,7 +44,7 @@ class AdapterSqlite extends Adapter { * @throws \Doctrine\DBAL\Exception * @deprecated 15.0.0 - use unique index and "try { $db->insert() } catch (UniqueConstraintViolationException $e) {}" instead, because it is more reliable and does not have the risk for deadlocks - see https://github.com/nextcloud/server/pull/12371 */ - public function insertIfNotExist($table, $input, array $compare = null) { + public function insertIfNotExist($table, $input, ?array $compare = null) { if (empty($compare)) { $compare = array_keys($input); } diff --git a/lib/private/DB/BacktraceDebugStack.php b/lib/private/DB/BacktraceDebugStack.php index 6a19be89225..4afd3ce6a13 100644 --- a/lib/private/DB/BacktraceDebugStack.php +++ b/lib/private/DB/BacktraceDebugStack.php @@ -2,23 +2,8 @@ declare(strict_types=1); /** - * @copyright Copyright (c) 2022 Robin Appelman <robin@icewind.nl> - * - * @license GNU AGPL version 3 or any later version - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as - * published by the Free Software Foundation, either version 3 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - * + * SPDX-FileCopyrightText: 2022 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later */ namespace OC\DB; diff --git a/lib/private/DB/Connection.php b/lib/private/DB/Connection.php index 85c6a72dfdb..8d300123a40 100644 --- a/lib/private/DB/Connection.php +++ b/lib/private/DB/Connection.php @@ -1,62 +1,40 @@ <?php declare(strict_types=1); - /** - * @copyright Copyright (c) 2016, ownCloud, Inc. - * - * @author Bart Visscher <bartv@thisnet.nl> - * @author Christoph Wurst <christoph@winzerhof-wurst.at> - * @author Joas Schilling <coding@schilljs.com> - * @author Julius Härtl <jus@bitgrid.net> - * @author Morris Jobke <hey@morrisjobke.de> - * @author Ole Ostergaard <ole.c.ostergaard@gmail.com> - * @author Ole Ostergaard <ole.ostergaard@knime.com> - * @author Philipp Schaffrath <github@philipp.schaffrath.email> - * @author Robin Appelman <robin@icewind.nl> - * @author Robin McCorkell <robin@mccorkell.me.uk> - * @author Roeland Jago Douma <roeland@famdouma.nl> - * @author Thomas Müller <thomas.mueller@tmit.eu> - * - * @license AGPL-3.0 - * - * This code is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License, version 3, - * as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License, version 3, - * along with this program. If not, see <http://www.gnu.org/licenses/> - * + * SPDX-FileCopyrightText: 2016-2024 Nextcloud GmbH and Nextcloud contributors + * SPDX-FileCopyrightText: 2016 ownCloud, Inc. + * SPDX-License-Identifier: AGPL-3.0-only */ namespace OC\DB; use Doctrine\Common\EventManager; use Doctrine\DBAL\Cache\QueryCacheProfile; use Doctrine\DBAL\Configuration; +use Doctrine\DBAL\Connections\PrimaryReadReplicaConnection; use Doctrine\DBAL\Driver; use Doctrine\DBAL\Exception; +use Doctrine\DBAL\Exception\ConnectionLost; use Doctrine\DBAL\Platforms\MySQLPlatform; use Doctrine\DBAL\Platforms\OraclePlatform; -use Doctrine\DBAL\Platforms\PostgreSQL94Platform; use Doctrine\DBAL\Platforms\SqlitePlatform; use Doctrine\DBAL\Result; use Doctrine\DBAL\Schema\Schema; use Doctrine\DBAL\Statement; +use OC\DB\QueryBuilder\QueryBuilder; +use OC\SystemConfig; use OCP\DB\QueryBuilder\IQueryBuilder; use OCP\Diagnostics\IEventLogger; use OCP\IRequestId; use OCP\PreConditionNotMetException; use OCP\Profiler\IProfiler; -use OC\DB\QueryBuilder\QueryBuilder; -use OC\SystemConfig; +use OCP\Server; +use Psr\Clock\ClockInterface; use Psr\Log\LoggerInterface; +use function count; +use function in_array; -class Connection extends \Doctrine\DBAL\Connection { +class Connection extends PrimaryReadReplicaConnection { /** @var string */ protected $tablePrefix; @@ -66,6 +44,8 @@ class Connection extends \Doctrine\DBAL\Connection { /** @var SystemConfig */ private $systemConfig; + private ClockInterface $clock; + private LoggerInterface $logger; protected $lockedTable = null; @@ -78,6 +58,15 @@ class Connection extends \Doctrine\DBAL\Connection { /** @var DbDataCollector|null */ protected $dbDataCollector = null; + private array $lastConnectionCheck = []; + + protected ?float $transactionActiveSince = null; + + /** @var array<string, int> */ + protected $tableDirtyWrites = []; + + protected bool $logRequestId; + protected string $requestId; /** * Initializes a new instance of the Connection class. @@ -85,7 +74,7 @@ class Connection extends \Doctrine\DBAL\Connection { * @throws \Exception */ public function __construct( - array $params, + private array $params, Driver $driver, ?Configuration $config = null, ?EventManager $eventManager = null @@ -104,10 +93,14 @@ class Connection extends \Doctrine\DBAL\Connection { $this->tablePrefix = $params['tablePrefix']; $this->systemConfig = \OC::$server->getSystemConfig(); - $this->logger = \OC::$server->get(LoggerInterface::class); + $this->clock = Server::get(ClockInterface::class); + $this->logger = Server::get(LoggerInterface::class); + + $this->logRequestId = $this->systemConfig->getValue('db.log_request_id', false); + $this->requestId = Server::get(IRequestId::class)->getId(); /** @var \OCP\Profiler\IProfiler */ - $profiler = \OC::$server->get(IProfiler::class); + $profiler = Server::get(IProfiler::class); if ($profiler->isEnabled()) { $this->dbDataCollector = new DbDataCollector($this); $profiler->add($this->dbDataCollector); @@ -115,20 +108,25 @@ class Connection extends \Doctrine\DBAL\Connection { $this->dbDataCollector->setDebugStack($debugStack); $this->_config->setSQLLogger($debugStack); } + + $this->setNestTransactionsWithSavepoints(true); } /** * @throws Exception */ - public function connect() { + public function connect($connectionName = null) { try { if ($this->_conn) { + $this->reconnectIfNeeded(); /** @psalm-suppress InternalMethod */ return parent::connect(); } + $this->lastConnectionCheck[$this->getConnectionName()] = time(); + // Only trigger the event logger for the initial connect call - $eventLogger = \OC::$server->get(IEventLogger::class); + $eventLogger = Server::get(IEventLogger::class); $eventLogger->start('connect:db', 'db connection opened'); /** @psalm-suppress InternalMethod */ $status = parent::connect(); @@ -141,6 +139,15 @@ class Connection extends \Doctrine\DBAL\Connection { } } + protected function performConnect(?string $connectionName = null): bool { + if (($connectionName ?? 'replica') === 'replica' + && count($this->params['replica']) === 1 + && $this->params['primary'] === $this->params['replica'][0]) { + return parent::performConnect('primary'); + } + return parent::performConnect($connectionName); + } + public function getStats(): array { return [ 'built' => $this->queriesBuilt, @@ -220,7 +227,7 @@ class Connection extends \Doctrine\DBAL\Connection { * @return Statement The prepared statement. * @throws Exception */ - public function prepare($statement, $limit = null, $offset = null): Statement { + public function prepare($sql, $limit = null, $offset = null): Statement { if ($limit === -1 || $limit === null) { $limit = null; } else { @@ -231,10 +238,9 @@ class Connection extends \Doctrine\DBAL\Connection { } if (!is_null($limit)) { $platform = $this->getDatabasePlatform(); - $statement = $platform->modifyLimitQuery($statement, $limit, $offset); + $sql = $platform->modifyLimitQuery($sql, $limit, $offset); } - $statement = $this->replaceTablePrefix($statement); - $statement = $this->adapter->fixupStatement($statement); + $statement = $this->finishQuery($sql); return parent::prepare($statement); } @@ -254,20 +260,64 @@ class Connection extends \Doctrine\DBAL\Connection { * * @throws \Doctrine\DBAL\Exception */ - public function executeQuery(string $sql, array $params = [], $types = [], QueryCacheProfile $qcp = null): Result { - $sql = $this->replaceTablePrefix($sql); - $sql = $this->adapter->fixupStatement($sql); + public function executeQuery(string $sql, array $params = [], $types = [], ?QueryCacheProfile $qcp = null): Result { + $tables = $this->getQueriedTables($sql); + $now = $this->clock->now()->getTimestamp(); + $dirtyTableWrites = []; + foreach ($tables as $table) { + $lastAccess = $this->tableDirtyWrites[$table] ?? 0; + // Only very recent writes are considered dirty + if ($lastAccess >= ($now - 3)) { + $dirtyTableWrites[] = $table; + } + } + if ($this->isTransactionActive()) { + // Transacted queries go to the primary. The consistency of the primary guarantees that we can not run + // into a dirty read. + } elseif (count($dirtyTableWrites) === 0) { + // No tables read that could have been written already in the same request and no transaction active + // so we can switch back to the replica for reading as long as no writes happen that switch back to the primary + // We cannot log here as this would log too early in the server boot process + $this->ensureConnectedToReplica(); + } else { + // Read to a table that has been written to previously + // While this might not necessarily mean that we did a read after write it is an indication for a code path to check + $this->logger->log( + (int) ($this->systemConfig->getValue('loglevel_dirty_database_queries', null) ?? 0), + 'dirty table reads: ' . $sql, + [ + 'tables' => array_keys($this->tableDirtyWrites), + 'reads' => $tables, + 'exception' => new \Exception('dirty table reads: ' . $sql), + ], + ); + // To prevent a dirty read on a replica that is slightly out of sync, we + // switch back to the primary. This is detrimental for performance but + // safer for consistency. + $this->ensureConnectedToPrimary(); + } + + $sql = $this->finishQuery($sql); $this->queriesExecuted++; $this->logQueryToFile($sql); return parent::executeQuery($sql, $params, $types, $qcp); } /** + * Helper function to get the list of tables affected by a given query + * used to track dirty tables that received a write with the current request + */ + private function getQueriedTables(string $sql): array { + $re = '/(\*PREFIX\*\w+)/mi'; + preg_match_all($re, $sql, $matches); + return array_map([$this, 'replaceTablePrefix'], $matches[0] ?? []); + } + + /** * @throws Exception */ public function executeUpdate(string $sql, array $params = [], array $types = []): int { - $sql = $this->replaceTablePrefix($sql); - $sql = $this->adapter->fixupStatement($sql); + $sql = $this->finishQuery($sql); $this->queriesExecuted++; $this->logQueryToFile($sql); return parent::executeUpdate($sql, $params, $types); @@ -288,8 +338,11 @@ class Connection extends \Doctrine\DBAL\Connection { * @throws \Doctrine\DBAL\Exception */ public function executeStatement($sql, array $params = [], array $types = []): int { - $sql = $this->replaceTablePrefix($sql); - $sql = $this->adapter->fixupStatement($sql); + $tables = $this->getQueriedTables($sql); + foreach ($tables as $table) { + $this->tableDirtyWrites[$table] = $this->clock->now()->getTimestamp(); + } + $sql = $this->finishQuery($sql); $this->queriesExecuted++; $this->logQueryToFile($sql); return (int)parent::executeStatement($sql, $params, $types); @@ -300,12 +353,23 @@ class Connection extends \Doctrine\DBAL\Connection { if ($logFile !== '' && is_writable(dirname($logFile)) && (!file_exists($logFile) || is_writable($logFile))) { $prefix = ''; if ($this->systemConfig->getValue('query_log_file_requestid') === 'yes') { - $prefix .= \OC::$server->get(IRequestId::class)->getId() . "\t"; + $prefix .= Server::get(IRequestId::class)->getId() . "\t"; } + $postfix = ''; + if ($this->systemConfig->getValue('query_log_file_backtrace') === 'yes') { + $trace = debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS); + array_pop($trace); + $postfix .= '; ' . json_encode($trace); + } + + // FIXME: Improve to log the actual target db host + $isPrimary = $this->connections['primary'] === $this->_conn; + $prefix .= ' ' . ($isPrimary === true ? 'primary' : 'replica') . ' '; + $prefix .= ' ' . $this->getTransactionNestingLevel() . ' '; file_put_contents( $this->systemConfig->getValue('query_log_file', ''), - $prefix . $sql . "\n", + $prefix . $sql . $postfix . "\n", FILE_APPEND ); } @@ -321,14 +385,14 @@ class Connection extends \Doctrine\DBAL\Connection { * * @param string $seqName Name of the sequence object from which the ID should be returned. * - * @return string the last inserted ID. + * @return int the last inserted ID. * @throws Exception */ - public function lastInsertId($seqName = null) { - if ($seqName) { - $seqName = $this->replaceTablePrefix($seqName); + public function lastInsertId($name = null): int { + if ($name) { + $name = $this->replaceTablePrefix($name); } - return $this->adapter->lastInsertId($seqName); + return $this->adapter->lastInsertId($name); } /** @@ -353,7 +417,7 @@ class Connection extends \Doctrine\DBAL\Connection { * @throws \Doctrine\DBAL\Exception * @deprecated 15.0.0 - use unique index and "try { $db->insert() } catch (UniqueConstraintViolationException $e) {}" instead, because it is more reliable and does not have the risk for deadlocks - see https://github.com/nextcloud/server/pull/12371 */ - public function insertIfNotExist($table, $input, array $compare = null) { + public function insertIfNotExist($table, $input, ?array $compare = null) { return $this->adapter->insertIfNotExist($table, $input, $compare); } @@ -516,6 +580,16 @@ class Connection extends \Doctrine\DBAL\Connection { return $schema->tablesExist([$table]); } + protected function finishQuery(string $statement): string { + $statement = $this->replaceTablePrefix($statement); + $statement = $this->adapter->fixupStatement($statement); + if ($this->logRequestId) { + return $statement . " /* reqid: " . $this->requestId . " */"; + } else { + return $statement; + } + } + // internal use /** * @param string $statement @@ -595,17 +669,66 @@ class Connection extends \Doctrine\DBAL\Connection { $random = \OC::$server->getSecureRandom(); $platform = $this->getDatabasePlatform(); $config = \OC::$server->getConfig(); - $dispatcher = \OC::$server->get(\OCP\EventDispatcher\IEventDispatcher::class); + $dispatcher = Server::get(\OCP\EventDispatcher\IEventDispatcher::class); if ($platform instanceof SqlitePlatform) { return new SQLiteMigrator($this, $config, $dispatcher); } elseif ($platform instanceof OraclePlatform) { return new OracleMigrator($this, $config, $dispatcher); - } elseif ($platform instanceof MySQLPlatform) { - return new MySQLMigrator($this, $config, $dispatcher); - } elseif ($platform instanceof PostgreSQL94Platform) { - return new PostgreSqlMigrator($this, $config, $dispatcher); } else { return new Migrator($this, $config, $dispatcher); } } + + public function beginTransaction() { + if (!$this->inTransaction()) { + $this->transactionActiveSince = microtime(true); + } + return parent::beginTransaction(); + } + + public function commit() { + $result = parent::commit(); + if ($this->getTransactionNestingLevel() === 0) { + $timeTook = microtime(true) - $this->transactionActiveSince; + $this->transactionActiveSince = null; + if ($timeTook > 1) { + $this->logger->debug('Transaction took ' . $timeTook . 's', ['exception' => new \Exception('Transaction took ' . $timeTook . 's')]); + } + } + return $result; + } + + public function rollBack() { + $result = parent::rollBack(); + if ($this->getTransactionNestingLevel() === 0) { + $timeTook = microtime(true) - $this->transactionActiveSince; + $this->transactionActiveSince = null; + if ($timeTook > 1) { + $this->logger->debug('Transaction rollback took longer than 1s: ' . $timeTook, ['exception' => new \Exception('Long running transaction rollback')]); + } + } + return $result; + } + + private function reconnectIfNeeded(): void { + if ( + !isset($this->lastConnectionCheck[$this->getConnectionName()]) || + time() <= $this->lastConnectionCheck[$this->getConnectionName()] + 30 || + $this->isTransactionActive() + ) { + return; + } + + try { + $this->_conn->query($this->getDriver()->getDatabasePlatform()->getDummySelectSQL()); + $this->lastConnectionCheck[$this->getConnectionName()] = time(); + } catch (ConnectionLost|\Exception $e) { + $this->logger->warning('Exception during connectivity check, closing and reconnecting', ['exception' => $e]); + $this->close(); + } + } + + private function getConnectionName(): string { + return $this->isConnectedToPrimary() ? 'primary' : 'replica'; + } } diff --git a/lib/private/DB/ConnectionAdapter.php b/lib/private/DB/ConnectionAdapter.php index a53c7ecd994..86a901a7de3 100644 --- a/lib/private/DB/ConnectionAdapter.php +++ b/lib/private/DB/ConnectionAdapter.php @@ -3,30 +3,17 @@ declare(strict_types=1); /** - * @copyright 2020 Christoph Wurst <christoph@winzerhof-wurst.at> - * - * @author Christoph Wurst <christoph@winzerhof-wurst.at> - * - * @license GNU AGPL version 3 or any later version - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as - * published by the Free Software Foundation, either version 3 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - * + * SPDX-FileCopyrightText: 2020 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later */ namespace OC\DB; use Doctrine\DBAL\Exception; use Doctrine\DBAL\Platforms\AbstractPlatform; +use Doctrine\DBAL\Platforms\MySQLPlatform; +use Doctrine\DBAL\Platforms\OraclePlatform; +use Doctrine\DBAL\Platforms\PostgreSQLPlatform; +use Doctrine\DBAL\Platforms\SqlitePlatform; use Doctrine\DBAL\Schema\Schema; use OC\DB\Exceptions\DbalException; use OCP\DB\IPreparedStatement; @@ -87,13 +74,13 @@ class ConnectionAdapter implements IDBConnection { public function lastInsertId(string $table): int { try { - return (int)$this->inner->lastInsertId($table); + return $this->inner->lastInsertId($table); } catch (Exception $e) { throw DbalException::wrap($e); } } - public function insertIfNotExist(string $table, array $input, array $compare = null) { + public function insertIfNotExist(string $table, array $input, ?array $compare = null) { try { return $this->inner->insertIfNotExist($table, $input, $compare); } catch (Exception $e) { @@ -242,4 +229,19 @@ class ConnectionAdapter implements IDBConnection { public function getInner(): Connection { return $this->inner; } + + public function getDatabaseProvider(): string { + $platform = $this->inner->getDatabasePlatform(); + if ($platform instanceof MySQLPlatform) { + return IDBConnection::PLATFORM_MYSQL; + } elseif ($platform instanceof OraclePlatform) { + return IDBConnection::PLATFORM_ORACLE; + } elseif ($platform instanceof PostgreSQLPlatform) { + return IDBConnection::PLATFORM_POSTGRES; + } elseif ($platform instanceof SqlitePlatform) { + return IDBConnection::PLATFORM_SQLITE; + } else { + throw new \Exception('Database ' . $platform::class . ' not supported'); + } + } } diff --git a/lib/private/DB/ConnectionFactory.php b/lib/private/DB/ConnectionFactory.php index 1b0ac436364..5e50d01538a 100644 --- a/lib/private/DB/ConnectionFactory.php +++ b/lib/private/DB/ConnectionFactory.php @@ -1,30 +1,9 @@ <?php + /** - * @copyright Copyright (c) 2016, ownCloud, Inc. - * - * @author Andreas Fischer <bantu@owncloud.com> - * @author Christoph Wurst <christoph@winzerhof-wurst.at> - * @author Daniel Kesselberg <mail@danielkesselberg.de> - * @author Joas Schilling <coding@schilljs.com> - * @author Jörn Friedrich Dreyer <jfd@butonic.de> - * @author Morris Jobke <hey@morrisjobke.de> - * @author Robin Appelman <robin@icewind.nl> - * @author Thomas Müller <thomas.mueller@tmit.eu> - * - * @license AGPL-3.0 - * - * This code is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License, version 3, - * as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License, version 3, - * along with this program. If not, see <http://www.gnu.org/licenses/> - * + * SPDX-FileCopyrightText: 2016-2024 Nextcloud GmbH and Nextcloud contributors + * SPDX-FileCopyrightText: 2016 ownCloud, Inc. + * SPDX-License-Identifier: AGPL-3.0-only */ namespace OC\DB; @@ -32,7 +11,6 @@ use Doctrine\Common\EventManager; use Doctrine\DBAL\Configuration; use Doctrine\DBAL\DriverManager; use Doctrine\DBAL\Event\Listeners\OracleSessionInit; -use Doctrine\DBAL\Event\Listeners\SQLSessionInit; use OC\SystemConfig; /** @@ -127,11 +105,17 @@ class ConnectionFactory { $normalizedType = $this->normalizeType($type); $eventManager = new EventManager(); $eventManager->addEventSubscriber(new SetTransactionIsolationLevel()); + $additionalConnectionParams = array_merge($this->createConnectionParams(), $additionalConnectionParams); switch ($normalizedType) { - case 'mysql': - $eventManager->addEventSubscriber( - new SQLSessionInit("SET SESSION AUTOCOMMIT=1")); + case 'pgsql': + // pg_connect used by Doctrine DBAL does not support URI notation (enclosed in brackets) + $matches = []; + if (preg_match('/^\[([^\]]+)\]$/', $additionalConnectionParams['host'], $matches)) { + // Host variable carries a port or socket. + $additionalConnectionParams['host'] = $matches[1]; + } break; + case 'oci': $eventManager->addEventSubscriber(new OracleSessionInit); // the driverOptions are unused in dbal and need to be mapped to the parameters @@ -139,7 +123,7 @@ class ConnectionFactory { $additionalConnectionParams = array_merge($additionalConnectionParams, $additionalConnectionParams['driverOptions']); } $host = $additionalConnectionParams['host']; - $port = isset($additionalConnectionParams['port']) ? $additionalConnectionParams['port'] : null; + $port = $additionalConnectionParams['port'] ?? null; $dbName = $additionalConnectionParams['dbname']; // we set the connect string as dbname and unset the host to coerce doctrine into using it as connect string @@ -159,7 +143,7 @@ class ConnectionFactory { } /** @var Connection $connection */ $connection = DriverManager::getConnection( - array_merge($this->getDefaultConnectionParams($type), $additionalConnectionParams), + $additionalConnectionParams, new Configuration(), $eventManager ); @@ -195,10 +179,10 @@ class ConnectionFactory { public function createConnectionParams(string $configPrefix = '') { $type = $this->config->getValue('dbtype', 'sqlite'); - $connectionParams = [ + $connectionParams = array_merge($this->getDefaultConnectionParams($type), [ 'user' => $this->config->getValue($configPrefix . 'dbuser', $this->config->getValue('dbuser', '')), 'password' => $this->config->getValue($configPrefix . 'dbpassword', $this->config->getValue('dbpassword', '')), - ]; + ]); $name = $this->config->getValue($configPrefix . 'dbname', $this->config->getValue('dbname', self::DEFAULT_DBNAME)); if ($this->normalizeType($type) === 'sqlite3') { @@ -237,7 +221,11 @@ class ConnectionFactory { $connectionParams['persistent'] = true; } - return $connectionParams; + $replica = $this->config->getValue($configPrefix . 'dbreplica', $this->config->getValue('dbreplica', [])) ?: [$connectionParams]; + return array_merge($connectionParams, [ + 'primary' => $connectionParams, + 'replica' => $replica, + ]); } /** diff --git a/lib/private/DB/DbDataCollector.php b/lib/private/DB/DbDataCollector.php index 60e3dbe797d..fcaa74daeab 100644 --- a/lib/private/DB/DbDataCollector.php +++ b/lib/private/DB/DbDataCollector.php @@ -2,25 +2,8 @@ declare(strict_types = 1); /** - * @copyright 2022 Carl Schwan <carl@carlschwan.eu> - * - * @author Carl Schwan <carl@carlschwan.eu> - * - * @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/>. - * + * SPDX-FileCopyrightText: 2022 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later */ namespace OC\DB; @@ -48,7 +31,7 @@ class DbDataCollector extends \OCP\DataCollector\AbstractDataCollector { /** * @inheritDoc */ - public function collect(Request $request, Response $response, \Throwable $exception = null): void { + public function collect(Request $request, Response $response, ?\Throwable $exception = null): void { $queries = $this->sanitizeQueries($this->debugStack->queries); $this->data = [ @@ -75,7 +58,7 @@ class DbDataCollector extends \OCP\DataCollector\AbstractDataCollector { private function sanitizeQuery(array $query): array { $query['explainable'] = true; $query['runnable'] = true; - if (null === $query['params']) { + if ($query['params'] === null) { $query['params'] = []; } if (!\is_array($query['params'])) { diff --git a/lib/private/DB/Exceptions/DbalException.php b/lib/private/DB/Exceptions/DbalException.php index 2b860a50ff3..05ea9e22a5d 100644 --- a/lib/private/DB/Exceptions/DbalException.php +++ b/lib/private/DB/Exceptions/DbalException.php @@ -3,26 +3,8 @@ declare(strict_types=1); /** - * @copyright 2021 Christoph Wurst <christoph@winzerhof-wurst.at> - * - * @author Christoph Wurst <christoph@winzerhof-wurst.at> - * @author Daniel Kesselberg <mail@danielkesselberg.de> - * - * @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/>. - * + * SPDX-FileCopyrightText: 2021 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later */ namespace OC\DB\Exceptions; @@ -35,6 +17,7 @@ use Doctrine\DBAL\Exception\DriverException; use Doctrine\DBAL\Exception\ForeignKeyConstraintViolationException; use Doctrine\DBAL\Exception\InvalidArgumentException; use Doctrine\DBAL\Exception\InvalidFieldNameException; +use Doctrine\DBAL\Exception\LockWaitTimeoutException; use Doctrine\DBAL\Exception\NonUniqueFieldNameException; use Doctrine\DBAL\Exception\NotNullConstraintViolationException; use Doctrine\DBAL\Exception\RetryableException; @@ -100,6 +83,9 @@ class DbalException extends Exception { /** * Other server errors */ + if ($this->original instanceof LockWaitTimeoutException) { + return parent::REASON_LOCK_WAIT_TIMEOUT; + } if ($this->original instanceof DatabaseObjectExistsException) { return parent::REASON_DATABASE_OBJECT_EXISTS; } diff --git a/lib/private/DB/MigrationException.php b/lib/private/DB/MigrationException.php index 5b08ab9cac2..5b50f8ed3a4 100644 --- a/lib/private/DB/MigrationException.php +++ b/lib/private/DB/MigrationException.php @@ -1,23 +1,9 @@ <?php + /** - * @copyright Copyright (c) 2016, ownCloud, Inc. - * - * @author Robin Appelman <robin@icewind.nl> - * - * @license AGPL-3.0 - * - * This code is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License, version 3, - * as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License, version 3, - * along with this program. If not, see <http://www.gnu.org/licenses/> - * + * SPDX-FileCopyrightText: 2016-2024 Nextcloud GmbH and Nextcloud contributors + * SPDX-FileCopyrightText: 2016 ownCloud, Inc. + * SPDX-License-Identifier: AGPL-3.0-only */ namespace OC\DB; diff --git a/lib/private/DB/MigrationService.php b/lib/private/DB/MigrationService.php index 71d7b51d149..4b91b6f0996 100644 --- a/lib/private/DB/MigrationService.php +++ b/lib/private/DB/MigrationService.php @@ -1,29 +1,8 @@ <?php /** - * @copyright Copyright (c) 2017 Joas Schilling <coding@schilljs.com> - * @copyright Copyright (c) 2017, ownCloud GmbH - * - * @author Christoph Wurst <christoph@winzerhof-wurst.at> - * @author Daniel Kesselberg <mail@danielkesselberg.de> - * @author Joas Schilling <coding@schilljs.com> - * @author Julius Härtl <jus@bitgrid.net> - * @author Morris Jobke <hey@morrisjobke.de> - * @author Robin Appelman <robin@icewind.nl> - * - * @license AGPL-3.0 - * - * This code is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License, version 3, - * as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License, version 3, - * along with this program. If not, see <http://www.gnu.org/licenses/> - * + * SPDX-FileCopyrightText: 2017 Nextcloud GmbH and Nextcloud contributors + * SPDX-FileCopyrightText: 2017 ownCloud GmbH + * SPDX-License-Identifier: AGPL-3.0-only */ namespace OC\DB; @@ -43,6 +22,7 @@ use OCP\AppFramework\QueryException; use OCP\DB\ISchemaWrapper; use OCP\Migration\IMigrationStep; use OCP\Migration\IOutput; +use OCP\Server; use Psr\Log\LoggerInterface; class MigrationService { @@ -51,6 +31,7 @@ class MigrationService { private string $migrationsPath; private string $migrationsNamespace; private IOutput $output; + private LoggerInterface $logger; private Connection $connection; private string $appName; private bool $checkOracle; @@ -58,11 +39,16 @@ class MigrationService { /** * @throws \Exception */ - public function __construct($appName, Connection $connection, ?IOutput $output = null, ?AppLocator $appLocator = null) { + public function __construct(string $appName, Connection $connection, ?IOutput $output = null, ?AppLocator $appLocator = null, ?LoggerInterface $logger = null) { $this->appName = $appName; $this->connection = $connection; + if ($logger === null) { + $this->logger = Server::get(LoggerInterface::class); + } else { + $this->logger = $logger; + } if ($output === null) { - $this->output = new SimpleOutput(\OC::$server->get(LoggerInterface::class), $appName); + $this->output = new SimpleOutput($this->logger, $appName); } else { $this->output = $output; } @@ -72,7 +58,7 @@ class MigrationService { $this->migrationsNamespace = 'OC\\Core\\Migrations'; $this->checkOracle = true; } else { - if (null === $appLocator) { + if ($appLocator === null) { $appLocator = new AppLocator(); } $appPath = $appLocator->getAppPath($appName); @@ -100,18 +86,15 @@ class MigrationService { /** * Returns the name of the app for which this migration is executed - * - * @return string */ - public function getApp() { + public function getApp(): string { return $this->appName; } /** - * @return bool * @codeCoverageIgnore - this will implicitly tested on installation */ - private function createMigrationTable() { + private function createMigrationTable(): bool { if ($this->migrationTableCreated) { return false; } @@ -188,7 +171,7 @@ class MigrationService { ->where($qb->expr()->eq('app', $qb->createNamedParameter($this->getApp()))) ->orderBy('version'); - $result = $qb->execute(); + $result = $qb->executeQuery(); $rows = $result->fetchAll(\PDO::FETCH_COLUMN); $result->closeCursor(); @@ -197,15 +180,17 @@ class MigrationService { /** * Returns all versions which are available in the migration folder - * - * @return array + * @return list<string> */ - public function getAvailableVersions() { + public function getAvailableVersions(): array { $this->ensureMigrationsAreLoaded(); return array_map('strval', array_keys($this->migrations)); } - protected function findMigrations() { + /** + * @return array<string, string> + */ + protected function findMigrations(): array { $directory = realpath($this->migrationsPath); if ($directory === false || !file_exists($directory) || !is_dir($directory)) { return []; @@ -322,10 +307,9 @@ class MigrationService { /** * Return the explicit version for the aliases; current, next, prev, latest * - * @param string $alias * @return mixed|null|string */ - public function getMigration($alias) { + public function getMigration(string $alias) { switch ($alias) { case 'current': return $this->getCurrentVersion(); @@ -342,29 +326,22 @@ class MigrationService { return '0'; } - /** - * @param string $version - * @param int $delta - * @return null|string - */ - private function getRelativeVersion($version, $delta) { + private function getRelativeVersion(string $version, int $delta): ?string { $this->ensureMigrationsAreLoaded(); $versions = $this->getAvailableVersions(); - array_unshift($versions, 0); + array_unshift($versions, '0'); + /** @var int $offset */ $offset = array_search($version, $versions, true); if ($offset === false || !isset($versions[$offset + $delta])) { // Unknown version or delta out of bounds. return null; } - return (string) $versions[$offset + $delta]; + return (string)$versions[$offset + $delta]; } - /** - * @return string - */ - private function getCurrentVersion() { + private function getCurrentVersion(): string { $m = $this->getMigratedVersions(); if (count($m) === 0) { return '0'; @@ -374,11 +351,9 @@ class MigrationService { } /** - * @param string $version - * @return string * @throws \InvalidArgumentException */ - private function getClass($version) { + private function getClass(string $version): string { $this->ensureMigrationsAreLoaded(); if (isset($this->migrations[$version])) { @@ -390,22 +365,18 @@ class MigrationService { /** * Allows to set an IOutput implementation which is used for logging progress and messages - * - * @param IOutput $output */ - public function setOutput(IOutput $output) { + public function setOutput(IOutput $output): void { $this->output = $output; } /** * Applies all not yet applied versions up to $to - * - * @param string $to - * @param bool $schemaOnly * @throws \InvalidArgumentException */ - public function migrate($to = 'latest', $schemaOnly = false) { + public function migrate(string $to = 'latest', bool $schemaOnly = false): void { if ($schemaOnly) { + $this->output->debug('Migrating schema only'); $this->migrateSchemaOnly($to); return; } @@ -425,11 +396,9 @@ class MigrationService { /** * Applies all not yet applied versions up to $to - * - * @param string $to * @throws \InvalidArgumentException */ - public function migrateSchemaOnly($to = 'latest') { + public function migrateSchemaOnly(string $to = 'latest'): void { // read known migrations $toBeExecuted = $this->getMigrationsToExecute($to); @@ -439,6 +408,7 @@ class MigrationService { $toSchema = null; foreach ($toBeExecuted as $version) { + $this->output->debug('- Reading ' . $version); $instance = $this->createInstance($version); $toSchema = $instance->changeSchema($this->output, function () use ($toSchema): ISchemaWrapper { @@ -447,16 +417,20 @@ class MigrationService { } if ($toSchema instanceof SchemaWrapper) { + $this->output->debug('- Checking target database schema'); $targetSchema = $toSchema->getWrappedSchema(); - $this->ensureUniqueNamesConstraints($targetSchema); + $this->ensureUniqueNamesConstraints($targetSchema, true); if ($this->checkOracle) { $beforeSchema = $this->connection->createSchema(); $this->ensureOracleConstraints($beforeSchema, $targetSchema, strlen($this->connection->getPrefix())); } + + $this->output->debug('- Migrate database schema'); $this->connection->migrateToSchema($targetSchema); $toSchema->performDropTableCalls(); } + $this->output->debug('- Mark migrations as executed'); foreach ($toBeExecuted as $version) { $this->markAsExecuted($version); } @@ -526,7 +500,7 @@ class MigrationService { if ($toSchema instanceof SchemaWrapper) { $targetSchema = $toSchema->getWrappedSchema(); - $this->ensureUniqueNamesConstraints($targetSchema); + $this->ensureUniqueNamesConstraints($targetSchema, $schemaOnly); if ($this->checkOracle) { $sourceSchema = $this->connection->createSchema(); $this->ensureOracleConstraints($sourceSchema, $targetSchema, strlen($this->connection->getPrefix())); @@ -662,14 +636,26 @@ class MigrationService { } /** + * Ensure naming constraints + * * Naming constraints: * - Index, sequence and primary key names must be unique within a Postgres Schema * + * Only on installation we want to break hard, so that all developers notice + * the bugs when installing the app on any database or CI, and can work on + * fixing their migrations before releasing a version incompatible with Postgres. + * + * In case of updates we might be running on production instances and the + * administrators being faced with the error would not know how to resolve it + * anyway. This can also happen with instances, that had the issue before the + * current update, so we don't want to make their life more complicated + * than needed. + * * @param Schema $targetSchema + * @param bool $isInstalling */ - public function ensureUniqueNamesConstraints(Schema $targetSchema): void { + public function ensureUniqueNamesConstraints(Schema $targetSchema, bool $isInstalling): void { $constraintNames = []; - $sequences = $targetSchema->getSequences(); foreach ($targetSchema->getTables() as $table) { @@ -680,14 +666,20 @@ class MigrationService { } if (isset($constraintNames[$thing->getName()])) { - throw new \InvalidArgumentException('Index name "' . $thing->getName() . '" for table "' . $table->getName() . '" collides with the constraint on table "' . $constraintNames[$thing->getName()] . '".'); + if ($isInstalling) { + throw new \InvalidArgumentException('Index name "' . $thing->getName() . '" for table "' . $table->getName() . '" collides with the constraint on table "' . $constraintNames[$thing->getName()] . '".'); + } + $this->logErrorOrWarning('Index name "' . $thing->getName() . '" for table "' . $table->getName() . '" collides with the constraint on table "' . $constraintNames[$thing->getName()] . '".'); } $constraintNames[$thing->getName()] = $table->getName(); } foreach ($table->getForeignKeys() as $thing) { if (isset($constraintNames[$thing->getName()])) { - throw new \InvalidArgumentException('Foreign key name "' . $thing->getName() . '" for table "' . $table->getName() . '" collides with the constraint on table "' . $constraintNames[$thing->getName()] . '".'); + if ($isInstalling) { + throw new \InvalidArgumentException('Foreign key name "' . $thing->getName() . '" for table "' . $table->getName() . '" collides with the constraint on table "' . $constraintNames[$thing->getName()] . '".'); + } + $this->logErrorOrWarning('Foreign key name "' . $thing->getName() . '" for table "' . $table->getName() . '" collides with the constraint on table "' . $constraintNames[$thing->getName()] . '".'); } $constraintNames[$thing->getName()] = $table->getName(); } @@ -700,7 +692,10 @@ class MigrationService { } if (isset($constraintNames[$indexName])) { - throw new \InvalidArgumentException('Primary index name "' . $indexName . '" for table "' . $table->getName() . '" collides with the constraint on table "' . $constraintNames[$thing->getName()] . '".'); + if ($isInstalling) { + throw new \InvalidArgumentException('Primary index name "' . $indexName . '" for table "' . $table->getName() . '" collides with the constraint on table "' . $constraintNames[$thing->getName()] . '".'); + } + $this->logErrorOrWarning('Primary index name "' . $indexName . '" for table "' . $table->getName() . '" collides with the constraint on table "' . $constraintNames[$thing->getName()] . '".'); } $constraintNames[$indexName] = $table->getName(); } @@ -708,12 +703,23 @@ class MigrationService { foreach ($sequences as $sequence) { if (isset($constraintNames[$sequence->getName()])) { - throw new \InvalidArgumentException('Sequence name "' . $sequence->getName() . '" for table "' . $table->getName() . '" collides with the constraint on table "' . $constraintNames[$thing->getName()] . '".'); + if ($isInstalling) { + throw new \InvalidArgumentException('Sequence name "' . $sequence->getName() . '" for table "' . $table->getName() . '" collides with the constraint on table "' . $constraintNames[$thing->getName()] . '".'); + } + $this->logErrorOrWarning('Sequence name "' . $sequence->getName() . '" for table "' . $table->getName() . '" collides with the constraint on table "' . $constraintNames[$thing->getName()] . '".'); } $constraintNames[$sequence->getName()] = 'sequence'; } } + protected function logErrorOrWarning(string $log): void { + if ($this->output instanceof SimpleOutput) { + $this->output->warning($log); + } else { + $this->logger->error($log); + } + } + private function ensureMigrationsAreLoaded() { if (empty($this->migrations)) { $this->migrations = $this->findMigrations(); diff --git a/lib/private/DB/Migrator.php b/lib/private/DB/Migrator.php index 74e5a285351..4fd457b4bc6 100644 --- a/lib/private/DB/Migrator.php +++ b/lib/private/DB/Migrator.php @@ -1,29 +1,9 @@ <?php + /** - * @copyright Copyright (c) 2016, ownCloud, Inc. - * - * @author Christoph Wurst <christoph@winzerhof-wurst.at> - * @author Daniel Kesselberg <mail@danielkesselberg.de> - * @author Joas Schilling <coding@schilljs.com> - * @author Morris Jobke <hey@morrisjobke.de> - * @author Robin Appelman <robin@icewind.nl> - * @author Roeland Jago Douma <roeland@famdouma.nl> - * @author Thomas Müller <thomas.mueller@tmit.eu> - * - * @license AGPL-3.0 - * - * This code is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License, version 3, - * as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License, version 3, - * along with this program. If not, see <http://www.gnu.org/licenses/> - * + * SPDX-FileCopyrightText: 2016-2024 Nextcloud GmbH and Nextcloud contributors + * SPDX-FileCopyrightText: 2016 ownCloud, Inc. + * SPDX-License-Identifier: AGPL-3.0-only */ namespace OC\DB; @@ -31,14 +11,13 @@ use Doctrine\DBAL\Connection; use Doctrine\DBAL\Exception; use Doctrine\DBAL\Platforms\MySQLPlatform; use Doctrine\DBAL\Schema\AbstractAsset; -use Doctrine\DBAL\Schema\Comparator; use Doctrine\DBAL\Schema\Schema; use Doctrine\DBAL\Schema\SchemaDiff; use Doctrine\DBAL\Types\StringType; use Doctrine\DBAL\Types\Type; +use OCP\EventDispatcher\IEventDispatcher; use OCP\IConfig; use function preg_match; -use OCP\EventDispatcher\IEventDispatcher; class Migrator { /** @var Connection */ @@ -53,8 +32,8 @@ class Migrator { private $noEmit = false; public function __construct(Connection $connection, - IConfig $config, - ?IEventDispatcher $dispatcher = null) { + IConfig $config, + ?IEventDispatcher $dispatcher = null) { $this->connection = $connection; $this->config = $config; $this->dispatcher = $dispatcher; @@ -75,7 +54,7 @@ class Migrator { $schemaDiff = $this->getDiff($targetSchema, $this->connection); $script = ''; - $sqls = $schemaDiff->toSql($this->connection->getDatabasePlatform()); + $sqls = $this->connection->getDatabasePlatform()->getAlterSchemaSQL($schemaDiff); foreach ($sqls as $sql) { $script .= $this->convertStatementToScript($sql); } @@ -95,18 +74,20 @@ class Migrator { } return preg_match($filterExpression, $asset) === 1; }); - return $this->connection->getSchemaManager()->createSchema(); + return $this->connection->createSchemaManager()->introspectSchema(); } /** * @return SchemaDiff */ protected function getDiff(Schema $targetSchema, Connection $connection) { - // adjust varchar columns with a length higher then getVarcharMaxLength to clob + // Adjust STRING columns with a length higher than 4000 to TEXT (clob) + // for consistency between the supported databases and + // old vs. new installations. foreach ($targetSchema->getTables() as $table) { foreach ($table->getColumns() as $column) { if ($column->getType() instanceof StringType) { - if ($column->getLength() > $connection->getDatabasePlatform()->getVarcharMaxLength()) { + if ($column->getLength() > 4000) { $column->setType(Type::getType('text')); $column->setLength(null); } @@ -122,7 +103,7 @@ class Migrator { } return preg_match($filterExpression, $asset) === 1; }); - $sourceSchema = $connection->getSchemaManager()->createSchema(); + $sourceSchema = $connection->createSchemaManager()->introspectSchema(); // remove tables we don't know about foreach ($sourceSchema->getTables() as $table) { @@ -137,15 +118,14 @@ class Migrator { } } - /** @psalm-suppress InternalMethod */ - $comparator = new Comparator(); - return $comparator->compare($sourceSchema, $targetSchema); + $comparator = $connection->createSchemaManager()->createComparator(); + return $comparator->compareSchemas($sourceSchema, $targetSchema); } /** * @throws Exception */ - protected function applySchema(Schema $targetSchema, Connection $connection = null) { + protected function applySchema(Schema $targetSchema, ?Connection $connection = null) { if (is_null($connection)) { $connection = $this->connection; } @@ -155,11 +135,11 @@ class Migrator { if (!$connection->getDatabasePlatform() instanceof MySQLPlatform) { $connection->beginTransaction(); } - $sqls = $schemaDiff->toSql($connection->getDatabasePlatform()); + $sqls = $connection->getDatabasePlatform()->getAlterSchemaSQL($schemaDiff); $step = 0; foreach ($sqls as $sql) { $this->emit($sql, $step++, count($sqls)); - $connection->query($sql); + $connection->executeQuery($sql); } if (!$connection->getDatabasePlatform() instanceof MySQLPlatform) { $connection->commit(); @@ -178,7 +158,7 @@ class Migrator { } protected function getFilterExpression() { - return '/^' . preg_quote($this->config->getSystemValueString('dbtableprefix', 'oc_')) . '/'; + return '/^' . preg_quote($this->config->getSystemValueString('dbtableprefix', 'oc_'), '/') . '/'; } protected function emit(string $sql, int $step, int $max): void { diff --git a/lib/private/DB/MigratorExecuteSqlEvent.php b/lib/private/DB/MigratorExecuteSqlEvent.php index 997a4eee53a..cfcfe7fa512 100644 --- a/lib/private/DB/MigratorExecuteSqlEvent.php +++ b/lib/private/DB/MigratorExecuteSqlEvent.php @@ -3,24 +3,8 @@ declare(strict_types=1); /** - * @copyright Copyright (c) 2022 Côme Chilliet <come.chilliet@nextcloud.com> - * - * @author Côme Chilliet <come.chilliet@nextcloud.com> - * - * @license AGPL-3.0 - * - * This code is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License, version 3, - * as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License, version 3, - * along with this program. If not, see <http://www.gnu.org/licenses/> - * + * SPDX-FileCopyrightText: 2022 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-only */ namespace OC\DB; diff --git a/lib/private/DB/MissingColumnInformation.php b/lib/private/DB/MissingColumnInformation.php index f651546b4b3..5e8402d394e 100644 --- a/lib/private/DB/MissingColumnInformation.php +++ b/lib/private/DB/MissingColumnInformation.php @@ -3,30 +3,13 @@ declare(strict_types=1); /** - * @copyright Copyright (c) 2020 Joas Schilling <coding@schilljs.com> - * - * @author Joas Schilling <coding@schilljs.com> - * - * @license GNU AGPL version 3 or any later version - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as - * published by the Free Software Foundation, either version 3 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - * + * SPDX-FileCopyrightText: 2020 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later */ namespace OC\DB; class MissingColumnInformation { - private $listOfMissingColumns = []; + private array $listOfMissingColumns = []; public function addHintForMissingColumn(string $tableName, string $columnName): void { $this->listOfMissingColumns[] = [ diff --git a/lib/private/DB/MissingIndexInformation.php b/lib/private/DB/MissingIndexInformation.php index 74498668349..99a81782858 100644 --- a/lib/private/DB/MissingIndexInformation.php +++ b/lib/private/DB/MissingIndexInformation.php @@ -3,40 +3,22 @@ declare(strict_types=1); /** - * @copyright Copyright (c) 2018 Morris Jobke <hey@morrisjobke.de> - * - * @author Morris Jobke <hey@morrisjobke.de> - * @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/>. - * + * SPDX-FileCopyrightText: 2018 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later */ namespace OC\DB; class MissingIndexInformation { - private $listOfMissingIndexes = []; + private array $listOfMissingIndices = []; - public function addHintForMissingSubject(string $tableName, string $indexName) { - $this->listOfMissingIndexes[] = [ + public function addHintForMissingIndex(string $tableName, string $indexName): void { + $this->listOfMissingIndices[] = [ 'tableName' => $tableName, 'indexName' => $indexName ]; } - public function getListOfMissingIndexes(): array { - return $this->listOfMissingIndexes; + public function getListOfMissingIndices(): array { + return $this->listOfMissingIndices; } } diff --git a/lib/private/DB/MissingPrimaryKeyInformation.php b/lib/private/DB/MissingPrimaryKeyInformation.php index f28c8cfb352..355ce86423a 100644 --- a/lib/private/DB/MissingPrimaryKeyInformation.php +++ b/lib/private/DB/MissingPrimaryKeyInformation.php @@ -3,32 +3,15 @@ declare(strict_types=1); /** - * @copyright Copyright (c) 2018 Morris Jobke <hey@morrisjobke.de> - * - * @author Joas Schilling <coding@schilljs.com> - * - * @license GNU AGPL version 3 or any later version - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as - * published by the Free Software Foundation, either version 3 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - * + * SPDX-FileCopyrightText: 2018 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later */ namespace OC\DB; class MissingPrimaryKeyInformation { - private $listOfMissingPrimaryKeys = []; + private array $listOfMissingPrimaryKeys = []; - public function addHintForMissingSubject(string $tableName) { + public function addHintForMissingPrimaryKey(string $tableName): void { $this->listOfMissingPrimaryKeys[] = [ 'tableName' => $tableName, ]; diff --git a/lib/private/DB/MySQLMigrator.php b/lib/private/DB/MySQLMigrator.php deleted file mode 100644 index 0f8cbb309f3..00000000000 --- a/lib/private/DB/MySQLMigrator.php +++ /dev/null @@ -1,50 +0,0 @@ -<?php -/** - * @copyright Copyright (c) 2016, ownCloud, Inc. - * - * @author Robin Appelman <robin@icewind.nl> - * @author Thomas Müller <thomas.mueller@tmit.eu> - * - * @license AGPL-3.0 - * - * This code is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License, version 3, - * as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License, version 3, - * along with this program. If not, see <http://www.gnu.org/licenses/> - * - */ -namespace OC\DB; - -use Doctrine\DBAL\Schema\Schema; - -class MySQLMigrator extends Migrator { - /** - * @param Schema $targetSchema - * @param \Doctrine\DBAL\Connection $connection - * @return \Doctrine\DBAL\Schema\SchemaDiff - */ - protected function getDiff(Schema $targetSchema, \Doctrine\DBAL\Connection $connection) { - $platform = $connection->getDatabasePlatform(); - $platform->registerDoctrineTypeMapping('enum', 'string'); - $platform->registerDoctrineTypeMapping('bit', 'string'); - - $schemaDiff = parent::getDiff($targetSchema, $connection); - - // identifiers need to be quoted for mysql - foreach ($schemaDiff->changedTables as $tableDiff) { - $tableDiff->name = $this->connection->quoteIdentifier($tableDiff->name); - foreach ($tableDiff->changedColumns as $column) { - $column->oldColumnName = $this->connection->quoteIdentifier($column->oldColumnName); - } - } - - return $schemaDiff; - } -} diff --git a/lib/private/DB/MySqlTools.php b/lib/private/DB/MySqlTools.php index b129aefec08..cd6b812be61 100644 --- a/lib/private/DB/MySqlTools.php +++ b/lib/private/DB/MySqlTools.php @@ -1,25 +1,7 @@ <?php /** - * @copyright Copyright (c) 2017, ownCloud GmbH - * - * @author Christoph Wurst <christoph@winzerhof-wurst.at> - * @author Joas Schilling <coding@schilljs.com> - * @author Thomas Müller <thomas.mueller@tmit.eu> - * - * @license AGPL-3.0 - * - * This code is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License, version 3, - * as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License, version 3, - * along with this program. If not, see <http://www.gnu.org/licenses/> - * + * SPDX-FileCopyrightText: 2017 ownCloud GmbH + * SPDX-License-Identifier: AGPL-3.0-only */ namespace OC\DB; diff --git a/lib/private/DB/OCSqlitePlatform.php b/lib/private/DB/OCSqlitePlatform.php index 059cb21fdaf..3e2b10e6e40 100644 --- a/lib/private/DB/OCSqlitePlatform.php +++ b/lib/private/DB/OCSqlitePlatform.php @@ -1,23 +1,9 @@ <?php + /** - * @copyright Copyright (c) 2016, ownCloud, Inc. - * - * @author Robin Appelman <robin@icewind.nl> - * - * @license AGPL-3.0 - * - * This code is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License, version 3, - * as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License, version 3, - * along with this program. If not, see <http://www.gnu.org/licenses/> - * + * SPDX-FileCopyrightText: 2016-2024 Nextcloud GmbH and Nextcloud contributors + * SPDX-FileCopyrightText: 2016 ownCloud, Inc. + * SPDX-License-Identifier: AGPL-3.0-only */ namespace OC\DB; diff --git a/lib/private/DB/ObjectParameter.php b/lib/private/DB/ObjectParameter.php index 61ac16018d8..1b013734c95 100644 --- a/lib/private/DB/ObjectParameter.php +++ b/lib/private/DB/ObjectParameter.php @@ -1,37 +1,13 @@ <?php declare(strict_types = 1); - -/* - * This file is part of the Symfony package. - * - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ /** - * @copyright 2022 Carl Schwan <carl@carlschwan.eu> - * - * @author Carl Schwan <carl@carlschwan.eu> - * @author Fabien Potencier <fabien@symfony.com> - * - * @license AGPL-3.0-or-later AND MIT - * - * 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/>. + * This file is part of the Symfony package. * + * SPDX-FileCopyrightText: 2022 Nextcloud GmbH and Nextcloud contributors + * SPDX-FileCopyrightText: 2022 Fabien Potencier <fabien@symfony.com> + * SPDX-License-Identifier: AGPL-3.0-or-later AND MIT */ - namespace OC\DB; final class ObjectParameter { diff --git a/lib/private/DB/OracleConnection.php b/lib/private/DB/OracleConnection.php index b7e040965ee..abfb69f129b 100644 --- a/lib/private/DB/OracleConnection.php +++ b/lib/private/DB/OracleConnection.php @@ -1,34 +1,17 @@ <?php + /** - * @copyright Copyright (c) 2016, ownCloud, Inc. - * - * @author Bart Visscher <bartv@thisnet.nl> - * @author Christoph Wurst <christoph@winzerhof-wurst.at> - * @author Joas Schilling <coding@schilljs.com> - * @author Lukas Reschke <lukas@statuscode.ch> - * @author Robin Appelman <robin@icewind.nl> - * @author Roeland Jago Douma <roeland@famdouma.nl> - * - * @license AGPL-3.0 - * - * This code is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License, version 3, - * as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License, version 3, - * along with this program. If not, see <http://www.gnu.org/licenses/> - * + * SPDX-FileCopyrightText: 2016-2024 Nextcloud GmbH and Nextcloud contributors + * SPDX-FileCopyrightText: 2016 ownCloud, Inc. + * SPDX-License-Identifier: AGPL-3.0-only */ namespace OC\DB; class OracleConnection extends Connection { /** * Quote the keys of the array + * @param array<string, string> $data + * @return array<string, string> */ private function quoteKeys(array $data) { $return = []; diff --git a/lib/private/DB/OracleMigrator.php b/lib/private/DB/OracleMigrator.php index 18deb97ec26..c5a7646dbb2 100644 --- a/lib/private/DB/OracleMigrator.php +++ b/lib/private/DB/OracleMigrator.php @@ -1,204 +1,122 @@ <?php + +declare(strict_types=1); /** - * @copyright Copyright (c) 2016, ownCloud, Inc. - * - * @author Christoph Wurst <christoph@winzerhof-wurst.at> - * @author Joas Schilling <coding@schilljs.com> - * @author Jörn Friedrich Dreyer <jfd@butonic.de> - * @author Piotr Mrowczynski <mrow4a@yahoo.com> - * @author Robin Appelman <robin@icewind.nl> - * @author Roeland Jago Douma <roeland@famdouma.nl> - * @author Thomas Müller <thomas.mueller@tmit.eu> - * @author Victor Dubiniuk <dubiniuk@owncloud.com> - * - * @license AGPL-3.0 - * - * This code is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License, version 3, - * as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License, version 3, - * along with this program. If not, see <http://www.gnu.org/licenses/> - * + * SPDX-FileCopyrightText: 2023 Nextcloud GmbH and Nextcloud contributors + * SPDX-FileCopyrightText: 2016 ownCloud, Inc. + * SPDX-License-Identifier: AGPL-3.0-only */ namespace OC\DB; use Doctrine\DBAL\Exception; -use Doctrine\DBAL\Schema\Column; -use Doctrine\DBAL\Schema\ColumnDiff; -use Doctrine\DBAL\Schema\ForeignKeyConstraint; -use Doctrine\DBAL\Schema\Index; use Doctrine\DBAL\Schema\Schema; -use Doctrine\DBAL\Schema\Table; class OracleMigrator extends Migrator { /** - * Quote a column's name but changing the name requires recreating - * the column instance and copying over all properties. - * - * @param Column $column old column - * @return Column new column instance with new name - */ - protected function quoteColumn(Column $column) { - $newColumn = new Column( - $this->connection->quoteIdentifier($column->getName()), - $column->getType() - ); - $newColumn->setAutoincrement($column->getAutoincrement()); - $newColumn->setColumnDefinition($column->getColumnDefinition()); - $newColumn->setComment($column->getComment()); - $newColumn->setDefault($column->getDefault()); - $newColumn->setFixed($column->getFixed()); - $newColumn->setLength($column->getLength()); - $newColumn->setNotnull($column->getNotnull()); - $newColumn->setPrecision($column->getPrecision()); - $newColumn->setScale($column->getScale()); - $newColumn->setUnsigned($column->getUnsigned()); - $newColumn->setPlatformOptions($column->getPlatformOptions()); - $newColumn->setCustomSchemaOptions($column->getPlatformOptions()); - return $newColumn; - } - - /** - * Quote an index's name but changing the name requires recreating - * the index instance and copying over all properties. - * - * @param Index $index old index - * @return Index new index instance with new name - */ - protected function quoteIndex($index) { - return new Index( - //TODO migrate existing uppercase indexes, then $this->connection->quoteIdentifier($index->getName()), - $index->getName(), - array_map(function ($columnName) { - return $this->connection->quoteIdentifier($columnName); - }, $index->getColumns()), - $index->isUnique(), - $index->isPrimary(), - $index->getFlags(), - $index->getOptions() - ); - } - - /** - * Quote an ForeignKeyConstraint's name but changing the name requires recreating - * the ForeignKeyConstraint instance and copying over all properties. - * - * @param ForeignKeyConstraint $fkc old fkc - * @return ForeignKeyConstraint new fkc instance with new name - */ - protected function quoteForeignKeyConstraint($fkc) { - return new ForeignKeyConstraint( - array_map(function ($columnName) { - return $this->connection->quoteIdentifier($columnName); - }, $fkc->getLocalColumns()), - $this->connection->quoteIdentifier($fkc->getForeignTableName()), - array_map(function ($columnName) { - return $this->connection->quoteIdentifier($columnName); - }, $fkc->getForeignColumns()), - $fkc->getName(), - $fkc->getOptions() - ); - } - - /** * @param Schema $targetSchema * @param \Doctrine\DBAL\Connection $connection * @return \Doctrine\DBAL\Schema\SchemaDiff * @throws Exception */ - protected function getDiff(Schema $targetSchema, \Doctrine\DBAL\Connection $connection) { - $schemaDiff = parent::getDiff($targetSchema, $connection); - + protected function getDiff(Schema $targetSchema, \Doctrine\DBAL\Connection $connection): \Doctrine\DBAL\Schema\SchemaDiff { // oracle forces us to quote the identifiers - $schemaDiff->newTables = array_map(function (Table $table) { - return new Table( + $quotedSchema = new Schema(); + foreach ($targetSchema->getTables() as $table) { + $quotedTable = $quotedSchema->createTable( $this->connection->quoteIdentifier($table->getName()), - array_map(function (Column $column) { - return $this->quoteColumn($column); - }, $table->getColumns()), - array_map(function (Index $index) { - return $this->quoteIndex($index); - }, $table->getIndexes()), - [], - array_map(function (ForeignKeyConstraint $fck) { - return $this->quoteForeignKeyConstraint($fck); - }, $table->getForeignKeys()), - $table->getOptions() ); - }, $schemaDiff->newTables); - $schemaDiff->removedTables = array_map(function (Table $table) { - return new Table( - $this->connection->quoteIdentifier($table->getName()), - $table->getColumns(), - $table->getIndexes(), - [], - $table->getForeignKeys(), - $table->getOptions() - ); - }, $schemaDiff->removedTables); - - foreach ($schemaDiff->changedTables as $tableDiff) { - $tableDiff->name = $this->connection->quoteIdentifier($tableDiff->name); - - $tableDiff->addedColumns = array_map(function (Column $column) { - return $this->quoteColumn($column); - }, $tableDiff->addedColumns); - - foreach ($tableDiff->changedColumns as $column) { - $column->oldColumnName = $this->connection->quoteIdentifier($column->oldColumnName); - // auto increment is not relevant for oracle and can anyhow not be applied on change - $column->changedProperties = array_diff($column->changedProperties, ['autoincrement', 'unsigned']); + foreach ($table->getColumns() as $column) { + $newColumn = $quotedTable->addColumn( + $this->connection->quoteIdentifier($column->getName()), + $column->getType()->getTypeRegistry()->lookupName($column->getType()), + ); + $newColumn->setAutoincrement($column->getAutoincrement()); + $newColumn->setColumnDefinition($column->getColumnDefinition()); + $newColumn->setComment($column->getComment()); + $newColumn->setDefault($column->getDefault()); + $newColumn->setFixed($column->getFixed()); + $newColumn->setLength($column->getLength()); + $newColumn->setNotnull($column->getNotnull()); + $newColumn->setPrecision($column->getPrecision()); + $newColumn->setScale($column->getScale()); + $newColumn->setUnsigned($column->getUnsigned()); + $newColumn->setPlatformOptions($column->getPlatformOptions()); } - // remove columns that no longer have changed (because autoincrement and unsigned are not supported) - $tableDiff->changedColumns = array_filter($tableDiff->changedColumns, function (ColumnDiff $column) { - return count($column->changedProperties) > 0; - }); - - $tableDiff->removedColumns = array_map(function (Column $column) { - return $this->quoteColumn($column); - }, $tableDiff->removedColumns); - - $tableDiff->renamedColumns = array_map(function (Column $column) { - return $this->quoteColumn($column); - }, $tableDiff->renamedColumns); - - $tableDiff->addedIndexes = array_map(function (Index $index) { - return $this->quoteIndex($index); - }, $tableDiff->addedIndexes); - $tableDiff->changedIndexes = array_map(function (Index $index) { - return $this->quoteIndex($index); - }, $tableDiff->changedIndexes); - - $tableDiff->removedIndexes = array_map(function (Index $index) { - return $this->quoteIndex($index); - }, $tableDiff->removedIndexes); + foreach ($table->getIndexes() as $index) { + if ($index->isPrimary()) { + $quotedTable->setPrimaryKey( + array_map(function ($columnName) { + return $this->connection->quoteIdentifier($columnName); + }, $index->getColumns()), + //TODO migrate existing uppercase indexes, then $this->connection->quoteIdentifier($index->getName()), + $index->getName(), + ); + } elseif ($index->isUnique()) { + $quotedTable->addUniqueIndex( + array_map(function ($columnName) { + return $this->connection->quoteIdentifier($columnName); + }, $index->getColumns()), + //TODO migrate existing uppercase indexes, then $this->connection->quoteIdentifier($index->getName()), + $index->getName(), + $index->getOptions(), + ); + } else { + $quotedTable->addIndex( + array_map(function ($columnName) { + return $this->connection->quoteIdentifier($columnName); + }, $index->getColumns()), + //TODO migrate existing uppercase indexes, then $this->connection->quoteIdentifier($index->getName()), + $index->getName(), + $index->getFlags(), + $index->getOptions(), + ); + } + } - $tableDiff->renamedIndexes = array_map(function (Index $index) { - return $this->quoteIndex($index); - }, $tableDiff->renamedIndexes); + foreach ($table->getUniqueConstraints() as $constraint) { + $quotedTable->addUniqueConstraint( + array_map(function ($columnName) { + return $this->connection->quoteIdentifier($columnName); + }, $constraint->getColumns()), + $this->connection->quoteIdentifier($constraint->getName()), + $constraint->getFlags(), + $constraint->getOptions(), + ); + } - $tableDiff->addedForeignKeys = array_map(function (ForeignKeyConstraint $fkc) { - return $this->quoteForeignKeyConstraint($fkc); - }, $tableDiff->addedForeignKeys); + foreach ($table->getForeignKeys() as $foreignKey) { + $quotedTable->addForeignKeyConstraint( + $this->connection->quoteIdentifier($foreignKey->getForeignTableName()), + array_map(function ($columnName) { + return $this->connection->quoteIdentifier($columnName); + }, $foreignKey->getLocalColumns()), + array_map(function ($columnName) { + return $this->connection->quoteIdentifier($columnName); + }, $foreignKey->getForeignColumns()), + $foreignKey->getOptions(), + $this->connection->quoteIdentifier($foreignKey->getName()), + ); + } - $tableDiff->changedForeignKeys = array_map(function (ForeignKeyConstraint $fkc) { - return $this->quoteForeignKeyConstraint($fkc); - }, $tableDiff->changedForeignKeys); + foreach ($table->getOptions() as $option => $value) { + $quotedTable->addOption( + $option, + $value, + ); + } + } - $tableDiff->removedForeignKeys = array_map(function (ForeignKeyConstraint $fkc) { - return $this->quoteForeignKeyConstraint($fkc); - }, $tableDiff->removedForeignKeys); + foreach ($targetSchema->getSequences() as $sequence) { + $quotedSchema->createSequence( + $sequence->getName(), + $sequence->getAllocationSize(), + $sequence->getInitialValue(), + ); } - return $schemaDiff; + return parent::getDiff($quotedSchema, $connection); } /** @@ -206,7 +124,7 @@ class OracleMigrator extends Migrator { * @return string */ protected function convertStatementToScript($statement) { - if (substr($statement, -1) === ';') { + if (str_ends_with($statement, ';')) { return $statement . PHP_EOL . '/' . PHP_EOL; } $script = $statement . ';'; diff --git a/lib/private/DB/PgSqlTools.php b/lib/private/DB/PgSqlTools.php index af385eb5136..35e8016191c 100644 --- a/lib/private/DB/PgSqlTools.php +++ b/lib/private/DB/PgSqlTools.php @@ -1,26 +1,9 @@ <?php + /** - * @copyright Copyright (c) 2016, ownCloud, Inc. - * - * @author Andreas Fischer <bantu@owncloud.com> - * @author Christoph Wurst <christoph@winzerhof-wurst.at> - * @author Morris Jobke <hey@morrisjobke.de> - * @author tbelau666 <thomas.belau@gmx.de> - * - * @license AGPL-3.0 - * - * This code is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License, version 3, - * as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License, version 3, - * along with this program. If not, see <http://www.gnu.org/licenses/> - * + * SPDX-FileCopyrightText: 2016-2024 Nextcloud GmbH and Nextcloud contributors + * SPDX-FileCopyrightText: 2016 ownCloud, Inc. + * SPDX-License-Identifier: AGPL-3.0-only */ namespace OC\DB; diff --git a/lib/private/DB/PostgreSqlMigrator.php b/lib/private/DB/PostgreSqlMigrator.php deleted file mode 100644 index 92a0842e1a7..00000000000 --- a/lib/private/DB/PostgreSqlMigrator.php +++ /dev/null @@ -1,55 +0,0 @@ -<?php -/** - * @copyright Copyright (c) 2016, ownCloud, Inc. - * - * @author Thomas Müller <thomas.mueller@tmit.eu> - * - * @license AGPL-3.0 - * - * This code is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License, version 3, - * as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License, version 3, - * along with this program. If not, see <http://www.gnu.org/licenses/> - * - */ -namespace OC\DB; - -use Doctrine\DBAL\Schema\Schema; - -class PostgreSqlMigrator extends Migrator { - /** - * @param Schema $targetSchema - * @param \Doctrine\DBAL\Connection $connection - * @return \Doctrine\DBAL\Schema\SchemaDiff - */ - protected function getDiff(Schema $targetSchema, \Doctrine\DBAL\Connection $connection) { - $schemaDiff = parent::getDiff($targetSchema, $connection); - - foreach ($schemaDiff->changedTables as $tableDiff) { - // fix default value in brackets - pg 9.4 is returning a negative default value in () - // see https://github.com/doctrine/dbal/issues/2427 - foreach ($tableDiff->changedColumns as $column) { - $column->changedProperties = array_filter($column->changedProperties, function ($changedProperties) use ($column) { - if ($changedProperties !== 'default') { - return true; - } - $fromDefault = $column->fromColumn->getDefault(); - $toDefault = $column->column->getDefault(); - $fromDefault = trim((string) $fromDefault, '()'); - - // by intention usage of != - return $fromDefault != $toDefault; - }); - } - } - - return $schemaDiff; - } -} diff --git a/lib/private/DB/PreparedStatement.php b/lib/private/DB/PreparedStatement.php index 849aa379832..5fdfa2b03e8 100644 --- a/lib/private/DB/PreparedStatement.php +++ b/lib/private/DB/PreparedStatement.php @@ -3,25 +3,8 @@ declare(strict_types=1); /** - * @copyright 2021 Christoph Wurst <christoph@winzerhof-wurst.at> - * - * @author Christoph Wurst <christoph@winzerhof-wurst.at> - * - * @license GNU AGPL version 3 or any later version - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as - * published by the Free Software Foundation, either version 3 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - * + * SPDX-FileCopyrightText: 2021 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later */ namespace OC\DB; diff --git a/lib/private/DB/QueryBuilder/CompositeExpression.php b/lib/private/DB/QueryBuilder/CompositeExpression.php index 0c958a96d68..493d804d54d 100644 --- a/lib/private/DB/QueryBuilder/CompositeExpression.php +++ b/lib/private/DB/QueryBuilder/CompositeExpression.php @@ -1,24 +1,9 @@ <?php + /** - * @copyright Copyright (c) 2016, ownCloud, Inc. - * - * @author Joas Schilling <coding@schilljs.com> - * @author Roeland Jago Douma <roeland@famdouma.nl> - * - * @license AGPL-3.0 - * - * This code is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License, version 3, - * as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License, version 3, - * along with this program. If not, see <http://www.gnu.org/licenses/> - * + * SPDX-FileCopyrightText: 2016-2024 Nextcloud GmbH and Nextcloud contributors + * SPDX-FileCopyrightText: 2016 ownCloud, Inc. + * SPDX-License-Identifier: AGPL-3.0-only */ namespace OC\DB\QueryBuilder; diff --git a/lib/private/DB/QueryBuilder/ExpressionBuilder/ExpressionBuilder.php b/lib/private/DB/QueryBuilder/ExpressionBuilder/ExpressionBuilder.php index ae4f19f5d18..a7af00b9f97 100644 --- a/lib/private/DB/QueryBuilder/ExpressionBuilder/ExpressionBuilder.php +++ b/lib/private/DB/QueryBuilder/ExpressionBuilder/ExpressionBuilder.php @@ -1,28 +1,9 @@ <?php + /** - * @copyright Copyright (c) 2016, ownCloud, Inc. - * - * @author Christoph Wurst <christoph@winzerhof-wurst.at> - * @author Daniel Kesselberg <mail@danielkesselberg.de> - * @author Joas Schilling <coding@schilljs.com> - * @author Robin Appelman <robin@icewind.nl> - * @author Roeland Jago Douma <roeland@famdouma.nl> - * @author Thomas Müller <thomas.mueller@tmit.eu> - * - * @license AGPL-3.0 - * - * This code is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License, version 3, - * as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License, version 3, - * along with this program. If not, see <http://www.gnu.org/licenses/> - * + * SPDX-FileCopyrightText: 2016-2024 Nextcloud GmbH and Nextcloud contributors + * SPDX-FileCopyrightText: 2016 ownCloud, Inc. + * SPDX-License-Identifier: AGPL-3.0-only */ namespace OC\DB\QueryBuilder\ExpressionBuilder; @@ -117,8 +98,8 @@ class ExpressionBuilder implements IExpressionBuilder { * @return string */ public function comparison($x, string $operator, $y, $type = null): string { - $x = $this->helper->quoteColumnName($x); - $y = $this->helper->quoteColumnName($y); + $x = $this->prepareColumn($x, $type); + $y = $this->prepareColumn($y, $type); return $this->expressionBuilder->comparison($x, $operator, $y); } @@ -140,8 +121,8 @@ class ExpressionBuilder implements IExpressionBuilder { * @return string */ public function eq($x, $y, $type = null): string { - $x = $this->helper->quoteColumnName($x); - $y = $this->helper->quoteColumnName($y); + $x = $this->prepareColumn($x, $type); + $y = $this->prepareColumn($y, $type); return $this->expressionBuilder->eq($x, $y); } @@ -162,8 +143,8 @@ class ExpressionBuilder implements IExpressionBuilder { * @return string */ public function neq($x, $y, $type = null): string { - $x = $this->helper->quoteColumnName($x); - $y = $this->helper->quoteColumnName($y); + $x = $this->prepareColumn($x, $type); + $y = $this->prepareColumn($y, $type); return $this->expressionBuilder->neq($x, $y); } @@ -184,8 +165,8 @@ class ExpressionBuilder implements IExpressionBuilder { * @return string */ public function lt($x, $y, $type = null): string { - $x = $this->helper->quoteColumnName($x); - $y = $this->helper->quoteColumnName($y); + $x = $this->prepareColumn($x, $type); + $y = $this->prepareColumn($y, $type); return $this->expressionBuilder->lt($x, $y); } @@ -206,8 +187,8 @@ class ExpressionBuilder implements IExpressionBuilder { * @return string */ public function lte($x, $y, $type = null): string { - $x = $this->helper->quoteColumnName($x); - $y = $this->helper->quoteColumnName($y); + $x = $this->prepareColumn($x, $type); + $y = $this->prepareColumn($y, $type); return $this->expressionBuilder->lte($x, $y); } @@ -228,8 +209,8 @@ class ExpressionBuilder implements IExpressionBuilder { * @return string */ public function gt($x, $y, $type = null): string { - $x = $this->helper->quoteColumnName($x); - $y = $this->helper->quoteColumnName($y); + $x = $this->prepareColumn($x, $type); + $y = $this->prepareColumn($y, $type); return $this->expressionBuilder->gt($x, $y); } @@ -250,8 +231,8 @@ class ExpressionBuilder implements IExpressionBuilder { * @return string */ public function gte($x, $y, $type = null): string { - $x = $this->helper->quoteColumnName($x); - $y = $this->helper->quoteColumnName($y); + $x = $this->prepareColumn($x, $type); + $y = $this->prepareColumn($y, $type); return $this->expressionBuilder->gte($x, $y); } @@ -435,4 +416,13 @@ class ExpressionBuilder implements IExpressionBuilder { $this->helper->quoteColumnName($column) ); } + + /** + * @param mixed $column + * @param mixed|null $type + * @return array|IQueryFunction|string + */ + protected function prepareColumn($column, $type) { + return $this->helper->quoteColumnNames($column); + } } diff --git a/lib/private/DB/QueryBuilder/ExpressionBuilder/MySqlExpressionBuilder.php b/lib/private/DB/QueryBuilder/ExpressionBuilder/MySqlExpressionBuilder.php index 618487e0f71..cd2fb5a5cc3 100644 --- a/lib/private/DB/QueryBuilder/ExpressionBuilder/MySqlExpressionBuilder.php +++ b/lib/private/DB/QueryBuilder/ExpressionBuilder/MySqlExpressionBuilder.php @@ -1,27 +1,9 @@ <?php + /** - * @copyright Copyright (c) 2016, ownCloud, Inc. - * - * @author Christoph Wurst <christoph@winzerhof-wurst.at> - * @author Joas Schilling <coding@schilljs.com> - * @author Robin Appelman <robin@icewind.nl> - * @author Roeland Jago Douma <roeland@famdouma.nl> - * @author Thomas Müller <thomas.mueller@tmit.eu> - * - * @license AGPL-3.0 - * - * This code is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License, version 3, - * as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License, version 3, - * along with this program. If not, see <http://www.gnu.org/licenses/> - * + * SPDX-FileCopyrightText: 2016-2024 Nextcloud GmbH and Nextcloud contributors + * SPDX-FileCopyrightText: 2016 ownCloud, Inc. + * SPDX-License-Identifier: AGPL-3.0-only */ namespace OC\DB\QueryBuilder\ExpressionBuilder; diff --git a/lib/private/DB/QueryBuilder/ExpressionBuilder/OCIExpressionBuilder.php b/lib/private/DB/QueryBuilder/ExpressionBuilder/OCIExpressionBuilder.php index caeb8009885..542e8d62ede 100644 --- a/lib/private/DB/QueryBuilder/ExpressionBuilder/OCIExpressionBuilder.php +++ b/lib/private/DB/QueryBuilder/ExpressionBuilder/OCIExpressionBuilder.php @@ -1,26 +1,9 @@ <?php + /** - * @copyright Copyright (c) 2016, ownCloud, Inc. - * - * @author Joas Schilling <coding@schilljs.com> - * @author Robin Appelman <robin@icewind.nl> - * @author Roeland Jago Douma <roeland@famdouma.nl> - * @author Thomas Müller <thomas.mueller@tmit.eu> - * - * @license AGPL-3.0 - * - * This code is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License, version 3, - * as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License, version 3, - * along with this program. If not, see <http://www.gnu.org/licenses/> - * + * SPDX-FileCopyrightText: 2016-2024 Nextcloud GmbH and Nextcloud contributors + * SPDX-FileCopyrightText: 2016 ownCloud, Inc. + * SPDX-License-Identifier: AGPL-3.0-only */ namespace OC\DB\QueryBuilder\ExpressionBuilder; @@ -39,80 +22,9 @@ class OCIExpressionBuilder extends ExpressionBuilder { protected function prepareColumn($column, $type) { if ($type === IQueryBuilder::PARAM_STR && !is_array($column) && !($column instanceof IParameter) && !($column instanceof ILiteral)) { $column = $this->castColumn($column, $type); - } else { - $column = $this->helper->quoteColumnNames($column); } - return $column; - } - - /** - * @inheritdoc - */ - public function comparison($x, string $operator, $y, $type = null): string { - $x = $this->prepareColumn($x, $type); - $y = $this->prepareColumn($y, $type); - - return $this->expressionBuilder->comparison($x, $operator, $y); - } - - /** - * @inheritdoc - */ - public function eq($x, $y, $type = null): string { - $x = $this->prepareColumn($x, $type); - $y = $this->prepareColumn($y, $type); - - return $this->expressionBuilder->eq($x, $y); - } - - /** - * @inheritdoc - */ - public function neq($x, $y, $type = null): string { - $x = $this->prepareColumn($x, $type); - $y = $this->prepareColumn($y, $type); - - return $this->expressionBuilder->neq($x, $y); - } - - /** - * @inheritdoc - */ - public function lt($x, $y, $type = null): string { - $x = $this->prepareColumn($x, $type); - $y = $this->prepareColumn($y, $type); - - return $this->expressionBuilder->lt($x, $y); - } - - /** - * @inheritdoc - */ - public function lte($x, $y, $type = null): string { - $x = $this->prepareColumn($x, $type); - $y = $this->prepareColumn($y, $type); - - return $this->expressionBuilder->lte($x, $y); - } - - /** - * @inheritdoc - */ - public function gt($x, $y, $type = null): string { - $x = $this->prepareColumn($x, $type); - $y = $this->prepareColumn($y, $type); - - return $this->expressionBuilder->gt($x, $y); - } - - /** - * @inheritdoc - */ - public function gte($x, $y, $type = null): string { - $x = $this->prepareColumn($x, $type); - $y = $this->prepareColumn($y, $type); - return $this->expressionBuilder->gte($x, $y); + return parent::prepareColumn($column, $type); } /** diff --git a/lib/private/DB/QueryBuilder/ExpressionBuilder/PgSqlExpressionBuilder.php b/lib/private/DB/QueryBuilder/ExpressionBuilder/PgSqlExpressionBuilder.php index 03b58e222f3..1a162f04a49 100644 --- a/lib/private/DB/QueryBuilder/ExpressionBuilder/PgSqlExpressionBuilder.php +++ b/lib/private/DB/QueryBuilder/ExpressionBuilder/PgSqlExpressionBuilder.php @@ -1,26 +1,9 @@ <?php + /** - * @copyright Copyright (c) 2016, ownCloud, Inc. - * - * @author Joas Schilling <coding@schilljs.com> - * @author Robin Appelman <robin@icewind.nl> - * @author Roeland Jago Douma <roeland@famdouma.nl> - * @author Thomas Müller <thomas.mueller@tmit.eu> - * - * @license AGPL-3.0 - * - * This code is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License, version 3, - * as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License, version 3, - * along with this program. If not, see <http://www.gnu.org/licenses/> - * + * SPDX-FileCopyrightText: 2016-2024 Nextcloud GmbH and Nextcloud contributors + * SPDX-FileCopyrightText: 2016 ownCloud, Inc. + * SPDX-License-Identifier: AGPL-3.0-only */ namespace OC\DB\QueryBuilder\ExpressionBuilder; diff --git a/lib/private/DB/QueryBuilder/ExpressionBuilder/SqliteExpressionBuilder.php b/lib/private/DB/QueryBuilder/ExpressionBuilder/SqliteExpressionBuilder.php index 289aa09b003..ac4698cdc76 100644 --- a/lib/private/DB/QueryBuilder/ExpressionBuilder/SqliteExpressionBuilder.php +++ b/lib/private/DB/QueryBuilder/ExpressionBuilder/SqliteExpressionBuilder.php @@ -1,28 +1,16 @@ <?php /** - * @copyright Copyright (c) 2017 Robin Appelman <robin@icewind.nl> - * - * @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/>. - * + * SPDX-FileCopyrightText: 2017 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later */ namespace OC\DB\QueryBuilder\ExpressionBuilder; +use OC\DB\QueryBuilder\QueryFunction; +use OCP\DB\QueryBuilder\ILiteral; +use OCP\DB\QueryBuilder\IParameter; +use OCP\DB\QueryBuilder\IQueryBuilder; +use OCP\DB\QueryBuilder\IQueryFunction; + class SqliteExpressionBuilder extends ExpressionBuilder { /** * @inheritdoc @@ -34,4 +22,33 @@ class SqliteExpressionBuilder extends ExpressionBuilder { public function iLike($x, $y, $type = null): string { return $this->like($this->functionBuilder->lower($x), $this->functionBuilder->lower($y), $type); } + + /** + * @param mixed $column + * @param mixed|null $type + * @return array|IQueryFunction|string + */ + protected function prepareColumn($column, $type) { + if ($type === IQueryBuilder::PARAM_DATE && !is_array($column) && !($column instanceof IParameter) && !($column instanceof ILiteral)) { + return $this->castColumn($column, $type); + } + + return parent::prepareColumn($column, $type); + } + + /** + * Returns a IQueryFunction that casts the column to the given type + * + * @param string $column + * @param mixed $type One of IQueryBuilder::PARAM_* + * @return IQueryFunction + */ + public function castColumn($column, $type): IQueryFunction { + if ($type === IQueryBuilder::PARAM_DATE) { + $column = $this->helper->quoteColumnName($column); + return new QueryFunction('DATETIME(' . $column . ')'); + } + + return parent::castColumn($column, $type); + } } diff --git a/lib/private/DB/QueryBuilder/FunctionBuilder/FunctionBuilder.php b/lib/private/DB/QueryBuilder/FunctionBuilder/FunctionBuilder.php index e0a7549a0ad..b168d2c1a84 100644 --- a/lib/private/DB/QueryBuilder/FunctionBuilder/FunctionBuilder.php +++ b/lib/private/DB/QueryBuilder/FunctionBuilder/FunctionBuilder.php @@ -1,25 +1,7 @@ <?php /** - * @copyright Copyright (c) 2017 Robin Appelman <robin@icewind.nl> - * - * @author Joas Schilling <coding@schilljs.com> - * @author Robin Appelman <robin@icewind.nl> - * - * @license GNU AGPL version 3 or any later version - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as - * published by the Free Software Foundation, either version 3 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - * + * SPDX-FileCopyrightText: 2017 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later */ namespace OC\DB\QueryBuilder\FunctionBuilder; diff --git a/lib/private/DB/QueryBuilder/FunctionBuilder/OCIFunctionBuilder.php b/lib/private/DB/QueryBuilder/FunctionBuilder/OCIFunctionBuilder.php index a9844ec3373..d0258eafea8 100644 --- a/lib/private/DB/QueryBuilder/FunctionBuilder/OCIFunctionBuilder.php +++ b/lib/private/DB/QueryBuilder/FunctionBuilder/OCIFunctionBuilder.php @@ -1,25 +1,7 @@ <?php /** - * @copyright Copyright (c) 2017 Robin Appelman <robin@icewind.nl> - * - * @author Joas Schilling <coding@schilljs.com> - * @author Robin Appelman <robin@icewind.nl> - * - * @license GNU AGPL version 3 or any later version - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as - * published by the Free Software Foundation, either version 3 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - * + * SPDX-FileCopyrightText: 2017 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later */ namespace OC\DB\QueryBuilder\FunctionBuilder; diff --git a/lib/private/DB/QueryBuilder/FunctionBuilder/PgSqlFunctionBuilder.php b/lib/private/DB/QueryBuilder/FunctionBuilder/PgSqlFunctionBuilder.php index 444f6aa83a4..ee430a6bd71 100644 --- a/lib/private/DB/QueryBuilder/FunctionBuilder/PgSqlFunctionBuilder.php +++ b/lib/private/DB/QueryBuilder/FunctionBuilder/PgSqlFunctionBuilder.php @@ -1,25 +1,7 @@ <?php /** - * @copyright Copyright (c) 2017 Robin Appelman <robin@icewind.nl> - * - * @author Joas Schilling <coding@schilljs.com> - * @author Robin Appelman <robin@icewind.nl> - * - * @license GNU AGPL version 3 or any later version - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as - * published by the Free Software Foundation, either version 3 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - * + * SPDX-FileCopyrightText: 2017 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later */ namespace OC\DB\QueryBuilder\FunctionBuilder; diff --git a/lib/private/DB/QueryBuilder/FunctionBuilder/SqliteFunctionBuilder.php b/lib/private/DB/QueryBuilder/FunctionBuilder/SqliteFunctionBuilder.php index bb97d2e29f9..956b2123f2c 100644 --- a/lib/private/DB/QueryBuilder/FunctionBuilder/SqliteFunctionBuilder.php +++ b/lib/private/DB/QueryBuilder/FunctionBuilder/SqliteFunctionBuilder.php @@ -1,25 +1,7 @@ <?php /** - * @copyright Copyright (c) 2017 Robin Appelman <robin@icewind.nl> - * - * @author Joas Schilling <coding@schilljs.com> - * @author Robin Appelman <robin@icewind.nl> - * - * @license GNU AGPL version 3 or any later version - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as - * published by the Free Software Foundation, either version 3 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - * + * SPDX-FileCopyrightText: 2017 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later */ namespace OC\DB\QueryBuilder\FunctionBuilder; diff --git a/lib/private/DB/QueryBuilder/Literal.php b/lib/private/DB/QueryBuilder/Literal.php index b4e8a36b86f..f0accce1d93 100644 --- a/lib/private/DB/QueryBuilder/Literal.php +++ b/lib/private/DB/QueryBuilder/Literal.php @@ -1,24 +1,9 @@ <?php + /** - * @copyright Copyright (c) 2016, ownCloud, Inc. - * - * @author Christoph Wurst <christoph@winzerhof-wurst.at> - * @author Joas Schilling <coding@schilljs.com> - * - * @license AGPL-3.0 - * - * This code is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License, version 3, - * as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License, version 3, - * along with this program. If not, see <http://www.gnu.org/licenses/> - * + * SPDX-FileCopyrightText: 2016-2024 Nextcloud GmbH and Nextcloud contributors + * SPDX-FileCopyrightText: 2016 ownCloud, Inc. + * SPDX-License-Identifier: AGPL-3.0-only */ namespace OC\DB\QueryBuilder; @@ -32,10 +17,7 @@ class Literal implements ILiteral { $this->literal = $literal; } - /** - * @return string - */ - public function __toString() { + public function __toString(): string { return (string) $this->literal; } } diff --git a/lib/private/DB/QueryBuilder/Parameter.php b/lib/private/DB/QueryBuilder/Parameter.php index b6cfa844e01..dbd723639fc 100644 --- a/lib/private/DB/QueryBuilder/Parameter.php +++ b/lib/private/DB/QueryBuilder/Parameter.php @@ -1,23 +1,9 @@ <?php + /** - * @copyright Copyright (c) 2016, ownCloud, Inc. - * - * @author Joas Schilling <coding@schilljs.com> - * - * @license AGPL-3.0 - * - * This code is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License, version 3, - * as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License, version 3, - * along with this program. If not, see <http://www.gnu.org/licenses/> - * + * SPDX-FileCopyrightText: 2016-2024 Nextcloud GmbH and Nextcloud contributors + * SPDX-FileCopyrightText: 2016 ownCloud, Inc. + * SPDX-License-Identifier: AGPL-3.0-only */ namespace OC\DB\QueryBuilder; @@ -31,10 +17,7 @@ class Parameter implements IParameter { $this->name = $name; } - /** - * @return string - */ - public function __toString() { + public function __toString(): string { return (string) $this->name; } } diff --git a/lib/private/DB/QueryBuilder/QueryBuilder.php b/lib/private/DB/QueryBuilder/QueryBuilder.php index 2f97b4a146c..0e7d8d2ff3e 100644 --- a/lib/private/DB/QueryBuilder/QueryBuilder.php +++ b/lib/private/DB/QueryBuilder/QueryBuilder.php @@ -1,32 +1,9 @@ <?php + /** - * @copyright Copyright (c) 2016, ownCloud, Inc. - * - * @author Arthur Schiwon <blizzz@arthur-schiwon.de> - * @author Christoph Wurst <christoph@winzerhof-wurst.at> - * @author Daniel Kesselberg <mail@danielkesselberg.de> - * @author J0WI <J0WI@users.noreply.github.com> - * @author Joas Schilling <coding@schilljs.com> - * @author Lukas Reschke <lukas@statuscode.ch> - * @author Robin Appelman <robin@icewind.nl> - * @author Roeland Jago Douma <roeland@famdouma.nl> - * @author Thomas Müller <thomas.mueller@tmit.eu> - * @author Vincent Petry <vincent@nextcloud.com> - * - * @license AGPL-3.0 - * - * This code is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License, version 3, - * as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License, version 3, - * along with this program. If not, see <http://www.gnu.org/licenses/> - * + * SPDX-FileCopyrightText: 2016-2024 Nextcloud GmbH and Nextcloud contributors + * SPDX-FileCopyrightText: 2016 ownCloud, Inc. + * SPDX-License-Identifier: AGPL-3.0-only */ namespace OC\DB\QueryBuilder; @@ -302,21 +279,6 @@ class QueryBuilder implements IQueryBuilder { throw new \RuntimeException('Invalid return type for query'); } - /** - * Monkey-patched compatibility layer for apps that were adapted for Nextcloud 22 before - * the first beta, where executeStatement was named executeUpdate. - * - * Static analysis should catch those misuses, but until then let's try to keep things - * running. - * - * @internal - * @deprecated - * @todo drop ASAP - */ - public function executeUpdate(): int { - return $this->executeStatement(); - } - public function executeStatement(): int { if ($this->getType() === \Doctrine\DBAL\Query\QueryBuilder::SELECT) { throw new \RuntimeException('Invalid query type, expected INSERT, DELETE or UPDATE statement'); @@ -866,7 +828,7 @@ class QueryBuilder implements IQueryBuilder { public function where(...$predicates) { if ($this->getQueryPart('where') !== null && $this->systemConfig->getValue('debug', false)) { // Only logging a warning, not throwing for now. - $e = new QueryException('Using where() on non-empty WHERE part, please verify it is intentional to not call whereAnd() or whereOr() instead. Otherwise consider creating a new query builder object or call resetQueryPart(\'where\') first.'); + $e = new QueryException('Using where() on non-empty WHERE part, please verify it is intentional to not call andWhere() or orWhere() instead. Otherwise consider creating a new query builder object or call resetQueryPart(\'where\') first.'); $this->logger->warning($e->getMessage(), ['exception' => $e]); } @@ -975,14 +937,10 @@ class QueryBuilder implements IQueryBuilder { * * @return $this This QueryBuilder instance. */ - public function addGroupBy(...$groupBys) { - if (count($groupBys) === 1 && is_array($groupBys[0])) { - $$groupBys = $groupBys[0]; - } - + public function addGroupBy(...$groupBy) { call_user_func_array( [$this->queryBuilder, 'addGroupBy'], - $this->helper->quoteColumnNames($groupBys) + $this->helper->quoteColumnNames($groupBy) ); return $this; @@ -1202,7 +1160,7 @@ class QueryBuilder implements IQueryBuilder { * @link http://www.zetacomponents.org * * @param mixed $value - * @param mixed $type + * @param IQueryBuilder::PARAM_* $type * @param string $placeHolder The name to bind with. The string must start with a colon ':'. * * @return IParameter the placeholder name used. @@ -1229,7 +1187,7 @@ class QueryBuilder implements IQueryBuilder { * </code> * * @param mixed $value - * @param integer $type + * @param IQueryBuilder::PARAM_* $type * * @return IParameter */ diff --git a/lib/private/DB/QueryBuilder/QueryFunction.php b/lib/private/DB/QueryBuilder/QueryFunction.php index 41798f233cd..9cdd6c31c7b 100644 --- a/lib/private/DB/QueryBuilder/QueryFunction.php +++ b/lib/private/DB/QueryBuilder/QueryFunction.php @@ -1,23 +1,9 @@ <?php + /** - * @copyright Copyright (c) 2016, ownCloud, Inc. - * - * @author Joas Schilling <coding@schilljs.com> - * - * @license AGPL-3.0 - * - * This code is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License, version 3, - * as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License, version 3, - * along with this program. If not, see <http://www.gnu.org/licenses/> - * + * SPDX-FileCopyrightText: 2016-2024 Nextcloud GmbH and Nextcloud contributors + * SPDX-FileCopyrightText: 2016 ownCloud, Inc. + * SPDX-License-Identifier: AGPL-3.0-only */ namespace OC\DB\QueryBuilder; @@ -31,10 +17,7 @@ class QueryFunction implements IQueryFunction { $this->function = $function; } - /** - * @return string - */ - public function __toString() { + public function __toString(): string { return (string) $this->function; } } diff --git a/lib/private/DB/QueryBuilder/QuoteHelper.php b/lib/private/DB/QueryBuilder/QuoteHelper.php index 727bb923d9c..a60a9731aa2 100644 --- a/lib/private/DB/QueryBuilder/QuoteHelper.php +++ b/lib/private/DB/QueryBuilder/QuoteHelper.php @@ -1,24 +1,9 @@ <?php + /** - * @copyright Copyright (c) 2016, ownCloud, Inc. - * - * @author Joas Schilling <coding@schilljs.com> - * @author Robin Appelman <robin@icewind.nl> - * - * @license AGPL-3.0 - * - * This code is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License, version 3, - * as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License, version 3, - * along with this program. If not, see <http://www.gnu.org/licenses/> - * + * SPDX-FileCopyrightText: 2016-2024 Nextcloud GmbH and Nextcloud contributors + * SPDX-FileCopyrightText: 2016 ownCloud, Inc. + * SPDX-License-Identifier: AGPL-3.0-only */ namespace OC\DB\QueryBuilder; diff --git a/lib/private/DB/ResultAdapter.php b/lib/private/DB/ResultAdapter.php index 63881b71e7d..8b004d471ec 100644 --- a/lib/private/DB/ResultAdapter.php +++ b/lib/private/DB/ResultAdapter.php @@ -3,25 +3,8 @@ declare(strict_types=1); /** - * @copyright 2021 Christoph Wurst <christoph@winzerhof-wurst.at> - * - * @author Christoph Wurst <christoph@winzerhof-wurst.at> - * - * @license GNU AGPL version 3 or any later version - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as - * published by the Free Software Foundation, either version 3 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - * + * SPDX-FileCopyrightText: 2021 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later */ namespace OC\DB; diff --git a/lib/private/DB/SQLiteMigrator.php b/lib/private/DB/SQLiteMigrator.php index cbb39070a48..6be17625476 100644 --- a/lib/private/DB/SQLiteMigrator.php +++ b/lib/private/DB/SQLiteMigrator.php @@ -1,31 +1,13 @@ <?php + /** - * @copyright Copyright (c) 2016, ownCloud, Inc. - * - * @author Robin Appelman <robin@icewind.nl> - * @author Thomas Müller <thomas.mueller@tmit.eu> - * @author Victor Dubiniuk <dubiniuk@owncloud.com> - * - * @license AGPL-3.0 - * - * This code is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License, version 3, - * as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License, version 3, - * along with this program. If not, see <http://www.gnu.org/licenses/> - * + * SPDX-FileCopyrightText: 2016-2024 Nextcloud GmbH and Nextcloud contributors + * SPDX-FileCopyrightText: 2016 ownCloud, Inc. + * SPDX-License-Identifier: AGPL-3.0-only */ namespace OC\DB; use Doctrine\DBAL\Schema\Schema; -use Doctrine\DBAL\Types\BigIntType; -use Doctrine\DBAL\Types\Type; class SQLiteMigrator extends Migrator { /** @@ -34,21 +16,12 @@ class SQLiteMigrator extends Migrator { * @return \Doctrine\DBAL\Schema\SchemaDiff */ protected function getDiff(Schema $targetSchema, \Doctrine\DBAL\Connection $connection) { - $platform = $connection->getDatabasePlatform(); - $platform->registerDoctrineTypeMapping('tinyint unsigned', 'integer'); - $platform->registerDoctrineTypeMapping('smallint unsigned', 'integer'); - $platform->registerDoctrineTypeMapping('varchar ', 'string'); - foreach ($targetSchema->getTables() as $table) { foreach ($table->getColumns() as $column) { // column comments are not supported on SQLite if ($column->getComment() !== null) { $column->setComment(null); } - // with sqlite autoincrement columns is of type integer - if ($column->getType() instanceof BigIntType && $column->getAutoincrement()) { - $column->setType(Type::getType('integer')); - } } } diff --git a/lib/private/DB/SQLiteSessionInit.php b/lib/private/DB/SQLiteSessionInit.php index 89f7e03c5f2..5fe0cb3abf6 100644 --- a/lib/private/DB/SQLiteSessionInit.php +++ b/lib/private/DB/SQLiteSessionInit.php @@ -1,27 +1,9 @@ <?php + /** - * @copyright Copyright (c) 2016, ownCloud, Inc. - * - * @author Christoph Wurst <christoph@winzerhof-wurst.at> - * @author Morris Jobke <hey@morrisjobke.de> - * @author Robin Appelman <robin@icewind.nl> - * @author Roeland Jago Douma <roeland@famdouma.nl> - * @author Thomas Müller <thomas.mueller@tmit.eu> - * - * @license AGPL-3.0 - * - * This code is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License, version 3, - * as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License, version 3, - * along with this program. If not, see <http://www.gnu.org/licenses/> - * + * SPDX-FileCopyrightText: 2016-2024 Nextcloud GmbH and Nextcloud contributors + * SPDX-FileCopyrightText: 2016 ownCloud, Inc. + * SPDX-License-Identifier: AGPL-3.0-only */ namespace OC\DB; diff --git a/lib/private/DB/SchemaWrapper.php b/lib/private/DB/SchemaWrapper.php index 9dae9ab6248..8ff952b8710 100644 --- a/lib/private/DB/SchemaWrapper.php +++ b/lib/private/DB/SchemaWrapper.php @@ -1,25 +1,7 @@ <?php /** - * @copyright Copyright (c) 2017 Joas Schilling <coding@schilljs.com> - * - * @author Christoph Wurst <christoph@winzerhof-wurst.at> - * @author Joas Schilling <coding@schilljs.com> - * - * @license GNU AGPL version 3 or any later version - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as - * published by the Free Software Foundation, either version 3 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - * + * SPDX-FileCopyrightText: 2017 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later */ namespace OC\DB; diff --git a/lib/private/DB/SetTransactionIsolationLevel.php b/lib/private/DB/SetTransactionIsolationLevel.php index b067edde441..cd18c4255e3 100644 --- a/lib/private/DB/SetTransactionIsolationLevel.php +++ b/lib/private/DB/SetTransactionIsolationLevel.php @@ -3,31 +3,16 @@ declare(strict_types=1); /** - * @copyright Copyright (c) 2019 Daniel Kesselberg <mail@danielkesselberg.de> - * - * @author Daniel Kesselberg <mail@danielkesselberg.de> - * - * @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/>. - * + * SPDX-FileCopyrightText: 2019 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later */ namespace OC\DB; use Doctrine\Common\EventSubscriber; +use Doctrine\DBAL\Connections\PrimaryReadReplicaConnection; use Doctrine\DBAL\Event\ConnectionEventArgs; use Doctrine\DBAL\Events; +use Doctrine\DBAL\Platforms\MySQLPlatform; use Doctrine\DBAL\TransactionIsolationLevel; class SetTransactionIsolationLevel implements EventSubscriber { @@ -36,7 +21,13 @@ class SetTransactionIsolationLevel implements EventSubscriber { * @return void */ public function postConnect(ConnectionEventArgs $args) { - $args->getConnection()->setTransactionIsolation(TransactionIsolationLevel::READ_COMMITTED); + $connection = $args->getConnection(); + if ($connection instanceof PrimaryReadReplicaConnection && $connection->isConnectedToPrimary()) { + $connection->setTransactionIsolation(TransactionIsolationLevel::READ_COMMITTED); + if ($connection->getDatabasePlatform() instanceof MySQLPlatform) { + $connection->executeStatement('SET SESSION AUTOCOMMIT=1'); + } + } } public function getSubscribedEvents() { diff --git a/lib/private/Dashboard/Manager.php b/lib/private/Dashboard/Manager.php index 18a66499167..45038b87f90 100644 --- a/lib/private/Dashboard/Manager.php +++ b/lib/private/Dashboard/Manager.php @@ -3,26 +3,8 @@ declare(strict_types=1); /** - * @copyright Copyright (c) 2020 Julius Härtl <jus@bitgrid.net> - * - * @author Joas Schilling <coding@schilljs.com> - * @author Julius Härtl <jus@bitgrid.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/>. - * + * SPDX-FileCopyrightText: 2020 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later */ namespace OC\Dashboard; @@ -33,15 +15,15 @@ use OCP\Dashboard\IManager; use OCP\Dashboard\IWidget; use Psr\Container\ContainerExceptionInterface; use Psr\Container\ContainerInterface; -use Throwable; use Psr\Log\LoggerInterface; +use Throwable; class Manager implements IManager { /** @var array */ private $lazyWidgets = []; - /** @var IWidget[] */ - private $widgets = []; + /** @var array<string, IWidget> */ + private array $widgets = []; private ContainerInterface $serverContainer; private ?IAppManager $appManager = null; @@ -115,7 +97,7 @@ class Manager implements IManager { $endTime = microtime(true); $duration = $endTime - $startTime; if ($duration > 1) { - \OC::$server->get(LoggerInterface::class)->error( + \OC::$server->get(LoggerInterface::class)->info( 'Dashboard widget {widget} took {duration} seconds to load.', [ 'widget' => $widget->getId(), @@ -134,6 +116,9 @@ class Manager implements IManager { $this->lazyWidgets = []; } + /** + * @return array<string, IWidget> + */ public function getWidgets(): array { $this->loadLazyPanels(); return $this->widgets; diff --git a/lib/private/DatabaseException.php b/lib/private/DatabaseException.php index 88287003af9..c0bb3eb7171 100644 --- a/lib/private/DatabaseException.php +++ b/lib/private/DatabaseException.php @@ -1,24 +1,9 @@ <?php + /** - * @copyright Copyright (c) 2016, ownCloud, Inc. - * - * @author Joas Schilling <coding@schilljs.com> - * @author Morris Jobke <hey@morrisjobke.de> - * - * @license AGPL-3.0 - * - * This code is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License, version 3, - * as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License, version 3, - * along with this program. If not, see <http://www.gnu.org/licenses/> - * + * SPDX-FileCopyrightText: 2016-2024 Nextcloud GmbH and Nextcloud contributors + * SPDX-FileCopyrightText: 2016 ownCloud, Inc. + * SPDX-License-Identifier: AGPL-3.0-only */ namespace OC; diff --git a/lib/private/DatabaseSetupException.php b/lib/private/DatabaseSetupException.php index 44583416dbc..2e99eafa4ad 100644 --- a/lib/private/DatabaseSetupException.php +++ b/lib/private/DatabaseSetupException.php @@ -1,24 +1,9 @@ <?php + /** - * @copyright Copyright (c) 2016, ownCloud, Inc. - * - * @author Joas Schilling <coding@schilljs.com> - * @author Morris Jobke <hey@morrisjobke.de> - * - * @license AGPL-3.0 - * - * This code is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License, version 3, - * as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License, version 3, - * along with this program. If not, see <http://www.gnu.org/licenses/> - * + * SPDX-FileCopyrightText: 2016-2024 Nextcloud GmbH and Nextcloud contributors + * SPDX-FileCopyrightText: 2016 ownCloud, Inc. + * SPDX-License-Identifier: AGPL-3.0-only */ namespace OC; diff --git a/lib/private/DateTimeFormatter.php b/lib/private/DateTimeFormatter.php index 1c8b4f6d3ab..cd765a2a14b 100644 --- a/lib/private/DateTimeFormatter.php +++ b/lib/private/DateTimeFormatter.php @@ -1,26 +1,9 @@ <?php + /** - * @copyright Copyright (c) 2016, ownCloud, Inc. - * - * @author Christoph Wurst <christoph@winzerhof-wurst.at> - * @author dartcafe <github@dartcafe.de> - * @author Joas Schilling <coding@schilljs.com> - * @author Morris Jobke <hey@morrisjobke.de> - * - * @license AGPL-3.0 - * - * This code is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License, version 3, - * as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License, version 3, - * along with this program. If not, see <http://www.gnu.org/licenses/> - * + * SPDX-FileCopyrightText: 2016-2024 Nextcloud GmbH and Nextcloud contributors + * SPDX-FileCopyrightText: 2016 ownCloud, Inc. + * SPDX-License-Identifier: AGPL-3.0-only */ namespace OC; @@ -77,7 +60,7 @@ class DateTimeFormatter implements \OCP\IDateTimeFormatter { * @param \DateTimeZone $timeZone The timezone to use * @return \DateTime */ - protected function getDateTime($timestamp, \DateTimeZone $timeZone = null) { + protected function getDateTime($timestamp, ?\DateTimeZone $timeZone = null) { if ($timestamp === null) { return new \DateTime('now', $timeZone); } elseif (!$timestamp instanceof \DateTime) { @@ -105,7 +88,7 @@ class DateTimeFormatter implements \OCP\IDateTimeFormatter { * @param \OCP\IL10N $l The locale to use * @return string Formatted date string */ - public function formatDate($timestamp, $format = 'long', \DateTimeZone $timeZone = null, \OCP\IL10N $l = null) { + public function formatDate($timestamp, $format = 'long', ?\DateTimeZone $timeZone = null, ?\OCP\IL10N $l = null) { return $this->format($timestamp, 'date', $format, $timeZone, $l); } @@ -124,8 +107,8 @@ class DateTimeFormatter implements \OCP\IDateTimeFormatter { * @param \OCP\IL10N $l The locale to use * @return string Formatted relative date string */ - public function formatDateRelativeDay($timestamp, $format = 'long', \DateTimeZone $timeZone = null, \OCP\IL10N $l = null) { - if (substr($format, -1) !== '*' && substr($format, -1) !== '*') { + public function formatDateRelativeDay($timestamp, $format = 'long', ?\DateTimeZone $timeZone = null, ?\OCP\IL10N $l = null) { + if (!str_ends_with($format, '^') && !str_ends_with($format, '*')) { $format .= '^'; } @@ -136,16 +119,15 @@ class DateTimeFormatter implements \OCP\IDateTimeFormatter { * Gives the relative date of the timestamp * Only works for past dates * - * @param int|\DateTime $timestamp Either a Unix timestamp or DateTime object + * @param int|\DateTime $timestamp Either a Unix timestamp or DateTime object * @param int|\DateTime $baseTimestamp Timestamp to compare $timestamp against, defaults to current time - * @return string Dates returned are: + * @param \OCP\IL10N $l The locale to use + * @return string Formatted date span. Dates returned are: * < 1 month => Today, Yesterday, n days ago * < 13 month => last month, n months ago * >= 13 month => last year, n years ago - * @param \OCP\IL10N $l The locale to use - * @return string Formatted date span */ - public function formatDateSpan($timestamp, $baseTimestamp = null, \OCP\IL10N $l = null) { + public function formatDateSpan($timestamp, $baseTimestamp = null, ?\OCP\IL10N $l = null) { $l = $this->getLocale($l); $timestamp = $this->getDateTime($timestamp); $timestamp->setTime(0, 0, 0); @@ -211,7 +193,7 @@ class DateTimeFormatter implements \OCP\IDateTimeFormatter { * @param \OCP\IL10N $l The locale to use * @return string Formatted time string */ - public function formatTime($timestamp, $format = 'medium', \DateTimeZone $timeZone = null, \OCP\IL10N $l = null) { + public function formatTime($timestamp, $format = 'medium', ?\DateTimeZone $timeZone = null, ?\OCP\IL10N $l = null) { return $this->format($timestamp, 'time', $format, $timeZone, $l); } @@ -220,17 +202,16 @@ class DateTimeFormatter implements \OCP\IDateTimeFormatter { * * @param int|\DateTime $timestamp Either a Unix timestamp or DateTime object * @param int|\DateTime $baseTimestamp Timestamp to compare $timestamp against, defaults to current time - * @return string Dates returned are: + * @param \OCP\IL10N $l The locale to use + * @return string Formatted time span. Dates returned are: * < 60 sec => seconds ago * < 1 hour => n minutes ago * < 1 day => n hours ago * < 1 month => Yesterday, n days ago * < 13 month => last month, n months ago * >= 13 month => last year, n years ago - * @param \OCP\IL10N $l The locale to use - * @return string Formatted time span */ - public function formatTimeSpan($timestamp, $baseTimestamp = null, \OCP\IL10N $l = null) { + public function formatTimeSpan($timestamp, $baseTimestamp = null, ?\OCP\IL10N $l = null) { $l = $this->getLocale($l); $timestamp = $this->getDateTime($timestamp); if ($baseTimestamp === null) { @@ -273,7 +254,7 @@ class DateTimeFormatter implements \OCP\IDateTimeFormatter { * @param \OCP\IL10N $l The locale to use * @return string Formatted date and time string */ - public function formatDateTime($timestamp, $formatDate = 'long', $formatTime = 'medium', \DateTimeZone $timeZone = null, \OCP\IL10N $l = null) { + public function formatDateTime($timestamp, $formatDate = 'long', $formatTime = 'medium', ?\DateTimeZone $timeZone = null, ?\OCP\IL10N $l = null) { return $this->format($timestamp, 'datetime', $formatDate . '|' . $formatTime, $timeZone, $l); } @@ -288,8 +269,8 @@ class DateTimeFormatter implements \OCP\IDateTimeFormatter { * @param \OCP\IL10N $l The locale to use * @return string Formatted relative date and time string */ - public function formatDateTimeRelativeDay($timestamp, $formatDate = 'long', $formatTime = 'medium', \DateTimeZone $timeZone = null, \OCP\IL10N $l = null) { - if (substr($formatDate, -1) !== '^' && substr($formatDate, -1) !== '*') { + public function formatDateTimeRelativeDay($timestamp, $formatDate = 'long', $formatTime = 'medium', ?\DateTimeZone $timeZone = null, ?\OCP\IL10N $l = null) { + if (!str_ends_with($formatDate, '^') && !str_ends_with($formatDate, '*')) { $formatDate .= '^'; } @@ -306,7 +287,7 @@ class DateTimeFormatter implements \OCP\IDateTimeFormatter { * @param \OCP\IL10N $l The locale to use * @return string Formatted date and time string */ - protected function format($timestamp, $type, $format, \DateTimeZone $timeZone = null, \OCP\IL10N $l = null) { + protected function format($timestamp, $type, $format, ?\DateTimeZone $timeZone = null, ?\OCP\IL10N $l = null) { $l = $this->getLocale($l); $timeZone = $this->getTimeZone($timeZone); $timestamp = $this->getDateTime($timestamp, $timeZone); diff --git a/lib/private/DateTimeZone.php b/lib/private/DateTimeZone.php index c514f8d7465..ca2e314fabd 100644 --- a/lib/private/DateTimeZone.php +++ b/lib/private/DateTimeZone.php @@ -1,26 +1,9 @@ <?php + /** - * @copyright Copyright (c) 2016, ownCloud, Inc. - * - * @author Arthur Schiwon <blizzz@arthur-schiwon.de> - * @author Christoph Wurst <christoph@winzerhof-wurst.at> - * @author Joas Schilling <coding@schilljs.com> - * @author Morris Jobke <hey@morrisjobke.de> - * - * @license AGPL-3.0 - * - * This code is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License, version 3, - * as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License, version 3, - * along with this program. If not, see <http://www.gnu.org/licenses/> - * + * SPDX-FileCopyrightText: 2016-2024 Nextcloud GmbH and Nextcloud contributors + * SPDX-FileCopyrightText: 2016 ownCloud, Inc. + * SPDX-License-Identifier: AGPL-3.0-only */ namespace OC; diff --git a/lib/private/Diagnostics/Event.php b/lib/private/Diagnostics/Event.php index 0c3aa6ae29c..cf36bf9f82a 100644 --- a/lib/private/Diagnostics/Event.php +++ b/lib/private/Diagnostics/Event.php @@ -1,24 +1,8 @@ <?php /** - * @copyright Copyright (c) 2016, ownCloud, Inc. - * - * @author Morris Jobke <hey@morrisjobke.de> - * @author Robin Appelman <robin@icewind.nl> - * - * @license AGPL-3.0 - * - * This code is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License, version 3, - * as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License, version 3, - * along with this program. If not, see <http://www.gnu.org/licenses/> - * + * SPDX-FileCopyrightText: 2016-2024 Nextcloud GmbH and Nextcloud contributors + * SPDX-FileCopyrightText: 2016 ownCloud, Inc. + * SPDX-License-Identifier: AGPL-3.0-only */ namespace OC\Diagnostics; @@ -101,7 +85,7 @@ class Event implements IEvent { return $this->end - $this->start; } - public function __toString() { + public function __toString(): string { return $this->getId() . ' ' . $this->getDescription() . ' ' . $this->getDuration(); } } diff --git a/lib/private/Diagnostics/EventLogger.php b/lib/private/Diagnostics/EventLogger.php index f1dd1addb08..11c59b9227a 100644 --- a/lib/private/Diagnostics/EventLogger.php +++ b/lib/private/Diagnostics/EventLogger.php @@ -1,26 +1,8 @@ <?php /** - * @copyright Copyright (c) 2016, ownCloud, Inc. - * - * @author Christoph Wurst <christoph@winzerhof-wurst.at> - * @author Morris Jobke <hey@morrisjobke.de> - * @author Piotr Mrówczyński <mrow4a@yahoo.com> - * @author Robin Appelman <robin@icewind.nl> - * - * @license AGPL-3.0 - * - * This code is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License, version 3, - * as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License, version 3, - * along with this program. If not, see <http://www.gnu.org/licenses/> - * + * SPDX-FileCopyrightText: 2016-2024 Nextcloud GmbH and Nextcloud contributors + * SPDX-FileCopyrightText: 2016 ownCloud, Inc. + * SPDX-License-Identifier: AGPL-3.0-only */ namespace OC\Diagnostics; diff --git a/lib/private/Diagnostics/Query.php b/lib/private/Diagnostics/Query.php index 6a26e57be0e..81610074709 100644 --- a/lib/private/Diagnostics/Query.php +++ b/lib/private/Diagnostics/Query.php @@ -1,25 +1,9 @@ <?php + /** - * @copyright Copyright (c) 2016, ownCloud, Inc. - * - * @author Morris Jobke <hey@morrisjobke.de> - * @author Piotr Mrówczyński <mrow4a@yahoo.com> - * @author Robin Appelman <robin@icewind.nl> - * - * @license AGPL-3.0 - * - * This code is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License, version 3, - * as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License, version 3, - * along with this program. If not, see <http://www.gnu.org/licenses/> - * + * SPDX-FileCopyrightText: 2016-2024 Nextcloud GmbH and Nextcloud contributors + * SPDX-FileCopyrightText: 2016 ownCloud, Inc. + * SPDX-License-Identifier: AGPL-3.0-only */ namespace OC\Diagnostics; diff --git a/lib/private/Diagnostics/QueryLogger.php b/lib/private/Diagnostics/QueryLogger.php index 5f401751077..5efe99d1a74 100644 --- a/lib/private/Diagnostics/QueryLogger.php +++ b/lib/private/Diagnostics/QueryLogger.php @@ -1,26 +1,9 @@ <?php + /** - * @copyright Copyright (c) 2016, ownCloud, Inc. - * - * @author Lukas Reschke <lukas@statuscode.ch> - * @author Morris Jobke <hey@morrisjobke.de> - * @author Piotr Mrówczyński <mrow4a@yahoo.com> - * @author Robin Appelman <robin@icewind.nl> - * - * @license AGPL-3.0 - * - * This code is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License, version 3, - * as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License, version 3, - * along with this program. If not, see <http://www.gnu.org/licenses/> - * + * SPDX-FileCopyrightText: 2016-2024 Nextcloud GmbH and Nextcloud contributors + * SPDX-FileCopyrightText: 2016 ownCloud, Inc. + * SPDX-License-Identifier: AGPL-3.0-only */ namespace OC\Diagnostics; @@ -49,7 +32,7 @@ class QueryLogger implements IQueryLogger { /** * @inheritdoc */ - public function startQuery($sql, array $params = null, array $types = null) { + public function startQuery($sql, ?array $params = null, ?array $types = null) { if ($this->activated) { $this->activeQuery = new Query($sql, $params, microtime(true), $this->getStack()); } diff --git a/lib/private/DirectEditing/Manager.php b/lib/private/DirectEditing/Manager.php index 2dd2abe5408..4b8c6af31e2 100644 --- a/lib/private/DirectEditing/Manager.php +++ b/lib/private/DirectEditing/Manager.php @@ -1,32 +1,11 @@ <?php /** - * @copyright Copyright (c) 2019 Julius Härtl <jus@bitgrid.net> - * - * @author Christoph Wurst <christoph@winzerhof-wurst.at> - * @author Julius Härtl <jus@bitgrid.net> - * @author Robin Appelman <robin@icewind.nl> - * @author Tobias Kaminsky <tobias@kaminsky.me> - * - * @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/>. - * + * SPDX-FileCopyrightText: 2019 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later */ namespace OC\DirectEditing; use Doctrine\DBAL\FetchMode; -use \OCP\Files\Folder; use OCP\AppFramework\Http\NotFoundResponse; use OCP\AppFramework\Http\Response; use OCP\AppFramework\Http\TemplateResponse; @@ -34,10 +13,11 @@ use OCP\Constants; use OCP\DB\QueryBuilder\IQueryBuilder; use OCP\DirectEditing\ACreateFromTemplate; use OCP\DirectEditing\IEditor; -use \OCP\DirectEditing\IManager; +use OCP\DirectEditing\IManager; use OCP\DirectEditing\IToken; use OCP\Encryption\IManager as EncryptionManager; use OCP\Files\File; +use OCP\Files\Folder; use OCP\Files\IRootFolder; use OCP\Files\Node; use OCP\Files\NotFoundException; @@ -153,7 +133,7 @@ class Manager implements IManager { throw new \RuntimeException('No creator found'); } - public function open(string $filePath, string $editorId = null, ?int $fileId = null): string { + public function open(string $filePath, ?string $editorId = null, ?int $fileId = null): string { $userFolder = $this->rootFolder->getUserFolder($this->userId); $file = $userFolder->get($filePath); if ($fileId !== null && $file instanceof Folder) { @@ -272,16 +252,14 @@ class Manager implements IManager { } public function invokeTokenScope($userId): void { - \OC_User::setIncognitoMode(true); \OC_User::setUserId($userId); } public function revertTokenScope(): void { $this->userSession->setUser(null); - \OC_User::setIncognitoMode(false); } - public function createToken($editorId, File $file, string $filePath, IShare $share = null): string { + public function createToken($editorId, File $file, string $filePath, ?IShare $share = null): string { $token = $this->random->generate(64, ISecureRandom::CHAR_HUMAN_READABLE); $query = $this->connection->getQueryBuilder(); $query->insert(self::TABLE_TOKENS) @@ -299,10 +277,9 @@ class Manager implements IManager { } /** - * @param $userId - * @param $fileId - * @param null $filePath - * @return Node + * @param string $userId + * @param int $fileId + * @param ?string $filePath * @throws NotFoundException */ public function getFileForToken($userId, $fileId, $filePath = null): Node { @@ -310,11 +287,11 @@ class Manager implements IManager { if ($filePath !== null) { return $userFolder->get($filePath); } - $files = $userFolder->getById($fileId); - if (count($files) === 0) { + $file = $userFolder->getFirstNodeById($fileId); + if (!$file) { throw new NotFoundException('File nound found by id ' . $fileId); } - return $files[0]; + return $file; } public function isEnabled(): bool { diff --git a/lib/private/DirectEditing/Token.php b/lib/private/DirectEditing/Token.php index 0ec911f9624..594cef98086 100644 --- a/lib/private/DirectEditing/Token.php +++ b/lib/private/DirectEditing/Token.php @@ -1,24 +1,7 @@ <?php /** - * @copyright Copyright (c) 2019 Julius Härtl <jus@bitgrid.net> - * - * @author Julius Härtl <jus@bitgrid.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/>. - * + * SPDX-FileCopyrightText: 2019 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later */ namespace OC\DirectEditing; diff --git a/lib/private/EmojiHelper.php b/lib/private/EmojiHelper.php index d6e44810fab..514973b2959 100644 --- a/lib/private/EmojiHelper.php +++ b/lib/private/EmojiHelper.php @@ -3,26 +3,8 @@ declare(strict_types=1); /** - * @copyright Copyright (c) 2020, Georg Ehrke - * - * @author Georg Ehrke <oc.list@georgehrke.com> - * @author Joas Schilling <coding@schilljs.com> - * - * @license GNU AGPL version 3 or any later version - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as - * published by the Free Software Foundation, either version 3 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - * + * SPDX-FileCopyrightText: 2020 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later */ namespace OC; diff --git a/lib/private/Encryption/DecryptAll.php b/lib/private/Encryption/DecryptAll.php index 7bf4ce62d28..f9a92d07d20 100644 --- a/lib/private/Encryption/DecryptAll.php +++ b/lib/private/Encryption/DecryptAll.php @@ -1,36 +1,16 @@ <?php + /** - * @copyright Copyright (c) 2016, ownCloud, Inc. - * - * @author Bjoern Schiessle <bjoern@schiessle.org> - * @author Björn Schießle <bjoern@schiessle.org> - * @author Christian Jürges <christian@eqipe.ch> - * @author Christoph Wurst <christoph@winzerhof-wurst.at> - * @author Joas Schilling <coding@schilljs.com> - * @author Roeland Jago Douma <roeland@famdouma.nl> - * @author sammo2828 <sammo2828@gmail.com> - * @author Vincent Petry <vincent@nextcloud.com> - * - * @license AGPL-3.0 - * - * This code is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License, version 3, - * as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License, version 3, - * along with this program. If not, see <http://www.gnu.org/licenses/> - * + * SPDX-FileCopyrightText: 2016-2024 Nextcloud GmbH and Nextcloud contributors + * SPDX-FileCopyrightText: 2016 ownCloud, Inc. + * SPDX-License-Identifier: AGPL-3.0-only */ namespace OC\Encryption; use OC\Encryption\Exceptions\DecryptionFailedException; use OC\Files\View; use OCP\Encryption\IEncryptionModule; +use OCP\Encryption\IManager; use OCP\IUserManager; use Symfony\Component\Console\Helper\ProgressBar; use Symfony\Component\Console\Input\InputInterface; @@ -43,31 +23,14 @@ class DecryptAll { /** @var InputInterface */ protected $input; - /** @var Manager */ - protected $encryptionManager; - - /** @var IUserManager */ - protected $userManager; - - /** @var View */ - protected $rootView; - /** @var array files which couldn't be decrypted */ protected $failed; - /** - * @param Manager $encryptionManager - * @param IUserManager $userManager - * @param View $rootView - */ public function __construct( - Manager $encryptionManager, - IUserManager $userManager, - View $rootView + protected IManager $encryptionManager, + protected IUserManager $userManager, + protected View $rootView ) { - $this->encryptionManager = $encryptionManager; - $this->userManager = $userManager; - $this->rootView = $rootView; $this->failed = []; } diff --git a/lib/private/Encryption/EncryptionWrapper.php b/lib/private/Encryption/EncryptionWrapper.php index 37264e81823..4ff8a3c0c6d 100644 --- a/lib/private/Encryption/EncryptionWrapper.php +++ b/lib/private/Encryption/EncryptionWrapper.php @@ -1,26 +1,9 @@ <?php + /** - * @copyright Copyright (c) 2016, ownCloud, Inc. - * - * @author Björn Schießle <bjoern@schiessle.org> - * @author Julius Härtl <jus@bitgrid.net> - * @author Robin Appelman <robin@icewind.nl> - * @author Roeland Jago Douma <roeland@famdouma.nl> - * - * @license AGPL-3.0 - * - * This code is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License, version 3, - * as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License, version 3, - * along with this program. If not, see <http://www.gnu.org/licenses/> - * + * SPDX-FileCopyrightText: 2016-2024 Nextcloud GmbH and Nextcloud contributors + * SPDX-FileCopyrightText: 2016 ownCloud, Inc. + * SPDX-License-Identifier: AGPL-3.0-only */ namespace OC\Encryption; @@ -29,7 +12,8 @@ use OC\Files\Storage\Wrapper\Encryption; use OC\Files\View; use OC\Memcache\ArrayCache; use OCP\Files\Mount\IMountPoint; -use OCP\Files\Storage; +use OCP\Files\Storage\IDisableEncryptionStorage; +use OCP\Files\Storage\IStorage; use Psr\Log\LoggerInterface; /** @@ -52,8 +36,8 @@ class EncryptionWrapper { * EncryptionWrapper constructor. */ public function __construct(ArrayCache $arrayCache, - Manager $manager, - LoggerInterface $logger + Manager $manager, + LoggerInterface $logger ) { $this->arrayCache = $arrayCache; $this->manager = $manager; @@ -64,18 +48,19 @@ class EncryptionWrapper { * Wraps the given storage when it is not a shared storage * * @param string $mountPoint - * @param Storage $storage + * @param IStorage $storage * @param IMountPoint $mount - * @return Encryption|Storage + * @param bool $force apply the wrapper even if the storage normally has encryption disabled, helpful for repair steps + * @return Encryption|IStorage */ - public function wrapStorage($mountPoint, Storage $storage, IMountPoint $mount) { + public function wrapStorage(string $mountPoint, IStorage $storage, IMountPoint $mount, bool $force = false) { $parameters = [ 'storage' => $storage, 'mountPoint' => $mountPoint, 'mount' => $mount ]; - if (!$storage->instanceOfStorage(Storage\IDisableEncryptionStorage::class) && $mountPoint !== '/') { + if ($force || (!$storage->instanceOfStorage(IDisableEncryptionStorage::class) && $mountPoint !== '/')) { $user = \OC::$server->getUserSession()->getUser(); $mountManager = Filesystem::getMountManager(); $uid = $user ? $user->getUID() : null; diff --git a/lib/private/Encryption/Exceptions/DecryptionFailedException.php b/lib/private/Encryption/Exceptions/DecryptionFailedException.php index c67edabd0ce..bdda5b381b6 100644 --- a/lib/private/Encryption/Exceptions/DecryptionFailedException.php +++ b/lib/private/Encryption/Exceptions/DecryptionFailedException.php @@ -1,24 +1,9 @@ <?php + /** - * @copyright Copyright (c) 2016, ownCloud, Inc. - * - * @author Clark Tomlinson <fallen013@gmail.com> - * @author Thomas Müller <thomas.mueller@tmit.eu> - * - * @license AGPL-3.0 - * - * This code is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License, version 3, - * as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License, version 3, - * along with this program. If not, see <http://www.gnu.org/licenses/> - * + * SPDX-FileCopyrightText: 2016-2024 Nextcloud GmbH and Nextcloud contributors + * SPDX-FileCopyrightText: 2016 ownCloud, Inc. + * SPDX-License-Identifier: AGPL-3.0-only */ namespace OC\Encryption\Exceptions; diff --git a/lib/private/Encryption/Exceptions/EmptyEncryptionDataException.php b/lib/private/Encryption/Exceptions/EmptyEncryptionDataException.php index 9a86d876d6a..a2829a8c09c 100644 --- a/lib/private/Encryption/Exceptions/EmptyEncryptionDataException.php +++ b/lib/private/Encryption/Exceptions/EmptyEncryptionDataException.php @@ -1,25 +1,9 @@ <?php + /** - * @copyright Copyright (c) 2016, ownCloud, Inc. - * - * @author Christoph Wurst <christoph@winzerhof-wurst.at> - * @author Clark Tomlinson <fallen013@gmail.com> - * @author Thomas Müller <thomas.mueller@tmit.eu> - * - * @license AGPL-3.0 - * - * This code is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License, version 3, - * as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License, version 3, - * along with this program. If not, see <http://www.gnu.org/licenses/> - * + * SPDX-FileCopyrightText: 2016-2024 Nextcloud GmbH and Nextcloud contributors + * SPDX-FileCopyrightText: 2016 ownCloud, Inc. + * SPDX-License-Identifier: AGPL-3.0-only */ namespace OC\Encryption\Exceptions; diff --git a/lib/private/Encryption/Exceptions/EncryptionFailedException.php b/lib/private/Encryption/Exceptions/EncryptionFailedException.php index 8797df51e8e..04ca6eba79c 100644 --- a/lib/private/Encryption/Exceptions/EncryptionFailedException.php +++ b/lib/private/Encryption/Exceptions/EncryptionFailedException.php @@ -1,25 +1,9 @@ <?php + /** - * @copyright Copyright (c) 2016, ownCloud, Inc. - * - * @author Christoph Wurst <christoph@winzerhof-wurst.at> - * @author Clark Tomlinson <fallen013@gmail.com> - * @author Thomas Müller <thomas.mueller@tmit.eu> - * - * @license AGPL-3.0 - * - * This code is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License, version 3, - * as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License, version 3, - * along with this program. If not, see <http://www.gnu.org/licenses/> - * + * SPDX-FileCopyrightText: 2016-2024 Nextcloud GmbH and Nextcloud contributors + * SPDX-FileCopyrightText: 2016 ownCloud, Inc. + * SPDX-License-Identifier: AGPL-3.0-only */ namespace OC\Encryption\Exceptions; diff --git a/lib/private/Encryption/Exceptions/EncryptionHeaderKeyExistsException.php b/lib/private/Encryption/Exceptions/EncryptionHeaderKeyExistsException.php index 99cfb1eb8d1..07007dc0ec1 100644 --- a/lib/private/Encryption/Exceptions/EncryptionHeaderKeyExistsException.php +++ b/lib/private/Encryption/Exceptions/EncryptionHeaderKeyExistsException.php @@ -1,24 +1,9 @@ <?php + /** - * @copyright Copyright (c) 2016, ownCloud, Inc. - * - * @author Björn Schießle <bjoern@schiessle.org> - * @author Thomas Müller <thomas.mueller@tmit.eu> - * - * @license AGPL-3.0 - * - * This code is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License, version 3, - * as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License, version 3, - * along with this program. If not, see <http://www.gnu.org/licenses/> - * + * SPDX-FileCopyrightText: 2016-2024 Nextcloud GmbH and Nextcloud contributors + * SPDX-FileCopyrightText: 2016 ownCloud, Inc. + * SPDX-License-Identifier: AGPL-3.0-only */ namespace OC\Encryption\Exceptions; diff --git a/lib/private/Encryption/Exceptions/EncryptionHeaderToLargeException.php b/lib/private/Encryption/Exceptions/EncryptionHeaderToLargeException.php index 45d70dd5f8f..dace9527305 100644 --- a/lib/private/Encryption/Exceptions/EncryptionHeaderToLargeException.php +++ b/lib/private/Encryption/Exceptions/EncryptionHeaderToLargeException.php @@ -1,24 +1,9 @@ <?php + /** - * @copyright Copyright (c) 2016, ownCloud, Inc. - * - * @author Clark Tomlinson <fallen013@gmail.com> - * @author Thomas Müller <thomas.mueller@tmit.eu> - * - * @license AGPL-3.0 - * - * This code is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License, version 3, - * as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License, version 3, - * along with this program. If not, see <http://www.gnu.org/licenses/> - * + * SPDX-FileCopyrightText: 2016-2024 Nextcloud GmbH and Nextcloud contributors + * SPDX-FileCopyrightText: 2016 ownCloud, Inc. + * SPDX-License-Identifier: AGPL-3.0-only */ namespace OC\Encryption\Exceptions; diff --git a/lib/private/Encryption/Exceptions/ModuleAlreadyExistsException.php b/lib/private/Encryption/Exceptions/ModuleAlreadyExistsException.php index d0a2756212b..72ff00befc3 100644 --- a/lib/private/Encryption/Exceptions/ModuleAlreadyExistsException.php +++ b/lib/private/Encryption/Exceptions/ModuleAlreadyExistsException.php @@ -1,24 +1,9 @@ <?php + /** - * @copyright Copyright (c) 2016, ownCloud, Inc. - * - * @author Björn Schießle <bjoern@schiessle.org> - * @author Thomas Müller <thomas.mueller@tmit.eu> - * - * @license AGPL-3.0 - * - * This code is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License, version 3, - * as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License, version 3, - * along with this program. If not, see <http://www.gnu.org/licenses/> - * + * SPDX-FileCopyrightText: 2016-2024 Nextcloud GmbH and Nextcloud contributors + * SPDX-FileCopyrightText: 2016 ownCloud, Inc. + * SPDX-License-Identifier: AGPL-3.0-only */ namespace OC\Encryption\Exceptions; diff --git a/lib/private/Encryption/Exceptions/ModuleDoesNotExistsException.php b/lib/private/Encryption/Exceptions/ModuleDoesNotExistsException.php index e0ce8616432..8ec382f176d 100644 --- a/lib/private/Encryption/Exceptions/ModuleDoesNotExistsException.php +++ b/lib/private/Encryption/Exceptions/ModuleDoesNotExistsException.php @@ -1,24 +1,9 @@ <?php + /** - * @copyright Copyright (c) 2016, ownCloud, Inc. - * - * @author Björn Schießle <bjoern@schiessle.org> - * @author Thomas Müller <thomas.mueller@tmit.eu> - * - * @license AGPL-3.0 - * - * This code is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License, version 3, - * as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License, version 3, - * along with this program. If not, see <http://www.gnu.org/licenses/> - * + * SPDX-FileCopyrightText: 2016-2024 Nextcloud GmbH and Nextcloud contributors + * SPDX-FileCopyrightText: 2016 ownCloud, Inc. + * SPDX-License-Identifier: AGPL-3.0-only */ namespace OC\Encryption\Exceptions; diff --git a/lib/private/Encryption/Exceptions/UnknownCipherException.php b/lib/private/Encryption/Exceptions/UnknownCipherException.php index fbb923ce521..a64a413bf8e 100644 --- a/lib/private/Encryption/Exceptions/UnknownCipherException.php +++ b/lib/private/Encryption/Exceptions/UnknownCipherException.php @@ -1,24 +1,9 @@ <?php + /** - * @copyright Copyright (c) 2016, ownCloud, Inc. - * - * @author Clark Tomlinson <fallen013@gmail.com> - * @author Thomas Müller <thomas.mueller@tmit.eu> - * - * @license AGPL-3.0 - * - * This code is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License, version 3, - * as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License, version 3, - * along with this program. If not, see <http://www.gnu.org/licenses/> - * + * SPDX-FileCopyrightText: 2016-2024 Nextcloud GmbH and Nextcloud contributors + * SPDX-FileCopyrightText: 2016 ownCloud, Inc. + * SPDX-License-Identifier: AGPL-3.0-only */ namespace OC\Encryption\Exceptions; diff --git a/lib/private/Encryption/File.php b/lib/private/Encryption/File.php index daab097ce7c..a29d62946c4 100644 --- a/lib/private/Encryption/File.php +++ b/lib/private/Encryption/File.php @@ -1,35 +1,15 @@ <?php + /** - * @copyright Copyright (c) 2016, ownCloud, Inc. - * - * @author Björn Schießle <bjoern@schiessle.org> - * @author Christoph Wurst <christoph@winzerhof-wurst.at> - * @author Joas Schilling <coding@schilljs.com> - * @author Morris Jobke <hey@morrisjobke.de> - * @author Roeland Jago Douma <roeland@famdouma.nl> - * @author Thomas Müller <thomas.mueller@tmit.eu> - * @author Vincent Petry <vincent@nextcloud.com> - * - * @license AGPL-3.0 - * - * This code is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License, version 3, - * as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License, version 3, - * along with this program. If not, see <http://www.gnu.org/licenses/> - * + * SPDX-FileCopyrightText: 2016-2024 Nextcloud GmbH and Nextcloud contributors + * SPDX-FileCopyrightText: 2016 ownCloud, Inc. + * SPDX-License-Identifier: AGPL-3.0-only */ namespace OC\Encryption; -use OCP\Cache\CappedMemoryCache; use OCA\Files_External\Service\GlobalStoragesService; use OCP\App\IAppManager; +use OCP\Cache\CappedMemoryCache; use OCP\Files\IRootFolder; use OCP\Files\NotFoundException; use OCP\Share\IManager; @@ -47,8 +27,8 @@ class File implements \OCP\Encryption\IFile { private ?IAppManager $appManager = null; public function __construct(Util $util, - IRootFolder $rootFolder, - IManager $shareManager) { + IRootFolder $rootFolder, + IManager $shareManager) { $this->util = $util; $this->cache = new CappedMemoryCache(); $this->rootFolder = $rootFolder; diff --git a/lib/private/Encryption/HookManager.php b/lib/private/Encryption/HookManager.php index 5081bcccf94..fc6546fb253 100644 --- a/lib/private/Encryption/HookManager.php +++ b/lib/private/Encryption/HookManager.php @@ -1,31 +1,15 @@ <?php + /** - * @copyright Copyright (c) 2016, ownCloud, Inc. - * - * @author Björn Schießle <bjoern@schiessle.org> - * @author Julius Härtl <jus@bitgrid.net> - * @author Thomas Müller <thomas.mueller@tmit.eu> - * - * @license AGPL-3.0 - * - * This code is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License, version 3, - * as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License, version 3, - * along with this program. If not, see <http://www.gnu.org/licenses/> - * + * SPDX-FileCopyrightText: 2016-2024 Nextcloud GmbH and Nextcloud contributors + * SPDX-FileCopyrightText: 2016 ownCloud, Inc. + * SPDX-License-Identifier: AGPL-3.0-only */ namespace OC\Encryption; use OC\Files\Filesystem; -use OC\Files\View; use OC\Files\SetupManager; +use OC\Files\View; use Psr\Log\LoggerInterface; class HookManager { diff --git a/lib/private/Encryption/Keys/Storage.php b/lib/private/Encryption/Keys/Storage.php index e88c305eeec..532d1a51dd6 100644 --- a/lib/private/Encryption/Keys/Storage.php +++ b/lib/private/Encryption/Keys/Storage.php @@ -1,29 +1,8 @@ <?php /** - * @copyright Copyright (c) 2016, ownCloud, Inc. - * - * @author Bjoern Schiessle <bjoern@schiessle.org> - * @author Björn Schießle <bjoern@schiessle.org> - * @author Christoph Wurst <christoph@winzerhof-wurst.at> - * @author Joas Schilling <coding@schilljs.com> - * @author Roeland Jago Douma <roeland@famdouma.nl> - * @author Thomas Müller <thomas.mueller@tmit.eu> - * @author Vincent Petry <vincent@nextcloud.com> - * - * @license AGPL-3.0 - * - * This code is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License, version 3, - * as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License, version 3, - * along with this program. If not, see <http://www.gnu.org/licenses/> - * + * SPDX-FileCopyrightText: 2016-2024 Nextcloud GmbH and Nextcloud contributors + * SPDX-FileCopyrightText: 2016 ownCloud, Inc. + * SPDX-License-Identifier: AGPL-3.0-only */ namespace OC\Encryption\Keys; @@ -98,14 +77,14 @@ class Storage implements IStorage { */ public function getFileKey($path, $keyId, $encryptionModuleId) { $realFile = $this->util->stripPartialFileExtension($path); - $keyDir = $this->getFileKeyDir($encryptionModuleId, $realFile); + $keyDir = $this->util->getFileKeyDir($encryptionModuleId, $realFile); $key = $this->getKey($keyDir . $keyId)['key']; if ($key === '' && $realFile !== $path) { // Check if the part file has keys and use them, if no normal keys // exist. This is required to fix copyBetweenStorage() when we // rename a .part file over storage borders. - $keyDir = $this->getFileKeyDir($encryptionModuleId, $path); + $keyDir = $this->util->getFileKeyDir($encryptionModuleId, $path); $key = $this->getKey($keyDir . $keyId)['key']; } @@ -135,7 +114,7 @@ class Storage implements IStorage { * @inheritdoc */ public function setFileKey($path, $keyId, $key, $encryptionModuleId) { - $keyDir = $this->getFileKeyDir($encryptionModuleId, $path); + $keyDir = $this->util->getFileKeyDir($encryptionModuleId, $path); return $this->setKey($keyDir . $keyId, [ 'key' => base64_encode($key), ]); @@ -177,7 +156,7 @@ class Storage implements IStorage { * @inheritdoc */ public function deleteFileKey($path, $keyId, $encryptionModuleId) { - $keyDir = $this->getFileKeyDir($encryptionModuleId, $path); + $keyDir = $this->util->getFileKeyDir($encryptionModuleId, $path); return !$this->view->file_exists($keyDir . $keyId) || $this->view->unlink($keyDir . $keyId); } @@ -185,7 +164,7 @@ class Storage implements IStorage { * @inheritdoc */ public function deleteAllFileKeys($path) { - $keyDir = $this->getFileKeyDir('', $path); + $keyDir = $this->util->getFileKeyDir('', $path); return !$this->view->file_exists($keyDir) || $this->view->deleteAll($keyDir); } @@ -356,26 +335,6 @@ class Storage implements IStorage { } /** - * get path to key folder for a given file - * - * @param string $encryptionModuleId - * @param string $path path to the file, relative to data/ - * @return string - */ - private function getFileKeyDir($encryptionModuleId, $path) { - [$owner, $filename] = $this->util->getUidAndFilename($path); - - // in case of system wide mount points the keys are stored directly in the data directory - if ($this->util->isSystemWideMountPoint($filename, $owner)) { - $keyPath = $this->root_dir . '/' . $this->keys_base_dir . $filename . '/'; - } else { - $keyPath = $this->root_dir . '/' . $owner . $this->keys_base_dir . $filename . '/'; - } - - return Filesystem::normalizePath($keyPath . $encryptionModuleId . '/', false); - } - - /** * move keys if a file was renamed * * @param string $source diff --git a/lib/private/Encryption/Manager.php b/lib/private/Encryption/Manager.php index f751bd94b28..a07c778bfe0 100644 --- a/lib/private/Encryption/Manager.php +++ b/lib/private/Encryption/Manager.php @@ -1,27 +1,9 @@ <?php + /** - * @copyright Copyright (c) 2016, ownCloud, Inc. - * - * @author Björn Schießle <bjoern@schiessle.org> - * @author Christoph Wurst <christoph@winzerhof-wurst.at> - * @author Joas Schilling <coding@schilljs.com> - * @author Roeland Jago Douma <roeland@famdouma.nl> - * @author Thomas Müller <thomas.mueller@tmit.eu> - * - * @license AGPL-3.0 - * - * This code is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License, version 3, - * as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License, version 3, - * along with this program. If not, see <http://www.gnu.org/licenses/> - * + * SPDX-FileCopyrightText: 2016-2024 Nextcloud GmbH and Nextcloud contributors + * SPDX-FileCopyrightText: 2016 ownCloud, Inc. + * SPDX-License-Identifier: AGPL-3.0-only */ namespace OC\Encryption; @@ -32,39 +14,24 @@ use OC\Memcache\ArrayCache; use OC\ServiceUnavailableException; use OCP\Encryption\IEncryptionModule; use OCP\Encryption\IManager; +use OCP\Files\Mount\IMountPoint; +use OCP\Files\Storage\IStorage; use OCP\IConfig; use OCP\IL10N; use Psr\Log\LoggerInterface; class Manager implements IManager { - /** @var array */ - protected $encryptionModules; - - /** @var IConfig */ - protected $config; - - protected LoggerInterface $logger; - - /** @var Il10n */ - protected $l; - - /** @var View */ - protected $rootView; - - /** @var Util */ - protected $util; - - /** @var ArrayCache */ - protected $arrayCache; - - public function __construct(IConfig $config, LoggerInterface $logger, IL10N $l10n, View $rootView, Util $util, ArrayCache $arrayCache) { + protected array $encryptionModules; + + public function __construct( + protected IConfig $config, + protected LoggerInterface $logger, + protected IL10N $l, + protected View $rootView, + protected Util $util, + protected ArrayCache $arrayCache, + ) { $this->encryptionModules = []; - $this->config = $config; - $this->logger = $logger; - $this->l = $l10n; - $this->rootView = $rootView; - $this->util = $util; - $this->arrayCache = $arrayCache; } /** @@ -234,6 +201,11 @@ class Manager implements IManager { } } + public function forceWrapStorage(IMountPoint $mountPoint, IStorage $storage) { + $encryptionWrapper = new EncryptionWrapper($this->arrayCache, $this, $this->logger); + return $encryptionWrapper->wrapStorage($mountPoint->getMountPoint(), $storage, $mountPoint, true); + } + /** * check if key storage is ready diff --git a/lib/private/Encryption/Update.php b/lib/private/Encryption/Update.php index 2e390177baf..87036403b8e 100644 --- a/lib/private/Encryption/Update.php +++ b/lib/private/Encryption/Update.php @@ -1,28 +1,9 @@ <?php + /** - * @copyright Copyright (c) 2016, ownCloud, Inc. - * - * @author Bjoern Schiessle <bjoern@schiessle.org> - * @author Björn Schießle <bjoern@schiessle.org> - * @author Christoph Wurst <christoph@winzerhof-wurst.at> - * @author Julius Härtl <jus@bitgrid.net> - * @author Roeland Jago Douma <roeland@famdouma.nl> - * @author Thomas Müller <thomas.mueller@tmit.eu> - * - * @license AGPL-3.0 - * - * This code is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License, version 3, - * as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License, version 3, - * along with this program. If not, see <http://www.gnu.org/licenses/> - * + * SPDX-FileCopyrightText: 2016-2024 Nextcloud GmbH and Nextcloud contributors + * SPDX-FileCopyrightText: 2016 ownCloud, Inc. + * SPDX-License-Identifier: AGPL-3.0-only */ namespace OC\Encryption; @@ -62,14 +43,14 @@ class Update { * @param string $uid */ public function __construct( - View $view, - Util $util, - Mount\Manager $mountManager, - Manager $encryptionManager, - File $file, - LoggerInterface $logger, - $uid - ) { + View $view, + Util $util, + Mount\Manager $mountManager, + Manager $encryptionManager, + File $file, + LoggerInterface $logger, + $uid + ) { $this->view = $view; $this->util = $util; $this->mountManager = $mountManager; diff --git a/lib/private/Encryption/Util.php b/lib/private/Encryption/Util.php index a468908ffc8..1fb08b15696 100644 --- a/lib/private/Encryption/Util.php +++ b/lib/private/Encryption/Util.php @@ -1,29 +1,9 @@ <?php + /** - * @copyright Copyright (c) 2016, ownCloud, Inc. - * - * @author Bjoern Schiessle <bjoern@schiessle.org> - * @author Björn Schießle <bjoern@schiessle.org> - * @author Christoph Wurst <christoph@winzerhof-wurst.at> - * @author Jan-Christoph Borchardt <hey@jancborchardt.net> - * @author Morris Jobke <hey@morrisjobke.de> - * @author Roeland Jago Douma <roeland@famdouma.nl> - * @author Thomas Müller <thomas.mueller@tmit.eu> - * - * @license AGPL-3.0 - * - * This code is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License, version 3, - * as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License, version 3, - * along with this program. If not, see <http://www.gnu.org/licenses/> - * + * SPDX-FileCopyrightText: 2016-2024 Nextcloud GmbH and Nextcloud contributors + * SPDX-FileCopyrightText: 2016 ownCloud, Inc. + * SPDX-License-Identifier: AGPL-3.0-only */ namespace OC\Encryption; @@ -105,7 +85,7 @@ class Util { * @return string * @throws ModuleDoesNotExistsException */ - public function getEncryptionModuleId(array $header = null) { + public function getEncryptionModuleId(?array $header = null) { $id = ''; $encryptionModuleKey = self::HEADER_ENCRYPTION_MODULE_KEY; @@ -357,4 +337,53 @@ class Util { public function getKeyStorageRoot(): string { return $this->config->getAppValue('core', 'encryption_key_storage_root', ''); } + + /** + * parse raw header to array + * + * @param string $rawHeader + * @return array + */ + public function parseRawHeader(string $rawHeader) { + $result = []; + if (str_starts_with($rawHeader, Util::HEADER_START)) { + $header = $rawHeader; + $endAt = strpos($header, Util::HEADER_END); + if ($endAt !== false) { + $header = substr($header, 0, $endAt + strlen(Util::HEADER_END)); + + // +1 to not start with an ':' which would result in empty element at the beginning + $exploded = explode(':', substr($header, strlen(Util::HEADER_START) + 1)); + + $element = array_shift($exploded); + while ($element !== Util::HEADER_END && $element !== null) { + $result[$element] = array_shift($exploded); + $element = array_shift($exploded); + } + } + } + + return $result; + } + + /** + * get path to key folder for a given file + * + * @param string $encryptionModuleId + * @param string $path path to the file, relative to data/ + * @return string + */ + public function getFileKeyDir(string $encryptionModuleId, string $path): string { + [$owner, $filename] = $this->getUidAndFilename($path); + $root = $this->getKeyStorageRoot(); + + // in case of system-wide mount points the keys are stored directly in the data directory + if ($this->isSystemWideMountPoint($filename, $owner)) { + $keyPath = $root . '/' . '/files_encryption/keys' . $filename . '/'; + } else { + $keyPath = $root . '/' . $owner . '/files_encryption/keys' . $filename . '/'; + } + + return Filesystem::normalizePath($keyPath . $encryptionModuleId . '/', false); + } } diff --git a/lib/private/EventDispatcher/EventDispatcher.php b/lib/private/EventDispatcher/EventDispatcher.php index 88c6b2cf32c..b7554c439ea 100644 --- a/lib/private/EventDispatcher/EventDispatcher.php +++ b/lib/private/EventDispatcher/EventDispatcher.php @@ -3,59 +3,28 @@ declare(strict_types=1); /** - * @copyright 2019 Christoph Wurst <christoph@winzerhof-wurst.at> - * - * @author Christoph Wurst <christoph@winzerhof-wurst.at> - * @author Joas Schilling <coding@schilljs.com> - * @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/>. - * + * SPDX-FileCopyrightText: 2019 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later */ namespace OC\EventDispatcher; -use OC\Log; -use Psr\Log\LoggerInterface; -use function get_class; use OC\Broadcast\Events\BroadcastEvent; +use OC\Log; use OCP\Broadcast\Events\IBroadcastEvent; use OCP\EventDispatcher\ABroadcastedEvent; use OCP\EventDispatcher\Event; use OCP\EventDispatcher\IEventDispatcher; -use OCP\IContainer; use OCP\IServerContainer; +use Psr\Log\LoggerInterface; use Symfony\Component\EventDispatcher\EventDispatcher as SymfonyDispatcher; +use function get_class; class EventDispatcher implements IEventDispatcher { - /** @var SymfonyDispatcher */ - private $dispatcher; - - /** @var IContainer */ - private $container; - - /** @var LoggerInterface */ - private $logger; - - public function __construct(SymfonyDispatcher $dispatcher, - IServerContainer $container, - LoggerInterface $logger) { - $this->dispatcher = $dispatcher; - $this->container = $container; - $this->logger = $logger; - + public function __construct( + private SymfonyDispatcher $dispatcher, + private IServerContainer $container, + private LoggerInterface $logger, + ) { // inject the event dispatcher into the logger // this is done here because there is a cyclic dependency between the event dispatcher and logger if ($this->logger instanceof Log || $this->logger instanceof Log\PsrLoggerAdapter) { @@ -64,19 +33,19 @@ class EventDispatcher implements IEventDispatcher { } public function addListener(string $eventName, - callable $listener, - int $priority = 0): void { + callable $listener, + int $priority = 0): void { $this->dispatcher->addListener($eventName, $listener, $priority); } public function removeListener(string $eventName, - callable $listener): void { + callable $listener): void { $this->dispatcher->removeListener($eventName, $listener); } public function addServiceListener(string $eventName, - string $className, - int $priority = 0): void { + string $className, + int $priority = 0): void { $listener = new ServiceEventListener( $this->container, $className, @@ -86,11 +55,15 @@ class EventDispatcher implements IEventDispatcher { $this->addListener($eventName, $listener, $priority); } + public function hasListeners(string $eventName): bool { + return $this->dispatcher->hasListeners($eventName); + } + /** * @deprecated */ public function dispatch(string $eventName, - Event $event): void { + Event $event): void { $this->dispatcher->dispatch($event, $eventName); if ($event instanceof ABroadcastedEvent && !$event->isPropagationStopped()) { diff --git a/lib/private/EventDispatcher/ServiceEventListener.php b/lib/private/EventDispatcher/ServiceEventListener.php index 21cdf7f8cc2..03a986ec78c 100644 --- a/lib/private/EventDispatcher/ServiceEventListener.php +++ b/lib/private/EventDispatcher/ServiceEventListener.php @@ -3,26 +3,8 @@ declare(strict_types=1); /** - * @copyright 2019 Christoph Wurst <christoph@winzerhof-wurst.at> - * - * @author Christoph Wurst <christoph@winzerhof-wurst.at> - * @author Joas Schilling <coding@schilljs.com> - * - * @license GNU AGPL version 3 or any later version - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as - * published by the Free Software Foundation, either version 3 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - * + * SPDX-FileCopyrightText: 2019 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later */ namespace OC\EventDispatcher; @@ -54,8 +36,8 @@ final class ServiceEventListener { private $service; public function __construct(IServerContainer $container, - string $class, - LoggerInterface $logger) { + string $class, + LoggerInterface $logger) { $this->container = $container; $this->class = $class; $this->logger = $logger; diff --git a/lib/private/legacy/OC_EventSource.php b/lib/private/EventSource.php index cd72ba1f2d5..dbeda25049e 100644 --- a/lib/private/legacy/OC_EventSource.php +++ b/lib/private/EventSource.php @@ -1,64 +1,34 @@ <?php -use OCP\IRequest; - +declare(strict_types=1); /** - * @copyright Copyright (c) 2016, ownCloud, Inc. - * - * @author Bart Visscher <bartv@thisnet.nl> - * @author Christian Oliff <christianoliff@yahoo.com> - * @author Christoph Wurst <christoph@winzerhof-wurst.at> - * @author Felix Moeller <mail@felixmoeller.de> - * @author Lukas Reschke <lukas@statuscode.ch> - * @author Morris Jobke <hey@morrisjobke.de> - * @author Robin Appelman <robin@icewind.nl> - * @author Thomas Müller <thomas.mueller@tmit.eu> - * - * @license AGPL-3.0 - * - * This code is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License, version 3, - * as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License, version 3, - * along with this program. If not, see <http://www.gnu.org/licenses/> - * + * SPDX-FileCopyrightText: 2020-2024 Nextcloud GmbH and Nextcloud contributors + * SPDX-FileCopyrightText: 2016 ownCloud, Inc. + * SPDX-License-Identifier: AGPL-3.0-only */ -class OC_EventSource implements \OCP\IEventSource { - /** - * @var bool - */ - private $fallback; - - /** - * @var int - */ - private $fallBackId = 0; +namespace OC; - /** - * @var bool - */ - private $started = false; +use OCP\IEventSource; +use OCP\IRequest; - private IRequest $request; +class EventSource implements IEventSource { + private bool $fallback = false; + private int $fallBackId = 0; + private bool $started = false; - public function __construct(IRequest $request) { - $this->request = $request; + public function __construct( + private IRequest $request, + ) { } - protected function init() { + protected function init(): void { if ($this->started) { return; } $this->started = true; // prevent php output buffering, caching and nginx buffering - OC_Util::obEnd(); + \OC_Util::obEnd(); header('Cache-Control: no-cache'); header('X-Accel-Buffering: no'); $this->fallback = isset($_GET['fallback']) and $_GET['fallback'] == 'true'; @@ -104,7 +74,7 @@ class OC_EventSource implements \OCP\IEventSource { */ public function send($type, $data = null) { if ($data and !preg_match('/^[A-Za-z0-9_]+$/', $type)) { - throw new BadMethodCallException('Type needs to be alphanumeric ('. $type .')'); + throw new \BadMethodCallException('Type needs to be alphanumeric ('. $type .')'); } $this->init(); if (is_null($data)) { @@ -113,13 +83,13 @@ class OC_EventSource implements \OCP\IEventSource { } if ($this->fallback) { $response = '<script type="text/javascript">window.parent.OC.EventSource.fallBackCallBack(' - . $this->fallBackId . ',"' . $type . '",' . OC_JSON::encode($data) . ')</script>' . PHP_EOL; + . $this->fallBackId . ',"' . ($type ?? '') . '",' . json_encode($data, JSON_HEX_TAG) . ')</script>' . PHP_EOL; echo $response; } else { if ($type) { echo 'event: ' . $type . PHP_EOL; } - echo 'data: ' . OC_JSON::encode($data) . PHP_EOL; + echo 'data: ' . json_encode($data, JSON_HEX_TAG) . PHP_EOL; } echo PHP_EOL; flush(); diff --git a/lib/private/EventSourceFactory.php b/lib/private/EventSourceFactory.php index 197c8bf9e6c..57888b1be4c 100644 --- a/lib/private/EventSourceFactory.php +++ b/lib/private/EventSourceFactory.php @@ -1,23 +1,10 @@ <?php + +declare(strict_types=1); + /** - * @copyright Copyright (c) 2023 Daniel Kesselberg <mail@danielkesselberg.de> - * - * @author Daniel Kesselberg <mail@danielkesselberg.de> - * - * @license AGPL-3.0-or-later - * - * This code is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License, version 3, - * as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License, version 3, - * along with this program. If not, see <http://www.gnu.org/licenses/> - * + * SPDX-FileCopyrightText: 2023 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later */ namespace OC; @@ -27,20 +14,12 @@ use OCP\IEventSourceFactory; use OCP\IRequest; class EventSourceFactory implements IEventSourceFactory { - private IRequest $request; - - - public function __construct(IRequest $request) { - $this->request = $request; + public function __construct( + private IRequest $request, + ) { } - /** - * Create a new event source - * - * @return IEventSource - * @since 28.0.0 - */ public function create(): IEventSource { - return new \OC_EventSource($this->request); + return new EventSource($this->request); } } diff --git a/lib/private/Federation/CloudFederationFactory.php b/lib/private/Federation/CloudFederationFactory.php index 391213c21e0..f5f25d14ea1 100644 --- a/lib/private/Federation/CloudFederationFactory.php +++ b/lib/private/Federation/CloudFederationFactory.php @@ -1,24 +1,7 @@ <?php /** - * @copyright Copyright (c) 2018 Bjoern Schiessle <bjoern@schiessle.org> - * - * @author Bjoern Schiessle <bjoern@schiessle.org> - * - * @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/>. - * + * SPDX-FileCopyrightText: 2018 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later */ namespace OC\Federation; diff --git a/lib/private/Federation/CloudFederationNotification.php b/lib/private/Federation/CloudFederationNotification.php index aa1c3dc9e65..855580843ba 100644 --- a/lib/private/Federation/CloudFederationNotification.php +++ b/lib/private/Federation/CloudFederationNotification.php @@ -1,24 +1,7 @@ <?php /** - * @copyright Copyright (c) 2018 Bjoern Schiessle <bjoern@schiessle.org> - * - * @author Bjoern Schiessle <bjoern@schiessle.org> - * - * @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/>. - * + * SPDX-FileCopyrightText: 2018 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later */ namespace OC\Federation; diff --git a/lib/private/Federation/CloudFederationProviderManager.php b/lib/private/Federation/CloudFederationProviderManager.php index b11c4060ab4..79b37b44c82 100644 --- a/lib/private/Federation/CloudFederationProviderManager.php +++ b/lib/private/Federation/CloudFederationProviderManager.php @@ -1,25 +1,10 @@ <?php + +declare(strict_types=1); + /** - * @copyright Copyright (c) 2018 Bjoern Schiessle <bjoern@schiessle.org> - * - * @author Bjoern Schiessle <bjoern@schiessle.org> - * @author Christoph Wurst <christoph@winzerhof-wurst.at> - * - * @license GNU AGPL version 3 or any later version - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as - * published by the Free Software Foundation, either version 3 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - * + * SPDX-FileCopyrightText: 2018 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later */ namespace OC\Federation; @@ -32,6 +17,10 @@ use OCP\Federation\ICloudFederationProviderManager; use OCP\Federation\ICloudFederationShare; use OCP\Federation\ICloudIdManager; use OCP\Http\Client\IClientService; +use OCP\Http\Client\IResponse; +use OCP\IConfig; +use OCP\OCM\Exceptions\OCMProviderException; +use OCP\OCM\IOCMDiscoveryService; use Psr\Log\LoggerInterface; /** @@ -43,40 +32,16 @@ use Psr\Log\LoggerInterface; */ class CloudFederationProviderManager implements ICloudFederationProviderManager { /** @var array list of available cloud federation providers */ - private $cloudFederationProvider; - - /** @var IAppManager */ - private $appManager; - - /** @var IClientService */ - private $httpClientService; - - /** @var ICloudIdManager */ - private $cloudIdManager; - - private LoggerInterface $logger; - - /** @var array cache OCM end-points */ - private $ocmEndPoints = []; - - private $supportedAPIVersion = '1.0-proposal1'; - - /** - * CloudFederationProviderManager constructor. - * - * @param IAppManager $appManager - * @param IClientService $httpClientService - * @param ICloudIdManager $cloudIdManager - */ - public function __construct(IAppManager $appManager, - IClientService $httpClientService, - ICloudIdManager $cloudIdManager, - LoggerInterface $logger) { - $this->cloudFederationProvider = []; - $this->appManager = $appManager; - $this->httpClientService = $httpClientService; - $this->cloudIdManager = $cloudIdManager; - $this->logger = $logger; + private array $cloudFederationProvider = []; + + public function __construct( + private IConfig $config, + private IAppManager $appManager, + private IClientService $httpClientService, + private ICloudIdManager $cloudIdManager, + private IOCMDiscoveryService $discoveryService, + private LoggerInterface $logger + ) { } @@ -128,18 +93,23 @@ class CloudFederationProviderManager implements ICloudFederationProviderManager } } + /** + * @deprecated 29.0.0 - Use {@see sendCloudShare()} instead and handle errors manually + */ public function sendShare(ICloudFederationShare $share) { $cloudID = $this->cloudIdManager->resolveCloudId($share->getShareWith()); - $ocmEndPoint = $this->getOCMEndPoint($cloudID->getRemote()); - if (empty($ocmEndPoint)) { + try { + $ocmProvider = $this->discoveryService->discover($cloudID->getRemote()); + } catch (OCMProviderException $e) { return false; } $client = $this->httpClientService->newClient(); try { - $response = $client->post($ocmEndPoint . '/shares', [ + $response = $client->post($ocmProvider->getEndPoint() . '/shares', [ 'body' => json_encode($share->getShare()), 'headers' => ['content-type' => 'application/json'], + 'verify' => !$this->config->getSystemValueBool('sharing.federation.allowSelfSignedCertificates', false), 'timeout' => 10, 'connect_timeout' => 10, ]); @@ -163,22 +133,52 @@ class CloudFederationProviderManager implements ICloudFederationProviderManager } /** + * @param ICloudFederationShare $share + * @return IResponse + * @throws OCMProviderException + */ + public function sendCloudShare(ICloudFederationShare $share): IResponse { + $cloudID = $this->cloudIdManager->resolveCloudId($share->getShareWith()); + $ocmProvider = $this->discoveryService->discover($cloudID->getRemote()); + + $client = $this->httpClientService->newClient(); + try { + return $client->post($ocmProvider->getEndPoint() . '/shares', [ + 'body' => json_encode($share->getShare()), + 'headers' => ['content-type' => 'application/json'], + 'verify' => !$this->config->getSystemValueBool('sharing.federation.allowSelfSignedCertificates', false), + 'timeout' => 10, + 'connect_timeout' => 10, + ]); + } catch (\Throwable $e) { + $this->logger->error('Error while sending share to federation server: ' . $e->getMessage(), ['exception' => $e]); + try { + return $client->getResponseFromThrowable($e); + } catch (\Throwable $e) { + throw new OCMProviderException($e->getMessage(), $e->getCode(), $e); + } + } + } + + /** * @param string $url * @param ICloudFederationNotification $notification * @return array|false + * @deprecated 29.0.0 - Use {@see sendCloudNotification()} instead and handle errors manually */ public function sendNotification($url, ICloudFederationNotification $notification) { - $ocmEndPoint = $this->getOCMEndPoint($url); - - if (empty($ocmEndPoint)) { + try { + $ocmProvider = $this->discoveryService->discover($url); + } catch (OCMProviderException $e) { return false; } $client = $this->httpClientService->newClient(); try { - $response = $client->post($ocmEndPoint . '/notifications', [ + $response = $client->post($ocmProvider->getEndPoint() . '/notifications', [ 'body' => json_encode($notification->getMessage()), 'headers' => ['content-type' => 'application/json'], + 'verify' => !$this->config->getSystemValueBool('sharing.federation.allowSelfSignedCertificates', false), 'timeout' => 10, 'connect_timeout' => 10, ]); @@ -195,43 +195,39 @@ class CloudFederationProviderManager implements ICloudFederationProviderManager } /** - * check if the new cloud federation API is ready to be used - * - * @return bool - */ - public function isReady() { - return $this->appManager->isEnabledForUser('cloud_federation_api'); - } - /** - * check if server supports the new OCM api and ask for the correct end-point - * - * @param string $url full base URL of the cloud server - * @return string + * @param string $url + * @param ICloudFederationNotification $notification + * @return IResponse + * @throws OCMProviderException */ - protected function getOCMEndPoint($url) { - if (isset($this->ocmEndPoints[$url])) { - return $this->ocmEndPoints[$url]; - } + public function sendCloudNotification(string $url, ICloudFederationNotification $notification): IResponse { + $ocmProvider = $this->discoveryService->discover($url); $client = $this->httpClientService->newClient(); try { - $response = $client->get($url . '/ocm-provider/', ['timeout' => 10, 'connect_timeout' => 10]); - } catch (\Exception $e) { - $this->ocmEndPoints[$url] = ''; - return ''; - } - - $result = $response->getBody(); - $result = json_decode($result, true); - - $supportedVersion = isset($result['apiVersion']) && $result['apiVersion'] === $this->supportedAPIVersion; - - if (isset($result['endPoint']) && $supportedVersion) { - $this->ocmEndPoints[$url] = $result['endPoint']; - return $result['endPoint']; + return $client->post($ocmProvider->getEndPoint() . '/notifications', [ + 'body' => json_encode($notification->getMessage()), + 'headers' => ['content-type' => 'application/json'], + 'verify' => !$this->config->getSystemValueBool('sharing.federation.allowSelfSignedCertificates', false), + 'timeout' => 10, + 'connect_timeout' => 10, + ]); + } catch (\Throwable $e) { + $this->logger->error('Error while sending notification to federation server: ' . $e->getMessage(), ['exception' => $e]); + try { + return $client->getResponseFromThrowable($e); + } catch (\Throwable $e) { + throw new OCMProviderException($e->getMessage(), $e->getCode(), $e); + } } + } - $this->ocmEndPoints[$url] = ''; - return ''; + /** + * check if the new cloud federation API is ready to be used + * + * @return bool + */ + public function isReady() { + return $this->appManager->isEnabledForUser('cloud_federation_api'); } } diff --git a/lib/private/Federation/CloudFederationShare.php b/lib/private/Federation/CloudFederationShare.php index 0f79ba521ea..aa86905f234 100644 --- a/lib/private/Federation/CloudFederationShare.php +++ b/lib/private/Federation/CloudFederationShare.php @@ -1,25 +1,7 @@ <?php /** - * @copyright Copyright (c) 2018 Bjoern Schiessle <bjoern@schiessle.org> - * - * @author Bjoern Schiessle <bjoern@schiessle.org> - * @author Joas Schilling <coding@schilljs.com> - * - * @license GNU AGPL version 3 or any later version - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as - * published by the Free Software Foundation, either version 3 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - * + * SPDX-FileCopyrightText: 2018 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later */ namespace OC\Federation; @@ -57,16 +39,16 @@ class CloudFederationShare implements ICloudFederationShare { * @param string $sharedSecret */ public function __construct($shareWith = '', - $name = '', - $description = '', - $providerId = '', - $owner = '', - $ownerDisplayName = '', - $sharedBy = '', - $sharedByDisplayName = '', - $shareType = '', - $resourceType = '', - $sharedSecret = '' + $name = '', + $description = '', + $providerId = '', + $owner = '', + $ownerDisplayName = '', + $sharedBy = '', + $sharedByDisplayName = '', + $shareType = '', + $resourceType = '', + $sharedSecret = '' ) { $this->setShareWith($shareWith); $this->setResourceName($name); diff --git a/lib/private/Federation/CloudId.php b/lib/private/Federation/CloudId.php index 50e974831a6..c20dbfc6418 100644 --- a/lib/private/Federation/CloudId.php +++ b/lib/private/Federation/CloudId.php @@ -3,27 +3,8 @@ declare(strict_types=1); /** - * @copyright Copyright (c) 2017, Robin Appelman <robin@icewind.nl> - * - * @author Arthur Schiwon <blizzz@arthur-schiwon.de> - * @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/>. - * + * SPDX-FileCopyrightText: 2017 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later */ namespace OC\Federation; diff --git a/lib/private/Federation/CloudIdManager.php b/lib/private/Federation/CloudIdManager.php index 22b06af386f..cd5e4d511c3 100644 --- a/lib/private/Federation/CloudIdManager.php +++ b/lib/private/Federation/CloudIdManager.php @@ -3,30 +3,8 @@ declare(strict_types=1); /** - * @copyright Copyright (c) 2017, Robin Appelman <robin@icewind.nl> - * - * @author Arthur Schiwon <blizzz@arthur-schiwon.de> - * @author Christoph Wurst <christoph@winzerhof-wurst.at> - * @author Guillaume Virlet <github@virlet.org> - * @author Joas Schilling <coding@schilljs.com> - * @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/>. - * + * SPDX-FileCopyrightText: 2017 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later */ namespace OC\Federation; @@ -168,17 +146,15 @@ class CloudIdManager implements ICloudIdManager { public function getCloudId(string $user, ?string $remote): ICloudId { $isLocal = $remote === null; if ($isLocal) { - $remote = rtrim($this->removeProtocolFromUrl($this->urlGenerator->getAbsoluteURL('/')), '/'); - $fixedRemote = $this->fixRemoteURL($remote); - $host = $fixedRemote; - } else { - // note that for remote id's we don't strip the protocol for the remote we use to construct the CloudId - // this way if a user has an explicit non-https cloud id this will be preserved - // we do still use the version without protocol for looking up the display name - $fixedRemote = $this->fixRemoteURL($remote); - $host = $this->removeProtocolFromUrl($fixedRemote); + $remote = rtrim($this->urlGenerator->getAbsoluteURL('/'), '/'); } + // note that for remote id's we don't strip the protocol for the remote we use to construct the CloudId + // this way if a user has an explicit non-https cloud id this will be preserved + // we do still use the version without protocol for looking up the display name + $remote = $this->fixRemoteURL($remote); + $host = $this->removeProtocolFromUrl($remote); + $key = $user . '@' . ($isLocal ? 'local' : $host); $cached = $this->cache[$key] ?? $this->memCache->get($key); if ($cached) { @@ -192,28 +168,30 @@ class CloudIdManager implements ICloudIdManager { } else { $displayName = $this->getDisplayNameFromContact($user . '@' . $host); } - $id = $user . '@' . $remote; + + // For the visible cloudID we only strip away https + $id = $user . '@' . $this->removeProtocolFromUrl($remote, true); $data = [ 'id' => $id, 'user' => $user, - 'remote' => $fixedRemote, + 'remote' => $remote, 'displayName' => $displayName, ]; $this->cache[$key] = $data; $this->memCache->set($key, $data, 15 * 60); - return new CloudId($id, $user, $fixedRemote, $displayName); + return new CloudId($id, $user, $remote, $displayName); } /** * @param string $url * @return string */ - public function removeProtocolFromUrl(string $url): string { + public function removeProtocolFromUrl(string $url, bool $httpsOnly = false): string { if (str_starts_with($url, 'https://')) { return substr($url, 8); } - if (str_starts_with($url, 'http://')) { + if (!$httpsOnly && str_starts_with($url, 'http://')) { return substr($url, 7); } diff --git a/lib/private/Files/AppData/AppData.php b/lib/private/Files/AppData/AppData.php index 237fcb42e03..c13372ae1d9 100644 --- a/lib/private/Files/AppData/AppData.php +++ b/lib/private/Files/AppData/AppData.php @@ -3,32 +3,14 @@ declare(strict_types=1); /** - * @copyright 2016 Roeland Jago Douma <roeland@famdouma.nl> - * - * @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/>. - * + * SPDX-FileCopyrightText: 2016 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later */ namespace OC\Files\AppData; -use OCP\Cache\CappedMemoryCache; use OC\Files\SimpleFS\SimpleFolder; use OC\SystemConfig; +use OCP\Cache\CappedMemoryCache; use OCP\Files\Folder; use OCP\Files\IAppData; use OCP\Files\IRootFolder; @@ -53,8 +35,8 @@ class AppData implements IAppData { * @param string $appId */ public function __construct(IRootFolder $rootFolder, - SystemConfig $systemConfig, - string $appId) { + SystemConfig $systemConfig, + string $appId) { $this->rootFolder = $rootFolder; $this->config = $systemConfig; $this->appId = $appId; diff --git a/lib/private/Files/AppData/Factory.php b/lib/private/Files/AppData/Factory.php index 03f8fdedcbd..38b73f370b8 100644 --- a/lib/private/Files/AppData/Factory.php +++ b/lib/private/Files/AppData/Factory.php @@ -3,26 +3,8 @@ declare(strict_types=1); /** - * @copyright 2016 Roeland Jago Douma <roeland@famdouma.nl> - * - * @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/>. - * + * SPDX-FileCopyrightText: 2016 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later */ namespace OC\Files\AppData; @@ -39,7 +21,7 @@ class Factory implements IAppDataFactory { private array $folders = []; public function __construct(IRootFolder $rootFolder, - SystemConfig $systemConfig) { + SystemConfig $systemConfig) { $this->rootFolder = $rootFolder; $this->config = $systemConfig; } diff --git a/lib/private/Files/Cache/Cache.php b/lib/private/Files/Cache/Cache.php index 67d01bb6999..6c4bd2c9637 100644 --- a/lib/private/Files/Cache/Cache.php +++ b/lib/private/Files/Cache/Cache.php @@ -1,55 +1,24 @@ <?php + /** - * @copyright Copyright (c) 2016, ownCloud, Inc. - * - * @author Andreas Fischer <bantu@owncloud.com> - * @author Ari Selseng <ari@selseng.net> - * @author Artem Kochnev <MrJeos@gmail.com> - * @author Björn Schießle <bjoern@schiessle.org> - * @author Christoph Wurst <christoph@winzerhof-wurst.at> - * @author Daniel Kesselberg <mail@danielkesselberg.de> - * @author Florin Peter <github@florin-peter.de> - * @author Frédéric Fortier <frederic.fortier@oronospolytechnique.com> - * @author Jens-Christian Fischer <jens-christian.fischer@switch.ch> - * @author Joas Schilling <coding@schilljs.com> - * @author John Molakvoæ <skjnldsv@protonmail.com> - * @author Jörn Friedrich Dreyer <jfd@butonic.de> - * @author Lukas Reschke <lukas@statuscode.ch> - * @author Michael Gapczynski <GapczynskiM@gmail.com> - * @author Morris Jobke <hey@morrisjobke.de> - * @author Robin Appelman <robin@icewind.nl> - * @author Robin McCorkell <robin@mccorkell.me.uk> - * @author Roeland Jago Douma <roeland@famdouma.nl> - * @author Vincent Petry <vincent@nextcloud.com> - * - * @license AGPL-3.0 - * - * This code is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License, version 3, - * as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License, version 3, - * along with this program. If not, see <http://www.gnu.org/licenses/> - * + * SPDX-FileCopyrightText: 2016-2024 Nextcloud GmbH and Nextcloud contributors + * SPDX-FileCopyrightText: 2016 ownCloud, Inc. + * SPDX-License-Identifier: AGPL-3.0-only */ - namespace OC\Files\Cache; +use Doctrine\DBAL\Exception\RetryableException; use Doctrine\DBAL\Exception\UniqueConstraintViolationException; use OC\Files\Search\SearchComparison; use OC\Files\Search\SearchQuery; use OC\Files\Storage\Wrapper\Encryption; +use OC\SystemConfig; use OCP\DB\QueryBuilder\IQueryBuilder; use OCP\EventDispatcher\IEventDispatcher; use OCP\Files\Cache\CacheEntryInsertedEvent; +use OCP\Files\Cache\CacheEntryRemovedEvent; use OCP\Files\Cache\CacheEntryUpdatedEvent; use OCP\Files\Cache\CacheInsertEvent; -use OCP\Files\Cache\CacheEntryRemovedEvent; use OCP\Files\Cache\CacheUpdateEvent; use OCP\Files\Cache\ICache; use OCP\Files\Cache\ICacheEntry; @@ -59,6 +28,7 @@ use OCP\Files\Search\ISearchComparison; use OCP\Files\Search\ISearchOperator; use OCP\Files\Search\ISearchQuery; use OCP\Files\Storage\IStorage; +use OCP\FilesMetadata\IFilesMetadataManager; use OCP\IDBConnection; use OCP\Util; use Psr\Log\LoggerInterface; @@ -81,61 +51,53 @@ class Cache implements ICache { /** * @var array partial data for the cache */ - protected $partial = []; - - /** - * @var string - */ - protected $storageId; - - private $storage; - - /** - * @var Storage $storageCache - */ - protected $storageCache; - - /** @var IMimeTypeLoader */ - protected $mimetypeLoader; - - /** - * @var IDBConnection - */ - protected $connection; - - /** - * @var IEventDispatcher - */ - protected $eventDispatcher; - - /** @var QuerySearchHelper */ - protected $querySearchHelper; - - /** - * @param IStorage $storage - */ - public function __construct(IStorage $storage) { + protected array $partial = []; + protected string $storageId; + protected Storage $storageCache; + protected IMimeTypeLoader$mimetypeLoader; + protected IDBConnection $connection; + protected SystemConfig $systemConfig; + protected LoggerInterface $logger; + protected QuerySearchHelper $querySearchHelper; + protected IEventDispatcher $eventDispatcher; + protected IFilesMetadataManager $metadataManager; + + public function __construct( + private IStorage $storage, + // this constructor is used in to many pleases to easily do proper di + // so instead we group it all together + ?CacheDependencies $dependencies = null, + ) { $this->storageId = $storage->getId(); - $this->storage = $storage; if (strlen($this->storageId) > 64) { $this->storageId = md5($this->storageId); } - - $this->storageCache = new Storage($storage); - $this->mimetypeLoader = \OC::$server->getMimeTypeLoader(); - $this->connection = \OC::$server->getDatabaseConnection(); - $this->eventDispatcher = \OC::$server->get(IEventDispatcher::class); - $this->querySearchHelper = \OCP\Server::get(QuerySearchHelper::class); + if (!$dependencies) { + $dependencies = \OC::$server->get(CacheDependencies::class); + } + $this->storageCache = new Storage($this->storage, true, $dependencies->getConnection()); + $this->mimetypeLoader = $dependencies->getMimeTypeLoader(); + $this->connection = $dependencies->getConnection(); + $this->systemConfig = $dependencies->getSystemConfig(); + $this->logger = $dependencies->getLogger(); + $this->querySearchHelper = $dependencies->getQuerySearchHelper(); + $this->eventDispatcher = $dependencies->getEventDispatcher(); + $this->metadataManager = $dependencies->getMetadataManager(); } protected function getQueryBuilder() { return new CacheQueryBuilder( $this->connection, - \OC::$server->getSystemConfig(), - \OC::$server->get(LoggerInterface::class) + $this->systemConfig, + $this->logger, + $this->metadataManager, ); } + public function getStorageCache(): Storage { + return $this->storageCache; + } + /** * Get the numeric storage id for this cache's storage * @@ -154,6 +116,7 @@ class Cache implements ICache { public function get($file) { $query = $this->getQueryBuilder(); $query->selectFileCache(); + $metadataQuery = $query->selectMetadata(); if (is_string($file) || $file == '') { // normalize file @@ -175,6 +138,7 @@ class Cache implements ICache { } elseif (!$data) { return $data; } else { + $data['metadata'] = $metadataQuery->extractMetadata($data)->asArray(); return self::cacheEntryFromData($data, $this->mimetypeLoader); } } @@ -239,11 +203,14 @@ class Cache implements ICache { ->whereParent($fileId) ->orderBy('name', 'ASC'); + $metadataQuery = $query->selectMetadata(); + $result = $query->execute(); $files = $result->fetchAll(); $result->closeCursor(); - return array_map(function (array $data) { + return array_map(function (array $data) use ($metadataQuery) { + $data['metadata'] = $metadataQuery->extractMetadata($data)->asArray(); return self::cacheEntryFromData($data, $this->mimetypeLoader); }, $files); } @@ -447,7 +414,7 @@ class Cache implements ICache { $params = []; $extensionParams = []; foreach ($data as $name => $value) { - if (array_search($name, $fields) !== false) { + if (in_array($name, $fields)) { if ($name === 'path') { $params['path_hash'] = md5($value); } elseif ($name === 'mimetype') { @@ -467,7 +434,7 @@ class Cache implements ICache { } $params[$name] = $value; } - if (array_search($name, $extensionFields) !== false) { + if (in_array($name, $extensionFields)) { $extensionParams[$name] = $value; } } @@ -586,8 +553,13 @@ class Cache implements ICache { return $cacheEntry->getPath(); }, $children); - $deletedIds = array_merge($deletedIds, $childIds); - $deletedPaths = array_merge($deletedPaths, $childPaths); + foreach ($childIds as $childId) { + $deletedIds[] = $childId; + } + + foreach ($childPaths as $childPath) { + $deletedPaths[] = $childPath; + } $query = $this->getQueryBuilder(); $query->delete('filecache_extended') @@ -599,9 +571,12 @@ class Cache implements ICache { } /** @var ICacheEntry[] $childFolders */ - $childFolders = array_filter($children, function ($child) { - return $child->getMimeType() == FileInfo::MIMETYPE_FOLDER; - }); + $childFolders = []; + foreach ($children as $child) { + if ($child->getMimeType() == FileInfo::MIMETYPE_FOLDER) { + $childFolders[] = $child; + } + } foreach ($childFolders as $folder) { $parentIds[] = $folder->getId(); $queue[] = $folder->getId(); @@ -612,6 +587,9 @@ class Cache implements ICache { $query->delete('filecache') ->whereParentInParameter('parentIds'); + // Sorting before chunking allows the db to find the entries close to each + // other in the index + sort($parentIds, SORT_NUMERIC); foreach (array_chunk($parentIds, 1000) as $parentIdChunk) { $query->setParameter('parentIds', $parentIdChunk, IQueryBuilder::PARAM_INT_ARRAY); $query->execute(); @@ -685,7 +663,6 @@ class Cache implements ICache { throw new \Exception('Invalid target storage id: ' . $targetStorageId); } - $this->connection->beginTransaction(); if ($sourceData['mimetype'] === 'httpd/unix-directory') { //update all child entries $sourceLength = mb_strlen($sourcePath); @@ -708,12 +685,31 @@ class Cache implements ICache { $query->set('encrypted', $query->createNamedParameter(0, IQueryBuilder::PARAM_INT)); } - try { - $query->execute(); - } catch (\OC\DatabaseException $e) { - $this->connection->rollBack(); - throw $e; + // Retry transaction in case of RetryableException like deadlocks. + // Retry up to 4 times because we should receive up to 4 concurrent requests from the frontend + $retryLimit = 4; + for ($i = 1; $i <= $retryLimit; $i++) { + try { + $this->connection->beginTransaction(); + $query->executeStatement(); + break; + } catch (\OC\DatabaseException $e) { + $this->connection->rollBack(); + throw $e; + } catch (RetryableException $e) { + // Simply throw if we already retried 4 times. + if ($i === $retryLimit) { + throw $e; + } + + $this->connection->rollBack(); + + // Sleep a bit to give some time to the other transaction to finish. + usleep(100 * 1000 * $i); + } } + } else { + $this->connection->beginTransaction(); } $query = $this->getQueryBuilder(); diff --git a/lib/private/Files/Cache/CacheDependencies.php b/lib/private/Files/Cache/CacheDependencies.php new file mode 100644 index 00000000000..61d2a2f9646 --- /dev/null +++ b/lib/private/Files/Cache/CacheDependencies.php @@ -0,0 +1,61 @@ +<?php + +/** + * SPDX-FileCopyrightText: 2023 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later + */ +namespace OC\Files\Cache; + +use OC\SystemConfig; +use OC\User\DisplayNameCache; +use OCP\EventDispatcher\IEventDispatcher; +use OCP\Files\IMimeTypeLoader; +use OCP\FilesMetadata\IFilesMetadataManager; +use OCP\IDBConnection; +use Psr\Log\LoggerInterface; + +class CacheDependencies { + public function __construct( + private IMimeTypeLoader $mimeTypeLoader, + private IDBConnection $connection, + private IEventDispatcher $eventDispatcher, + private QuerySearchHelper $querySearchHelper, + private SystemConfig $systemConfig, + private LoggerInterface $logger, + private IFilesMetadataManager $metadataManager, + private DisplayNameCache $displayNameCache, + ) { + } + + public function getMimeTypeLoader(): IMimeTypeLoader { + return $this->mimeTypeLoader; + } + + public function getConnection(): IDBConnection { + return $this->connection; + } + + public function getEventDispatcher(): IEventDispatcher { + return $this->eventDispatcher; + } + + public function getQuerySearchHelper(): QuerySearchHelper { + return $this->querySearchHelper; + } + + public function getSystemConfig(): SystemConfig { + return $this->systemConfig; + } + + public function getLogger(): LoggerInterface { + return $this->logger; + } + + public function getDisplayNameCache(): DisplayNameCache { + return $this->displayNameCache; + } + + public function getMetadataManager(): IFilesMetadataManager { + return $this->metadataManager; + } +} diff --git a/lib/private/Files/Cache/CacheEntry.php b/lib/private/Files/Cache/CacheEntry.php index ce9df2823c8..e9417c8012a 100644 --- a/lib/private/Files/Cache/CacheEntry.php +++ b/lib/private/Files/Cache/CacheEntry.php @@ -1,24 +1,9 @@ <?php + /** - * @copyright Copyright (c) 2016, ownCloud, Inc. - * - * @author Christoph Wurst <christoph@winzerhof-wurst.at> - * @author Robin Appelman <robin@icewind.nl> - * - * @license AGPL-3.0 - * - * This code is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License, version 3, - * as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License, version 3, - * along with this program. If not, see <http://www.gnu.org/licenses/> - * + * SPDX-FileCopyrightText: 2016-2024 Nextcloud GmbH and Nextcloud contributors + * SPDX-FileCopyrightText: 2016 ownCloud, Inc. + * SPDX-License-Identifier: AGPL-3.0-only */ namespace OC\Files\Cache; @@ -134,7 +119,7 @@ class CacheEntry implements ICacheEntry { } public function getUnencryptedSize(): int { - if (isset($this->data['unencrypted_size']) && $this->data['unencrypted_size'] > 0) { + if ($this->data['encrypted'] && isset($this->data['unencrypted_size']) && $this->data['unencrypted_size'] > 0) { return $this->data['unencrypted_size']; } else { return $this->data['size'] ?? 0; diff --git a/lib/private/Files/Cache/CacheQueryBuilder.php b/lib/private/Files/Cache/CacheQueryBuilder.php index 34d2177b84e..9bf5f970458 100644 --- a/lib/private/Files/Cache/CacheQueryBuilder.php +++ b/lib/private/Files/Cache/CacheQueryBuilder.php @@ -3,31 +3,16 @@ declare(strict_types=1); /** - * @copyright Copyright (c) 2019 Robin Appelman <robin@icewind.nl> - * - * @author Robin Appelman <robin@icewind.nl> - * - * @license GNU AGPL version 3 or any later version - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as - * published by the Free Software Foundation, either version 3 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - * + * SPDX-FileCopyrightText: 2019 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later */ namespace OC\Files\Cache; use OC\DB\QueryBuilder\QueryBuilder; use OC\SystemConfig; use OCP\DB\QueryBuilder\IQueryBuilder; +use OCP\FilesMetadata\IFilesMetadataManager; +use OCP\FilesMetadata\IMetadataQuery; use OCP\IDBConnection; use Psr\Log\LoggerInterface; @@ -35,9 +20,14 @@ use Psr\Log\LoggerInterface; * Query builder with commonly used helpers for filecache queries */ class CacheQueryBuilder extends QueryBuilder { - private $alias = null; - - public function __construct(IDBConnection $connection, SystemConfig $systemConfig, LoggerInterface $logger) { + private ?string $alias = null; + + public function __construct( + IDBConnection $connection, + SystemConfig $systemConfig, + LoggerInterface $logger, + private IFilesMetadataManager $filesMetadataManager, + ) { parent::__construct($connection, $systemConfig, $logger); } @@ -60,10 +50,10 @@ class CacheQueryBuilder extends QueryBuilder { return $this; } - public function selectFileCache(string $alias = null, bool $joinExtendedCache = true) { + public function selectFileCache(?string $alias = null, bool $joinExtendedCache = true) { $name = $alias ?: 'filecache'; $this->select("$name.fileid", 'storage', 'path', 'path_hash', "$name.parent", "$name.name", 'mimetype', 'mimepart', 'size', 'mtime', - 'storage_mtime', 'encrypted', 'etag', 'permissions', 'checksum', 'unencrypted_size') + 'storage_mtime', 'encrypted', 'etag', "$name.permissions", 'checksum', 'unencrypted_size') ->from('filecache', $name); if ($joinExtendedCache) { @@ -126,4 +116,15 @@ class CacheQueryBuilder extends QueryBuilder { return $this; } + + /** + * join metadata to current query builder and returns an helper + * + * @return IMetadataQuery + */ + public function selectMetadata(): IMetadataQuery { + $metadataQuery = $this->filesMetadataManager->getMetadataQuery($this, $this->alias, 'fileid'); + $metadataQuery->retrieveMetadata(); + return $metadataQuery; + } } diff --git a/lib/private/Files/Cache/FailedCache.php b/lib/private/Files/Cache/FailedCache.php index d60e09ea329..8ba2ac491bf 100644 --- a/lib/private/Files/Cache/FailedCache.php +++ b/lib/private/Files/Cache/FailedCache.php @@ -1,23 +1,9 @@ <?php + /** - * @copyright Copyright (c) 2016, ownCloud, Inc. - * - * @author Robin Appelman <robin@icewind.nl> - * - * @license AGPL-3.0 - * - * This code is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License, version 3, - * as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License, version 3, - * along with this program. If not, see <http://www.gnu.org/licenses/> - * + * SPDX-FileCopyrightText: 2016-2024 Nextcloud GmbH and Nextcloud contributors + * SPDX-FileCopyrightText: 2016 ownCloud, Inc. + * SPDX-License-Identifier: AGPL-3.0-only */ namespace OC\Files\Cache; diff --git a/lib/private/Files/Cache/FileAccess.php b/lib/private/Files/Cache/FileAccess.php new file mode 100644 index 00000000000..5818017bd66 --- /dev/null +++ b/lib/private/Files/Cache/FileAccess.php @@ -0,0 +1,99 @@ +<?php + +declare(strict_types=1); + +/** + * SPDX-FileCopyrightText: 2024 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later + */ +namespace OC\Files\Cache; + +use OC\FilesMetadata\FilesMetadataManager; +use OC\SystemConfig; +use OCP\DB\QueryBuilder\IQueryBuilder; +use OCP\Files\Cache\IFileAccess; +use OCP\Files\IMimeTypeLoader; +use OCP\IDBConnection; +use Psr\Log\LoggerInterface; + +/** + * Low level access to the file cache + */ +class FileAccess implements IFileAccess { + public function __construct( + private IDBConnection $connection, + private SystemConfig $systemConfig, + private LoggerInterface $logger, + private FilesMetadataManager $metadataManager, + private IMimeTypeLoader $mimeTypeLoader, + ) { + } + + private function getQuery(): CacheQueryBuilder { + return new CacheQueryBuilder( + $this->connection, + $this->systemConfig, + $this->logger, + $this->metadataManager, + ); + } + + public function getByFileIdInStorage(int $fileId, int $storageId): ?CacheEntry { + $items = array_values($this->getByFileIdsInStorage([$fileId], $storageId)); + return $items[0] ?? null; + } + + public function getByPathInStorage(string $path, int $storageId): ?CacheEntry { + $query = $this->getQuery()->selectFileCache(); + $query->andWhere($query->expr()->eq('filecache.path_hash', $query->createNamedParameter(md5($path)))); + $query->andWhere($query->expr()->eq('filecache.storage', $query->createNamedParameter($storageId, IQueryBuilder::PARAM_INT))); + + $row = $query->executeQuery()->fetch(); + return $row ? Cache::cacheEntryFromData($row, $this->mimeTypeLoader) : null; + } + + public function getByFileId(int $fileId): ?CacheEntry { + $items = array_values($this->getByFileIds([$fileId])); + return $items[0] ?? null; + } + + /** + * @param array[] $rows + * @return array<int, CacheEntry> + */ + private function rowsToEntries(array $rows): array { + $result = []; + foreach ($rows as $row) { + $entry = Cache::cacheEntryFromData($row, $this->mimeTypeLoader); + $result[$entry->getId()] = $entry; + } + return $result; + } + + /** + * @param int[] $fileIds + * @return array<int, CacheEntry> + */ + public function getByFileIds(array $fileIds): array { + $query = $this->getQuery()->selectFileCache(); + $query->andWhere($query->expr()->in('filecache.fileid', $query->createNamedParameter($fileIds, IQueryBuilder::PARAM_INT_ARRAY))); + + $rows = $query->executeQuery()->fetchAll(); + return $this->rowsToEntries($rows); + } + + /** + * @param int[] $fileIds + * @param int $storageId + * @return array<int, CacheEntry> + */ + public function getByFileIdsInStorage(array $fileIds, int $storageId): array { + $fileIds = array_values($fileIds); + $query = $this->getQuery()->selectFileCache(); + $query->andWhere($query->expr()->in('filecache.fileid', $query->createNamedParameter($fileIds, IQueryBuilder::PARAM_INT_ARRAY))); + $query->andWhere($query->expr()->eq('filecache.storage', $query->createNamedParameter($storageId, IQueryBuilder::PARAM_INT))); + + $rows = $query->executeQuery()->fetchAll(); + return $this->rowsToEntries($rows); + } +} diff --git a/lib/private/Files/Cache/HomeCache.php b/lib/private/Files/Cache/HomeCache.php index 83e5cb89b8a..248cdc818f0 100644 --- a/lib/private/Files/Cache/HomeCache.php +++ b/lib/private/Files/Cache/HomeCache.php @@ -1,30 +1,9 @@ <?php + /** - * @copyright Copyright (c) 2016, ownCloud, Inc. - * - * @author Andreas Fischer <bantu@owncloud.com> - * @author Björn Schießle <bjoern@schiessle.org> - * @author Christoph Wurst <christoph@winzerhof-wurst.at> - * @author Joas Schilling <coding@schilljs.com> - * @author Jörn Friedrich Dreyer <jfd@butonic.de> - * @author Morris Jobke <hey@morrisjobke.de> - * @author Robin Appelman <robin@icewind.nl> - * @author Vincent Petry <vincent@nextcloud.com> - * - * @license AGPL-3.0 - * - * This code is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License, version 3, - * as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License, version 3, - * along with this program. If not, see <http://www.gnu.org/licenses/> - * + * SPDX-FileCopyrightText: 2016-2024 Nextcloud GmbH and Nextcloud contributors + * SPDX-FileCopyrightText: 2016 ownCloud, Inc. + * SPDX-License-Identifier: AGPL-3.0-only */ namespace OC\Files\Cache; diff --git a/lib/private/Files/Cache/HomePropagator.php b/lib/private/Files/Cache/HomePropagator.php index 6dba09d756b..d4ac8a7c8e3 100644 --- a/lib/private/Files/Cache/HomePropagator.php +++ b/lib/private/Files/Cache/HomePropagator.php @@ -1,23 +1,9 @@ <?php + /** - * @copyright Copyright (c) 2016, ownCloud, Inc. - * - * @author Robin Appelman <robin@icewind.nl> - * - * @license AGPL-3.0 - * - * This code is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License, version 3, - * as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License, version 3, - * along with this program. If not, see <http://www.gnu.org/licenses/> - * + * SPDX-FileCopyrightText: 2016-2024 Nextcloud GmbH and Nextcloud contributors + * SPDX-FileCopyrightText: 2016 ownCloud, Inc. + * SPDX-License-Identifier: AGPL-3.0-only */ namespace OC\Files\Cache; diff --git a/lib/private/Files/Cache/LocalRootScanner.php b/lib/private/Files/Cache/LocalRootScanner.php index df5ddd0075b..3f4f70b865b 100644 --- a/lib/private/Files/Cache/LocalRootScanner.php +++ b/lib/private/Files/Cache/LocalRootScanner.php @@ -3,25 +3,8 @@ declare(strict_types=1); /** - * @copyright Copyright (c) 2020 Robin Appelman <robin@icewind.nl> - * - * @author Robin Appelman <robin@icewind.nl> - * - * @license GNU AGPL version 3 or any later version - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as - * published by the Free Software Foundation, either version 3 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - * + * SPDX-FileCopyrightText: 2020 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later */ namespace OC\Files\Cache; diff --git a/lib/private/Files/Cache/MoveFromCacheTrait.php b/lib/private/Files/Cache/MoveFromCacheTrait.php index 77cd7ea6d8c..db35c6bb7f8 100644 --- a/lib/private/Files/Cache/MoveFromCacheTrait.php +++ b/lib/private/Files/Cache/MoveFromCacheTrait.php @@ -1,23 +1,9 @@ <?php + /** - * @copyright Copyright (c) 2016, ownCloud, Inc. - * - * @author Robin Appelman <robin@icewind.nl> - * - * @license AGPL-3.0 - * - * This code is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License, version 3, - * as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License, version 3, - * along with this program. If not, see <http://www.gnu.org/licenses/> - * + * SPDX-FileCopyrightText: 2016-2024 Nextcloud GmbH and Nextcloud contributors + * SPDX-FileCopyrightText: 2016 ownCloud, Inc. + * SPDX-License-Identifier: AGPL-3.0-only */ namespace OC\Files\Cache; diff --git a/lib/private/Files/Cache/NullWatcher.php b/lib/private/Files/Cache/NullWatcher.php index 2e83c1006bb..e3659214849 100644 --- a/lib/private/Files/Cache/NullWatcher.php +++ b/lib/private/Files/Cache/NullWatcher.php @@ -3,25 +3,8 @@ declare(strict_types=1); /** - * @copyright Copyright (c) 2020 Robin Appelman <robin@icewind.nl> - * - * @author Robin Appelman <robin@icewind.nl> - * - * @license GNU AGPL version 3 or any later version - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as - * published by the Free Software Foundation, either version 3 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - * + * SPDX-FileCopyrightText: 2020 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later */ namespace OC\Files\Cache; diff --git a/lib/private/Files/Cache/Propagator.php b/lib/private/Files/Cache/Propagator.php index 327d0d80bf2..5580dcf22a8 100644 --- a/lib/private/Files/Cache/Propagator.php +++ b/lib/private/Files/Cache/Propagator.php @@ -1,27 +1,10 @@ <?php + /** - * @copyright Copyright (c) 2016, ownCloud, Inc. - * - * @author Joas Schilling <coding@schilljs.com> - * @author Robin Appelman <robin@icewind.nl> - * @author Roeland Jago Douma <roeland@famdouma.nl> - * - * @license AGPL-3.0 - * - * This code is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License, version 3, - * as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License, version 3, - * along with this program. If not, see <http://www.gnu.org/licenses/> - * + * SPDX-FileCopyrightText: 2016-2024 Nextcloud GmbH and Nextcloud contributors + * SPDX-FileCopyrightText: 2016 ownCloud, Inc. + * SPDX-License-Identifier: AGPL-3.0-only */ - namespace OC\Files\Cache; use OC\DB\Exceptions\DbalException; diff --git a/lib/private/Files/Cache/QuerySearchHelper.php b/lib/private/Files/Cache/QuerySearchHelper.php index 15c089a0f11..c31d62e1a86 100644 --- a/lib/private/Files/Cache/QuerySearchHelper.php +++ b/lib/private/Files/Cache/QuerySearchHelper.php @@ -1,27 +1,7 @@ <?php /** - * @copyright Copyright (c) 2017 Robin Appelman <robin@icewind.nl> - * - * @author Christoph Wurst <christoph@winzerhof-wurst.at> - * @author Robin Appelman <robin@icewind.nl> - * @author Roeland Jago Douma <roeland@famdouma.nl> - * @author Tobias Kaminsky <tobias@kaminsky.me> - * - * @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/>. - * + * SPDX-FileCopyrightText: 2017 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later */ namespace OC\Files\Cache; @@ -37,52 +17,47 @@ use OCP\Files\IRootFolder; use OCP\Files\Mount\IMountPoint; use OCP\Files\Search\ISearchBinaryOperator; use OCP\Files\Search\ISearchQuery; +use OCP\FilesMetadata\IFilesMetadataManager; +use OCP\FilesMetadata\IMetadataQuery; use OCP\IDBConnection; use OCP\IGroupManager; use OCP\IUser; use Psr\Log\LoggerInterface; class QuerySearchHelper { - /** @var IMimeTypeLoader */ - private $mimetypeLoader; - /** @var IDBConnection */ - private $connection; - /** @var SystemConfig */ - private $systemConfig; - private LoggerInterface $logger; - /** @var SearchBuilder */ - private $searchBuilder; - /** @var QueryOptimizer */ - private $queryOptimizer; - private IGroupManager $groupManager; - public function __construct( - IMimeTypeLoader $mimetypeLoader, - IDBConnection $connection, - SystemConfig $systemConfig, - LoggerInterface $logger, - SearchBuilder $searchBuilder, - QueryOptimizer $queryOptimizer, - IGroupManager $groupManager, + private IMimeTypeLoader $mimetypeLoader, + private IDBConnection $connection, + private SystemConfig $systemConfig, + private LoggerInterface $logger, + private SearchBuilder $searchBuilder, + private QueryOptimizer $queryOptimizer, + private IGroupManager $groupManager, + private IFilesMetadataManager $filesMetadataManager, ) { - $this->mimetypeLoader = $mimetypeLoader; - $this->connection = $connection; - $this->systemConfig = $systemConfig; - $this->logger = $logger; - $this->searchBuilder = $searchBuilder; - $this->queryOptimizer = $queryOptimizer; - $this->groupManager = $groupManager; } protected function getQueryBuilder() { return new CacheQueryBuilder( $this->connection, $this->systemConfig, - $this->logger + $this->logger, + $this->filesMetadataManager, ); } - protected function applySearchConstraints(CacheQueryBuilder $query, ISearchQuery $searchQuery, array $caches): void { + /** + * @param CacheQueryBuilder $query + * @param ISearchQuery $searchQuery + * @param array $caches + * @param IMetadataQuery|null $metadataQuery + */ + protected function applySearchConstraints( + CacheQueryBuilder $query, + ISearchQuery $searchQuery, + array $caches, + ?IMetadataQuery $metadataQuery = null + ): void { $storageFilters = array_values(array_map(function (ICache $cache) { return $cache->getQueryFilterForStorage(); }, $caches)); @@ -90,12 +65,12 @@ class QuerySearchHelper { $filter = new SearchBinaryOperator(ISearchBinaryOperator::OPERATOR_AND, [$searchQuery->getSearchOperation(), $storageFilter]); $this->queryOptimizer->processOperator($filter); - $searchExpr = $this->searchBuilder->searchOperatorToDBExpr($query, $filter); + $searchExpr = $this->searchBuilder->searchOperatorToDBExpr($query, $filter, $metadataQuery); if ($searchExpr) { $query->andWhere($searchExpr); } - $this->searchBuilder->addSearchOrdersToQuery($query, $searchQuery->getOrder()); + $this->searchBuilder->addSearchOrdersToQuery($query, $searchQuery->getOrder(), $metadataQuery); if ($searchQuery->getLimit()) { $query->setMaxResults($searchQuery->getLimit()); @@ -144,6 +119,11 @@ class QuerySearchHelper { )); } + + protected function equipQueryForShares(CacheQueryBuilder $query): void { + $query->join('file', 'share', 's', $query->expr()->eq('file.fileid', 's.file_source')); + } + /** * Perform a file system search in multiple caches * @@ -175,19 +155,26 @@ class QuerySearchHelper { $query = $builder->selectFileCache('file', false); $requestedFields = $this->searchBuilder->extractRequestedFields($searchQuery->getSearchOperation()); + if (in_array('systemtag', $requestedFields)) { $this->equipQueryForSystemTags($query, $this->requireUser($searchQuery)); } if (in_array('tagname', $requestedFields) || in_array('favorite', $requestedFields)) { $this->equipQueryForDavTags($query, $this->requireUser($searchQuery)); } + if (in_array('owner', $requestedFields) || in_array('share_with', $requestedFields) || in_array('share_type', $requestedFields)) { + $this->equipQueryForShares($query); + } - $this->applySearchConstraints($query, $searchQuery, $caches); + $metadataQuery = $query->selectMetadata(); + + $this->applySearchConstraints($query, $searchQuery, $caches, $metadataQuery); $result = $query->execute(); $files = $result->fetchAll(); - $rawEntries = array_map(function (array $data) { + $rawEntries = array_map(function (array $data) use ($metadataQuery) { + $data['metadata'] = $metadataQuery->extractMetadata($data)->asArray(); return Cache::cacheEntryFromData($data, $this->mimetypeLoader); }, $files); diff --git a/lib/private/Files/Cache/Scanner.php b/lib/private/Files/Cache/Scanner.php index 52268032409..c44fc6fbd3d 100644 --- a/lib/private/Files/Cache/Scanner.php +++ b/lib/private/Files/Cache/Scanner.php @@ -1,50 +1,22 @@ <?php + /** - * @copyright Copyright (c) 2016, ownCloud, Inc. - * - * @author Ari Selseng <ari@selseng.net> - * @author Arthur Schiwon <blizzz@arthur-schiwon.de> - * @author Björn Schießle <bjoern@schiessle.org> - * @author Christoph Wurst <christoph@winzerhof-wurst.at> - * @author Daniel Jagszent <daniel@jagszent.de> - * @author Joas Schilling <coding@schilljs.com> - * @author Jörn Friedrich Dreyer <jfd@butonic.de> - * @author Lukas Reschke <lukas@statuscode.ch> - * @author Martin Mattel <martin.mattel@diemattels.at> - * @author Morris Jobke <hey@morrisjobke.de> - * @author Owen Winkler <a_github@midnightcircus.com> - * @author Robin Appelman <robin@icewind.nl> - * @author Robin McCorkell <robin@mccorkell.me.uk> - * @author Thomas Müller <thomas.mueller@tmit.eu> - * @author Vincent Petry <vincent@nextcloud.com> - * - * @license AGPL-3.0 - * - * This code is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License, version 3, - * as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License, version 3, - * along with this program. If not, see <http://www.gnu.org/licenses/> - * + * SPDX-FileCopyrightText: 2016-2024 Nextcloud GmbH and Nextcloud contributors + * SPDX-FileCopyrightText: 2016 ownCloud, Inc. + * SPDX-License-Identifier: AGPL-3.0-only */ namespace OC\Files\Cache; use Doctrine\DBAL\Exception; use OC\Files\Storage\Wrapper\Encryption; +use OC\Files\Storage\Wrapper\Jail; +use OC\Hooks\BasicEmitter; use OCP\Files\Cache\IScanner; use OCP\Files\ForbiddenException; use OCP\Files\NotFoundException; use OCP\Files\Storage\IReliableEtagStorage; use OCP\IDBConnection; use OCP\Lock\ILockingProvider; -use OC\Files\Storage\Wrapper\Jail; -use OC\Hooks\BasicEmitter; use Psr\Log\LoggerInterface; /** @@ -96,7 +68,7 @@ class Scanner extends BasicEmitter implements IScanner { $this->storageId = $this->storage->getId(); $this->cache = $storage->getCache(); $this->cacheActive = !\OC::$server->getConfig()->getSystemValueBool('filesystem_cache_readonly', false); - $this->lockingProvider = \OC::$server->getLockingProvider(); + $this->lockingProvider = \OC::$server->get(ILockingProvider::class); $this->connection = \OC::$server->get(IDBConnection::class); } @@ -203,7 +175,9 @@ class Scanner extends BasicEmitter implements IScanner { $fileId = $cacheData['fileid']; $data['fileid'] = $fileId; // only reuse data if the file hasn't explicitly changed - if (isset($data['storage_mtime']) && isset($cacheData['storage_mtime']) && $data['storage_mtime'] === $cacheData['storage_mtime']) { + $mtimeUnchanged = isset($data['storage_mtime']) && isset($cacheData['storage_mtime']) && $data['storage_mtime'] === $cacheData['storage_mtime']; + // if the folder is marked as unscanned, never reuse etags + if ($mtimeUnchanged && $cacheData['size'] !== -1) { $data['mtime'] = $cacheData['mtime']; if (($reuseExisting & self::REUSE_SIZE) && ($data['size'] === -1)) { $data['size'] = $cacheData['size']; @@ -219,7 +193,13 @@ class Scanner extends BasicEmitter implements IScanner { } // Only update metadata that has changed - $newData = array_diff_assoc($data, $cacheData->getData()); + // i.e. get all the values in $data that are not present in the cache already + $newData = $this->array_diff_assoc_multi($data, $cacheData->getData()); + + // make it known to the caller that etag has been changed and needs propagation + if (isset($newData['etag'])) { + $data['etag_changed'] = true; + } } else { // we only updated unencrypted_size if it's already set unset($data['unencrypted_size']); @@ -363,6 +343,50 @@ class Scanner extends BasicEmitter implements IScanner { } /** + * Compares $array1 against $array2 and returns all the values in $array1 that are not in $array2 + * Note this is a one-way check - i.e. we don't care about things that are in $array2 that aren't in $array1 + * + * Supports multi-dimensional arrays + * Also checks keys/indexes + * Comparisons are strict just like array_diff_assoc + * Order of keys/values does not matter + * + * @param array $array1 + * @param array $array2 + * @return array with the differences between $array1 and $array1 + * @throws \InvalidArgumentException if $array1 isn't an actual array + * + */ + protected function array_diff_assoc_multi(array $array1, array $array2) { + + $result = []; + + foreach ($array1 as $key => $value) { + + // if $array2 doesn't have the same key, that's a result + if (!array_key_exists($key, $array2)) { + $result[$key] = $value; + continue; + } + + // if $array2's value for the same key is different, that's a result + if ($array2[$key] !== $value && !is_array($value)) { + $result[$key] = $value; + continue; + } + + if (is_array($value)) { + $nestedDiff = $this->array_diff_assoc_multi($value, $array2[$key]); + if (!empty($nestedDiff)) { + $result[$key] = $nestedDiff; + continue; + } + } + } + return $result; + } + + /** * Get the children currently in the cache * * @param int $folderId @@ -385,19 +409,23 @@ class Scanner extends BasicEmitter implements IScanner { * @param int $reuse a combination of self::REUSE_* * @param int $folderId id for the folder to be scanned * @param bool $lock set to false to disable getting an additional read lock during scanning - * @param int $oldSize the size of the folder before (re)scanning the children + * @param int|float $oldSize the size of the folder before (re)scanning the children * @return int|float the size of the scanned folder or -1 if the size is unknown at this stage */ - protected function scanChildren(string $path, $recursive, int $reuse, int $folderId, bool $lock, int $oldSize) { + protected function scanChildren(string $path, $recursive, int $reuse, int $folderId, bool $lock, int|float $oldSize, &$etagChanged = false) { if ($reuse === -1) { $reuse = ($recursive === self::SCAN_SHALLOW) ? self::REUSE_ETAG | self::REUSE_SIZE : self::REUSE_ETAG; } $this->emit('\OC\Files\Cache\Scanner', 'scanFolder', [$path, $this->storageId]); $size = 0; - $childQueue = $this->handleChildren($path, $recursive, $reuse, $folderId, $lock, $size); + $childQueue = $this->handleChildren($path, $recursive, $reuse, $folderId, $lock, $size, $etagChanged); foreach ($childQueue as $child => [$childId, $childSize]) { - $childSize = $this->scanChildren($child, $recursive, $reuse, $childId, $lock, $childSize); + // "etag changed" propagates up, but not down, so we pass `false` to the children even if we already know that the etag of the current folder changed + $childEtagChanged = false; + $childSize = $this->scanChildren($child, $recursive, $reuse, $childId, $lock, $childSize, $childEtagChanged); + $etagChanged |= $childEtagChanged; + if ($childSize === -1) { $size = -1; } elseif ($size !== -1) { @@ -410,15 +438,27 @@ class Scanner extends BasicEmitter implements IScanner { if ($this->storage->instanceOfStorage(Encryption::class)) { $this->cache->calculateFolderSize($path); } else { - if ($this->cacheActive && $oldSize !== $size) { - $this->cache->update($folderId, ['size' => $size]); + if ($this->cacheActive) { + $updatedData = []; + if ($oldSize !== $size) { + $updatedData['size'] = $size; + } + if ($etagChanged) { + $updatedData['etag'] = uniqid(); + } + if ($updatedData) { + $this->cache->update($folderId, $updatedData); + } } } $this->emit('\OC\Files\Cache\Scanner', 'postScanFolder', [$path, $this->storageId]); return $size; } - private function handleChildren($path, $recursive, $reuse, $folderId, $lock, &$size) { + /** + * @param bool|IScanner::SCAN_RECURSIVE_INCOMPLETE $recursive + */ + private function handleChildren(string $path, $recursive, int $reuse, int $folderId, bool $lock, int|float &$size, bool &$etagChanged): array { // we put this in it's own function so it cleans up the memory before we start recursing $existingChildren = $this->getExistingChildren($folderId); $newChildren = iterator_to_array($this->storage->getDirectoryContent($path)); @@ -436,7 +476,7 @@ class Scanner extends BasicEmitter implements IScanner { $childQueue = []; $newChildNames = []; foreach ($newChildren as $fileMeta) { - $permissions = isset($fileMeta['scan_permissions']) ? $fileMeta['scan_permissions'] : $fileMeta['permissions']; + $permissions = $fileMeta['scan_permissions'] ?? $fileMeta['permissions']; if ($permissions === 0) { continue; } @@ -453,7 +493,7 @@ class Scanner extends BasicEmitter implements IScanner { $newChildNames[] = $file; $child = $path ? $path . '/' . $file : $file; try { - $existingData = isset($existingChildren[$file]) ? $existingChildren[$file] : false; + $existingData = $existingChildren[$file] ?? false; $data = $this->scanFile($child, $reuse, $folderId, $existingData, $lock, $fileMeta); if ($data) { if ($data['mimetype'] === 'httpd/unix-directory' && $recursive === self::SCAN_RECURSIVE) { @@ -466,6 +506,10 @@ class Scanner extends BasicEmitter implements IScanner { } elseif ($size !== -1) { $size += $data['size']; } + + if (isset($data['etag_changed']) && $data['etag_changed']) { + $etagChanged = true; + } } } catch (Exception $ex) { // might happen if inserting duplicate while a scanning diff --git a/lib/private/Files/Cache/SearchBuilder.php b/lib/private/Files/Cache/SearchBuilder.php index b9a70bbd39b..32502cb8258 100644 --- a/lib/private/Files/Cache/SearchBuilder.php +++ b/lib/private/Files/Cache/SearchBuilder.php @@ -1,27 +1,7 @@ <?php /** - * @copyright Copyright (c) 2017 Robin Appelman <robin@icewind.nl> - * - * @author Christoph Wurst <christoph@winzerhof-wurst.at> - * @author Robin Appelman <robin@icewind.nl> - * @author Roeland Jago Douma <roeland@famdouma.nl> - * @author Tobias Kaminsky <tobias@kaminsky.me> - * - * @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/>. - * + * SPDX-FileCopyrightText: 2017 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later */ namespace OC\Files\Cache; @@ -32,11 +12,16 @@ use OCP\Files\Search\ISearchBinaryOperator; use OCP\Files\Search\ISearchComparison; use OCP\Files\Search\ISearchOperator; use OCP\Files\Search\ISearchOrder; +use OCP\FilesMetadata\IMetadataQuery; /** * Tools for transforming search queries into database queries + * + * @psalm-import-type ParamSingleValue from ISearchComparison + * @psalm-import-type ParamValue from ISearchComparison */ class SearchBuilder { + /** @var array<string, string> */ protected static $searchOperatorMap = [ ISearchComparison::COMPARE_LIKE => 'iLike', ISearchComparison::COMPARE_LIKE_CASE_SENSITIVE => 'like', @@ -45,8 +30,11 @@ class SearchBuilder { ISearchComparison::COMPARE_GREATER_THAN_EQUAL => 'gte', ISearchComparison::COMPARE_LESS_THAN => 'lt', ISearchComparison::COMPARE_LESS_THAN_EQUAL => 'lte', + ISearchComparison::COMPARE_DEFINED => 'isNotNull', + ISearchComparison::COMPARE_IN => 'in', ]; + /** @var array<string, string> */ protected static $searchOperatorNegativeMap = [ ISearchComparison::COMPARE_LIKE => 'notLike', ISearchComparison::COMPARE_LIKE_CASE_SENSITIVE => 'notLike', @@ -55,6 +43,39 @@ class SearchBuilder { ISearchComparison::COMPARE_GREATER_THAN_EQUAL => 'lt', ISearchComparison::COMPARE_LESS_THAN => 'gte', ISearchComparison::COMPARE_LESS_THAN_EQUAL => 'gt', + ISearchComparison::COMPARE_DEFINED => 'isNull', + ISearchComparison::COMPARE_IN => 'notIn', + ]; + + /** @var array<string, string> */ + protected static $fieldTypes = [ + 'mimetype' => 'string', + 'mtime' => 'integer', + 'name' => 'string', + 'path' => 'string', + 'size' => 'integer', + 'tagname' => 'string', + 'systemtag' => 'string', + 'favorite' => 'boolean', + 'fileid' => 'integer', + 'storage' => 'integer', + 'share_with' => 'string', + 'share_type' => 'integer', + 'owner' => 'string', + ]; + + /** @var array<string, int> */ + protected static $paramTypeMap = [ + 'string' => IQueryBuilder::PARAM_STR, + 'integer' => IQueryBuilder::PARAM_INT, + 'boolean' => IQueryBuilder::PARAM_BOOL, + ]; + + /** @var array<string, int> */ + protected static $paramArrayTypeMap = [ + 'string' => IQueryBuilder::PARAM_STR_ARRAY, + 'integer' => IQueryBuilder::PARAM_INT_ARRAY, + 'boolean' => IQueryBuilder::PARAM_INT_ARRAY, ]; public const TAG_FAVORITE = '_$!<Favorite>!$_'; @@ -76,7 +97,7 @@ class SearchBuilder { return array_reduce($operator->getArguments(), function (array $fields, ISearchOperator $operator) { return array_unique(array_merge($fields, $this->extractRequestedFields($operator))); }, []); - } elseif ($operator instanceof ISearchComparison) { + } elseif ($operator instanceof ISearchComparison && !$operator->getExtra()) { return [$operator->getField()]; } return []; @@ -86,13 +107,21 @@ class SearchBuilder { * @param IQueryBuilder $builder * @param ISearchOperator[] $operators */ - public function searchOperatorArrayToDBExprArray(IQueryBuilder $builder, array $operators) { - return array_filter(array_map(function ($operator) use ($builder) { - return $this->searchOperatorToDBExpr($builder, $operator); + public function searchOperatorArrayToDBExprArray( + IQueryBuilder $builder, + array $operators, + ?IMetadataQuery $metadataQuery = null + ) { + return array_filter(array_map(function ($operator) use ($builder, $metadataQuery) { + return $this->searchOperatorToDBExpr($builder, $operator, $metadataQuery); }, $operators)); } - public function searchOperatorToDBExpr(IQueryBuilder $builder, ISearchOperator $operator) { + public function searchOperatorToDBExpr( + IQueryBuilder $builder, + ISearchOperator $operator, + ?IMetadataQuery $metadataQuery = null + ) { $expr = $builder->expr(); if ($operator instanceof ISearchBinaryOperator) { @@ -104,62 +133,99 @@ class SearchBuilder { case ISearchBinaryOperator::OPERATOR_NOT: $negativeOperator = $operator->getArguments()[0]; if ($negativeOperator instanceof ISearchComparison) { - return $this->searchComparisonToDBExpr($builder, $negativeOperator, self::$searchOperatorNegativeMap); + return $this->searchComparisonToDBExpr($builder, $negativeOperator, self::$searchOperatorNegativeMap, $metadataQuery); } else { throw new \InvalidArgumentException('Binary operators inside "not" is not supported'); } // no break case ISearchBinaryOperator::OPERATOR_AND: - return call_user_func_array([$expr, 'andX'], $this->searchOperatorArrayToDBExprArray($builder, $operator->getArguments())); + return call_user_func_array([$expr, 'andX'], $this->searchOperatorArrayToDBExprArray($builder, $operator->getArguments(), $metadataQuery)); case ISearchBinaryOperator::OPERATOR_OR: - return call_user_func_array([$expr, 'orX'], $this->searchOperatorArrayToDBExprArray($builder, $operator->getArguments())); + return call_user_func_array([$expr, 'orX'], $this->searchOperatorArrayToDBExprArray($builder, $operator->getArguments(), $metadataQuery)); default: throw new \InvalidArgumentException('Invalid operator type: ' . $operator->getType()); } } elseif ($operator instanceof ISearchComparison) { - return $this->searchComparisonToDBExpr($builder, $operator, self::$searchOperatorMap); + return $this->searchComparisonToDBExpr($builder, $operator, self::$searchOperatorMap, $metadataQuery); } else { throw new \InvalidArgumentException('Invalid operator type: ' . get_class($operator)); } } - private function searchComparisonToDBExpr(IQueryBuilder $builder, ISearchComparison $comparison, array $operatorMap) { - $this->validateComparison($comparison); + private function searchComparisonToDBExpr( + IQueryBuilder $builder, + ISearchComparison $comparison, + array $operatorMap, + ?IMetadataQuery $metadataQuery = null + ) { + if ($comparison->getExtra()) { + [$field, $value, $type, $paramType] = $this->getExtraOperatorField($comparison, $metadataQuery); + } else { + [$field, $value, $type, $paramType] = $this->getOperatorFieldAndValue($comparison); + } - [$field, $value, $type] = $this->getOperatorFieldAndValue($comparison); if (isset($operatorMap[$type])) { $queryOperator = $operatorMap[$type]; - return $builder->expr()->$queryOperator($field, $this->getParameterForValue($builder, $value)); + return $builder->expr()->$queryOperator($field, $this->getParameterForValue($builder, $value, $paramType)); } else { throw new \InvalidArgumentException('Invalid operator type: ' . $comparison->getType()); } } - private function getOperatorFieldAndValue(ISearchComparison $operator) { + /** + * @param ISearchComparison $operator + * @return list{string, ParamValue, string, string} + */ + private function getOperatorFieldAndValue(ISearchComparison $operator): array { + $this->validateComparison($operator); $field = $operator->getField(); $value = $operator->getValue(); $type = $operator->getType(); + $pathEqHash = $operator->getQueryHint(ISearchComparison::HINT_PATH_EQ_HASH, true); + return $this->getOperatorFieldAndValueInner($field, $value, $type, $pathEqHash); + } + + /** + * @param string $field + * @param ParamValue $value + * @param string $type + * @return list{string, ParamValue, string, string} + */ + private function getOperatorFieldAndValueInner(string $field, mixed $value, string $type, bool $pathEqHash): array { + $paramType = self::$fieldTypes[$field]; + if ($type === ISearchComparison::COMPARE_IN) { + $resultField = $field; + $values = []; + foreach ($value as $arrayValue) { + /** @var ParamSingleValue $arrayValue */ + [$arrayField, $arrayValue] = $this->getOperatorFieldAndValueInner($field, $arrayValue, ISearchComparison::COMPARE_EQUAL, $pathEqHash); + $resultField = $arrayField; + $values[] = $arrayValue; + } + return [$resultField, $values, ISearchComparison::COMPARE_IN, $paramType]; + } if ($field === 'mimetype') { $value = (string)$value; - if ($operator->getType() === ISearchComparison::COMPARE_EQUAL) { - $value = (int)$this->mimetypeLoader->getId($value); - } elseif ($operator->getType() === ISearchComparison::COMPARE_LIKE) { + if ($type === ISearchComparison::COMPARE_EQUAL) { + $value = $this->mimetypeLoader->getId($value); + } elseif ($type === ISearchComparison::COMPARE_LIKE) { // transform "mimetype='foo/%'" to "mimepart='foo'" if (preg_match('|(.+)/%|', $value, $matches)) { $field = 'mimepart'; - $value = (int)$this->mimetypeLoader->getId($matches[1]); + $value = $this->mimetypeLoader->getId($matches[1]); $type = ISearchComparison::COMPARE_EQUAL; } elseif (str_contains($value, '%')) { throw new \InvalidArgumentException('Unsupported query value for mimetype: ' . $value . ', only values in the format "mime/type" or "mime/%" are supported'); } else { $field = 'mimetype'; - $value = (int)$this->mimetypeLoader->getId($value); + $value = $this->mimetypeLoader->getId($value); $type = ISearchComparison::COMPARE_EQUAL; } } } elseif ($field === 'favorite') { $field = 'tag.category'; $value = self::TAG_FAVORITE; + $paramType = 'string'; } elseif ($field === 'name') { $field = 'file.name'; } elseif ($field === 'tagname') { @@ -168,59 +234,82 @@ class SearchBuilder { $field = 'systemtag.name'; } elseif ($field === 'fileid') { $field = 'file.fileid'; - } elseif ($field === 'path' && $type === ISearchComparison::COMPARE_EQUAL && $operator->getQueryHint(ISearchComparison::HINT_PATH_EQ_HASH, true)) { + } elseif ($field === 'path' && $type === ISearchComparison::COMPARE_EQUAL && $pathEqHash) { $field = 'path_hash'; $value = md5((string)$value); + } elseif ($field === 'owner') { + $field = 'uid_owner'; } - return [$field, $value, $type]; + return [$field, $value, $type, $paramType]; } private function validateComparison(ISearchComparison $operator) { - $types = [ - 'mimetype' => 'string', - 'mtime' => 'integer', - 'name' => 'string', - 'path' => 'string', - 'size' => 'integer', - 'tagname' => 'string', - 'systemtag' => 'string', - 'favorite' => 'boolean', - 'fileid' => 'integer', - 'storage' => 'integer', - ]; $comparisons = [ - 'mimetype' => ['eq', 'like'], + 'mimetype' => ['eq', 'like', 'in'], 'mtime' => ['eq', 'gt', 'lt', 'gte', 'lte'], - 'name' => ['eq', 'like', 'clike'], - 'path' => ['eq', 'like', 'clike'], + 'name' => ['eq', 'like', 'clike', 'in'], + 'path' => ['eq', 'like', 'clike', 'in'], 'size' => ['eq', 'gt', 'lt', 'gte', 'lte'], 'tagname' => ['eq', 'like'], 'systemtag' => ['eq', 'like'], 'favorite' => ['eq'], - 'fileid' => ['eq'], - 'storage' => ['eq'], + 'fileid' => ['eq', 'in'], + 'storage' => ['eq', 'in'], + 'share_with' => ['eq'], + 'share_type' => ['eq'], + 'owner' => ['eq'], ]; - if (!isset($types[$operator->getField()])) { + if (!isset(self::$fieldTypes[$operator->getField()])) { throw new \InvalidArgumentException('Unsupported comparison field ' . $operator->getField()); } - $type = $types[$operator->getField()]; - if (gettype($operator->getValue()) !== $type) { - throw new \InvalidArgumentException('Invalid type for field ' . $operator->getField()); + $type = self::$fieldTypes[$operator->getField()]; + if ($operator->getType() === ISearchComparison::COMPARE_IN) { + if (!is_array($operator->getValue())) { + throw new \InvalidArgumentException('Invalid type for field ' . $operator->getField()); + } + foreach ($operator->getValue() as $arrayValue) { + if (gettype($arrayValue) !== $type) { + throw new \InvalidArgumentException('Invalid type in array for field ' . $operator->getField()); + } + } + } else { + if (gettype($operator->getValue()) !== $type) { + throw new \InvalidArgumentException('Invalid type for field ' . $operator->getField()); + } } if (!in_array($operator->getType(), $comparisons[$operator->getField()])) { throw new \InvalidArgumentException('Unsupported comparison for field ' . $operator->getField() . ': ' . $operator->getType()); } } - private function getParameterForValue(IQueryBuilder $builder, $value) { + + private function getExtraOperatorField(ISearchComparison $operator, IMetadataQuery $metadataQuery): array { + $paramType = self::$fieldTypes[$operator->getField()]; + $field = $operator->getField(); + $value = $operator->getValue(); + $type = $operator->getType(); + + switch($operator->getExtra()) { + case IMetadataQuery::EXTRA: + $metadataQuery->joinIndex($field); // join index table if not joined yet + $field = $metadataQuery->getMetadataValueField($field); + break; + default: + throw new \InvalidArgumentException('Invalid extra type: ' . $operator->getExtra()); + } + + return [$field, $value, $type, $paramType]; + } + + private function getParameterForValue(IQueryBuilder $builder, $value, string $paramType) { if ($value instanceof \DateTime) { $value = $value->getTimestamp(); } - if (is_numeric($value)) { - $type = IQueryBuilder::PARAM_INT; + if (is_array($value)) { + $type = self::$paramArrayTypeMap[$paramType]; } else { - $type = IQueryBuilder::PARAM_STR; + $type = self::$paramTypeMap[$paramType]; } return $builder->createNamedParameter($value, $type); } @@ -228,24 +317,32 @@ class SearchBuilder { /** * @param IQueryBuilder $query * @param ISearchOrder[] $orders + * @param IMetadataQuery|null $metadataQuery */ - public function addSearchOrdersToQuery(IQueryBuilder $query, array $orders) { + public function addSearchOrdersToQuery(IQueryBuilder $query, array $orders, ?IMetadataQuery $metadataQuery = null): void { foreach ($orders as $order) { $field = $order->getField(); - if ($field === 'fileid') { - $field = 'file.fileid'; - } + switch ($order->getExtra()) { + case IMetadataQuery::EXTRA: + $metadataQuery->joinIndex($field); // join index table if not joined yet + $field = $metadataQuery->getMetadataValueField($order->getField()); + break; - // Mysql really likes to pick an index for sorting if it can't fully satisfy the where - // filter with an index, since search queries pretty much never are fully filtered by index - // mysql often picks an index for sorting instead of the much more useful index for filtering. - // - // By changing the order by to an expression, mysql isn't smart enough to see that it could still - // use the index, so it instead picks an index for the filtering - if ($field === 'mtime') { - $field = $query->func()->add($field, $query->createNamedParameter(0)); - } + default: + if ($field === 'fileid') { + $field = 'file.fileid'; + } + // Mysql really likes to pick an index for sorting if it can't fully satisfy the where + // filter with an index, since search queries pretty much never are fully filtered by index + // mysql often picks an index for sorting instead of the much more useful index for filtering. + // + // By changing the order by to an expression, mysql isn't smart enough to see that it could still + // use the index, so it instead picks an index for the filtering + if ($field === 'mtime') { + $field = $query->func()->add($field, $query->createNamedParameter(0)); + } + } $query->addOrderBy($field, $order->getDirection()); } } diff --git a/lib/private/Files/Cache/Storage.php b/lib/private/Files/Cache/Storage.php index 01fc638cef8..0929907fcff 100644 --- a/lib/private/Files/Cache/Storage.php +++ b/lib/private/Files/Cache/Storage.php @@ -1,36 +1,15 @@ <?php + /** - * @copyright Copyright (c) 2016, ownCloud, Inc. - * - * @author Arthur Schiwon <blizzz@arthur-schiwon.de> - * @author Joas Schilling <coding@schilljs.com> - * @author Jörn Friedrich Dreyer <jfd@butonic.de> - * @author Morris Jobke <hey@morrisjobke.de> - * @author Robin Appelman <robin@icewind.nl> - * @author Robin McCorkell <robin@mccorkell.me.uk> - * @author Roeland Jago Douma <roeland@famdouma.nl> - * @author Thomas Müller <thomas.mueller@tmit.eu> - * @author Vincent Petry <vincent@nextcloud.com> - * - * @license AGPL-3.0 - * - * This code is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License, version 3, - * as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License, version 3, - * along with this program. If not, see <http://www.gnu.org/licenses/> - * + * SPDX-FileCopyrightText: 2016-2024 Nextcloud GmbH and Nextcloud contributors + * SPDX-FileCopyrightText: 2016 ownCloud, Inc. + * SPDX-License-Identifier: AGPL-3.0-only */ namespace OC\Files\Cache; use OCP\DB\QueryBuilder\IQueryBuilder; use OCP\Files\Storage\IStorage; +use OCP\IDBConnection; use Psr\Log\LoggerInterface; /** @@ -65,7 +44,7 @@ class Storage { * @param bool $isAvailable * @throws \RuntimeException */ - public function __construct($storage, $isAvailable = true) { + public function __construct($storage, $isAvailable, IDBConnection $connection) { if ($storage instanceof IStorage) { $this->storageId = $storage->getId(); } else { @@ -76,7 +55,6 @@ class Storage { if ($row = self::getStorageById($this->storageId)) { $this->numericId = (int)$row['numeric_id']; } else { - $connection = \OC::$server->getDatabaseConnection(); $available = $isAvailable ? 1 : 0; if ($connection->insertIfNotExist('*PREFIX*storages', ['id' => $this->storageId, 'available' => $available])) { $this->numericId = $connection->lastInsertId('*PREFIX*storages'); diff --git a/lib/private/Files/Cache/StorageGlobal.php b/lib/private/Files/Cache/StorageGlobal.php index 74cbd5abdb2..b55c0fcdb91 100644 --- a/lib/private/Files/Cache/StorageGlobal.php +++ b/lib/private/Files/Cache/StorageGlobal.php @@ -1,25 +1,8 @@ <?php + /** - * @copyright Robin Appelman <robin@icewind.nl> - * - * @author Joas Schilling <coding@schilljs.com> - * @author Robin Appelman <robin@icewind.nl> - * - * @license GNU AGPL version 3 or any later version - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as - * published by the Free Software Foundation, either version 3 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - * + * SPDX-FileCopyrightText: 2016 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later */ namespace OC\Files\Cache; diff --git a/lib/private/Files/Cache/Updater.php b/lib/private/Files/Cache/Updater.php index 457dd207e9d..e8c6d32599e 100644 --- a/lib/private/Files/Cache/Updater.php +++ b/lib/private/Files/Cache/Updater.php @@ -1,29 +1,9 @@ <?php + /** - * @copyright Copyright (c) 2016, ownCloud, Inc. - * - * @author Björn Schießle <bjoern@schiessle.org> - * @author Christoph Wurst <christoph@winzerhof-wurst.at> - * @author Daniel Jagszent <daniel@jagszent.de> - * @author Michael Gapczynski <GapczynskiM@gmail.com> - * @author Morris Jobke <hey@morrisjobke.de> - * @author Robin Appelman <robin@icewind.nl> - * @author Vincent Petry <vincent@nextcloud.com> - * - * @license AGPL-3.0 - * - * This code is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License, version 3, - * as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License, version 3, - * along with this program. If not, see <http://www.gnu.org/licenses/> - * + * SPDX-FileCopyrightText: 2016-2024 Nextcloud GmbH and Nextcloud contributors + * SPDX-FileCopyrightText: 2016 ownCloud, Inc. + * SPDX-License-Identifier: AGPL-3.0-only */ namespace OC\Files\Cache; @@ -119,7 +99,7 @@ class Updater implements IUpdater { * @param string $path * @param int $time */ - public function update($path, $time = null) { + public function update($path, $time = null, ?int $sizeDifference = null) { if (!$this->enabled or Scanner::isPartialFile($path)) { return; } @@ -128,20 +108,22 @@ class Updater implements IUpdater { } $data = $this->scanner->scan($path, Scanner::SCAN_SHALLOW, -1, false); - if ( - isset($data['oldSize']) && isset($data['size']) && - !$data['encrypted'] // encryption is a pita and touches the cache itself - ) { + + if (isset($data['oldSize']) && isset($data['size'])) { $sizeDifference = $data['size'] - $data['oldSize']; - } else { - // scanner didn't provide size info, fallback to full size calculation - $sizeDifference = 0; - if ($this->cache instanceof Cache) { - $this->cache->correctFolderSize($path, $data); - } + } + + // encryption is a pita and touches the cache itself + if (isset($data['encrypted']) && !!$data['encrypted']) { + $sizeDifference = null; + } + + // scanner didn't provide size info, fallback to full size calculation + if ($this->cache instanceof Cache && $sizeDifference === null) { + $this->cache->correctFolderSize($path, $data); } $this->correctParentStorageMtime($path); - $this->propagator->propagateChange($path, $time, $sizeDifference); + $this->propagator->propagateChange($path, $time, $sizeDifference ?? 0); } /** diff --git a/lib/private/Files/Cache/Watcher.php b/lib/private/Files/Cache/Watcher.php index acc76f263dc..2e42b716695 100644 --- a/lib/private/Files/Cache/Watcher.php +++ b/lib/private/Files/Cache/Watcher.php @@ -1,28 +1,9 @@ <?php + /** - * @copyright Copyright (c) 2016, ownCloud, Inc. - * - * @author Christoph Wurst <christoph@winzerhof-wurst.at> - * @author Daniel Jagszent <daniel@jagszent.de> - * @author Morris Jobke <hey@morrisjobke.de> - * @author Robin Appelman <robin@icewind.nl> - * @author Roeland Jago Douma <roeland@famdouma.nl> - * @author Vincent Petry <vincent@nextcloud.com> - * - * @license AGPL-3.0 - * - * This code is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License, version 3, - * as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License, version 3, - * along with this program. If not, see <http://www.gnu.org/licenses/> - * + * SPDX-FileCopyrightText: 2016-2024 Nextcloud GmbH and Nextcloud contributors + * SPDX-FileCopyrightText: 2016 ownCloud, Inc. + * SPDX-License-Identifier: AGPL-3.0-only */ namespace OC\Files\Cache; @@ -129,7 +110,7 @@ class Watcher implements IWatcher { * @return bool */ public function needsUpdate($path, $cachedData) { - if ($this->watchPolicy === self::CHECK_ALWAYS or ($this->watchPolicy === self::CHECK_ONCE and array_search($path, $this->checkedPaths) === false)) { + if ($this->watchPolicy === self::CHECK_ALWAYS or ($this->watchPolicy === self::CHECK_ONCE and !in_array($path, $this->checkedPaths))) { $this->checkedPaths[] = $path; return $this->storage->hasUpdated($path, $cachedData['storage_mtime']); } diff --git a/lib/private/Files/Cache/Wrapper/CacheJail.php b/lib/private/Files/Cache/Wrapper/CacheJail.php index d8cf3eb61d7..ea0f992114a 100644 --- a/lib/private/Files/Cache/Wrapper/CacheJail.php +++ b/lib/private/Files/Cache/Wrapper/CacheJail.php @@ -1,35 +1,17 @@ <?php + /** - * @copyright Copyright (c) 2016, ownCloud, Inc. - * - * @author Christoph Wurst <christoph@winzerhof-wurst.at> - * @author Daniel Jagszent <daniel@jagszent.de> - * @author Morris Jobke <hey@morrisjobke.de> - * @author Robin Appelman <robin@icewind.nl> - * @author Robin McCorkell <robin@mccorkell.me.uk> - * @author Roeland Jago Douma <roeland@famdouma.nl> - * @author Thomas Müller <thomas.mueller@tmit.eu> - * - * @license AGPL-3.0 - * - * This code is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License, version 3, - * as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License, version 3, - * along with this program. If not, see <http://www.gnu.org/licenses/> - * + * SPDX-FileCopyrightText: 2016-2024 Nextcloud GmbH and Nextcloud contributors + * SPDX-FileCopyrightText: 2016 ownCloud, Inc. + * SPDX-License-Identifier: AGPL-3.0-only */ namespace OC\Files\Cache\Wrapper; use OC\Files\Cache\Cache; +use OC\Files\Cache\CacheDependencies; use OC\Files\Search\SearchBinaryOperator; use OC\Files\Search\SearchComparison; +use OCP\Files\Cache\ICache; use OCP\Files\Cache\ICacheEntry; use OCP\Files\Search\ISearchBinaryOperator; use OCP\Files\Search\ISearchComparison; @@ -45,15 +27,13 @@ class CacheJail extends CacheWrapper { protected $root; protected $unjailedRoot; - /** - * @param ?\OCP\Files\Cache\ICache $cache - * @param string $root - */ - public function __construct($cache, $root) { - parent::__construct($cache); + public function __construct( + ?ICache $cache, + string $root, + ?CacheDependencies $dependencies = null, + ) { + parent::__construct($cache, $dependencies); $this->root = $root; - $this->connection = \OC::$server->getDatabaseConnection(); - $this->mimetypeLoader = \OC::$server->getMimeTypeLoader(); if ($cache instanceof CacheJail) { $this->unjailedRoot = $cache->getSourcePath($root); @@ -88,7 +68,7 @@ class CacheJail extends CacheWrapper { * @param null|string $root * @return null|string the jailed path or null if the path is outside the jail */ - protected function getJailedPath(string $path, string $root = null) { + protected function getJailedPath(string $path, ?string $root = null) { if ($root === null) { $root = $this->getRoot(); } diff --git a/lib/private/Files/Cache/Wrapper/CachePermissionsMask.php b/lib/private/Files/Cache/Wrapper/CachePermissionsMask.php index cbf16a909ff..ff17cb79ac7 100644 --- a/lib/private/Files/Cache/Wrapper/CachePermissionsMask.php +++ b/lib/private/Files/Cache/Wrapper/CachePermissionsMask.php @@ -1,24 +1,9 @@ <?php + /** - * @copyright Copyright (c) 2016, ownCloud, Inc. - * - * @author Morris Jobke <hey@morrisjobke.de> - * @author Robin Appelman <robin@icewind.nl> - * - * @license AGPL-3.0 - * - * This code is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License, version 3, - * as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License, version 3, - * along with this program. If not, see <http://www.gnu.org/licenses/> - * + * SPDX-FileCopyrightText: 2016-2024 Nextcloud GmbH and Nextcloud contributors + * SPDX-FileCopyrightText: 2016 ownCloud, Inc. + * SPDX-License-Identifier: AGPL-3.0-only */ namespace OC\Files\Cache\Wrapper; diff --git a/lib/private/Files/Cache/Wrapper/CacheWrapper.php b/lib/private/Files/Cache/Wrapper/CacheWrapper.php index 39a78f31343..17f1031d1cc 100644 --- a/lib/private/Files/Cache/Wrapper/CacheWrapper.php +++ b/lib/private/Files/Cache/Wrapper/CacheWrapper.php @@ -1,62 +1,39 @@ <?php + /** - * @copyright Copyright (c) 2016, ownCloud, Inc. - * - * @author Ari Selseng <ari@selseng.net> - * @author Christoph Wurst <christoph@winzerhof-wurst.at> - * @author Daniel Jagszent <daniel@jagszent.de> - * @author Joas Schilling <coding@schilljs.com> - * @author Morris Jobke <hey@morrisjobke.de> - * @author Robin Appelman <robin@icewind.nl> - * @author Robin McCorkell <robin@mccorkell.me.uk> - * @author Roeland Jago Douma <roeland@famdouma.nl> - * @author Vincent Petry <vincent@nextcloud.com> - * - * @license AGPL-3.0 - * - * This code is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License, version 3, - * as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License, version 3, - * along with this program. If not, see <http://www.gnu.org/licenses/> - * + * SPDX-FileCopyrightText: 2016-2024 Nextcloud GmbH and Nextcloud contributors + * SPDX-FileCopyrightText: 2016 ownCloud, Inc. + * SPDX-License-Identifier: AGPL-3.0-only */ namespace OC\Files\Cache\Wrapper; use OC\Files\Cache\Cache; -use OC\Files\Cache\QuerySearchHelper; +use OC\Files\Cache\CacheDependencies; use OCP\Files\Cache\ICache; use OCP\Files\Cache\ICacheEntry; -use OCP\Files\IMimeTypeLoader; use OCP\Files\Search\ISearchOperator; use OCP\Files\Search\ISearchQuery; -use OCP\IDBConnection; +use OCP\Server; class CacheWrapper extends Cache { /** - * @var \OCP\Files\Cache\ICache + * @var ?ICache */ protected $cache; - /** - * @param \OCP\Files\Cache\ICache $cache - */ - public function __construct($cache) { + public function __construct(?ICache $cache, ?CacheDependencies $dependencies = null) { $this->cache = $cache; - if ($cache instanceof Cache) { + if (!$dependencies && $cache instanceof Cache) { $this->mimetypeLoader = $cache->mimetypeLoader; $this->connection = $cache->connection; $this->querySearchHelper = $cache->querySearchHelper; } else { - $this->mimetypeLoader = \OC::$server->get(IMimeTypeLoader::class); - $this->connection = \OC::$server->get(IDBConnection::class); - $this->querySearchHelper = \OC::$server->get(QuerySearchHelper::class); + if (!$dependencies) { + $dependencies = Server::get(CacheDependencies::class); + } + $this->mimetypeLoader = $dependencies->getMimeTypeLoader(); + $this->connection = $dependencies->getConnection(); + $this->querySearchHelper = $dependencies->getQuerySearchHelper(); } } @@ -91,7 +68,7 @@ class CacheWrapper extends Cache { */ public function get($file) { $result = $this->getCache()->get($file); - if ($result) { + if ($result instanceof ICacheEntry) { $result = $this->formatCacheEntry($result); } return $result; diff --git a/lib/private/Files/Cache/Wrapper/JailPropagator.php b/lib/private/Files/Cache/Wrapper/JailPropagator.php index 25e53ded39d..19ca4a13ece 100644 --- a/lib/private/Files/Cache/Wrapper/JailPropagator.php +++ b/lib/private/Files/Cache/Wrapper/JailPropagator.php @@ -1,24 +1,7 @@ <?php /** - * @copyright Copyright (c) 2017 Robin Appelman <robin@icewind.nl> - * - * @author Robin Appelman <robin@icewind.nl> - * - * @license GNU AGPL version 3 or any later version - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as - * published by the Free Software Foundation, either version 3 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - * + * SPDX-FileCopyrightText: 2017 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later */ namespace OC\Files\Cache\Wrapper; diff --git a/lib/private/Files/Cache/Wrapper/JailWatcher.php b/lib/private/Files/Cache/Wrapper/JailWatcher.php new file mode 100644 index 00000000000..9bd7da57233 --- /dev/null +++ b/lib/private/Files/Cache/Wrapper/JailWatcher.php @@ -0,0 +1,58 @@ +<?php + +declare(strict_types=1); +/** + * SPDX-FileCopyrightText: 2024 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later + */ +namespace OC\Files\Cache\Wrapper; + +use OC\Files\Cache\Watcher; + +class JailWatcher extends Watcher { + private string $root; + private Watcher $watcher; + + public function __construct(Watcher $watcher, string $root) { + $this->watcher = $watcher; + $this->root = $root; + } + + protected function getRoot(): string { + return $this->root; + } + + protected function getSourcePath($path): string { + if ($path === '') { + return $this->getRoot(); + } else { + return $this->getRoot() . '/' . ltrim($path, '/'); + } + } + + public function setPolicy($policy) { + $this->watcher->setPolicy($policy); + } + + public function getPolicy() { + return $this->watcher->getPolicy(); + } + + + public function checkUpdate($path, $cachedEntry = null) { + return $this->watcher->checkUpdate($this->getSourcePath($path), $cachedEntry); + } + + public function update($path, $cachedData) { + $this->watcher->update($this->getSourcePath($path), $cachedData); + } + + public function needsUpdate($path, $cachedData) { + return $this->watcher->needsUpdate($this->getSourcePath($path), $cachedData); + } + + public function cleanFolder($path) { + $this->watcher->cleanFolder($this->getSourcePath($path)); + } + +} diff --git a/lib/private/Files/Config/CachedMountFileInfo.php b/lib/private/Files/Config/CachedMountFileInfo.php index 11a9a505808..41dbec87ef5 100644 --- a/lib/private/Files/Config/CachedMountFileInfo.php +++ b/lib/private/Files/Config/CachedMountFileInfo.php @@ -1,25 +1,7 @@ <?php /** - * @copyright Copyright (c) 2017 Robin Appelman <robin@icewind.nl> - * - * @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/>. - * + * SPDX-FileCopyrightText: 2017 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later */ namespace OC\Files\Config; diff --git a/lib/private/Files/Config/CachedMountInfo.php b/lib/private/Files/Config/CachedMountInfo.php index 43c9fae63ec..80423dcae40 100644 --- a/lib/private/Files/Config/CachedMountInfo.php +++ b/lib/private/Files/Config/CachedMountInfo.php @@ -1,24 +1,9 @@ <?php + /** - * @copyright Copyright (c) 2016, ownCloud, Inc. - * - * @author Robin Appelman <robin@icewind.nl> - * @author Semih Serhat Karakaya <karakayasemi@itu.edu.tr> - * - * @license AGPL-3.0 - * - * This code is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License, version 3, - * as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License, version 3, - * along with this program. If not, see <http://www.gnu.org/licenses/> - * + * SPDX-FileCopyrightText: 2016-2024 Nextcloud GmbH and Nextcloud contributors + * SPDX-FileCopyrightText: 2016 ownCloud, Inc. + * SPDX-License-Identifier: AGPL-3.0-only */ namespace OC\Files\Config; @@ -35,6 +20,7 @@ class CachedMountInfo implements ICachedMountInfo { protected ?int $mountId; protected string $rootInternalPath; protected string $mountProvider; + protected string $key; /** * CachedMountInfo constructor. @@ -52,7 +38,7 @@ class CachedMountInfo implements ICachedMountInfo { int $rootId, string $mountPoint, string $mountProvider, - int $mountId = null, + ?int $mountId = null, string $rootInternalPath = '' ) { $this->user = $user; @@ -65,6 +51,7 @@ class CachedMountInfo implements ICachedMountInfo { throw new \Exception("Mount provider $mountProvider name exceeds the limit of 128 characters"); } $this->mountProvider = $mountProvider; + $this->key = $rootId . '::' . $mountPoint; } /** @@ -95,12 +82,7 @@ class CachedMountInfo implements ICachedMountInfo { // TODO injection etc Filesystem::initMountPoints($this->getUser()->getUID()); $userNode = \OC::$server->getUserFolder($this->getUser()->getUID()); - $nodes = $userNode->getParent()->getById($this->getRootId()); - if (count($nodes) > 0) { - return $nodes[0]; - } else { - return null; - } + return $userNode->getParent()->getFirstNodeById($this->getRootId()); } /** @@ -132,4 +114,8 @@ class CachedMountInfo implements ICachedMountInfo { public function getMountProvider(): string { return $this->mountProvider; } + + public function getKey(): string { + return $this->key; + } } diff --git a/lib/private/Files/Config/LazyPathCachedMountInfo.php b/lib/private/Files/Config/LazyPathCachedMountInfo.php new file mode 100644 index 00000000000..d2396109b1a --- /dev/null +++ b/lib/private/Files/Config/LazyPathCachedMountInfo.php @@ -0,0 +1,48 @@ +<?php + +declare(strict_types=1); +/** + * SPDX-FileCopyrightText: 2022 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later + */ +namespace OC\Files\Config; + +use OCP\IUser; + +class LazyPathCachedMountInfo extends CachedMountInfo { + // we don't allow \ in paths so it makes a great placeholder + private const PATH_PLACEHOLDER = '\\PLACEHOLDER\\'; + + /** @var callable(CachedMountInfo): string */ + protected $rootInternalPathCallback; + + /** + * @param IUser $user + * @param int $storageId + * @param int $rootId + * @param string $mountPoint + * @param string $mountProvider + * @param int|null $mountId + * @param callable(CachedMountInfo): string $rootInternalPathCallback + * @throws \Exception + */ + public function __construct( + IUser $user, + int $storageId, + int $rootId, + string $mountPoint, + string $mountProvider, + ?int $mountId, + callable $rootInternalPathCallback, + ) { + parent::__construct($user, $storageId, $rootId, $mountPoint, $mountProvider, $mountId, self::PATH_PLACEHOLDER); + $this->rootInternalPathCallback = $rootInternalPathCallback; + } + + public function getRootInternalPath(): string { + if ($this->rootInternalPath === self::PATH_PLACEHOLDER) { + $this->rootInternalPath = ($this->rootInternalPathCallback)($this); + } + return $this->rootInternalPath; + } +} diff --git a/lib/private/Files/Config/LazyStorageMountInfo.php b/lib/private/Files/Config/LazyStorageMountInfo.php index 78055a2cdb8..eb2c60dfa46 100644 --- a/lib/private/Files/Config/LazyStorageMountInfo.php +++ b/lib/private/Files/Config/LazyStorageMountInfo.php @@ -1,23 +1,9 @@ <?php + /** - * @copyright Copyright (c) 2016, ownCloud, Inc. - * - * @author Robin Appelman <robin@icewind.nl> - * - * @license AGPL-3.0 - * - * This code is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License, version 3, - * as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License, version 3, - * along with this program. If not, see <http://www.gnu.org/licenses/> - * + * SPDX-FileCopyrightText: 2016-2024 Nextcloud GmbH and Nextcloud contributors + * SPDX-FileCopyrightText: 2016 ownCloud, Inc. + * SPDX-License-Identifier: AGPL-3.0-only */ namespace OC\Files\Config; @@ -39,6 +25,7 @@ class LazyStorageMountInfo extends CachedMountInfo { $this->rootId = 0; $this->storageId = 0; $this->mountPoint = ''; + $this->key = ''; } /** @@ -87,4 +74,11 @@ class LazyStorageMountInfo extends CachedMountInfo { public function getMountProvider(): string { return $this->mount->getMountProvider(); } + + public function getKey(): string { + if (!$this->key) { + $this->key = $this->getRootId() . '::' . $this->getMountPoint(); + } + return $this->key; + } } diff --git a/lib/private/Files/Config/MountProviderCollection.php b/lib/private/Files/Config/MountProviderCollection.php index ae6481e45bb..0e103690b6b 100644 --- a/lib/private/Files/Config/MountProviderCollection.php +++ b/lib/private/Files/Config/MountProviderCollection.php @@ -1,26 +1,9 @@ <?php + /** - * @copyright Copyright (c) 2016, ownCloud, Inc. - * - * @author Christoph Wurst <christoph@winzerhof-wurst.at> - * @author Morris Jobke <hey@morrisjobke.de> - * @author Robin Appelman <robin@icewind.nl> - * @author Roeland Jago Douma <roeland@famdouma.nl> - * - * @license AGPL-3.0 - * - * This code is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License, version 3, - * as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License, version 3, - * along with this program. If not, see <http://www.gnu.org/licenses/> - * + * SPDX-FileCopyrightText: 2016-2024 Nextcloud GmbH and Nextcloud contributors + * SPDX-FileCopyrightText: 2016 ownCloud, Inc. + * SPDX-License-Identifier: AGPL-3.0-only */ namespace OC\Files\Config; @@ -119,7 +102,7 @@ class MountProviderCollection implements IMountProviderCollection, Emitter { return $this->getUserMountsForProviders($user, $providers); } - public function addMountForUser(IUser $user, IMountManager $mountManager, callable $providerFilter = null) { + public function addMountForUser(IUser $user, IMountManager $mountManager, ?callable $providerFilter = null) { // shared mount provider gets to go last since it needs to know existing files // to check for name collisions $firstMounts = []; @@ -238,6 +221,11 @@ class MountProviderCollection implements IMountProviderCollection, Emitter { $mounts = array_reduce($mounts, function (array $mounts, array $providerMounts) { return array_merge($mounts, $providerMounts); }, []); + + if (count($mounts) === 0) { + throw new \Exception("No root mounts provided by any provider"); + } + return $mounts; } diff --git a/lib/private/Files/Config/UserMountCache.php b/lib/private/Files/Config/UserMountCache.php index 90f94b6598e..0afdf9cdcc2 100644 --- a/lib/private/Files/Config/UserMountCache.php +++ b/lib/private/Files/Config/UserMountCache.php @@ -1,41 +1,19 @@ <?php + /** - * @copyright Copyright (c) 2016, ownCloud, Inc. - * - * @author Dariusz Olszewski <starypatyk@users.noreply.github.com> - * @author Joas Schilling <coding@schilljs.com> - * @author Julius Härtl <jus@bitgrid.net> - * @author Lukas Reschke <lukas@statuscode.ch> - * @author Morris Jobke <hey@morrisjobke.de> - * @author Robin Appelman <robin@icewind.nl> - * @author Roeland Jago Douma <roeland@famdouma.nl> - * @author Vincent Petry <vincent@nextcloud.com> - * - * @license AGPL-3.0 - * - * This code is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License, version 3, - * as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License, version 3, - * along with this program. If not, see <http://www.gnu.org/licenses/> - * + * SPDX-FileCopyrightText: 2016-2024 Nextcloud GmbH and Nextcloud contributors + * SPDX-FileCopyrightText: 2016 ownCloud, Inc. + * SPDX-License-Identifier: AGPL-3.0-only */ namespace OC\Files\Config; +use OC\User\LazyUser; use OCP\Cache\CappedMemoryCache; -use OCA\Files_Sharing\SharedMount; use OCP\DB\QueryBuilder\IQueryBuilder; use OCP\Diagnostics\IEventLogger; use OCP\Files\Config\ICachedMountFileInfo; use OCP\Files\Config\ICachedMountInfo; use OCP\Files\Config\IUserMountCache; -use OCP\Files\Mount\IMountPoint; use OCP\Files\NotFoundException; use OCP\IDBConnection; use OCP\IUser; @@ -54,6 +32,11 @@ class UserMountCache implements IUserMountCache { * @var CappedMemoryCache<ICachedMountInfo[]> **/ private CappedMemoryCache $mountsForUsers; + /** + * fileid => internal path mapping for cached mount info. + * @var CappedMemoryCache<string> + **/ + private CappedMemoryCache $internalPathCache; private LoggerInterface $logger; /** @var CappedMemoryCache<array> */ private CappedMemoryCache $cacheInfoCache; @@ -73,46 +56,33 @@ class UserMountCache implements IUserMountCache { $this->logger = $logger; $this->eventLogger = $eventLogger; $this->cacheInfoCache = new CappedMemoryCache(); + $this->internalPathCache = new CappedMemoryCache(); $this->mountsForUsers = new CappedMemoryCache(); } - public function registerMounts(IUser $user, array $mounts, array $mountProviderClasses = null) { + public function registerMounts(IUser $user, array $mounts, ?array $mountProviderClasses = null) { $this->eventLogger->start('fs:setup:user:register', 'Registering mounts for user'); - // filter out non-proper storages coming from unit tests - $mounts = array_filter($mounts, function (IMountPoint $mount) { - return $mount instanceof SharedMount || ($mount->getStorage() && $mount->getStorage()->getCache()); - }); - /** @var ICachedMountInfo[] $newMounts */ - $newMounts = array_map(function (IMountPoint $mount) use ($user) { + /** @var array<string, ICachedMountInfo> $newMounts */ + $newMounts = []; + foreach ($mounts as $mount) { // filter out any storages which aren't scanned yet since we aren't interested in files from those storages (yet) - if ($mount->getStorageRootId() === -1) { - return null; - } else { - return new LazyStorageMountInfo($user, $mount); + if ($mount->getStorageRootId() !== -1) { + $mountInfo = new LazyStorageMountInfo($user, $mount); + $newMounts[$mountInfo->getKey()] = $mountInfo; } - }, $mounts); - $newMounts = array_values(array_filter($newMounts)); - $newMountKeys = array_map(function (ICachedMountInfo $mount) { - return $mount->getRootId() . '::' . $mount->getMountPoint(); - }, $newMounts); - $newMounts = array_combine($newMountKeys, $newMounts); + } $cachedMounts = $this->getMountsForUser($user); if (is_array($mountProviderClasses)) { $cachedMounts = array_filter($cachedMounts, function (ICachedMountInfo $mountInfo) use ($mountProviderClasses, $newMounts) { // for existing mounts that didn't have a mount provider set // we still want the ones that map to new mounts - $mountKey = $mountInfo->getRootId() . '::' . $mountInfo->getMountPoint(); - if ($mountInfo->getMountProvider() === '' && isset($newMounts[$mountKey])) { + if ($mountInfo->getMountProvider() === '' && isset($newMounts[$mountInfo->getKey()])) { return true; } return in_array($mountInfo->getMountProvider(), $mountProviderClasses); }); } - $cachedRootKeys = array_map(function (ICachedMountInfo $mount) { - return $mount->getRootId() . '::' . $mount->getMountPoint(); - }, $cachedMounts); - $cachedMounts = array_combine($cachedRootKeys, $cachedMounts); $addedMounts = []; $removedMounts = []; @@ -131,46 +101,44 @@ class UserMountCache implements IUserMountCache { $changedMounts = $this->findChangedMounts($newMounts, $cachedMounts); - $this->connection->beginTransaction(); - try { - foreach ($addedMounts as $mount) { - $this->addToCache($mount); - /** @psalm-suppress InvalidArgument */ - $this->mountsForUsers[$user->getUID()][] = $mount; - } - foreach ($removedMounts as $mount) { - $this->removeFromCache($mount); - $index = array_search($mount, $this->mountsForUsers[$user->getUID()]); - unset($this->mountsForUsers[$user->getUID()][$index]); - } - foreach ($changedMounts as $mount) { - $this->updateCachedMount($mount); + if ($addedMounts || $removedMounts || $changedMounts) { + $this->connection->beginTransaction(); + $userUID = $user->getUID(); + try { + foreach ($addedMounts as $mount) { + $this->addToCache($mount); + /** @psalm-suppress InvalidArgument */ + $this->mountsForUsers[$userUID][$mount->getKey()] = $mount; + } + foreach ($removedMounts as $mount) { + $this->removeFromCache($mount); + unset($this->mountsForUsers[$userUID][$mount->getKey()]); + } + foreach ($changedMounts as $mount) { + $this->updateCachedMount($mount); + /** @psalm-suppress InvalidArgument */ + $this->mountsForUsers[$userUID][$mount->getKey()] = $mount; + } + $this->connection->commit(); + } catch (\Throwable $e) { + $this->connection->rollBack(); + throw $e; } - $this->connection->commit(); - } catch (\Throwable $e) { - $this->connection->rollBack(); - throw $e; } $this->eventLogger->end('fs:setup:user:register'); } /** - * @param ICachedMountInfo[] $newMounts - * @param ICachedMountInfo[] $cachedMounts + * @param array<string, ICachedMountInfo> $newMounts + * @param array<string, ICachedMountInfo> $cachedMounts * @return ICachedMountInfo[] */ private function findChangedMounts(array $newMounts, array $cachedMounts) { - $new = []; - foreach ($newMounts as $mount) { - $new[$mount->getRootId() . '::' . $mount->getMountPoint()] = $mount; - } $changed = []; - foreach ($cachedMounts as $cachedMount) { - $key = $cachedMount->getRootId() . '::' . $cachedMount->getMountPoint(); - if (isset($new[$key])) { - $newMount = $new[$key]; + foreach ($cachedMounts as $key => $cachedMount) { + if (isset($newMounts[$key])) { + $newMount = $newMounts[$key]; if ( - $newMount->getMountPoint() !== $cachedMount->getMountPoint() || $newMount->getStorageId() !== $cachedMount->getStorageId() || $newMount->getMountId() !== $cachedMount->getMountId() || $newMount->getMountProvider() !== $cachedMount->getMountProvider() @@ -222,24 +190,38 @@ class UserMountCache implements IUserMountCache { $query->execute(); } - private function dbRowToMountInfo(array $row) { - $user = $this->userManager->get($row['user_id']); - if (is_null($user)) { - return null; - } + /** + * @param array $row + * @param (callable(CachedMountInfo): string)|null $pathCallback + * @return CachedMountInfo + */ + private function dbRowToMountInfo(array $row, ?callable $pathCallback = null): ICachedMountInfo { + $user = new LazyUser($row['user_id'], $this->userManager); $mount_id = $row['mount_id']; if (!is_null($mount_id)) { $mount_id = (int)$mount_id; } - return new CachedMountInfo( - $user, - (int)$row['storage_id'], - (int)$row['root_id'], - $row['mount_point'], - $row['mount_provider_class'] ?? '', - $mount_id, - isset($row['path']) ? $row['path'] : '', - ); + if ($pathCallback) { + return new LazyPathCachedMountInfo( + $user, + (int)$row['storage_id'], + (int)$row['root_id'], + $row['mount_point'], + $row['mount_provider_class'] ?? '', + $mount_id, + $pathCallback, + ); + } else { + return new CachedMountInfo( + $user, + (int)$row['storage_id'], + (int)$row['root_id'], + $row['mount_point'], + $row['mount_provider_class'] ?? '', + $mount_id, + $row['path'] ?? '', + ); + } } /** @@ -247,20 +229,43 @@ class UserMountCache implements IUserMountCache { * @return ICachedMountInfo[] */ public function getMountsForUser(IUser $user) { - if (!isset($this->mountsForUsers[$user->getUID()])) { + $userUID = $user->getUID(); + if (!$this->userManager->userExists($userUID)) { + return []; + } + if (!isset($this->mountsForUsers[$userUID])) { $builder = $this->connection->getQueryBuilder(); - $query = $builder->select('storage_id', 'root_id', 'user_id', 'mount_point', 'mount_id', 'f.path', 'mount_provider_class') + $query = $builder->select('storage_id', 'root_id', 'user_id', 'mount_point', 'mount_id', 'mount_provider_class') ->from('mounts', 'm') - ->innerJoin('m', 'filecache', 'f', $builder->expr()->eq('m.root_id', 'f.fileid')) - ->where($builder->expr()->eq('user_id', $builder->createPositionalParameter($user->getUID()))); + ->where($builder->expr()->eq('user_id', $builder->createPositionalParameter($userUID))); $result = $query->execute(); $rows = $result->fetchAll(); $result->closeCursor(); - $this->mountsForUsers[$user->getUID()] = array_filter(array_map([$this, 'dbRowToMountInfo'], $rows)); + /** @var array<string, ICachedMountInfo> $mounts */ + $mounts = []; + foreach ($rows as $row) { + $mount = $this->dbRowToMountInfo($row, [$this, 'getInternalPathForMountInfo']); + if ($mount !== null) { + $mounts[$mount->getKey()] = $mount; + } + } + $this->mountsForUsers[$userUID] = $mounts; } - return $this->mountsForUsers[$user->getUID()]; + return $this->mountsForUsers[$userUID]; + } + + public function getInternalPathForMountInfo(CachedMountInfo $info): string { + $cached = $this->internalPathCache->get($info->getRootId()); + if ($cached !== null) { + return $cached; + } + $builder = $this->connection->getQueryBuilder(); + $query = $builder->select('path') + ->from('filecache') + ->where($builder->expr()->eq('fileid', $builder->createPositionalParameter($info->getRootId()))); + return $query->executeQuery()->fetchOne() ?: ''; } /** @@ -345,30 +350,22 @@ class UserMountCache implements IUserMountCache { } catch (NotFoundException $e) { return []; } - $builder = $this->connection->getQueryBuilder(); - $query = $builder->select('storage_id', 'root_id', 'user_id', 'mount_point', 'mount_id', 'f.path', 'mount_provider_class') - ->from('mounts', 'm') - ->innerJoin('m', 'filecache', 'f', $builder->expr()->eq('m.root_id', 'f.fileid')) - ->where($builder->expr()->eq('storage_id', $builder->createPositionalParameter($storageId, IQueryBuilder::PARAM_INT))); - - if ($user) { - $query->andWhere($builder->expr()->eq('user_id', $builder->createPositionalParameter($user))); - } + $mountsForStorage = $this->getMountsForStorageId($storageId, $user); - $result = $query->execute(); - $rows = $result->fetchAll(); - $result->closeCursor(); - // filter mounts that are from the same storage but a different directory - $filteredMounts = array_filter($rows, function (array $row) use ($internalPath, $fileId) { - if ($fileId === (int)$row['root_id']) { + // filter mounts that are from the same storage but not a parent of the file we care about + $filteredMounts = array_filter($mountsForStorage, function (ICachedMountInfo $mount) use ($internalPath, $fileId) { + if ($fileId === $mount->getRootId()) { return true; } - $internalMountPath = $row['path'] ?? ''; + $internalMountPath = $mount->getRootInternalPath(); + + return $internalMountPath === '' || str_starts_with($internalPath, $internalMountPath . '/'); + }); - return $internalMountPath === '' || substr($internalPath, 0, strlen($internalMountPath) + 1) === $internalMountPath . '/'; + $filteredMounts = array_filter($filteredMounts, function (ICachedMountInfo $mount) { + return $this->userManager->userExists($mount->getUser()->getUID()); }); - $filteredMounts = array_filter(array_map([$this, 'dbRowToMountInfo'], $filteredMounts)); return array_map(function (ICachedMountInfo $mount) use ($internalPath) { return new CachedMountFileInfo( $mount->getUser(), @@ -463,7 +460,7 @@ class UserMountCache implements IUserMountCache { }, $mounts); $mounts = array_combine($mountPoints, $mounts); - $current = $path; + $current = rtrim($path, '/'); // walk up the directory tree until we find a path that has a mountpoint set // the loop will return if a mountpoint is found or break if none are found while (true) { diff --git a/lib/private/Files/Config/UserMountCacheListener.php b/lib/private/Files/Config/UserMountCacheListener.php index eef91a03853..40995de8986 100644 --- a/lib/private/Files/Config/UserMountCacheListener.php +++ b/lib/private/Files/Config/UserMountCacheListener.php @@ -1,23 +1,9 @@ <?php + /** - * @copyright Copyright (c) 2016, ownCloud, Inc. - * - * @author Robin Appelman <robin@icewind.nl> - * - * @license AGPL-3.0 - * - * This code is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License, version 3, - * as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License, version 3, - * along with this program. If not, see <http://www.gnu.org/licenses/> - * + * SPDX-FileCopyrightText: 2016-2024 Nextcloud GmbH and Nextcloud contributors + * SPDX-FileCopyrightText: 2016 ownCloud, Inc. + * SPDX-License-Identifier: AGPL-3.0-only */ namespace OC\Files\Config; diff --git a/lib/private/Files/FileInfo.php b/lib/private/Files/FileInfo.php index 3937ee16a7c..c3dcb531342 100644 --- a/lib/private/Files/FileInfo.php +++ b/lib/private/Files/FileInfo.php @@ -1,43 +1,22 @@ <?php + /** - * @copyright Copyright (c) 2016, ownCloud, Inc. - * - * @author Christoph Wurst <christoph@winzerhof-wurst.at> - * @author Joas Schilling <coding@schilljs.com> - * @author Julius Härtl <jus@bitgrid.net> - * @author Lukas Reschke <lukas@statuscode.ch> - * @author Morris Jobke <hey@morrisjobke.de> - * @author Piotr M <mrow4a@yahoo.com> - * @author Robin Appelman <robin@icewind.nl> - * @author Robin McCorkell <robin@mccorkell.me.uk> - * @author Roeland Jago Douma <roeland@famdouma.nl> - * @author tbartenstein <tbartenstein@users.noreply.github.com> - * @author Thomas Müller <thomas.mueller@tmit.eu> - * @author Vincent Petry <vincent@nextcloud.com> - * - * @license AGPL-3.0 - * - * This code is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License, version 3, - * as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License, version 3, - * along with this program. If not, see <http://www.gnu.org/licenses/> - * + * SPDX-FileCopyrightText: 2016-2024 Nextcloud GmbH and Nextcloud contributors + * SPDX-FileCopyrightText: 2016 ownCloud, Inc. + * SPDX-License-Identifier: AGPL-3.0-only */ namespace OC\Files; -use OCA\Files_Sharing\ISharedStorage; +use OC\Files\Mount\HomeMountPoint; +use OCA\Files_Sharing\External\Mount; +use OCA\Files_Sharing\ISharedMountPoint; use OCP\Files\Cache\ICacheEntry; -use OCP\Files\IHomeStorage; use OCP\Files\Mount\IMountPoint; use OCP\IUser; +/** + * @template-implements \ArrayAccess<string,mixed> + */ class FileInfo implements \OCP\Files\FileInfo, \ArrayAccess { private array|ICacheEntry $data; /** @@ -121,21 +100,14 @@ class FileInfo implements \OCP\Files\FileInfo, \ArrayAccess { */ #[\ReturnTypeWillChange] public function offsetGet($offset) { - if ($offset === 'type') { - return $this->getType(); - } elseif ($offset === 'etag') { - return $this->getEtag(); - } elseif ($offset === 'size') { - return $this->getSize(); - } elseif ($offset === 'mtime') { - return $this->getMTime(); - } elseif ($offset === 'permissions') { - return $this->getPermissions(); - } elseif (isset($this->data[$offset])) { - return $this->data[$offset]; - } else { - return null; - } + return match ($offset) { + 'type' => $this->getType(), + 'etag' => $this->getEtag(), + 'size' => $this->getSize(), + 'mtime' => $this->getMTime(), + 'permissions' => $this->getPermissions(), + default => $this->data[$offset] ?? null, + }; } /** @@ -183,7 +155,9 @@ class FileInfo implements \OCP\Files\FileInfo, \ArrayAccess { * @return string */ public function getName() { - return isset($this->data['name']) ? $this->data['name'] : basename($this->getPath()); + return empty($this->data['name']) + ? basename($this->getPath()) + : $this->data['name']; } /** @@ -207,7 +181,7 @@ class FileInfo implements \OCP\Files\FileInfo, \ArrayAccess { if ($includeMounts) { $this->updateEntryfromSubMounts(); - if (isset($this->data['unencrypted_size']) && $this->data['unencrypted_size'] > 0) { + if ($this->isEncrypted() && isset($this->data['unencrypted_size']) && $this->data['unencrypted_size'] > 0) { return $this->data['unencrypted_size']; } else { return isset($this->data['size']) ? 0 + $this->data['size'] : 0; @@ -229,11 +203,11 @@ class FileInfo implements \OCP\Files\FileInfo, \ArrayAccess { * @return bool */ public function isEncrypted() { - return $this->data['encrypted']; + return $this->data['encrypted'] ?? false; } /** - * Return the currently version used for the HMAC in the encryption app + * Return the current version used for the HMAC in the encryption app */ public function getEncryptedVersion(): int { return isset($this->data['encryptedVersion']) ? (int) $this->data['encryptedVersion'] : 1; @@ -243,11 +217,7 @@ class FileInfo implements \OCP\Files\FileInfo, \ArrayAccess { * @return int */ public function getPermissions() { - $perms = (int) $this->data['permissions']; - if (\OCP\Util::isSharingDisabledForUser() || ($this->isShared() && !\OC\Share\Share::isResharingAllowed())) { - $perms = $perms & ~\OCP\Constants::PERMISSION_SHARE; - } - return $perms; + return (int) $this->data['permissions']; } /** @@ -315,13 +285,12 @@ class FileInfo implements \OCP\Files\FileInfo, \ArrayAccess { * @return bool */ public function isShared() { - $storage = $this->getStorage(); - return $storage->instanceOfStorage(ISharedStorage::class); + return $this->mount instanceof ISharedMountPoint; } public function isMounted() { - $storage = $this->getStorage(); - return !($storage->instanceOfStorage(IHomeStorage::class) || $storage->instanceOfStorage(ISharedStorage::class)); + $isHome = $this->mount instanceof HomeMountPoint; + return !$isHome && !$this->isShared(); } /** @@ -416,4 +385,16 @@ class FileInfo implements \OCP\Files\FileInfo, \ArrayAccess { public function getUploadTime(): int { return (int) $this->data['upload_time']; } + + public function getParentId(): int { + return $this->data['parent'] ?? -1; + } + + /** + * @inheritDoc + * @return array<string, int|string|bool|float|string[]|int[]> + */ + public function getMetadata(): array { + return $this->data['metadata'] ?? []; + } } diff --git a/lib/private/Files/Filesystem.php b/lib/private/Files/Filesystem.php index 5f7c0c403db..db7420c3c4c 100644 --- a/lib/private/Files/Filesystem.php +++ b/lib/private/Files/Filesystem.php @@ -1,45 +1,15 @@ <?php + /** - * @copyright Copyright (c) 2016, ownCloud, Inc. - * - * @author Arthur Schiwon <blizzz@arthur-schiwon.de> - * @author Bart Visscher <bartv@thisnet.nl> - * @author Christopher Schäpers <kondou@ts.unde.re> - * @author Christoph Wurst <christoph@winzerhof-wurst.at> - * @author Florin Peter <github@florin-peter.de> - * @author Joas Schilling <coding@schilljs.com> - * @author Jörn Friedrich Dreyer <jfd@butonic.de> - * @author korelstar <korelstar@users.noreply.github.com> - * @author Lukas Reschke <lukas@statuscode.ch> - * @author Michael Gapczynski <GapczynskiM@gmail.com> - * @author Morris Jobke <hey@morrisjobke.de> - * @author Robin Appelman <robin@icewind.nl> - * @author Robin McCorkell <robin@mccorkell.me.uk> - * @author Roeland Jago Douma <roeland@famdouma.nl> - * @author Sam Tuke <mail@samtuke.com> - * @author Stephan Peijnik <speijnik@anexia-it.com> - * @author Vincent Petry <vincent@nextcloud.com> - * - * @license AGPL-3.0 - * - * This code is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License, version 3, - * as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License, version 3, - * along with this program. If not, see <http://www.gnu.org/licenses/> - * + * SPDX-FileCopyrightText: 2016-2024 Nextcloud GmbH and Nextcloud contributors + * SPDX-FileCopyrightText: 2016 ownCloud, Inc. + * SPDX-License-Identifier: AGPL-3.0-only */ namespace OC\Files; -use OCP\Cache\CappedMemoryCache; use OC\Files\Mount\MountPoint; use OC\User\NoUserException; +use OCP\Cache\CappedMemoryCache; use OCP\EventDispatcher\IEventDispatcher; use OCP\Files\Events\Node\FilesystemTornDownEvent; use OCP\Files\Mount\IMountManager; @@ -48,6 +18,7 @@ use OCP\Files\Storage\IStorageFactory; use OCP\IUser; use OCP\IUserManager; use OCP\IUserSession; +use Psr\Log\LoggerInterface; class Filesystem { private static ?Mount\Manager $mounts = null; @@ -200,7 +171,7 @@ class Filesystem { */ public static function addStorageWrapper($wrapperName, $wrapper, $priority = 50) { if (self::$logWarningWhenAddingStorageWrapper) { - \OC::$server->getLogger()->warning("Storage wrapper '{wrapper}' was not registered via the 'OC_Filesystem - preSetup' hook which could cause potential problems.", [ + \OCP\Server::get(LoggerInterface::class)->warning("Storage wrapper '{wrapper}' was not registered via the 'OC_Filesystem - preSetup' hook which could cause potential problems.", [ 'wrapper' => $wrapperName, 'app' => 'filesystem', ]); diff --git a/lib/private/Files/Lock/LockManager.php b/lib/private/Files/Lock/LockManager.php index e2af532a01c..978c378e506 100644 --- a/lib/private/Files/Lock/LockManager.php +++ b/lib/private/Files/Lock/LockManager.php @@ -1,5 +1,9 @@ <?php +/** + * SPDX-FileCopyrightText: 2022 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later + */ namespace OC\Files\Lock; use OCP\Files\Lock\ILock; @@ -7,8 +11,11 @@ use OCP\Files\Lock\ILockManager; use OCP\Files\Lock\ILockProvider; use OCP\Files\Lock\LockContext; use OCP\PreConditionNotMetException; +use Psr\Container\ContainerExceptionInterface; +use Psr\Container\NotFoundExceptionInterface; class LockManager implements ILockManager { + private ?string $lockProviderClass = null; private ?ILockProvider $lockProvider = null; private ?LockContext $lockInScope = null; @@ -20,12 +27,34 @@ class LockManager implements ILockManager { $this->lockProvider = $lockProvider; } + public function registerLazyLockProvider(string $lockProviderClass): void { + if ($this->lockProviderClass || $this->lockProvider) { + throw new PreConditionNotMetException('There is already a registered lock provider'); + } + + $this->lockProviderClass = $lockProviderClass; + } + + private function getLockProvider(): ?ILockProvider { + if ($this->lockProvider) { + return $this->lockProvider; + } + if ($this->lockProviderClass) { + try { + $this->lockProvider = \OCP\Server::get($this->lockProviderClass); + } catch (NotFoundExceptionInterface|ContainerExceptionInterface $e) { + } + } + + return $this->lockProvider; + } + public function isLockProviderAvailable(): bool { - return $this->lockProvider !== null; + return $this->getLockProvider() !== null; } public function runInScope(LockContext $lock, callable $callback): void { - if (!$this->lockProvider) { + if (!$this->getLockProvider()) { $callback(); return; } @@ -47,26 +76,26 @@ class LockManager implements ILockManager { } public function getLocks(int $fileId): array { - if (!$this->lockProvider) { + if (!$this->getLockProvider()) { throw new PreConditionNotMetException('No lock provider available'); } - return $this->lockProvider->getLocks($fileId); + return $this->getLockProvider()->getLocks($fileId); } public function lock(LockContext $lockInfo): ILock { - if (!$this->lockProvider) { + if (!$this->getLockProvider()) { throw new PreConditionNotMetException('No lock provider available'); } - return $this->lockProvider->lock($lockInfo); + return $this->getLockProvider()->lock($lockInfo); } public function unlock(LockContext $lockInfo): void { - if (!$this->lockProvider) { + if (!$this->getLockProvider()) { throw new PreConditionNotMetException('No lock provider available'); } - $this->lockProvider->unlock($lockInfo); + $this->getLockProvider()->unlock($lockInfo); } } diff --git a/lib/private/Files/Mount/CacheMountProvider.php b/lib/private/Files/Mount/CacheMountProvider.php index 903b93276c0..27c7eec9da3 100644 --- a/lib/private/Files/Mount/CacheMountProvider.php +++ b/lib/private/Files/Mount/CacheMountProvider.php @@ -1,23 +1,9 @@ <?php + /** - * @copyright Copyright (c) 2016, ownCloud, Inc. - * - * @author Robin Appelman <robin@icewind.nl> - * - * @license AGPL-3.0 - * - * This code is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License, version 3, - * as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License, version 3, - * along with this program. If not, see <http://www.gnu.org/licenses/> - * + * SPDX-FileCopyrightText: 2016-2024 Nextcloud GmbH and Nextcloud contributors + * SPDX-FileCopyrightText: 2016 ownCloud, Inc. + * SPDX-License-Identifier: AGPL-3.0-only */ namespace OC\Files\Mount; diff --git a/lib/private/Files/Mount/HomeMountPoint.php b/lib/private/Files/Mount/HomeMountPoint.php new file mode 100644 index 00000000000..0aa4b54ddf0 --- /dev/null +++ b/lib/private/Files/Mount/HomeMountPoint.php @@ -0,0 +1,34 @@ +<?php + +declare(strict_types=1); +/** + * SPDX-FileCopyrightText: 2023 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later + */ + +namespace OC\Files\Mount; + +use OCP\Files\Storage\IStorageFactory; +use OCP\IUser; + +class HomeMountPoint extends MountPoint { + private IUser $user; + + public function __construct( + IUser $user, + $storage, + string $mountpoint, + ?array $arguments = null, + ?IStorageFactory $loader = null, + ?array $mountOptions = null, + ?int $mountId = null, + ?string $mountProvider = null + ) { + parent::__construct($storage, $mountpoint, $arguments, $loader, $mountOptions, $mountId, $mountProvider); + $this->user = $user; + } + + public function getUser(): IUser { + return $this->user; + } +} diff --git a/lib/private/Files/Mount/LocalHomeMountProvider.php b/lib/private/Files/Mount/LocalHomeMountProvider.php index 25a67fc1574..a2b3d3b2a99 100644 --- a/lib/private/Files/Mount/LocalHomeMountProvider.php +++ b/lib/private/Files/Mount/LocalHomeMountProvider.php @@ -1,23 +1,9 @@ <?php + /** - * @copyright Copyright (c) 2016, ownCloud, Inc. - * - * @author Robin Appelman <robin@icewind.nl> - * - * @license AGPL-3.0 - * - * This code is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License, version 3, - * as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License, version 3, - * along with this program. If not, see <http://www.gnu.org/licenses/> - * + * SPDX-FileCopyrightText: 2016-2024 Nextcloud GmbH and Nextcloud contributors + * SPDX-FileCopyrightText: 2016 ownCloud, Inc. + * SPDX-License-Identifier: AGPL-3.0-only */ namespace OC\Files\Mount; @@ -38,6 +24,6 @@ class LocalHomeMountProvider implements IHomeMountProvider { */ public function getHomeMountForUser(IUser $user, IStorageFactory $loader) { $arguments = ['user' => $user]; - return new MountPoint('\OC\Files\Storage\Home', '/' . $user->getUID(), $arguments, $loader, null, null, self::class); + return new HomeMountPoint($user, '\OC\Files\Storage\Home', '/' . $user->getUID(), $arguments, $loader, null, null, self::class); } } diff --git a/lib/private/Files/Mount/Manager.php b/lib/private/Files/Mount/Manager.php index 805cce658a6..c2267af3c96 100644 --- a/lib/private/Files/Mount/Manager.php +++ b/lib/private/Files/Mount/Manager.php @@ -1,38 +1,18 @@ <?php declare(strict_types=1); - /** - * @copyright Copyright (c) 2016, ownCloud, Inc. - * - * @author Björn Schießle <bjoern@schiessle.org> - * @author Morris Jobke <hey@morrisjobke.de> - * @author Robin Appelman <robin@icewind.nl> - * @author Robin McCorkell <robin@mccorkell.me.uk> - * @author Roeland Jago Douma <roeland@famdouma.nl> - * - * @license AGPL-3.0 - * - * This code is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License, version 3, - * as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License, version 3, - * along with this program. If not, see <http://www.gnu.org/licenses/> - * + * SPDX-FileCopyrightText: 2016-2024 Nextcloud GmbH and Nextcloud contributors + * SPDX-FileCopyrightText: 2016 ownCloud, Inc. + * SPDX-License-Identifier: AGPL-3.0-only */ - namespace OC\Files\Mount; -use OCP\Cache\CappedMemoryCache; use OC\Files\Filesystem; use OC\Files\SetupManager; use OC\Files\SetupManagerFactory; +use OCP\Cache\CappedMemoryCache; +use OCP\Files\Config\ICachedMountInfo; use OCP\Files\Mount\IMountManager; use OCP\Files\Mount\IMountPoint; use OCP\Files\NotFoundException; @@ -99,6 +79,15 @@ class Manager implements IMountManager { return $this->pathCache[$path]; } + + + if (count($this->mounts) === 0) { + $this->setupManager->setupRoot(); + if (count($this->mounts) === 0) { + throw new \Exception("No mounts even after explicitly setting up the root mounts"); + } + } + $current = $path; while (true) { $mountPoint = $current . '/'; @@ -115,7 +104,7 @@ class Manager implements IMountManager { } } - throw new NotFoundException("No mount for path " . $path . " existing mounts: " . implode(",", array_keys($this->mounts))); + throw new NotFoundException("No mount for path " . $path . " existing mounts (" . count($this->mounts) ."): " . implode(",", array_keys($this->mounts))); } /** @@ -226,4 +215,21 @@ class Manager implements IMountManager { }); } } + + /** + * Return the mount matching a cached mount info (or mount file info) + * + * @param ICachedMountInfo $info + * + * @return IMountPoint|null + */ + public function getMountFromMountInfo(ICachedMountInfo $info): ?IMountPoint { + $this->setupManager->setupForPath($info->getMountPoint()); + foreach ($this->mounts as $mount) { + if ($mount->getMountPoint() === $info->getMountPoint()) { + return $mount; + } + } + return null; + } } diff --git a/lib/private/Files/Mount/MountPoint.php b/lib/private/Files/Mount/MountPoint.php index f526928cc15..cb3a3e8a22d 100644 --- a/lib/private/Files/Mount/MountPoint.php +++ b/lib/private/Files/Mount/MountPoint.php @@ -1,32 +1,9 @@ <?php + /** - * @copyright Copyright (c) 2016, ownCloud, Inc. - * - * @author Arthur Schiwon <blizzz@arthur-schiwon.de> - * @author Björn Schießle <bjoern@schiessle.org> - * @author Christoph Wurst <christoph@winzerhof-wurst.at> - * @author Georg Ehrke <oc.list@georgehrke.com> - * @author Jörn Friedrich Dreyer <jfd@butonic.de> - * @author Morris Jobke <hey@morrisjobke.de> - * @author Robin Appelman <robin@icewind.nl> - * @author Robin McCorkell <robin@mccorkell.me.uk> - * @author Roeland Jago Douma <roeland@famdouma.nl> - * @author Vincent Petry <vincent@nextcloud.com> - * - * @license AGPL-3.0 - * - * This code is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License, version 3, - * as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License, version 3, - * along with this program. If not, see <http://www.gnu.org/licenses/> - * + * SPDX-FileCopyrightText: 2016-2024 Nextcloud GmbH and Nextcloud contributors + * SPDX-FileCopyrightText: 2016 ownCloud, Inc. + * SPDX-License-Identifier: AGPL-3.0-only */ namespace OC\Files\Mount; @@ -94,11 +71,11 @@ class MountPoint implements IMountPoint { public function __construct( $storage, string $mountpoint, - array $arguments = null, - IStorageFactory $loader = null, - array $mountOptions = null, - int $mountId = null, - string $mountProvider = null + ?array $arguments = null, + ?IStorageFactory $loader = null, + ?array $mountOptions = null, + ?int $mountId = null, + ?string $mountProvider = null ) { if (is_null($arguments)) { $arguments = []; @@ -272,7 +249,7 @@ class MountPoint implements IMountPoint { * @return mixed */ public function getOption($name, $default) { - return isset($this->mountOptions[$name]) ? $this->mountOptions[$name] : $default; + return $this->mountOptions[$name] ?? $default; } /** diff --git a/lib/private/Files/Mount/MoveableMount.php b/lib/private/Files/Mount/MoveableMount.php index 7dbed24504e..755733bf651 100644 --- a/lib/private/Files/Mount/MoveableMount.php +++ b/lib/private/Files/Mount/MoveableMount.php @@ -1,24 +1,9 @@ <?php + /** - * @copyright Copyright (c) 2016, ownCloud, Inc. - * - * @author Morris Jobke <hey@morrisjobke.de> - * @author Robin Appelman <robin@icewind.nl> - * - * @license AGPL-3.0 - * - * This code is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License, version 3, - * as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License, version 3, - * along with this program. If not, see <http://www.gnu.org/licenses/> - * + * SPDX-FileCopyrightText: 2016-2024 Nextcloud GmbH and Nextcloud contributors + * SPDX-FileCopyrightText: 2016 ownCloud, Inc. + * SPDX-License-Identifier: AGPL-3.0-only */ namespace OC\Files\Mount; diff --git a/lib/private/Files/Mount/ObjectHomeMountProvider.php b/lib/private/Files/Mount/ObjectHomeMountProvider.php index 77912adfd34..99c52108fa8 100644 --- a/lib/private/Files/Mount/ObjectHomeMountProvider.php +++ b/lib/private/Files/Mount/ObjectHomeMountProvider.php @@ -1,26 +1,9 @@ <?php + /** - * @copyright Copyright (c) 2016, ownCloud, Inc. - * - * @author Arthur Schiwon <blizzz@arthur-schiwon.de> - * @author Robin Appelman <robin@icewind.nl> - * @author Roeland Jago Douma <roeland@famdouma.nl> - * @author Vlastimil Pecinka <pecinka@email.cz> - * - * @license AGPL-3.0 - * - * This code is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License, version 3, - * as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License, version 3, - * along with this program. If not, see <http://www.gnu.org/licenses/> - * + * SPDX-FileCopyrightText: 2016-2024 Nextcloud GmbH and Nextcloud contributors + * SPDX-FileCopyrightText: 2016 ownCloud, Inc. + * SPDX-License-Identifier: AGPL-3.0-only */ namespace OC\Files\Mount; @@ -65,7 +48,7 @@ class ObjectHomeMountProvider implements IHomeMountProvider { return null; } - return new MountPoint('\OC\Files\ObjectStore\HomeObjectStoreStorage', '/' . $user->getUID(), $config['arguments'], $loader, null, null, self::class); + return new HomeMountPoint($user, '\OC\Files\ObjectStore\HomeObjectStoreStorage', '/' . $user->getUID(), $config['arguments'], $loader, null, null, self::class); } /** @@ -122,7 +105,7 @@ class ObjectHomeMountProvider implements IHomeMountProvider { $config['arguments']['bucket'] = ''; } $mapper = new \OC\Files\ObjectStore\Mapper($user, $this->config); - $numBuckets = isset($config['arguments']['num_buckets']) ? $config['arguments']['num_buckets'] : 64; + $numBuckets = $config['arguments']['num_buckets'] ?? 64; $config['arguments']['bucket'] .= $mapper->getBucket($numBuckets); $this->config->setUserValue($user->getUID(), 'homeobjectstore', 'bucket', $config['arguments']['bucket']); diff --git a/lib/private/Files/Mount/ObjectStorePreviewCacheMountProvider.php b/lib/private/Files/Mount/ObjectStorePreviewCacheMountProvider.php index 3a20afba5a5..1546ef98f50 100644 --- a/lib/private/Files/Mount/ObjectStorePreviewCacheMountProvider.php +++ b/lib/private/Files/Mount/ObjectStorePreviewCacheMountProvider.php @@ -3,25 +3,8 @@ declare(strict_types=1); /** - * @copyright Copyright (c) 2020, Morris Jobke <hey@morrisjobke.de> - * - * @author Morris Jobke <hey@morrisjobke.de> - * - * @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/>. - * + * SPDX-FileCopyrightText: 2020 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later */ namespace OC\Files\Mount; diff --git a/lib/private/Files/Mount/RootMountProvider.php b/lib/private/Files/Mount/RootMountProvider.php index 794421181c7..7ad7a7f059c 100644 --- a/lib/private/Files/Mount/RootMountProvider.php +++ b/lib/private/Files/Mount/RootMountProvider.php @@ -2,23 +2,8 @@ declare(strict_types=1); /** - * @copyright Copyright (c) 2022 Robin Appelman <robin@icewind.nl> - * - * @license GNU AGPL version 3 or any later version - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as - * published by the Free Software Foundation, either version 3 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - * + * SPDX-FileCopyrightText: 2022 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later */ namespace OC\Files\Mount; diff --git a/lib/private/Files/Node/File.php b/lib/private/Files/Node/File.php index b6cd6571651..eb6411d7d13 100644 --- a/lib/private/Files/Node/File.php +++ b/lib/private/Files/Node/File.php @@ -1,30 +1,9 @@ <?php + /** - * @copyright Copyright (c) 2016, ownCloud, Inc. - * - * @author Arthur Schiwon <blizzz@arthur-schiwon.de> - * @author Christoph Wurst <christoph@winzerhof-wurst.at> - * @author Joas Schilling <coding@schilljs.com> - * @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 Vincent Petry <vincent@nextcloud.com> - * - * @license AGPL-3.0 - * - * This code is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License, version 3, - * as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License, version 3, - * along with this program. If not, see <http://www.gnu.org/licenses/> - * + * SPDX-FileCopyrightText: 2016-2024 Nextcloud GmbH and Nextcloud contributors + * SPDX-FileCopyrightText: 2016 ownCloud, Inc. + * SPDX-License-Identifier: AGPL-3.0-only */ namespace OC\Files\Node; diff --git a/lib/private/Files/Node/Folder.php b/lib/private/Files/Node/Folder.php index ccd10da9d0c..148dc28318f 100644 --- a/lib/private/Files/Node/Folder.php +++ b/lib/private/Files/Node/Folder.php @@ -1,33 +1,8 @@ <?php /** - * @copyright Copyright (c) 2016, ownCloud, Inc. - * @copyright Copyright (c) 2022 Informatyka Boguslawski sp. z o.o. sp.k., http://www.ib.pl/ - * - * @author Arthur Schiwon <blizzz@arthur-schiwon.de> - * @author Christoph Wurst <christoph@winzerhof-wurst.at> - * @author Georg Ehrke <oc.list@georgehrke.com> - * @author Joas Schilling <coding@schilljs.com> - * @author Julius Härtl <jus@bitgrid.net> - * @author Morris Jobke <hey@morrisjobke.de> - * @author Robin Appelman <robin@icewind.nl> - * @author Robin McCorkell <robin@mccorkell.me.uk> - * @author Roeland Jago Douma <roeland@famdouma.nl> - * @author Vincent Petry <vincent@nextcloud.com> - * - * @license AGPL-3.0 - * - * This code is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License, version 3, - * as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License, version 3, - * along with this program. If not, see <http://www.gnu.org/licenses/> - * + * SPDX-FileCopyrightText: 2022 Nextcloud GmbH and Nextcloud contributors + * SPDX-FileCopyrightText: 2016 ownCloud, Inc. + * SPDX-License-Identifier: AGPL-3.0-only */ namespace OC\Files\Node; @@ -177,7 +152,7 @@ class Folder extends Node implements \OCP\Files\Folder { * @throws \OCP\Files\NotPermittedException */ public function newFile($path, $content = null) { - if (empty($path)) { + if ($path === '') { throw new NotPermittedException('Could not create as provided path is empty'); } if ($this->checkPermissions(\OCP\Constants::PERMISSION_CREATE)) { @@ -199,7 +174,7 @@ class Folder extends Node implements \OCP\Files\Folder { throw new NotPermittedException('No create permission for path "' . $path . '"'); } - private function queryFromOperator(ISearchOperator $operator, string $uid = null, int $limit = 0, int $offset = 0): ISearchQuery { + private function queryFromOperator(ISearchOperator $operator, ?string $uid = null, int $limit = 0, int $offset = 0): ISearchQuery { if ($uid === null) { $user = null; } else { @@ -307,13 +282,17 @@ class Folder extends Node implements \OCP\Files\Folder { /** * @param int $id - * @return \OC\Files\Node\Node[] + * @return \OCP\Files\Node[] */ public function getById($id) { return $this->root->getByIdInPath((int)$id, $this->getPath()); } - protected function getAppDataDirectoryName(): string { + public function getFirstNodeById(int $id): ?\OCP\Files\Node { + return current($this->getById($id)) ?: null; + } + + public function getAppDataDirectoryName(): string { $instanceId = \OC::$server->getConfig()->getSystemValueString('instanceid'); return 'appdata_' . $instanceId; } diff --git a/lib/private/Files/Node/HookConnector.php b/lib/private/Files/Node/HookConnector.php index a8e76d95c22..8fd2ffa3369 100644 --- a/lib/private/Files/Node/HookConnector.php +++ b/lib/private/Files/Node/HookConnector.php @@ -1,28 +1,10 @@ <?php declare(strict_types=1); - /** - * @copyright Copyright (c) 2016, ownCloud, Inc. - * - * @author Arthur Schiwon <blizzz@arthur-schiwon.de> - * @author Robin Appelman <robin@icewind.nl> - * @author Roeland Jago Douma <roeland@famdouma.nl> - * - * @license AGPL-3.0 - * - * This code is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License, version 3, - * as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License, version 3, - * along with this program. If not, see <http://www.gnu.org/licenses/> - * + * SPDX-FileCopyrightText: 2016-2024 Nextcloud GmbH and Nextcloud contributors + * SPDX-FileCopyrightText: 2016 ownCloud, Inc. + * SPDX-License-Identifier: AGPL-3.0-only */ namespace OC\Files\Node; @@ -30,6 +12,7 @@ use OC\Files\Filesystem; use OC\Files\View; use OCP\EventDispatcher\GenericEvent; use OCP\EventDispatcher\IEventDispatcher; +use OCP\Exceptions\AbortedEventException; use OCP\Files\Events\Node\BeforeNodeCopiedEvent; use OCP\Files\Events\Node\BeforeNodeCreatedEvent; use OCP\Files\Events\Node\BeforeNodeDeletedEvent; @@ -46,27 +29,18 @@ use OCP\Files\Events\Node\NodeWrittenEvent; use OCP\Files\FileInfo; use OCP\Files\IRootFolder; use OCP\Util; +use Psr\Log\LoggerInterface; class HookConnector { - /** @var IRootFolder */ - private $root; - - /** @var View */ - private $view; - /** @var FileInfo[] */ - private $deleteMetaCache = []; - - /** @var IEventDispatcher */ - private $dispatcher; + private array $deleteMetaCache = []; public function __construct( - IRootFolder $root, - View $view, - IEventDispatcher $dispatcher) { - $this->root = $root; - $this->view = $view; - $this->dispatcher = $dispatcher; + private IRootFolder $root, + private View $view, + private IEventDispatcher $dispatcher, + private LoggerInterface $logger + ) { } public function viewToNode() { @@ -134,7 +108,12 @@ class HookConnector { $this->dispatcher->dispatch('\OCP\Files::preDelete', new GenericEvent($node)); $event = new BeforeNodeDeletedEvent($node); - $this->dispatcher->dispatchTyped($event); + try { + $this->dispatcher->dispatchTyped($event); + } catch (AbortedEventException $e) { + $arguments['run'] = false; + $this->logger->warning('delete process aborted', ['exception' => $e]); + } } public function postDelete($arguments) { @@ -172,7 +151,12 @@ class HookConnector { $this->dispatcher->dispatch('\OCP\Files::preRename', new GenericEvent([$source, $target])); $event = new BeforeNodeRenamedEvent($source, $target); - $this->dispatcher->dispatchTyped($event); + try { + $this->dispatcher->dispatchTyped($event); + } catch (AbortedEventException $e) { + $arguments['run'] = false; + $this->logger->warning('rename process aborted', ['exception' => $e]); + } } public function postRename($arguments) { @@ -192,7 +176,12 @@ class HookConnector { $this->dispatcher->dispatch('\OCP\Files::preCopy', new GenericEvent([$source, $target])); $event = new BeforeNodeCopiedEvent($source, $target); - $this->dispatcher->dispatchTyped($event); + try { + $this->dispatcher->dispatchTyped($event); + } catch (AbortedEventException $e) { + $arguments['run'] = false; + $this->logger->warning('copy process aborted', ['exception' => $e]); + } } public function postCopy($arguments) { diff --git a/lib/private/Files/Node/LazyFolder.php b/lib/private/Files/Node/LazyFolder.php index d495d6f4c57..83ed7e534a7 100644 --- a/lib/private/Files/Node/LazyFolder.php +++ b/lib/private/Files/Node/LazyFolder.php @@ -1,35 +1,19 @@ <?php declare(strict_types=1); - /** - * @copyright Copyright (c) 2020 Robin Appelman <robin@icewind.nl> - * - * @author Robin Appelman <robin@icewind.nl> - * - * @license GNU AGPL version 3 or any later version - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as - * published by the Free Software Foundation, either version 3 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - * + * SPDX-FileCopyrightText: 2020 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later */ - namespace OC\Files\Node; +use OC\Files\Filesystem; use OC\Files\Utils\PathHelper; -use OCP\Files\Folder; use OCP\Constants; +use OCP\Files\Folder; +use OCP\Files\IRootFolder; use OCP\Files\Mount\IMountPoint; +use OCP\Files\NotPermittedException; /** * Class LazyFolder @@ -41,23 +25,33 @@ use OCP\Files\Mount\IMountPoint; */ class LazyFolder implements Folder { /** @var \Closure(): Folder */ - private $folderClosure; - - /** @var LazyFolder | null */ - protected $folder = null; - + private \Closure $folderClosure; + protected ?Folder $folder = null; + protected IRootFolder $rootFolder; protected array $data; /** - * LazyFolder constructor. - * + * @param IRootFolder $rootFolder * @param \Closure(): Folder $folderClosure + * @param array $data */ - public function __construct(\Closure $folderClosure, array $data = []) { + public function __construct(IRootFolder $rootFolder, \Closure $folderClosure, array $data = []) { + $this->rootFolder = $rootFolder; $this->folderClosure = $folderClosure; $this->data = $data; } + protected function getRootFolder(): IRootFolder { + return $this->rootFolder; + } + + protected function getRealFolder(): Folder { + if ($this->folder === null) { + $this->folder = call_user_func($this->folderClosure); + } + return $this->folder; + } + /** * Magic method to first get the real rootFolder and then * call $method with $args on it @@ -67,11 +61,7 @@ class LazyFolder implements Folder { * @return mixed */ public function __call($method, $args) { - if ($this->folder === null) { - $this->folder = call_user_func($this->folderClosure); - } - - return call_user_func_array([$this->folder, $method], $args); + return call_user_func_array([$this->getRealFolder(), $method], $args); } /** @@ -91,7 +81,7 @@ class LazyFolder implements Folder { /** * @inheritDoc */ - public function removeListener($scope = null, $method = null, callable $callback = null) { + public function removeListener($scope = null, $method = null, ?callable $callback = null) { $this->__call(__FUNCTION__, func_get_args()); } @@ -148,7 +138,7 @@ class LazyFolder implements Folder { * @inheritDoc */ public function get($path) { - return $this->__call(__FUNCTION__, func_get_args()); + return $this->getRootFolder()->get($this->getFullPath($path)); } /** @@ -207,6 +197,9 @@ class LazyFolder implements Folder { * @inheritDoc */ public function getId() { + if (isset($this->data['fileid'])) { + return $this->data['fileid']; + } return $this->__call(__FUNCTION__, func_get_args()); } @@ -221,6 +214,9 @@ class LazyFolder implements Folder { * @inheritDoc */ public function getMTime() { + if (isset($this->data['mtime'])) { + return $this->data['mtime']; + } return $this->__call(__FUNCTION__, func_get_args()); } @@ -228,6 +224,9 @@ class LazyFolder implements Folder { * @inheritDoc */ public function getSize($includeMounts = true): int|float { + if (isset($this->data['size'])) { + return $this->data['size']; + } return $this->__call(__FUNCTION__, func_get_args()); } @@ -235,6 +234,9 @@ class LazyFolder implements Folder { * @inheritDoc */ public function getEtag() { + if (isset($this->data['etag'])) { + return $this->data['etag']; + } return $this->__call(__FUNCTION__, func_get_args()); } @@ -299,6 +301,12 @@ class LazyFolder implements Folder { * @inheritDoc */ public function getName() { + if (isset($this->data['path'])) { + return basename($this->data['path']); + } + if (isset($this->data['name'])) { + return $this->data['name']; + } return $this->__call(__FUNCTION__, func_get_args()); } @@ -390,6 +398,13 @@ class LazyFolder implements Folder { * @inheritDoc */ public function getFullPath($path) { + if (isset($this->data['path'])) { + $path = PathHelper::normalizePath($path); + if (!Filesystem::isValidPath($path)) { + throw new NotPermittedException('Invalid path "' . $path . '"'); + } + return $this->data['path'] . $path; + } return $this->__call(__FUNCTION__, func_get_args()); } @@ -457,7 +472,11 @@ class LazyFolder implements Folder { * @inheritDoc */ public function getById($id) { - return $this->__call(__FUNCTION__, func_get_args()); + return $this->getRootFolder()->getByIdInPath((int)$id, $this->getPath()); + } + + public function getFirstNodeById(int $id): ?\OCP\Files\Node { + return $this->getRootFolder()->getFirstNodeByIdInPath($id, $this->getPath()); } /** @@ -533,4 +552,19 @@ class LazyFolder implements Folder { public function getRelativePath($path) { return PathHelper::getRelativePath($this->getPath(), $path); } + + public function getParentId(): int { + if (isset($this->data['parent'])) { + return $this->data['parent']; + } + return $this->__call(__FUNCTION__, func_get_args()); + } + + /** + * @inheritDoc + * @return array<string, int|string|bool|float|string[]|int[]> + */ + public function getMetadata(): array { + return $this->data['metadata'] ?? $this->__call(__FUNCTION__, func_get_args()); + } } diff --git a/lib/private/Files/Node/LazyRoot.php b/lib/private/Files/Node/LazyRoot.php index c01b9fdbb83..bc3f3a2e80f 100644 --- a/lib/private/Files/Node/LazyRoot.php +++ b/lib/private/Files/Node/LazyRoot.php @@ -1,28 +1,17 @@ <?php + /** - * @copyright Copyright (c) 2016, ownCloud, Inc. - * - * @author Robin Appelman <robin@icewind.nl> - * @author Roeland Jago Douma <roeland@famdouma.nl> - * - * @license AGPL-3.0 - * - * This code is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License, version 3, - * as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License, version 3, - * along with this program. If not, see <http://www.gnu.org/licenses/> - * + * SPDX-FileCopyrightText: 2016-2024 Nextcloud GmbH and Nextcloud contributors + * SPDX-FileCopyrightText: 2016 ownCloud, Inc. + * SPDX-License-Identifier: AGPL-3.0-only */ namespace OC\Files\Node; +use OCP\Files\Cache\ICacheEntry; use OCP\Files\IRootFolder; +use OCP\Files\Mount\IMountPoint; +use OCP\Files\Node; +use OCP\Files\Node as INode; /** * Class LazyRoot @@ -33,9 +22,18 @@ use OCP\Files\IRootFolder; * @package OC\Files\Node */ class LazyRoot extends LazyFolder implements IRootFolder { - /** - * @inheritDoc - */ + public function __construct(\Closure $folderClosure, array $data = []) { + parent::__construct($this, $folderClosure, $data); + } + + protected function getRootFolder(): IRootFolder { + $folder = $this->getRealFolder(); + if (!$folder instanceof IRootFolder) { + throw new \Exception('Lazy root folder closure didn\'t return a root folder'); + } + return $folder; + } + public function getUserFolder($userId) { return $this->__call(__FUNCTION__, func_get_args()); } @@ -43,4 +41,16 @@ class LazyRoot extends LazyFolder implements IRootFolder { public function getByIdInPath(int $id, string $path) { return $this->__call(__FUNCTION__, func_get_args()); } + + public function getFirstNodeByIdInPath(int $id, string $path): ?Node { + return $this->__call(__FUNCTION__, func_get_args()); + } + + public function getNodeFromCacheEntryAndMount(ICacheEntry $cacheEntry, IMountPoint $mountPoint): INode { + return $this->getRootFolder()->getNodeFromCacheEntryAndMount($cacheEntry, $mountPoint); + } + + public function getAppDataDirectoryName(): string { + return $this->__call(__FUNCTION__, func_get_args()); + } } diff --git a/lib/private/Files/Node/LazyUserFolder.php b/lib/private/Files/Node/LazyUserFolder.php index 8fbdec4b49d..f9908b9b7b6 100644 --- a/lib/private/Files/Node/LazyUserFolder.php +++ b/lib/private/Files/Node/LazyUserFolder.php @@ -2,51 +2,34 @@ declare(strict_types=1); /** - * @copyright Copyright (c) 2022 Robin Appelman <robin@icewind.nl> - * - * @license GNU AGPL version 3 or any later version - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as - * published by the Free Software Foundation, either version 3 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - * + * SPDX-FileCopyrightText: 2016-2024 Nextcloud GmbH and Nextcloud contributors + * SPDX-FileCopyrightText: 2022 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later */ - namespace OC\Files\Node; -use OCP\Files\FileInfo; use OCP\Constants; +use OCP\Files\File; +use OCP\Files\FileInfo; +use OCP\Files\Folder; use OCP\Files\IRootFolder; use OCP\Files\Mount\IMountManager; use OCP\Files\NotFoundException; -use OCP\Files\Folder; -use OCP\Files\File; use OCP\IUser; use Psr\Log\LoggerInterface; class LazyUserFolder extends LazyFolder { - private IRootFolder $root; private IUser $user; private string $path; private IMountManager $mountManager; public function __construct(IRootFolder $rootFolder, IUser $user, IMountManager $mountManager) { - $this->root = $rootFolder; $this->user = $user; $this->mountManager = $mountManager; $this->path = '/' . $user->getUID() . '/files'; - parent::__construct(function () use ($user): Folder { + parent::__construct($rootFolder, function () use ($user): Folder { try { - $node = $this->root->get($this->path); + $node = $this->getRootFolder()->get($this->path); if ($node instanceof File) { $e = new \RuntimeException(); \OCP\Server::get(LoggerInterface::class)->error('User root storage is not a folder: ' . $this->path, [ @@ -56,31 +39,20 @@ class LazyUserFolder extends LazyFolder { } return $node; } catch (NotFoundException $e) { - if (!$this->root->nodeExists('/' . $user->getUID())) { - $this->root->newFolder('/' . $user->getUID()); + if (!$this->getRootFolder()->nodeExists('/' . $user->getUID())) { + $this->getRootFolder()->newFolder('/' . $user->getUID()); } - return $this->root->newFolder($this->path); + return $this->getRootFolder()->newFolder($this->path); } }, [ 'path' => $this->path, - 'permissions' => Constants::PERMISSION_ALL, + // Sharing user root folder is not allowed + 'permissions' => Constants::PERMISSION_ALL ^ Constants::PERMISSION_SHARE, 'type' => FileInfo::TYPE_FOLDER, 'mimetype' => FileInfo::MIMETYPE_FOLDER, ]); } - public function get($path) { - return $this->root->get('/' . $this->user->getUID() . '/files/' . ltrim($path, '/')); - } - - /** - * @param int $id - * @return \OCP\Files\Node[] - */ - public function getById($id) { - return $this->root->getByIdInPath((int)$id, $this->getPath()); - } - public function getMountPoint() { if ($this->folder !== null) { return $this->folder->getMountPoint(); diff --git a/lib/private/Files/Node/Node.php b/lib/private/Files/Node/Node.php index 61ae762638f..e7f533e73a9 100644 --- a/lib/private/Files/Node/Node.php +++ b/lib/private/Files/Node/Node.php @@ -1,31 +1,9 @@ <?php + /** - * @copyright Copyright (c) 2016, ownCloud, Inc. - * - * @author Arthur Schiwon <blizzz@arthur-schiwon.de> - * @author Bernhard Posselt <dev@bernhard-posselt.com> - * @author Christoph Wurst <christoph@winzerhof-wurst.at> - * @author Joas Schilling <coding@schilljs.com> - * @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 Vincent Petry <vincent@nextcloud.com> - * - * @license AGPL-3.0 - * - * This code is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License, version 3, - * as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License, version 3, - * along with this program. If not, see <http://www.gnu.org/licenses/> - * + * SPDX-FileCopyrightText: 2016-2024 Nextcloud GmbH and Nextcloud contributors + * SPDX-FileCopyrightText: 2016 ownCloud, Inc. + * SPDX-License-Identifier: AGPL-3.0-only */ namespace OC\Files\Node; @@ -43,7 +21,7 @@ use OCP\Files\NotPermittedException; use OCP\Lock\LockedException; use OCP\PreConditionNotMetException; -// FIXME: this class really should be abstract +// FIXME: this class really should be abstract (+1) class Node implements INode { /** * @var \OC\Files\View $view @@ -59,10 +37,7 @@ class Node implements INode { protected ?FileInfo $fileInfo; - /** - * @var Node|null - */ - protected $parent; + protected ?INode $parent; private bool $infoHasSubMountsIncluded; @@ -72,7 +47,7 @@ class Node implements INode { * @param string $path * @param FileInfo $fileInfo */ - public function __construct(IRootFolder $root, $view, $path, $fileInfo = null, ?Node $parent = null, bool $infoHasSubMountsIncluded = true) { + public function __construct(IRootFolder $root, $view, $path, $fileInfo = null, ?INode $parent = null, bool $infoHasSubMountsIncluded = true) { if (Filesystem::normalizePath($view->getRoot()) !== '/') { throw new PreConditionNotMetException('The view passed to the node should not have any fake root set'); } @@ -126,7 +101,7 @@ class Node implements INode { /** * @param string[] $hooks */ - protected function sendHooks($hooks, array $args = null) { + protected function sendHooks($hooks, ?array $args = null) { $args = !empty($args) ? $args : [$this]; /** @var IEventDispatcher $dispatcher */ $dispatcher = \OC::$server->get(IEventDispatcher::class); @@ -134,7 +109,14 @@ class Node implements INode { if (method_exists($this->root, 'emit')) { $this->root->emit('\OC\Files', $hook, $args); } - $dispatcher->dispatch('\OCP\Files::' . $hook, new GenericEvent($args)); + + if (in_array($hook, ['preWrite', 'postWrite', 'preCreate', 'postCreate', 'preTouch', 'postTouch', 'preDelete', 'postDelete'], true)) { + $event = new GenericEvent($args[0]); + } else { + $event = new GenericEvent($args); + } + + $dispatcher->dispatch('\OCP\Files::' . $hook, $event); } } @@ -300,7 +282,25 @@ class Node implements INode { return $this->root; } - $this->parent = $this->root->get($newPath); + // Manually fetch the parent if the current node doesn't have a file info yet + try { + $fileInfo = $this->getFileInfo(); + } catch (NotFoundException) { + $this->parent = $this->root->get($newPath); + /** @var \OCP\Files\Folder $this->parent */ + return $this->parent; + } + + // gather the metadata we already know about our parent + $parentData = [ + 'path' => $newPath, + 'fileid' => $fileInfo->getParentId(), + ]; + + // and create lazy folder with it instead of always querying + $this->parent = new LazyFolder($this->root, function () use ($newPath) { + return $this->root->get($newPath); + }, $parentData); } return $this->parent; @@ -328,13 +328,7 @@ class Node implements INode { * @return bool */ public function isValidPath($path) { - if (!$path || $path[0] !== '/') { - $path = '/' . $path; - } - if (strstr($path, '/../') || strrchr($path, '/') === '/..') { - return false; - } - return true; + return Filesystem::isValidPath($path); } public function isMounted() { @@ -477,4 +471,16 @@ class Node implements INode { public function getUploadTime(): int { return $this->getFileInfo()->getUploadTime(); } + + public function getParentId(): int { + return $this->fileInfo->getParentId(); + } + + /** + * @inheritDoc + * @return array<string, int|string|bool|float|string[]|int[]> + */ + public function getMetadata(): array { + return $this->fileInfo->getMetadata(); + } } diff --git a/lib/private/Files/Node/NonExistingFile.php b/lib/private/Files/Node/NonExistingFile.php index f42f2e33b72..d154876432e 100644 --- a/lib/private/Files/Node/NonExistingFile.php +++ b/lib/private/Files/Node/NonExistingFile.php @@ -1,24 +1,9 @@ <?php + /** - * @copyright Copyright (c) 2016, ownCloud, Inc. - * - * @author Morris Jobke <hey@morrisjobke.de> - * @author Robin Appelman <robin@icewind.nl> - * - * @license AGPL-3.0 - * - * This code is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License, version 3, - * as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License, version 3, - * along with this program. If not, see <http://www.gnu.org/licenses/> - * + * SPDX-FileCopyrightText: 2016-2024 Nextcloud GmbH and Nextcloud contributors + * SPDX-FileCopyrightText: 2016 ownCloud, Inc. + * SPDX-License-Identifier: AGPL-3.0-only */ namespace OC\Files\Node; diff --git a/lib/private/Files/Node/NonExistingFolder.php b/lib/private/Files/Node/NonExistingFolder.php index 34621b18f19..5650c99fe73 100644 --- a/lib/private/Files/Node/NonExistingFolder.php +++ b/lib/private/Files/Node/NonExistingFolder.php @@ -1,25 +1,9 @@ <?php + /** - * @copyright Copyright (c) 2016, ownCloud, Inc. - * - * @author Morris Jobke <hey@morrisjobke.de> - * @author Robin Appelman <robin@icewind.nl> - * @author Vincent Petry <vincent@nextcloud.com> - * - * @license AGPL-3.0 - * - * This code is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License, version 3, - * as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License, version 3, - * along with this program. If not, see <http://www.gnu.org/licenses/> - * + * SPDX-FileCopyrightText: 2016-2024 Nextcloud GmbH and Nextcloud contributors + * SPDX-FileCopyrightText: 2016 ownCloud, Inc. + * SPDX-License-Identifier: AGPL-3.0-only */ namespace OC\Files\Node; @@ -162,6 +146,10 @@ class NonExistingFolder extends Folder { throw new NotFoundException(); } + public function getFirstNodeById(int $id): ?\OCP\Files\Node { + throw new NotFoundException(); + } + public function getFreeSpace() { throw new NotFoundException(); } diff --git a/lib/private/Files/Node/Root.php b/lib/private/Files/Node/Root.php index 7bd88981ff2..5fb4013cfc9 100644 --- a/lib/private/Files/Node/Root.php +++ b/lib/private/Files/Node/Root.php @@ -1,38 +1,12 @@ <?php + /** - * @copyright Copyright (c) 2016, ownCloud, Inc. - * - * @author Bernhard Posselt <dev@bernhard-posselt.com> - * @author Christoph Wurst <christoph@winzerhof-wurst.at> - * @author Joas Schilling <coding@schilljs.com> - * @author Jörn Friedrich Dreyer <jfd@butonic.de> - * @author Julius Härtl <jus@bitgrid.net> - * @author Lukas Reschke <lukas@statuscode.ch> - * @author Morris Jobke <hey@morrisjobke.de> - * @author Robin Appelman <robin@icewind.nl> - * @author Roeland Jago Douma <roeland@famdouma.nl> - * @author Stefan Weil <sw@weilnetz.de> - * @author Vincent Petry <vincent@nextcloud.com> - * - * @license AGPL-3.0 - * - * This code is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License, version 3, - * as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License, version 3, - * along with this program. If not, see <http://www.gnu.org/licenses/> - * + * SPDX-FileCopyrightText: 2016-2024 Nextcloud GmbH and Nextcloud contributors + * SPDX-FileCopyrightText: 2016 ownCloud, Inc. + * SPDX-License-Identifier: AGPL-3.0-only */ - namespace OC\Files\Node; -use OCP\Cache\CappedMemoryCache; use OC\Files\FileInfo; use OC\Files\Mount\Manager; use OC\Files\Mount\MountPoint; @@ -40,7 +14,9 @@ use OC\Files\Utils\PathHelper; use OC\Files\View; use OC\Hooks\PublicEmitter; use OC\User\NoUserException; +use OCP\Cache\CappedMemoryCache; use OCP\EventDispatcher\IEventDispatcher; +use OCP\Files\Cache\ICacheEntry; use OCP\Files\Config\IUserMountCache; use OCP\Files\Events\Node\FilesystemTornDownEvent; use OCP\Files\IRootFolder; @@ -48,6 +24,8 @@ use OCP\Files\Mount\IMountPoint; use OCP\Files\Node as INode; use OCP\Files\NotFoundException; use OCP\Files\NotPermittedException; +use OCP\ICache; +use OCP\ICacheFactory; use OCP\IUser; use OCP\IUserManager; use Psr\Log\LoggerInterface; @@ -80,6 +58,7 @@ class Root extends Folder implements IRootFolder { private LoggerInterface $logger; private IUserManager $userManager; private IEventDispatcher $eventDispatcher; + private ICache $pathByIdCache; /** * @param Manager $manager @@ -93,7 +72,8 @@ class Root extends Folder implements IRootFolder { IUserMountCache $userMountCache, LoggerInterface $logger, IUserManager $userManager, - IEventDispatcher $eventDispatcher + IEventDispatcher $eventDispatcher, + ICacheFactory $cacheFactory, ) { parent::__construct($this, $view, ''); $this->mountManager = $manager; @@ -106,6 +86,7 @@ class Root extends Folder implements IRootFolder { $eventDispatcher->addListener(FilesystemTornDownEvent::class, function () { $this->userFolderCache = new CappedMemoryCache(); }); + $this->pathByIdCache = $cacheFactory->createLocal('path-by-id'); } /** @@ -131,7 +112,7 @@ class Root extends Folder implements IRootFolder { * @param string $method optional * @param callable $callback optional */ - public function removeListener($scope = null, $method = null, callable $callback = null) { + public function removeListener($scope = null, $method = null, ?callable $callback = null) { $this->emitter->removeListener($scope, $method, $callback); } @@ -382,7 +363,7 @@ class Root extends Folder implements IRootFolder { try { $folder = $this->get('/' . $userId . '/files'); if (!$folder instanceof \OCP\Files\Folder) { - throw new \Exception("User folder for $userId exists as a file"); + throw new \Exception("Account folder for \"$userId\" exists as a file"); } } catch (NotFoundException $e) { if (!$this->nodeExists('/' . $userId)) { @@ -404,6 +385,31 @@ class Root extends Folder implements IRootFolder { return $this->userMountCache; } + public function getFirstNodeByIdInPath(int $id, string $path): ?INode { + // scope the cache by user, so we don't return nodes for different users + if ($this->user) { + $cachedPath = $this->pathByIdCache->get($this->user->getUID() . '::' . $id); + if ($cachedPath && str_starts_with($path, $cachedPath)) { + // getting the node by path is significantly cheaper than finding it by id + $node = $this->get($cachedPath); + // by validating that the cached path still has the requested fileid we can work around the need to invalidate the cached path + // if the cached path is invalid or a different file now we fall back to the uncached logic + if ($node && $node->getId() === $id) { + return $node; + } + } + } + $node = current($this->getByIdInPath($id, $path)); + if (!$node) { + return null; + } + + if ($this->user) { + $this->pathByIdCache->set($this->user->getUID() . '::' . $id, $node->getPath()); + } + return $node; + } + /** * @param int $id * @return Node[] @@ -487,4 +493,29 @@ class Root extends Folder implements IRootFolder { }); return $folders; } + + public function getNodeFromCacheEntryAndMount(ICacheEntry $cacheEntry, IMountPoint $mountPoint): INode { + $path = $cacheEntry->getPath(); + $fullPath = $mountPoint->getMountPoint() . $path; + // todo: LazyNode? + $info = new FileInfo($fullPath, $mountPoint->getStorage(), $path, $cacheEntry, $mountPoint); + $parentPath = dirname($fullPath); + $parent = new LazyFolder($this, function () use ($parentPath) { + $parent = $this->get($parentPath); + if ($parent instanceof \OCP\Files\Folder) { + return $parent; + } else { + throw new \Exception("parent $parentPath is not a folder"); + } + }, [ + 'path' => $parentPath, + ]); + $isDir = $info->getType() === FileInfo::TYPE_FOLDER; + $view = new View(''); + if ($isDir) { + return new Folder($this, $view, $path, $info, $parent); + } else { + return new File($this, $view, $path, $info, $parent); + } + } } diff --git a/lib/private/Files/Notify/Change.php b/lib/private/Files/Notify/Change.php index decb0db96b1..d2448e5deec 100644 --- a/lib/private/Files/Notify/Change.php +++ b/lib/private/Files/Notify/Change.php @@ -1,24 +1,7 @@ <?php /** - * @copyright Copyright (c) 2017 Robin Appelman <robin@icewind.nl> - * - * @author Robin Appelman <robin@icewind.nl> - * - * @license GNU AGPL version 3 or any later version - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as - * published by the Free Software Foundation, either version 3 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - * + * SPDX-FileCopyrightText: 2017 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later */ namespace OC\Files\Notify; diff --git a/lib/private/Files/Notify/RenameChange.php b/lib/private/Files/Notify/RenameChange.php index e581278c504..98fd7099a58 100644 --- a/lib/private/Files/Notify/RenameChange.php +++ b/lib/private/Files/Notify/RenameChange.php @@ -1,24 +1,7 @@ <?php /** - * @copyright Copyright (c) 2017 Robin Appelman <robin@icewind.nl> - * - * @author Robin Appelman <robin@icewind.nl> - * - * @license GNU AGPL version 3 or any later version - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as - * published by the Free Software Foundation, either version 3 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - * + * SPDX-FileCopyrightText: 2017 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later */ namespace OC\Files\Notify; diff --git a/lib/private/Files/ObjectStore/AppdataPreviewObjectStoreStorage.php b/lib/private/Files/ObjectStore/AppdataPreviewObjectStoreStorage.php index 2f6db935236..66fa74172d3 100644 --- a/lib/private/Files/ObjectStore/AppdataPreviewObjectStoreStorage.php +++ b/lib/private/Files/ObjectStore/AppdataPreviewObjectStoreStorage.php @@ -3,32 +3,18 @@ declare(strict_types=1); /** - * @copyright Copyright (c) 2020, Morris Jobke <hey@morrisjobke.de> - * - * @author Morris Jobke <hey@morrisjobke.de> - * - * @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/>. - * + * SPDX-FileCopyrightText: 2020 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later */ namespace OC\Files\ObjectStore; class AppdataPreviewObjectStoreStorage extends ObjectStoreStorage { - /** @var string */ - private $internalId; + private string $internalId; + /** + * @param array $params + * @throws \Exception + */ public function __construct($params) { if (!isset($params['internal-id'])) { throw new \Exception('missing id in parameters'); @@ -37,7 +23,7 @@ class AppdataPreviewObjectStoreStorage extends ObjectStoreStorage { parent::__construct($params); } - public function getId() { + public function getId(): string { return 'object::appdata::preview:' . $this->internalId; } } diff --git a/lib/private/Files/ObjectStore/Azure.php b/lib/private/Files/ObjectStore/Azure.php index 553f593b299..55400d4131c 100644 --- a/lib/private/Files/ObjectStore/Azure.php +++ b/lib/private/Files/ObjectStore/Azure.php @@ -1,24 +1,7 @@ <?php /** - * @copyright Copyright (c) 2018 Robin Appelman <robin@icewind.nl> - * - * @author Robin Appelman <robin@icewind.nl> - * - * @license GNU AGPL version 3 or any later version - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as - * published by the Free Software Foundation, either version 3 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - * + * SPDX-FileCopyrightText: 2018 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later */ namespace OC\Files\ObjectStore; @@ -100,7 +83,7 @@ class Azure implements IObjectStore { return $blob->getContentStream(); } - public function writeObject($urn, $stream, string $mimetype = null) { + public function writeObject($urn, $stream, ?string $mimetype = null) { $options = new CreateBlockBlobOptions(); if ($mimetype) { $options->setContentType($mimetype); diff --git a/lib/private/Files/ObjectStore/HomeObjectStoreStorage.php b/lib/private/Files/ObjectStore/HomeObjectStoreStorage.php index 824adcc1d0e..b543d223f4c 100644 --- a/lib/private/Files/ObjectStore/HomeObjectStoreStorage.php +++ b/lib/private/Files/ObjectStore/HomeObjectStoreStorage.php @@ -1,46 +1,34 @@ <?php + /** - * @copyright Copyright (c) 2016, ownCloud, Inc. - * - * @author Björn Schießle <bjoern@schiessle.org> - * @author Christoph Wurst <christoph@winzerhof-wurst.at> - * @author Jörn Friedrich Dreyer <jfd@butonic.de> - * @author Morris Jobke <hey@morrisjobke.de> - * @author Roeland Jago Douma <roeland@famdouma.nl> - * - * @license AGPL-3.0 - * - * This code is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License, version 3, - * as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License, version 3, - * along with this program. If not, see <http://www.gnu.org/licenses/> - * + * SPDX-FileCopyrightText: 2016-2024 Nextcloud GmbH and Nextcloud contributors + * SPDX-FileCopyrightText: 2016 ownCloud, Inc. + * SPDX-License-Identifier: AGPL-3.0-only */ namespace OC\Files\ObjectStore; -use OC\User\User; +use Exception; +use OCP\Files\IHomeStorage; +use OCP\IUser; + +class HomeObjectStoreStorage extends ObjectStoreStorage implements IHomeStorage { + protected IUser $user; -class HomeObjectStoreStorage extends ObjectStoreStorage implements \OCP\Files\IHomeStorage { /** * The home user storage requires a user object to create a unique storage id + * * @param array $params + * @throws Exception */ public function __construct($params) { - if (! isset($params['user']) || ! $params['user'] instanceof User) { - throw new \Exception('missing user object in parameters'); + if (! isset($params['user']) || ! $params['user'] instanceof IUser) { + throw new Exception('missing user object in parameters'); } $this->user = $params['user']; parent::__construct($params); } - public function getId() { + public function getId(): string { return 'object::user:' . $this->user->getUID(); } @@ -48,20 +36,13 @@ class HomeObjectStoreStorage extends ObjectStoreStorage implements \OCP\Files\IH * get the owner of a path * * @param string $path The path to get the owner - * @return false|string uid + * @return string uid */ - public function getOwner($path) { - if (is_object($this->user)) { - return $this->user->getUID(); - } - return false; + public function getOwner($path): string { + return $this->user->getUID(); } - /** - * @param string $path, optional - * @return \OC\User\User - */ - public function getUser($path = null) { + public function getUser(): IUser { return $this->user; } } diff --git a/lib/private/Files/ObjectStore/Mapper.php b/lib/private/Files/ObjectStore/Mapper.php index ef3c6878d81..e1174a285a6 100644 --- a/lib/private/Files/ObjectStore/Mapper.php +++ b/lib/private/Files/ObjectStore/Mapper.php @@ -1,24 +1,9 @@ <?php + /** - * @copyright Copyright (c) 2016, ownCloud, Inc. - * - * @author Robin Appelman <robin@icewind.nl> - * @author Roeland Jago Douma <roeland@famdouma.nl> - * - * @license AGPL-3.0 - * - * This code is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License, version 3, - * as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License, version 3, - * along with this program. If not, see <http://www.gnu.org/licenses/> - * + * SPDX-FileCopyrightText: 2016-2024 Nextcloud GmbH and Nextcloud contributors + * SPDX-FileCopyrightText: 2016 ownCloud, Inc. + * SPDX-License-Identifier: AGPL-3.0-only */ namespace OC\Files\ObjectStore; diff --git a/lib/private/Files/ObjectStore/ObjectStoreScanner.php b/lib/private/Files/ObjectStore/ObjectStoreScanner.php index f001f90fdaa..d8a77d36dee 100644 --- a/lib/private/Files/ObjectStore/ObjectStoreScanner.php +++ b/lib/private/Files/ObjectStore/ObjectStoreScanner.php @@ -1,28 +1,9 @@ <?php + /** - * @copyright Copyright (c) 2016, ownCloud, Inc. - * - * @author Christoph Wurst <christoph@winzerhof-wurst.at> - * @author Joas Schilling <coding@schilljs.com> - * @author Jörn Friedrich Dreyer <jfd@butonic.de> - * @author Morris Jobke <hey@morrisjobke.de> - * @author Robin Appelman <robin@icewind.nl> - * @author Roeland Jago Douma <roeland@famdouma.nl> - * - * @license AGPL-3.0 - * - * This code is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License, version 3, - * as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License, version 3, - * along with this program. If not, see <http://www.gnu.org/licenses/> - * + * SPDX-FileCopyrightText: 2023 Nextcloud GmbH and Nextcloud contributors + * SPDX-FileCopyrightText: 2016 ownCloud, Inc. + * SPDX-License-Identifier: AGPL-3.0-only */ namespace OC\Files\ObjectStore; @@ -39,7 +20,7 @@ class ObjectStoreScanner extends Scanner { return []; } - protected function scanChildren(string $path, $recursive, int $reuse, int $folderId, bool $lock, int $oldSize) { + protected function scanChildren(string $path, $recursive, int $reuse, int $folderId, bool $lock, int|float $oldSize, &$etagChanged = false) { return 0; } diff --git a/lib/private/Files/ObjectStore/ObjectStoreStorage.php b/lib/private/Files/ObjectStore/ObjectStoreStorage.php index d918bd98729..389f744eab4 100644 --- a/lib/private/Files/ObjectStore/ObjectStoreStorage.php +++ b/lib/private/Files/ObjectStore/ObjectStoreStorage.php @@ -1,33 +1,10 @@ <?php + /** - * @copyright Copyright (c) 2016, ownCloud, Inc. - * - * @author Bjoern Schiessle <bjoern@schiessle.org> - * @author Christoph Wurst <christoph@winzerhof-wurst.at> - * @author Joas Schilling <coding@schilljs.com> - * @author Jörn Friedrich Dreyer <jfd@butonic.de> - * @author Marcel Klehr <mklehr@gmx.net> - * @author Morris Jobke <hey@morrisjobke.de> - * @author Robin Appelman <robin@icewind.nl> - * @author Roeland Jago Douma <roeland@famdouma.nl> - * @author Tigran Mkrtchyan <tigran.mkrtchyan@desy.de> - * - * @license AGPL-3.0 - * - * This code is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License, version 3, - * as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License, version 3, - * along with this program. If not, see <http://www.gnu.org/licenses/> - * + * SPDX-FileCopyrightText: 2016-2024 Nextcloud GmbH and Nextcloud contributors + * SPDX-FileCopyrightText: 2016 ownCloud, Inc. + * SPDX-License-Identifier: AGPL-3.0-only */ - namespace OC\Files\ObjectStore; use Aws\S3\Exception\S3Exception; @@ -47,30 +24,24 @@ use OCP\Files\ObjectStore\IObjectStore; use OCP\Files\ObjectStore\IObjectStoreMultiPartUpload; use OCP\Files\Storage\IChunkedFileWrite; use OCP\Files\Storage\IStorage; +use Psr\Log\LoggerInterface; class ObjectStoreStorage extends \OC\Files\Storage\Common implements IChunkedFileWrite { use CopyDirectory; - /** - * @var \OCP\Files\ObjectStore\IObjectStore $objectStore - */ - protected $objectStore; - /** - * @var string $id - */ - protected $id; - /** - * @var \OC\User\User $user - */ - protected $user; - - private $objectPrefix = 'urn:oid:'; + protected IObjectStore $objectStore; + protected string $id; + private string $objectPrefix = 'urn:oid:'; - private $logger; + private LoggerInterface $logger; - /** @var bool */ - protected $validateWrites = true; + private bool $handleCopiesAsOwned; + protected bool $validateWrites = true; + /** + * @param array $params + * @throws \Exception + */ public function __construct($params) { if (isset($params['objectstore']) && $params['objectstore'] instanceof IObjectStore) { $this->objectStore = $params['objectstore']; @@ -88,8 +59,9 @@ class ObjectStoreStorage extends \OC\Files\Storage\Common implements IChunkedFil if (isset($params['validateWrites'])) { $this->validateWrites = (bool)$params['validateWrites']; } + $this->handleCopiesAsOwned = (bool)($params['handleCopiesAsOwned'] ?? false); - $this->logger = \OC::$server->getLogger(); + $this->logger = \OCP\Server::get(LoggerInterface::class); } public function mkdir($path, bool $force = false) { @@ -225,10 +197,13 @@ class ObjectStoreStorage extends \OC\Files\Storage\Common implements IChunkedFil $this->objectStore->deleteObject($this->getURN($entry->getId())); } catch (\Exception $ex) { if ($ex->getCode() !== 404) { - $this->logger->logException($ex, [ - 'app' => 'objectstore', - 'message' => 'Could not delete object ' . $this->getURN($entry->getId()) . ' for ' . $entry->getPath(), - ]); + $this->logger->error( + 'Could not delete object ' . $this->getURN($entry->getId()) . ' for ' . $entry->getPath(), + [ + 'app' => 'objectstore', + 'exception' => $ex, + ] + ); return false; } //removing from cache is ok as it does not exist in the objectstore anyway @@ -291,7 +266,7 @@ class ObjectStoreStorage extends \OC\Files\Storage\Common implements IChunkedFil return IteratorDirectory::wrap($files); } catch (\Exception $e) { - $this->logger->logException($e); + $this->logger->error($e->getMessage(), ['exception' => $e]); return false; } } @@ -341,16 +316,22 @@ class ObjectStoreStorage extends \OC\Files\Storage\Common implements IChunkedFil } return $handle; } catch (NotFoundException $e) { - $this->logger->logException($e, [ - 'app' => 'objectstore', - 'message' => 'Could not get object ' . $this->getURN($stat['fileid']) . ' for file ' . $path, - ]); + $this->logger->error( + 'Could not get object ' . $this->getURN($stat['fileid']) . ' for file ' . $path, + [ + 'app' => 'objectstore', + 'exception' => $e, + ] + ); throw $e; - } catch (\Exception $ex) { - $this->logger->logException($ex, [ - 'app' => 'objectstore', - 'message' => 'Could not get object ' . $this->getURN($stat['fileid']) . ' for file ' . $path, - ]); + } catch (\Exception $e) { + $this->logger->error( + 'Could not get object ' . $this->getURN($stat['fileid']) . ' for file ' . $path, + [ + 'app' => 'objectstore', + 'exception' => $e, + ] + ); return false; } } else { @@ -447,10 +428,13 @@ class ObjectStoreStorage extends \OC\Files\Storage\Common implements IChunkedFil ]; $this->getCache()->put($path, $stat); } catch (\Exception $ex) { - $this->logger->logException($ex, [ - 'app' => 'objectstore', - 'message' => 'Could not create object for ' . $path, - ]); + $this->logger->error( + 'Could not create object for ' . $path, + [ + 'app' => 'objectstore', + 'exception' => $ex, + ] + ); throw $ex; } } @@ -487,7 +471,7 @@ class ObjectStoreStorage extends \OC\Files\Storage\Common implements IChunkedFil return $result; } - public function writeStream(string $path, $stream, int $size = null): int { + public function writeStream(string $path, $stream, ?int $size = null): int { $stat = $this->stat($path); if (empty($stat)) { // create new file @@ -545,20 +529,28 @@ class ObjectStoreStorage extends \OC\Files\Storage\Common implements IChunkedFil * Else people lose access to existing files */ $this->getCache()->remove($uploadPath); - $this->logger->logException($ex, [ - 'app' => 'objectstore', - 'message' => 'Could not create object ' . $urn . ' for ' . $path, - ]); + $this->logger->error( + 'Could not create object ' . $urn . ' for ' . $path, + [ + 'app' => 'objectstore', + 'exception' => $ex, + ] + ); } else { - $this->logger->logException($ex, [ - 'app' => 'objectstore', - 'message' => 'Could not update object ' . $urn . ' for ' . $path, - ]); + $this->logger->error( + 'Could not update object ' . $urn . ' for ' . $path, + [ + 'app' => 'objectstore', + 'exception' => $ex, + ] + ); } throw $ex; // make this bubble up } if ($exists) { + // Always update the unencrypted size, for encryption the Encryption wrapper will update this afterwards anyways + $stat['unencrypted_size'] = $stat['size']; $this->getCache()->update($fileId, $stat); } else { if (!$this->validateWrites || $this->objectStore->objectExists($urn)) { @@ -649,6 +641,10 @@ class ObjectStoreStorage extends \OC\Files\Storage\Common implements IChunkedFil try { $this->objectStore->copyObject($sourceUrn, $targetUrn); + if ($this->handleCopiesAsOwned) { + // Copied the file thus we gain all permissions as we are the owner now ! warning while this aligns with local storage it should not be used and instead fix local storage ! + $cache->update($targetId, ['permissions' => \OCP\Constants::PERMISSION_ALL]); + } } catch (\Exception $e) { $cache->remove($to); @@ -712,10 +708,13 @@ class ObjectStoreStorage extends \OC\Files\Storage\Common implements IChunkedFil } } catch (S3MultipartUploadException|S3Exception $e) { $this->objectStore->abortMultipartUpload($urn, $writeToken); - $this->logger->logException($e, [ - 'app' => 'objectstore', - 'message' => 'Could not compete multipart upload ' . $urn . ' with uploadId ' . $writeToken, - ]); + $this->logger->error( + 'Could not compete multipart upload ' . $urn . ' with uploadId ' . $writeToken, + [ + 'app' => 'objectstore', + 'exception' => $e, + ] + ); throw new GenericFileException('Could not write chunked file'); } return $size; diff --git a/lib/private/Files/ObjectStore/S3.php b/lib/private/Files/ObjectStore/S3.php index b1cd89388ae..72c19d951e4 100644 --- a/lib/private/Files/ObjectStore/S3.php +++ b/lib/private/Files/ObjectStore/S3.php @@ -1,25 +1,7 @@ <?php /** - * @copyright Copyright (c) 2016 Robin Appelman <robin@icewind.nl> - * - * @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/>. - * + * SPDX-FileCopyrightText: 2016 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later */ namespace OC\Files\ObjectStore; diff --git a/lib/private/Files/ObjectStore/S3ConfigTrait.php b/lib/private/Files/ObjectStore/S3ConfigTrait.php new file mode 100644 index 00000000000..3a399e6413d --- /dev/null +++ b/lib/private/Files/ObjectStore/S3ConfigTrait.php @@ -0,0 +1,37 @@ +<?php + +declare(strict_types=1); +/** + * SPDX-FileCopyrightText: 2024 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later + */ +namespace OC\Files\ObjectStore; + +/** + * Shared configuration between ConnectionTrait and ObjectTrait to ensure both to be in sync + */ +trait S3ConfigTrait { + protected array $params; + + protected string $bucket; + + /** Maximum number of concurrent multipart uploads */ + protected int $concurrency; + + protected int $timeout; + + protected string $proxy; + + protected string $storageClass; + + /** @var int Part size in bytes (float is added for 32bit support) */ + protected int|float $uploadPartSize; + + /** @var int Limit on PUT in bytes (float is added for 32bit support) */ + private int|float $putSizeLimit; + + /** @var int Limit on COPY in bytes (float is added for 32bit support) */ + private int|float $copySizeLimit; + + private bool $useMultipartCopy = true; +} diff --git a/lib/private/Files/ObjectStore/S3ConnectionTrait.php b/lib/private/Files/ObjectStore/S3ConnectionTrait.php index deb03571c76..02f5e5ded04 100644 --- a/lib/private/Files/ObjectStore/S3ConnectionTrait.php +++ b/lib/private/Files/ObjectStore/S3ConnectionTrait.php @@ -1,35 +1,8 @@ <?php /** - * @copyright Copyright (c) 2016 Robin Appelman <robin@icewind.nl> - * - * @author Arthur Schiwon <blizzz@arthur-schiwon.de> - * @author Christoph Wurst <christoph@winzerhof-wurst.at> - * @author Florent <florent@coppint.com> - * @author James Letendre <James.Letendre@gmail.com> - * @author Morris Jobke <hey@morrisjobke.de> - * @author Robin Appelman <robin@icewind.nl> - * @author Roeland Jago Douma <roeland@famdouma.nl> - * @author S. Cat <33800996+sparrowjack63@users.noreply.github.com> - * @author Stephen Cuppett <steve@cuppett.com> - * @author Jasper Weyne <jasperweyne@gmail.com> - * - * @license GNU AGPL version 3 or any later version - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as - * published by the Free Software Foundation, either version 3 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - * + * SPDX-FileCopyrightText: 2016 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later */ - namespace OC\Files\ObjectStore; use Aws\ClientResolver; @@ -44,34 +17,13 @@ use OCP\ICertificateManager; use Psr\Log\LoggerInterface; trait S3ConnectionTrait { - /** @var array */ - protected $params; - - /** @var S3Client */ - protected $connection; - - /** @var string */ - protected $id; - - /** @var string */ - protected $bucket; + use S3ConfigTrait; - /** @var int */ - protected $timeout; + protected string $id; - /** @var string */ - protected $proxy; + protected bool $test; - /** @var string */ - protected $storageClass; - - /** @var int */ - protected $uploadPartSize; - - /** @var int */ - private $putSizeLimit; - - protected $test; + protected ?S3Client $connection = null; protected function parseParams($params) { if (empty($params['bucket'])) { @@ -82,17 +34,27 @@ trait S3ConnectionTrait { $this->test = isset($params['test']); $this->bucket = $params['bucket']; + // Default to 5 like the S3 SDK does + $this->concurrency = $params['concurrency'] ?? 5; $this->proxy = $params['proxy'] ?? false; $this->timeout = $params['timeout'] ?? 15; $this->storageClass = !empty($params['storageClass']) ? $params['storageClass'] : 'STANDARD'; $this->uploadPartSize = $params['uploadPartSize'] ?? 524288000; $this->putSizeLimit = $params['putSizeLimit'] ?? 104857600; + $this->copySizeLimit = $params['copySizeLimit'] ?? 5242880000; + $this->useMultipartCopy = (bool)($params['useMultipartCopy'] ?? true); $params['region'] = empty($params['region']) ? 'eu-west-1' : $params['region']; + $params['s3-accelerate'] = $params['hostname'] == 's3-accelerate.amazonaws.com' || $params['hostname'] == 's3-accelerate.dualstack.amazonaws.com'; $params['hostname'] = empty($params['hostname']) ? 's3.' . $params['region'] . '.amazonaws.com' : $params['hostname']; if (!isset($params['port']) || $params['port'] === '') { $params['port'] = (isset($params['use_ssl']) && $params['use_ssl'] === false) ? 80 : 443; } - $params['verify_bucket_exists'] = empty($params['verify_bucket_exists']) ? true : $params['verify_bucket_exists']; + $params['verify_bucket_exists'] = $params['verify_bucket_exists'] ?? true; + + if ($params['s3-accelerate']) { + $params['verify_bucket_exists'] = false; + } + $this->params = $params; } @@ -111,7 +73,7 @@ trait S3ConnectionTrait { * @throws \Exception if connection could not be made */ public function getConnection() { - if (!is_null($this->connection)) { + if ($this->connection !== null) { return $this->connection; } @@ -128,7 +90,7 @@ trait S3ConnectionTrait { ); $options = [ - 'version' => isset($this->params['version']) ? $this->params['version'] : 'latest', + 'version' => $this->params['version'] ?? 'latest', 'credentials' => $provider, 'endpoint' => $base_url, 'region' => $this->params['region'], @@ -139,6 +101,13 @@ trait S3ConnectionTrait { 'http' => ['verify' => $this->getCertificateBundlePath()], 'use_aws_shared_config_files' => false, ]; + + if ($this->params['s3-accelerate']) { + $options['use_accelerate_endpoint'] = true; + } else { + $options['endpoint'] = $base_url; + } + if ($this->getProxy()) { $options['http']['proxy'] = $this->getProxy(); } @@ -167,7 +136,9 @@ trait S3ConnectionTrait { 'exception' => $e, 'app' => 'objectstore', ]); - throw new \Exception('Creation of bucket "' . $this->bucket . '" failed. ' . $e->getMessage()); + if ($e->getAwsErrorCode() !== "BucketAlreadyOwnedByYou") { + throw new \Exception('Creation of bucket "' . $this->bucket . '" failed. ' . $e->getMessage()); + } } } diff --git a/lib/private/Files/ObjectStore/S3ObjectTrait.php b/lib/private/Files/ObjectStore/S3ObjectTrait.php index e0d0f2ce9c7..5d00c184ca7 100644 --- a/lib/private/Files/ObjectStore/S3ObjectTrait.php +++ b/lib/private/Files/ObjectStore/S3ObjectTrait.php @@ -1,32 +1,13 @@ <?php + /** - * @copyright Copyright (c) 2017 Robin Appelman <robin@icewind.nl> - * - * @author Christoph Wurst <christoph@winzerhof-wurst.at> - * @author Florent <florent@coppint.com> - * @author Morris Jobke <hey@morrisjobke.de> - * @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/>. - * + * SPDX-FileCopyrightText: 2017 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later */ namespace OC\Files\ObjectStore; use Aws\S3\Exception\S3MultipartUploadException; +use Aws\S3\MultipartCopy; use Aws\S3\MultipartUploader; use Aws\S3\S3Client; use GuzzleHttp\Psr7; @@ -35,6 +16,8 @@ use OC\Files\Stream\SeekableHttpStream; use Psr\Http\Message\StreamInterface; trait S3ObjectTrait { + use S3ConfigTrait; + /** * Returns the connection * @@ -103,7 +86,7 @@ trait S3ObjectTrait { * @param string|null $mimetype the mimetype to set for the remove object @since 22.0.0 * @throws \Exception when something goes wrong, message will be logged */ - protected function writeSingle(string $urn, StreamInterface $stream, string $mimetype = null): void { + protected function writeSingle(string $urn, StreamInterface $stream, ?string $mimetype = null): void { $this->getConnection()->putObject([ 'Bucket' => $this->bucket, 'Key' => $urn, @@ -123,9 +106,10 @@ trait S3ObjectTrait { * @param string|null $mimetype the mimetype to set for the remove object * @throws \Exception when something goes wrong, message will be logged */ - protected function writeMultiPart(string $urn, StreamInterface $stream, string $mimetype = null): void { + protected function writeMultiPart(string $urn, StreamInterface $stream, ?string $mimetype = null): void { $uploader = new MultipartUploader($this->getConnection(), $stream, [ 'bucket' => $this->bucket, + 'concurrency' => $this->concurrency, 'key' => $urn, 'part_size' => $this->uploadPartSize, 'params' => [ @@ -155,7 +139,7 @@ trait S3ObjectTrait { * @throws \Exception when something goes wrong, message will be logged * @since 7.0.0 */ - public function writeObject($urn, $stream, string $mimetype = null) { + public function writeObject($urn, $stream, ?string $mimetype = null) { $psrStream = Utils::streamFor($stream); // ($psrStream->isSeekable() && $psrStream->getSize() !== null) evaluates to true for a On-Seekable stream @@ -189,9 +173,31 @@ trait S3ObjectTrait { return $this->getConnection()->doesObjectExist($this->bucket, $urn, $this->getSSECParameters()); } - public function copyObject($from, $to) { - $this->getConnection()->copy($this->getBucket(), $from, $this->getBucket(), $to, 'private', [ - 'params' => $this->getSSECParameters() + $this->getSSECParameters(true) - ]); + public function copyObject($from, $to, array $options = []) { + $sourceMetadata = $this->getConnection()->headObject([ + 'Bucket' => $this->getBucket(), + 'Key' => $from, + ] + $this->getSSECParameters()); + + $size = (int)($sourceMetadata->get('Size') ?? $sourceMetadata->get('ContentLength')); + + if ($this->useMultipartCopy && $size > $this->copySizeLimit) { + $copy = new MultipartCopy($this->getConnection(), [ + "source_bucket" => $this->getBucket(), + "source_key" => $from + ], array_merge([ + "bucket" => $this->getBucket(), + "key" => $to, + "acl" => "private", + "params" => $this->getSSECParameters() + $this->getSSECParameters(true), + "source_metadata" => $sourceMetadata + ], $options)); + $copy->copy(); + } else { + $this->getConnection()->copy($this->getBucket(), $from, $this->getBucket(), $to, 'private', array_merge([ + 'params' => $this->getSSECParameters() + $this->getSSECParameters(true), + 'mup_threshold' => PHP_INT_MAX, + ], $options)); + } } } diff --git a/lib/private/Files/ObjectStore/S3Signature.php b/lib/private/Files/ObjectStore/S3Signature.php index cf3d29c4185..fd24a34b090 100644 --- a/lib/private/Files/ObjectStore/S3Signature.php +++ b/lib/private/Files/ObjectStore/S3Signature.php @@ -1,26 +1,7 @@ <?php /** - * @copyright Copyright (c) 2016 Robin Appelman <robin@icewind.nl> - * - * @author Christoph Wurst <christoph@winzerhof-wurst.at> - * @author Daniel Kesselberg <mail@danielkesselberg.de> - * @author Robin Appelman <robin@icewind.nl> - * - * @license GNU AGPL version 3 or any later version - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as - * published by the Free Software Foundation, either version 3 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - * + * SPDX-FileCopyrightText: 2016 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later */ namespace OC\Files\ObjectStore; diff --git a/lib/private/Files/ObjectStore/StorageObjectStore.php b/lib/private/Files/ObjectStore/StorageObjectStore.php index 85926be897e..5e7125e18a6 100644 --- a/lib/private/Files/ObjectStore/StorageObjectStore.php +++ b/lib/private/Files/ObjectStore/StorageObjectStore.php @@ -1,25 +1,7 @@ <?php /** - * @copyright Copyright (c) 2016 Robin Appelman <robin@icewind.nl> - * - * @author Christoph Wurst <christoph@winzerhof-wurst.at> - * @author Robin Appelman <robin@icewind.nl> - * - * @license GNU AGPL version 3 or any later version - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as - * published by the Free Software Foundation, either version 3 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - * + * SPDX-FileCopyrightText: 2016 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later */ namespace OC\Files\ObjectStore; @@ -64,7 +46,7 @@ class StorageObjectStore implements IObjectStore { throw new \Exception(); } - public function writeObject($urn, $stream, string $mimetype = null) { + public function writeObject($urn, $stream, ?string $mimetype = null) { $handle = $this->storage->fopen($urn, 'w'); if ($handle) { stream_copy_to_stream($stream, $handle); diff --git a/lib/private/Files/ObjectStore/Swift.php b/lib/private/Files/ObjectStore/Swift.php index b463cb9d44d..aa8b3bb34ec 100644 --- a/lib/private/Files/ObjectStore/Swift.php +++ b/lib/private/Files/ObjectStore/Swift.php @@ -1,27 +1,9 @@ <?php + /** - * @copyright Copyright (c) 2016, ownCloud, Inc. - * - * @author Adrian Brzezinski <adrian.brzezinski@eo.pl> - * @author Jörn Friedrich Dreyer <jfd@butonic.de> - * @author Morris Jobke <hey@morrisjobke.de> - * @author Robin Appelman <robin@icewind.nl> - * @author Roeland Jago Douma <roeland@famdouma.nl> - * - * @license AGPL-3.0 - * - * This code is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License, version 3, - * as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License, version 3, - * along with this program. If not, see <http://www.gnu.org/licenses/> - * + * SPDX-FileCopyrightText: 2016-2024 Nextcloud GmbH and Nextcloud contributors + * SPDX-FileCopyrightText: 2016 ownCloud, Inc. + * SPDX-License-Identifier: AGPL-3.0-only */ namespace OC\Files\ObjectStore; @@ -45,7 +27,7 @@ class Swift implements IObjectStore { /** @var SwiftFactory */ private $swiftFactory; - public function __construct($params, SwiftFactory $connectionFactory = null) { + public function __construct($params, ?SwiftFactory $connectionFactory = null) { $this->swiftFactory = $connectionFactory ?: new SwiftFactory( \OC::$server->getMemCacheFactory()->createDistributed('swift::'), $params, @@ -74,7 +56,7 @@ class Swift implements IObjectStore { return $this->params['container']; } - public function writeObject($urn, $stream, string $mimetype = null) { + public function writeObject($urn, $stream, ?string $mimetype = null) { $tmpFile = \OC::$server->getTempManager()->getTemporaryFile('swiftwrite'); file_put_contents($tmpFile, $stream); $handle = fopen($tmpFile, 'rb'); diff --git a/lib/private/Files/ObjectStore/SwiftFactory.php b/lib/private/Files/ObjectStore/SwiftFactory.php index bd75ccada2e..0db5c9762d2 100644 --- a/lib/private/Files/ObjectStore/SwiftFactory.php +++ b/lib/private/Files/ObjectStore/SwiftFactory.php @@ -3,32 +3,8 @@ declare(strict_types=1); /** - * @copyright Copyright (c) 2018 Robin Appelman <robin@icewind.nl> - * - * @author Adrian Brzezinski <adrian.brzezinski@eo.pl> - * @author Christoph Wurst <christoph@winzerhof-wurst.at> - * @author Julien Lutran <julien.lutran@corp.ovh.com> - * @author Morris Jobke <hey@morrisjobke.de> - * @author Robin Appelman <robin@icewind.nl> - * @author Roeland Jago Douma <roeland@famdouma.nl> - * @author Volker <skydiablo@gmx.net> - * @author William Pain <pain.william@gmail.com> - * - * @license GNU AGPL version 3 or any later version - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as - * published by the Free Software Foundation, either version 3 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - * + * SPDX-FileCopyrightText: 2018 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later */ namespace OC\Files\ObjectStore; diff --git a/lib/private/Files/ObjectStore/SwiftV2CachingAuthService.php b/lib/private/Files/ObjectStore/SwiftV2CachingAuthService.php index b1478762550..266781af142 100644 --- a/lib/private/Files/ObjectStore/SwiftV2CachingAuthService.php +++ b/lib/private/Files/ObjectStore/SwiftV2CachingAuthService.php @@ -3,33 +3,19 @@ declare(strict_types=1); /** - * @copyright Copyright (c) 2018 Robin Appelman <robin@icewind.nl> - * - * @author Robin Appelman <robin@icewind.nl> - * - * @license GNU AGPL version 3 or any later version - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as - * published by the Free Software Foundation, either version 3 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - * + * SPDX-FileCopyrightText: 2018 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later */ namespace OC\Files\ObjectStore; +use OpenStack\Common\Auth\Token; use OpenStack\Identity\v2\Service; class SwiftV2CachingAuthService extends Service { public function authenticate(array $options = []): array { - if (!empty($options['v2cachedToken'])) { + if (isset($options['v2cachedToken'], $options['v2serviceUrl']) + && $options['v2cachedToken'] instanceof Token + && is_string($options['v2serviceUrl'])) { return [$options['v2cachedToken'], $options['v2serviceUrl']]; } else { return parent::authenticate($options); diff --git a/lib/private/Files/Search/QueryOptimizer/FlattenNestedBool.php b/lib/private/Files/Search/QueryOptimizer/FlattenNestedBool.php new file mode 100644 index 00000000000..2bde6f0bbdb --- /dev/null +++ b/lib/private/Files/Search/QueryOptimizer/FlattenNestedBool.php @@ -0,0 +1,33 @@ +<?php + +/** + * SPDX-FileCopyrightText: 2023 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later + */ +namespace OC\Files\Search\QueryOptimizer; + +use OC\Files\Search\SearchBinaryOperator; +use OCP\Files\Search\ISearchBinaryOperator; +use OCP\Files\Search\ISearchOperator; + +class FlattenNestedBool extends QueryOptimizerStep { + public function processOperator(ISearchOperator &$operator) { + if ( + $operator instanceof SearchBinaryOperator && ( + $operator->getType() === ISearchBinaryOperator::OPERATOR_OR || + $operator->getType() === ISearchBinaryOperator::OPERATOR_AND + ) + ) { + $newArguments = []; + foreach ($operator->getArguments() as $oldArgument) { + if ($oldArgument instanceof SearchBinaryOperator && $oldArgument->getType() === $operator->getType()) { + $newArguments = array_merge($newArguments, $oldArgument->getArguments()); + } else { + $newArguments[] = $oldArgument; + } + } + $operator->setArguments($newArguments); + } + parent::processOperator($operator); + } +} diff --git a/lib/private/Files/Search/QueryOptimizer/FlattenSingleArgumentBinaryOperation.php b/lib/private/Files/Search/QueryOptimizer/FlattenSingleArgumentBinaryOperation.php new file mode 100644 index 00000000000..cb04351534a --- /dev/null +++ b/lib/private/Files/Search/QueryOptimizer/FlattenSingleArgumentBinaryOperation.php @@ -0,0 +1,31 @@ +<?php + +/** + * SPDX-FileCopyrightText: 2023 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later + */ +namespace OC\Files\Search\QueryOptimizer; + +use OCP\Files\Search\ISearchBinaryOperator; +use OCP\Files\Search\ISearchOperator; + +/** + * replace single argument AND and OR operations with their single argument + */ +class FlattenSingleArgumentBinaryOperation extends ReplacingOptimizerStep { + public function processOperator(ISearchOperator &$operator): bool { + parent::processOperator($operator); + if ( + $operator instanceof ISearchBinaryOperator && + count($operator->getArguments()) === 1 && + ( + $operator->getType() === ISearchBinaryOperator::OPERATOR_OR || + $operator->getType() === ISearchBinaryOperator::OPERATOR_AND + ) + ) { + $operator = $operator->getArguments()[0]; + return true; + } + return false; + } +} diff --git a/lib/private/Files/Search/QueryOptimizer/MergeDistributiveOperations.php b/lib/private/Files/Search/QueryOptimizer/MergeDistributiveOperations.php new file mode 100644 index 00000000000..4949ca7396b --- /dev/null +++ b/lib/private/Files/Search/QueryOptimizer/MergeDistributiveOperations.php @@ -0,0 +1,99 @@ +<?php + +/** + * SPDX-FileCopyrightText: 2023 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later + */ +namespace OC\Files\Search\QueryOptimizer; + +use OC\Files\Search\SearchBinaryOperator; +use OC\Files\Search\SearchComparison; +use OCP\Files\Search\ISearchBinaryOperator; +use OCP\Files\Search\ISearchOperator; + +/** + * Attempt to transform + * + * (A AND B) OR (A AND C) OR (A AND D AND E) into A AND (B OR C OR (D AND E)) + * + * This is always valid because logical 'AND' and 'OR' are distributive[1]. + * + * [1]: https://en.wikipedia.org/wiki/Distributive_property + */ +class MergeDistributiveOperations extends ReplacingOptimizerStep { + public function processOperator(ISearchOperator &$operator): bool { + if ($operator instanceof SearchBinaryOperator) { + // either 'AND' or 'OR' + $topLevelType = $operator->getType(); + + // split the arguments into groups that share a first argument + $groups = $this->groupBinaryOperatorsByChild($operator->getArguments(), 0); + $outerOperations = array_map(function (array $operators) use ($topLevelType) { + // no common operations, no need to change anything + if (count($operators) === 1) { + return $operators[0]; + } + + // for groups with size >1 we know they are binary operators with at least 1 child + /** @var ISearchBinaryOperator $firstArgument */ + $firstArgument = $operators[0]; + + // we already checked that all arguments have the same type, so this type applies for all, either 'AND' or 'OR' + $innerType = $firstArgument->getType(); + + // the common operation we move out ('A' from the example) + $extractedLeftHand = $firstArgument->getArguments()[0]; + + // for each argument we remove the extracted operation to get the leftovers ('B', 'C' and '(D AND E)' in the example) + // note that we leave them inside the "inner" binary operation for when the "inner" operation contained more than two parts + // in the (common) case where the "inner" operation only has 1 item left it will be cleaned up in a follow up step + $rightHandArguments = array_map(function (ISearchOperator $inner) { + /** @var ISearchBinaryOperator $inner */ + $arguments = $inner->getArguments(); + array_shift($arguments); + if (count($arguments) === 1) { + return $arguments[0]; + } + return new SearchBinaryOperator($inner->getType(), $arguments); + }, $operators); + + // combine the extracted operation ('A') with the remaining bit ('(B OR C OR (D AND E))') + // note that because of how distribution work, we use the "outer" type "inside" and the "inside" type "outside". + $extractedRightHand = new SearchBinaryOperator($topLevelType, $rightHandArguments); + return new SearchBinaryOperator( + $innerType, + [$extractedLeftHand, $extractedRightHand] + ); + }, $groups); + + // combine all groups again + $operator = new SearchBinaryOperator($topLevelType, $outerOperations); + parent::processOperator($operator); + return true; + } + return parent::processOperator($operator); + } + + /** + * Group a list of binary search operators that have a common argument + * + * Non-binary operators, or empty binary operators will each get their own 1-sized group + * + * @param ISearchOperator[] $operators + * @return ISearchOperator[][] + */ + private function groupBinaryOperatorsByChild(array $operators, int $index = 0): array { + $result = []; + foreach ($operators as $operator) { + if ($operator instanceof ISearchBinaryOperator && count($operator->getArguments()) > 0) { + /** @var SearchBinaryOperator|SearchComparison $child */ + $child = $operator->getArguments()[$index]; + $childKey = (string)$child; + $result[$childKey][] = $operator; + } else { + $result[] = [$operator]; + } + } + return array_values($result); + } +} diff --git a/lib/private/Files/Search/QueryOptimizer/OrEqualsToIn.php b/lib/private/Files/Search/QueryOptimizer/OrEqualsToIn.php new file mode 100644 index 00000000000..3f2b36823ab --- /dev/null +++ b/lib/private/Files/Search/QueryOptimizer/OrEqualsToIn.php @@ -0,0 +1,74 @@ +<?php + +/** + * SPDX-FileCopyrightText: 2023 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later + */ +namespace OC\Files\Search\QueryOptimizer; + +use OC\Files\Search\SearchBinaryOperator; +use OC\Files\Search\SearchComparison; +use OCP\Files\Search\ISearchBinaryOperator; +use OCP\Files\Search\ISearchComparison; +use OCP\Files\Search\ISearchOperator; + +/** + * transform (field == A OR field == B ...) into field IN (A, B, ...) + */ +class OrEqualsToIn extends ReplacingOptimizerStep { + public function processOperator(ISearchOperator &$operator): bool { + if ( + $operator instanceof ISearchBinaryOperator && + $operator->getType() === ISearchBinaryOperator::OPERATOR_OR + ) { + $groups = $this->groupEqualsComparisonsByField($operator->getArguments()); + $newParts = array_map(function (array $group) { + if (count($group) > 1) { + // because of the logic from `groupEqualsComparisonsByField` we now that group is all comparisons on the same field + /** @var ISearchComparison[] $group */ + $field = $group[0]->getField(); + $values = array_map(function (ISearchComparison $comparison) { + /** @var string|integer|bool|\DateTime $value */ + $value = $comparison->getValue(); + return $value; + }, $group); + $in = new SearchComparison(ISearchComparison::COMPARE_IN, $field, $values); + $pathEqHash = array_reduce($group, function ($pathEqHash, ISearchComparison $comparison) { + return $comparison->getQueryHint(ISearchComparison::HINT_PATH_EQ_HASH, true) && $pathEqHash; + }, true); + $in->setQueryHint(ISearchComparison::HINT_PATH_EQ_HASH, $pathEqHash); + return $in; + } else { + return $group[0]; + } + }, $groups); + if (count($newParts) === 1) { + $operator = $newParts[0]; + } else { + $operator = new SearchBinaryOperator(ISearchBinaryOperator::OPERATOR_OR, $newParts); + } + parent::processOperator($operator); + return true; + } + parent::processOperator($operator); + return false; + } + + /** + * Non-equals operators are put in a separate group for each + * + * @param ISearchOperator[] $operators + * @return ISearchOperator[][] + */ + private function groupEqualsComparisonsByField(array $operators): array { + $result = []; + foreach ($operators as $operator) { + if ($operator instanceof ISearchComparison && $operator->getType() === ISearchComparison::COMPARE_EQUAL) { + $result[$operator->getField()][] = $operator; + } else { + $result[] = [$operator]; + } + } + return array_values($result); + } +} diff --git a/lib/private/Files/Search/QueryOptimizer/PathPrefixOptimizer.php b/lib/private/Files/Search/QueryOptimizer/PathPrefixOptimizer.php index 0caa9b12a02..9d50746d5d1 100644 --- a/lib/private/Files/Search/QueryOptimizer/PathPrefixOptimizer.php +++ b/lib/private/Files/Search/QueryOptimizer/PathPrefixOptimizer.php @@ -2,25 +2,9 @@ declare(strict_types=1); /** - * @copyright Copyright (c) 2021 Robin Appelman <robin@icewind.nl> - * - * @license GNU AGPL version 3 or any later version - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as - * published by the Free Software Foundation, either version 3 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - * + * SPDX-FileCopyrightText: 2021 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later */ - namespace OC\Files\Search\QueryOptimizer; use OC\Files\Search\SearchComparison; @@ -48,7 +32,7 @@ class PathPrefixOptimizer extends QueryOptimizerStep { } public function processOperator(ISearchOperator &$operator) { - if (!$this->useHashEq && $operator instanceof ISearchComparison && $operator->getField() === 'path' && $operator->getType() === ISearchComparison::COMPARE_EQUAL) { + if (!$this->useHashEq && $operator instanceof ISearchComparison && !$operator->getExtra() && $operator->getField() === 'path' && $operator->getType() === ISearchComparison::COMPARE_EQUAL) { $operator->setQueryHint(ISearchComparison::HINT_PATH_EQ_HASH, false); } @@ -69,7 +53,7 @@ class PathPrefixOptimizer extends QueryOptimizerStep { private function operatorPairIsPathPrefix(ISearchOperator $like, ISearchOperator $equal): bool { return ( $like instanceof ISearchComparison && $equal instanceof ISearchComparison && - $like->getField() === 'path' && $equal->getField() === 'path' && + !$like->getExtra() && !$equal->getExtra() && $like->getField() === 'path' && $equal->getField() === 'path' && $like->getType() === ISearchComparison::COMPARE_LIKE_CASE_SENSITIVE && $equal->getType() === ISearchComparison::COMPARE_EQUAL && $like->getValue() === SearchComparison::escapeLikeParameter($equal->getValue()) . '/%' ); diff --git a/lib/private/Files/Search/QueryOptimizer/QueryOptimizer.php b/lib/private/Files/Search/QueryOptimizer/QueryOptimizer.php index 160b27b7f11..5259ca25ad3 100644 --- a/lib/private/Files/Search/QueryOptimizer/QueryOptimizer.php +++ b/lib/private/Files/Search/QueryOptimizer/QueryOptimizer.php @@ -2,23 +2,8 @@ declare(strict_types=1); /** - * @copyright Copyright (c) 2021 Robin Appelman <robin@icewind.nl> - * - * @license GNU AGPL version 3 or any later version - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as - * published by the Free Software Foundation, either version 3 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - * + * SPDX-FileCopyrightText: 2021 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later */ namespace OC\Files\Search\QueryOptimizer; @@ -29,15 +14,20 @@ class QueryOptimizer { /** @var QueryOptimizerStep[] */ private $steps = []; - public function __construct( - PathPrefixOptimizer $pathPrefixOptimizer - ) { + public function __construct() { + // note that the order here is relevant $this->steps = [ - $pathPrefixOptimizer + new PathPrefixOptimizer(), + new MergeDistributiveOperations(), + new FlattenSingleArgumentBinaryOperation(), + new FlattenNestedBool(), + new OrEqualsToIn(), + new FlattenNestedBool(), + new SplitLargeIn(), ]; } - public function processOperator(ISearchOperator $operator) { + public function processOperator(ISearchOperator &$operator) { foreach ($this->steps as $step) { $step->inspectOperator($operator); } diff --git a/lib/private/Files/Search/QueryOptimizer/QueryOptimizerStep.php b/lib/private/Files/Search/QueryOptimizer/QueryOptimizerStep.php index b16898e9087..15b5b580ec1 100644 --- a/lib/private/Files/Search/QueryOptimizer/QueryOptimizerStep.php +++ b/lib/private/Files/Search/QueryOptimizer/QueryOptimizerStep.php @@ -2,23 +2,8 @@ declare(strict_types=1); /** - * @copyright Copyright (c) 2021 Robin Appelman <robin@icewind.nl> - * - * @license GNU AGPL version 3 or any later version - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as - * published by the Free Software Foundation, either version 3 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - * + * SPDX-FileCopyrightText: 2021 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later */ namespace OC\Files\Search\QueryOptimizer; diff --git a/lib/private/Files/Search/QueryOptimizer/ReplacingOptimizerStep.php b/lib/private/Files/Search/QueryOptimizer/ReplacingOptimizerStep.php new file mode 100644 index 00000000000..a9c9ba876bc --- /dev/null +++ b/lib/private/Files/Search/QueryOptimizer/ReplacingOptimizerStep.php @@ -0,0 +1,37 @@ +<?php + +/** + * SPDX-FileCopyrightText: 2023 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later + */ +namespace OC\Files\Search\QueryOptimizer; + +use OC\Files\Search\SearchBinaryOperator; +use OCP\Files\Search\ISearchOperator; + +/** + * Optimizer step that can replace the $operator altogether instead of just modifying it + * These steps need some extra logic to properly replace the arguments of binary operators + */ +class ReplacingOptimizerStep extends QueryOptimizerStep { + /** + * Allow optimizer steps to modify query operators + * + * Returns true if the reference $operator points to a new value + */ + public function processOperator(ISearchOperator &$operator): bool { + if ($operator instanceof SearchBinaryOperator) { + $modified = false; + $arguments = $operator->getArguments(); + foreach ($arguments as &$argument) { + if ($this->processOperator($argument)) { + $modified = true; + } + } + if ($modified) { + $operator->setArguments($arguments); + } + } + return false; + } +} diff --git a/lib/private/Files/Search/QueryOptimizer/SplitLargeIn.php b/lib/private/Files/Search/QueryOptimizer/SplitLargeIn.php new file mode 100644 index 00000000000..5c7655b49c1 --- /dev/null +++ b/lib/private/Files/Search/QueryOptimizer/SplitLargeIn.php @@ -0,0 +1,36 @@ +<?php + +/** + * SPDX-FileCopyrightText: 2024 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later + */ +namespace OC\Files\Search\QueryOptimizer; + +use OC\Files\Search\SearchBinaryOperator; +use OC\Files\Search\SearchComparison; +use OCP\Files\Search\ISearchBinaryOperator; +use OCP\Files\Search\ISearchComparison; +use OCP\Files\Search\ISearchOperator; + +/** + * transform IN (1000+ element) into (IN (1000 elements) OR IN(...)) + */ +class SplitLargeIn extends ReplacingOptimizerStep { + public function processOperator(ISearchOperator &$operator): bool { + if ( + $operator instanceof ISearchComparison && + $operator->getType() === ISearchComparison::COMPARE_IN && + count($operator->getValue()) > 1000 + ) { + $chunks = array_chunk($operator->getValue(), 1000); + $chunkComparisons = array_map(function (array $values) use ($operator) { + return new SearchComparison(ISearchComparison::COMPARE_IN, $operator->getField(), $values); + }, $chunks); + + $operator = new SearchBinaryOperator(ISearchBinaryOperator::OPERATOR_OR, $chunkComparisons); + return true; + } + parent::processOperator($operator); + return false; + } +} diff --git a/lib/private/Files/Search/SearchBinaryOperator.php b/lib/private/Files/Search/SearchBinaryOperator.php index d7bba8f1b4e..59a1ba2dfaf 100644 --- a/lib/private/Files/Search/SearchBinaryOperator.php +++ b/lib/private/Files/Search/SearchBinaryOperator.php @@ -1,24 +1,7 @@ <?php /** - * @copyright Copyright (c) 2017 Robin Appelman <robin@icewind.nl> - * - * @author Robin Appelman <robin@icewind.nl> - * - * @license GNU AGPL version 3 or any later version - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as - * published by the Free Software Foundation, either version 3 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - * + * SPDX-FileCopyrightText: 2017 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later */ namespace OC\Files\Search; @@ -28,7 +11,7 @@ use OCP\Files\Search\ISearchOperator; class SearchBinaryOperator implements ISearchBinaryOperator { /** @var string */ private $type; - /** @var ISearchOperator[] */ + /** @var (SearchBinaryOperator|SearchComparison)[] */ private $arguments; private $hints = []; @@ -36,7 +19,7 @@ class SearchBinaryOperator implements ISearchBinaryOperator { * SearchBinaryOperator constructor. * * @param string $type - * @param ISearchOperator[] $arguments + * @param (SearchBinaryOperator|SearchComparison)[] $arguments */ public function __construct($type, array $arguments) { $this->type = $type; @@ -57,6 +40,14 @@ class SearchBinaryOperator implements ISearchBinaryOperator { return $this->arguments; } + /** + * @param ISearchOperator[] $arguments + * @return void + */ + public function setArguments(array $arguments): void { + $this->arguments = $arguments; + } + public function getQueryHint(string $name, $default) { return $this->hints[$name] ?? $default; } @@ -64,4 +55,11 @@ class SearchBinaryOperator implements ISearchBinaryOperator { public function setQueryHint(string $name, $value): void { $this->hints[$name] = $value; } + + public function __toString(): string { + if ($this->type === ISearchBinaryOperator::OPERATOR_NOT) { + return '(not ' . $this->arguments[0] . ')'; + } + return '(' . implode(' ' . $this->type . ' ', $this->arguments) . ')'; + } } diff --git a/lib/private/Files/Search/SearchComparison.php b/lib/private/Files/Search/SearchComparison.php index 122a1f730b4..e3a8f800506 100644 --- a/lib/private/Files/Search/SearchComparison.php +++ b/lib/private/Files/Search/SearchComparison.php @@ -1,70 +1,53 @@ <?php + +declare(strict_types=1); /** - * @copyright Copyright (c) 2017 Robin Appelman <robin@icewind.nl> - * - * @author Robin Appelman <robin@icewind.nl> - * - * @license GNU AGPL version 3 or any later version - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as - * published by the Free Software Foundation, either version 3 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - * + * SPDX-FileCopyrightText: 2017 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later */ namespace OC\Files\Search; use OCP\Files\Search\ISearchComparison; +/** + * @psalm-import-type ParamValue from ISearchComparison + */ class SearchComparison implements ISearchComparison { - /** @var string */ - private $type; - /** @var string */ - private $field; - /** @var string|integer|\DateTime */ - private $value; - private $hints = []; + private array $hints = []; - /** - * SearchComparison constructor. - * - * @param string $type - * @param string $field - * @param \DateTime|int|string $value - */ - public function __construct($type, $field, $value) { - $this->type = $type; - $this->field = $field; - $this->value = $value; + public function __construct( + private string $type, + private string $field, + /** @var ParamValue $value */ + private \DateTime|int|string|bool|array $value, + private string $extra = '' + ) { } /** * @return string */ - public function getType() { + public function getType(): string { return $this->type; } /** * @return string */ - public function getField() { + public function getField(): string { return $this->field; } + public function getValue(): string|int|bool|\DateTime|array { + return $this->value; + } + /** - * @return \DateTime|int|string + * @return string + * @since 28.0.0 */ - public function getValue() { - return $this->value; + public function getExtra(): string { + return $this->extra; } public function getQueryHint(string $name, $default) { @@ -78,4 +61,8 @@ class SearchComparison implements ISearchComparison { public static function escapeLikeParameter(string $param): string { return addcslashes($param, '\\_%'); } + + public function __toString(): string { + return $this->field . ' ' . $this->type . ' ' . json_encode($this->value); + } } diff --git a/lib/private/Files/Search/SearchOrder.php b/lib/private/Files/Search/SearchOrder.php index 1395a87ac72..f3f62b933a1 100644 --- a/lib/private/Files/Search/SearchOrder.php +++ b/lib/private/Files/Search/SearchOrder.php @@ -1,24 +1,7 @@ <?php /** - * @copyright Copyright (c) 2017 Robin Appelman <robin@icewind.nl> - * - * @author Robin Appelman <robin@icewind.nl> - * - * @license GNU AGPL version 3 or any later version - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as - * published by the Free Software Foundation, either version 3 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - * + * SPDX-FileCopyrightText: 2017 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later */ namespace OC\Files\Search; @@ -26,34 +9,33 @@ use OCP\Files\FileInfo; use OCP\Files\Search\ISearchOrder; class SearchOrder implements ISearchOrder { - /** @var string */ - private $direction; - /** @var string */ - private $field; + public function __construct( + private string $direction, + private string $field, + private string $extra = '' + ) { + } /** - * SearchOrder constructor. - * - * @param string $direction - * @param string $field + * @return string */ - public function __construct($direction, $field) { - $this->direction = $direction; - $this->field = $field; + public function getDirection(): string { + return $this->direction; } /** * @return string */ - public function getDirection() { - return $this->direction; + public function getField(): string { + return $this->field; } /** * @return string + * @since 28.0.0 */ - public function getField() { - return $this->field; + public function getExtra(): string { + return $this->extra; } public function sortFileInfo(FileInfo $a, FileInfo $b): int { diff --git a/lib/private/Files/Search/SearchQuery.php b/lib/private/Files/Search/SearchQuery.php index 7c76bdff978..e7cb031da3e 100644 --- a/lib/private/Files/Search/SearchQuery.php +++ b/lib/private/Files/Search/SearchQuery.php @@ -1,24 +1,7 @@ <?php /** - * @copyright Copyright (c) 2017 Robin Appelman <robin@icewind.nl> - * - * @author Robin Appelman <robin@icewind.nl> - * - * @license GNU AGPL version 3 or any later version - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as - * published by the Free Software Foundation, either version 3 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - * + * SPDX-FileCopyrightText: 2017 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later */ namespace OC\Files\Search; diff --git a/lib/private/Files/SetupManager.php b/lib/private/Files/SetupManager.php index b44ead003a8..53befe57e36 100644 --- a/lib/private/Files/SetupManager.php +++ b/lib/private/Files/SetupManager.php @@ -2,30 +2,15 @@ declare(strict_types=1); /** - * @copyright Copyright (c) 2022 Robin Appelman <robin@icewind.nl> - * - * @license GNU AGPL version 3 or any later version - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as - * published by the Free Software Foundation, either version 3 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - * + * SPDX-FileCopyrightText: 2022 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later */ namespace OC\Files; use OC\Files\Config\MountProviderCollection; +use OC\Files\Mount\HomeMountPoint; use OC\Files\Mount\MountPoint; -use OC\Files\ObjectStore\HomeObjectStoreStorage; use OC\Files\Storage\Common; use OC\Files\Storage\Home; use OC\Files\Storage\Storage; @@ -34,9 +19,15 @@ use OC\Files\Storage\Wrapper\Encoding; use OC\Files\Storage\Wrapper\PermissionsMask; use OC\Files\Storage\Wrapper\Quota; use OC\Lockdown\Filesystem\NullStorage; +use OC\Share\Share; +use OC\Share20\ShareDisableChecker; use OC_App; use OC_Hook; use OC_Util; +use OCA\Files_External\Config\ConfigAdapter; +use OCA\Files_Sharing\External\Mount; +use OCA\Files_Sharing\ISharedMountPoint; +use OCA\Files_Sharing\SharedMount; use OCP\Constants; use OCP\Diagnostics\IEventLogger; use OCP\EventDispatcher\IEventDispatcher; @@ -64,52 +55,35 @@ use Psr\Log\LoggerInterface; class SetupManager { private bool $rootSetup = false; - private IEventLogger $eventLogger; - private MountProviderCollection $mountProviderCollection; - private IMountManager $mountManager; - private IUserManager $userManager; // List of users for which at least one mount is setup private array $setupUsers = []; // List of users for which all mounts are setup private array $setupUsersComplete = []; /** @var array<string, string[]> */ private array $setupUserMountProviders = []; - private IEventDispatcher $eventDispatcher; - private IUserMountCache $userMountCache; - private ILockdownManager $lockdownManager; - private IUserSession $userSession; private ICache $cache; - private LoggerInterface $logger; - private IConfig $config; private bool $listeningForProviders; private array $fullSetupRequired = []; private bool $setupBuiltinWrappersDone = false; + private bool $forceFullSetup = false; public function __construct( - IEventLogger $eventLogger, - MountProviderCollection $mountProviderCollection, - IMountManager $mountManager, - IUserManager $userManager, - IEventDispatcher $eventDispatcher, - IUserMountCache $userMountCache, - ILockdownManager $lockdownManager, - IUserSession $userSession, + private IEventLogger $eventLogger, + private MountProviderCollection $mountProviderCollection, + private IMountManager $mountManager, + private IUserManager $userManager, + private IEventDispatcher $eventDispatcher, + private IUserMountCache $userMountCache, + private ILockdownManager $lockdownManager, + private IUserSession $userSession, ICacheFactory $cacheFactory, - LoggerInterface $logger, - IConfig $config + private LoggerInterface $logger, + private IConfig $config, + private ShareDisableChecker $shareDisableChecker, ) { - $this->eventLogger = $eventLogger; - $this->mountProviderCollection = $mountProviderCollection; - $this->mountManager = $mountManager; - $this->userManager = $userManager; - $this->eventDispatcher = $eventDispatcher; - $this->userMountCache = $userMountCache; - $this->lockdownManager = $lockdownManager; - $this->logger = $logger; - $this->userSession = $userSession; $this->cache = $cacheFactory->createDistributed('setupmanager::'); $this->listeningForProviders = false; - $this->config = $config; + $this->forceFullSetup = $this->config->getSystemValueBool('debug.force-full-fs-setup'); $this->setupListeners(); } @@ -133,52 +107,55 @@ class SetupManager { $prevLogging = Filesystem::logWarningWhenAddingStorageWrapper(false); Filesystem::addStorageWrapper('mount_options', function ($mountPoint, IStorage $storage, IMountPoint $mount) { - if ($storage->instanceOfStorage(Common::class)) { + if ($mount->getOptions() && $storage->instanceOfStorage(Common::class)) { $storage->setMountOptions($mount->getOptions()); } return $storage; }); - Filesystem::addStorageWrapper('enable_sharing', function ($mountPoint, IStorage $storage, IMountPoint $mount) { - if (!$mount->getOption('enable_sharing', true)) { - return new PermissionsMask([ - 'storage' => $storage, - 'mask' => Constants::PERMISSION_ALL - Constants::PERMISSION_SHARE, - ]); + $reSharingEnabled = Share::isResharingAllowed(); + $user = $this->userSession->getUser(); + $sharingEnabledForUser = $user ? !$this->shareDisableChecker->sharingDisabledForUser($user->getUID()) : true; + Filesystem::addStorageWrapper( + 'sharing_mask', + function ($mountPoint, IStorage $storage, IMountPoint $mount) use ($reSharingEnabled, $sharingEnabledForUser) { + $sharingEnabledForMount = $mount->getOption('enable_sharing', true); + $isShared = $mount instanceof ISharedMountPoint; + if (!$sharingEnabledForMount || !$sharingEnabledForUser || (!$reSharingEnabled && $isShared)) { + return new PermissionsMask([ + 'storage' => $storage, + 'mask' => Constants::PERMISSION_ALL - Constants::PERMISSION_SHARE, + ]); + } + return $storage; } - return $storage; - }); + ); // install storage availability wrapper, before most other wrappers - Filesystem::addStorageWrapper('oc_availability', function ($mountPoint, IStorage $storage) { - if (!$storage->instanceOfStorage('\OCA\Files_Sharing\SharedStorage') && !$storage->isLocal()) { + Filesystem::addStorageWrapper('oc_availability', function ($mountPoint, IStorage $storage, IMountPoint $mount) { + $externalMount = $mount instanceof ConfigAdapter || $mount instanceof Mount; + if ($externalMount && !$storage->isLocal()) { return new Availability(['storage' => $storage]); } return $storage; }); Filesystem::addStorageWrapper('oc_encoding', function ($mountPoint, IStorage $storage, IMountPoint $mount) { - if ($mount->getOption('encoding_compatibility', false) && !$storage->instanceOfStorage('\OCA\Files_Sharing\SharedStorage')) { + if ($mount->getOption('encoding_compatibility', false) && !$mount instanceof SharedMount) { return new Encoding(['storage' => $storage]); } return $storage; }); $quotaIncludeExternal = $this->config->getSystemValue('quota_include_external_storage', false); - Filesystem::addStorageWrapper('oc_quota', function ($mountPoint, $storage) use ($quotaIncludeExternal) { + Filesystem::addStorageWrapper('oc_quota', function ($mountPoint, $storage, IMountPoint $mount) use ($quotaIncludeExternal) { // set up quota for home storages, even for other users // which can happen when using sharing - - /** - * @var Storage $storage - */ - if ($storage->instanceOfStorage(HomeObjectStoreStorage::class) || $storage->instanceOfStorage(Home::class)) { - if (is_object($storage->getUser())) { - $user = $storage->getUser(); - return new Quota(['storage' => $storage, 'quotaCallback' => function () use ($user) { - return OC_Util::getUserQuota($user); - }, 'root' => 'files', 'include_external_storage' => $quotaIncludeExternal]); - } + if ($mount instanceof HomeMountPoint) { + $user = $mount->getUser(); + return new Quota(['storage' => $storage, 'quotaCallback' => function () use ($user) { + return OC_Util::getUserQuota($user); + }, 'root' => 'files', 'include_external_storage' => $quotaIncludeExternal]); } return $storage; @@ -345,12 +322,13 @@ class SetupManager { if ($this->rootSetup) { return; } + + $this->setupBuiltinWrappers(); + $this->rootSetup = true; $this->eventLogger->start('fs:setup:root', 'Setup root filesystem'); - $this->setupBuiltinWrappers(); - $rootMounts = $this->mountProviderCollection->getRootMounts(); foreach ($rootMounts as $rootMountProvider) { $this->mountManager->addMount($rootMountProvider); @@ -479,6 +457,10 @@ class SetupManager { } private function fullSetupRequired(IUser $user): bool { + if ($this->forceFullSetup) { + return true; + } + // we perform a "cached" setup only after having done the full setup recently // this is also used to trigger a full setup after handling events that are likely // to change the available mounts diff --git a/lib/private/Files/SetupManagerFactory.php b/lib/private/Files/SetupManagerFactory.php index 1d9efbd411f..6ae38c6fdbc 100644 --- a/lib/private/Files/SetupManagerFactory.php +++ b/lib/private/Files/SetupManagerFactory.php @@ -2,27 +2,13 @@ declare(strict_types=1); /** - * @copyright Copyright (c) 2022 Robin Appelman <robin@icewind.nl> - * - * @license GNU AGPL version 3 or any later version - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as - * published by the Free Software Foundation, either version 3 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - * + * SPDX-FileCopyrightText: 2022 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later */ namespace OC\Files; +use OC\Share20\ShareDisableChecker; use OCP\Diagnostics\IEventLogger; use OCP\EventDispatcher\IEventDispatcher; use OCP\Files\Config\IMountProviderCollection; @@ -36,40 +22,21 @@ use OCP\Lockdown\ILockdownManager; use Psr\Log\LoggerInterface; class SetupManagerFactory { - private IEventLogger $eventLogger; - private IMountProviderCollection $mountProviderCollection; - private IUserManager $userManager; - private IEventDispatcher $eventDispatcher; - private IUserMountCache $userMountCache; - private ILockdownManager $lockdownManager; - private IUserSession $userSession; private ?SetupManager $setupManager; - private ICacheFactory $cacheFactory; - private LoggerInterface $logger; - private IConfig $config; public function __construct( - IEventLogger $eventLogger, - IMountProviderCollection $mountProviderCollection, - IUserManager $userManager, - IEventDispatcher $eventDispatcher, - IUserMountCache $userMountCache, - ILockdownManager $lockdownManager, - IUserSession $userSession, - ICacheFactory $cacheFactory, - LoggerInterface $logger, - IConfig $config + private IEventLogger $eventLogger, + private IMountProviderCollection $mountProviderCollection, + private IUserManager $userManager, + private IEventDispatcher $eventDispatcher, + private IUserMountCache $userMountCache, + private ILockdownManager $lockdownManager, + private IUserSession $userSession, + private ICacheFactory $cacheFactory, + private LoggerInterface $logger, + private IConfig $config, + private ShareDisableChecker $shareDisableChecker, ) { - $this->eventLogger = $eventLogger; - $this->mountProviderCollection = $mountProviderCollection; - $this->userManager = $userManager; - $this->eventDispatcher = $eventDispatcher; - $this->userMountCache = $userMountCache; - $this->lockdownManager = $lockdownManager; - $this->userSession = $userSession; - $this->cacheFactory = $cacheFactory; - $this->logger = $logger; - $this->config = $config; $this->setupManager = null; } @@ -86,7 +53,8 @@ class SetupManagerFactory { $this->userSession, $this->cacheFactory, $this->logger, - $this->config + $this->config, + $this->shareDisableChecker, ); } return $this->setupManager; diff --git a/lib/private/Files/SimpleFS/NewSimpleFile.php b/lib/private/Files/SimpleFS/NewSimpleFile.php index 50511b5a5f9..d0986592c03 100644 --- a/lib/private/Files/SimpleFS/NewSimpleFile.php +++ b/lib/private/Files/SimpleFS/NewSimpleFile.php @@ -3,26 +3,8 @@ declare(strict_types=1); /** - * @copyright Copyright (c) 2020 Robin Appelman <robin@icewind.nl> - * - * @author Christoph Wurst <christoph@winzerhof-wurst.at> - * @author Robin Appelman <robin@icewind.nl> - * - * @license GNU AGPL version 3 or any later version - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as - * published by the Free Software Foundation, either version 3 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - * + * SPDX-FileCopyrightText: 2020 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later */ namespace OC\Files\SimpleFS; diff --git a/lib/private/Files/SimpleFS/SimpleFile.php b/lib/private/Files/SimpleFS/SimpleFile.php index 76ab06feaab..cbb3af4db29 100644 --- a/lib/private/Files/SimpleFS/SimpleFile.php +++ b/lib/private/Files/SimpleFS/SimpleFile.php @@ -1,26 +1,7 @@ <?php /** - * @copyright 2016 Roeland Jago Douma <roeland@famdouma.nl> - * - * @author Christoph Wurst <christoph@winzerhof-wurst.at> - * @author Julius Härtl <jus@bitgrid.net> - * @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/>. - * + * SPDX-FileCopyrightText: 2016 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later */ namespace OC\Files\SimpleFS; diff --git a/lib/private/Files/SimpleFS/SimpleFolder.php b/lib/private/Files/SimpleFS/SimpleFolder.php index 4d24aa138c1..0da552e402b 100644 --- a/lib/private/Files/SimpleFS/SimpleFolder.php +++ b/lib/private/Files/SimpleFS/SimpleFolder.php @@ -1,26 +1,7 @@ <?php /** - * @copyright 2016 Roeland Jago Douma <roeland@famdouma.nl> - * - * @author Christoph Wurst <christoph@winzerhof-wurst.at> - * @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/>. - * + * SPDX-FileCopyrightText: 2016 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later */ namespace OC\Files\SimpleFS; @@ -28,8 +9,8 @@ use OCP\Files\File; use OCP\Files\Folder; use OCP\Files\Node; use OCP\Files\NotFoundException; -use OCP\Files\SimpleFS\ISimpleFolder; use OCP\Files\SimpleFS\ISimpleFile; +use OCP\Files\SimpleFS\ISimpleFolder; class SimpleFolder implements ISimpleFolder { /** @var Folder */ diff --git a/lib/private/Files/Storage/Common.php b/lib/private/Files/Storage/Common.php index 5ab411434d0..e613e435f03 100644 --- a/lib/private/Files/Storage/Common.php +++ b/lib/private/Files/Storage/Common.php @@ -1,48 +1,14 @@ <?php + /** - * @copyright Copyright (c) 2016, ownCloud, Inc. - * - * @author Arthur Schiwon <blizzz@arthur-schiwon.de> - * @author Bart Visscher <bartv@thisnet.nl> - * @author Christoph Wurst <christoph@winzerhof-wurst.at> - * @author Greta Doci <gretadoci@gmail.com> - * @author hkjolhede <hkjolhede@gmail.com> - * @author Joas Schilling <coding@schilljs.com> - * @author Jörn Friedrich Dreyer <jfd@butonic.de> - * @author Julius Härtl <jus@bitgrid.net> - * @author Lukas Reschke <lukas@statuscode.ch> - * @author Martin Mattel <martin.mattel@diemattels.at> - * @author Michael Gapczynski <GapczynskiM@gmail.com> - * @author Morris Jobke <hey@morrisjobke.de> - * @author Robin Appelman <robin@icewind.nl> - * @author Robin McCorkell <robin@mccorkell.me.uk> - * @author Roeland Jago Douma <roeland@famdouma.nl> - * @author Roland Tapken <roland@bitarbeiter.net> - * @author Sam Tuke <mail@samtuke.com> - * @author scambra <sergio@entrecables.com> - * @author Stefan Weil <sw@weilnetz.de> - * @author Thomas Müller <thomas.mueller@tmit.eu> - * @author Vincent Petry <vincent@nextcloud.com> - * @author Vinicius Cubas Brand <vinicius@eita.org.br> - * - * @license AGPL-3.0 - * - * This code is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License, version 3, - * as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License, version 3, - * along with this program. If not, see <http://www.gnu.org/licenses/> - * + * SPDX-FileCopyrightText: 2016-2024 Nextcloud GmbH and Nextcloud contributors + * SPDX-FileCopyrightText: 2016 ownCloud, Inc. + * SPDX-License-Identifier: AGPL-3.0-only */ namespace OC\Files\Storage; use OC\Files\Cache\Cache; +use OC\Files\Cache\CacheDependencies; use OC\Files\Cache\Propagator; use OC\Files\Cache\Scanner; use OC\Files\Cache\Updater; @@ -61,8 +27,10 @@ use OCP\Files\ReservedWordException; use OCP\Files\Storage\ILockingStorage; use OCP\Files\Storage\IStorage; use OCP\Files\Storage\IWriteStreamStorage; +use OCP\Files\StorageNotAvailableException; use OCP\Lock\ILockingProvider; use OCP\Lock\LockedException; +use OCP\Server; use Psr\Log\LoggerInterface; /** @@ -338,12 +306,20 @@ abstract class Common implements Storage, ILockingStorage, IWriteStreamStorage { return $this->filemtime($path) > $time; } + protected function getCacheDependencies(): CacheDependencies { + static $dependencies = null; + if (!$dependencies) { + $dependencies = Server::get(CacheDependencies::class); + } + return $dependencies; + } + public function getCache($path = '', $storage = null) { if (!$storage) { $storage = $this; } if (!isset($storage->cache)) { - $storage->cache = new Cache($storage); + $storage->cache = new Cache($storage, $this->getCacheDependencies()); } return $storage->cache; } @@ -398,13 +374,7 @@ abstract class Common implements Storage, ILockingStorage, IWriteStreamStorage { } public function getStorageCache($storage = null) { - if (!$storage) { - $storage = $this; - } - if (!isset($this->storageCache)) { - $this->storageCache = new \OC\Files\Cache\Storage($storage); - } - return $this->storageCache; + return $this->getCache($storage)->getStorageCache(); } /** @@ -562,7 +532,9 @@ abstract class Common implements Storage, ILockingStorage, IWriteStreamStorage { * @throws InvalidPathException */ protected function verifyPosixPath($fileName) { - $this->scanForInvalidCharacters($fileName, "\\/"); + $invalidChars = \OCP\Util::getForbiddenFileNameChars(); + $this->scanForInvalidCharacters($fileName, $invalidChars); + $fileName = trim($fileName); $reservedNames = ['*']; if (in_array($fileName, $reservedNames)) { @@ -572,11 +544,11 @@ abstract class Common implements Storage, ILockingStorage, IWriteStreamStorage { /** * @param string $fileName - * @param string $invalidChars + * @param string[] $invalidChars * @throws InvalidPathException */ - private function scanForInvalidCharacters($fileName, $invalidChars) { - foreach (str_split($invalidChars) as $char) { + private function scanForInvalidCharacters(string $fileName, array $invalidChars) { + foreach ($invalidChars as $char) { if (str_contains($fileName, $char)) { throw new InvalidCharacterInPathException(); } @@ -601,7 +573,7 @@ abstract class Common implements Storage, ILockingStorage, IWriteStreamStorage { * @return mixed */ public function getMountOption($name, $default = null) { - return isset($this->mountOptions[$name]) ? $this->mountOptions[$name] : $default; + return $this->mountOptions[$name] ?? $default; } /** @@ -663,7 +635,7 @@ abstract class Common implements Storage, ILockingStorage, IWriteStreamStorage { private function isSameStorage(IStorage $storage): bool { while ($storage->instanceOfStorage(Wrapper::class)) { /** - * @var Wrapper $sourceStorage + * @var Wrapper $storage */ $storage = $storage->getWrapperStorage(); } @@ -861,6 +833,19 @@ abstract class Common implements Storage, ILockingStorage, IWriteStreamStorage { } /** + * Allow setting the storage owner + * + * This can be used for storages that do not have a dedicated owner, where we want to + * pass the user that we setup the mountpoint for along to the storage layer + * + * @param string|null $user + * @return void + */ + public function setOwner(?string $user): void { + $this->owner = $user; + } + + /** * @return bool */ public function needsPartFile() { @@ -875,7 +860,7 @@ abstract class Common implements Storage, ILockingStorage, IWriteStreamStorage { * @param int $size * @return int */ - public function writeStream(string $path, $stream, int $size = null): int { + public function writeStream(string $path, $stream, ?int $size = null): int { $target = $this->fopen($path, 'w'); if (!$target) { throw new GenericFileException("Failed to open $path for writing"); @@ -894,6 +879,11 @@ abstract class Common implements Storage, ILockingStorage, IWriteStreamStorage { public function getDirectoryContent($directory): \Traversable { $dh = $this->opendir($directory); + + if ($dh === false) { + throw new StorageNotAvailableException('Directory listing failed'); + } + if (is_resource($dh)) { $basePath = rtrim($directory, '/'); while (($file = readdir($dh)) !== false) { diff --git a/lib/private/Files/Storage/CommonTest.php b/lib/private/Files/Storage/CommonTest.php index 3800bba2b52..b92e493bc2c 100644 --- a/lib/private/Files/Storage/CommonTest.php +++ b/lib/private/Files/Storage/CommonTest.php @@ -1,30 +1,9 @@ <?php + /** - * @copyright Copyright (c) 2016, ownCloud, Inc. - * - * @author Bart Visscher <bartv@thisnet.nl> - * @author Christopher Schäpers <kondou@ts.unde.re> - * @author Christoph Wurst <christoph@winzerhof-wurst.at> - * @author Felix Moeller <mail@felixmoeller.de> - * @author Michael Gapczynski <GapczynskiM@gmail.com> - * @author Morris Jobke <hey@morrisjobke.de> - * @author Robin Appelman <robin@icewind.nl> - * @author Thomas Müller <thomas.mueller@tmit.eu> - * - * @license AGPL-3.0 - * - * This code is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License, version 3, - * as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License, version 3, - * along with this program. If not, see <http://www.gnu.org/licenses/> - * + * SPDX-FileCopyrightText: 2016-2024 Nextcloud GmbH and Nextcloud contributors + * SPDX-FileCopyrightText: 2016 ownCloud, Inc. + * SPDX-License-Identifier: AGPL-3.0-only */ namespace OC\Files\Storage; diff --git a/lib/private/Files/Storage/DAV.php b/lib/private/Files/Storage/DAV.php index 70f22a17034..50e0dd5271a 100644 --- a/lib/private/Files/Storage/DAV.php +++ b/lib/private/Files/Storage/DAV.php @@ -1,39 +1,9 @@ <?php + /** - * @copyright Copyright (c) 2016, ownCloud, Inc. - * - * @author Arthur Schiwon <blizzz@arthur-schiwon.de> - * @author Bart Visscher <bartv@thisnet.nl> - * @author Björn Schießle <bjoern@schiessle.org> - * @author Carlos Cerrillo <ccerrillo@gmail.com> - * @author Christoph Wurst <christoph@winzerhof-wurst.at> - * @author Daniel Kesselberg <mail@danielkesselberg.de> - * @author Joas Schilling <coding@schilljs.com> - * @author Jörn Friedrich Dreyer <jfd@butonic.de> - * @author Lukas Reschke <lukas@statuscode.ch> - * @author Michael Gapczynski <GapczynskiM@gmail.com> - * @author Morris Jobke <hey@morrisjobke.de> - * @author Philipp Kapfer <philipp.kapfer@gmx.at> - * @author Robin Appelman <robin@icewind.nl> - * @author Roeland Jago Douma <roeland@famdouma.nl> - * @author Thomas Müller <thomas.mueller@tmit.eu> - * @author Tigran Mkrtchyan <tigran.mkrtchyan@desy.de> - * @author Vincent Petry <vincent@nextcloud.com> - * - * @license AGPL-3.0 - * - * This code is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License, version 3, - * as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License, version 3, - * along with this program. If not, see <http://www.gnu.org/licenses/> - * + * SPDX-FileCopyrightText: 2016-2024 Nextcloud GmbH and Nextcloud contributors + * SPDX-FileCopyrightText: 2016 ownCloud, Inc. + * SPDX-License-Identifier: AGPL-3.0-only */ namespace OC\Files\Storage; @@ -55,11 +25,11 @@ use OCP\ICertificateManager; use OCP\IConfig; use OCP\Util; use Psr\Http\Message\ResponseInterface; +use Psr\Log\LoggerInterface; use Sabre\DAV\Client; use Sabre\DAV\Xml\Property\ResourceType; use Sabre\HTTP\ClientException; use Sabre\HTTP\ClientHttpException; -use Psr\Log\LoggerInterface; use Sabre\HTTP\RequestInterface; /** @@ -120,9 +90,9 @@ class DAV extends Common { if (isset($params['host']) && isset($params['user']) && isset($params['password'])) { $host = $params['host']; //remove leading http[s], will be generated in createBaseUri() - if (substr($host, 0, 8) == "https://") { + if (str_starts_with($host, "https://")) { $host = substr($host, 8); - } elseif (substr($host, 0, 7) == "http://") { + } elseif (str_starts_with($host, "http://")) { $host = substr($host, 7); } $this->host = $host; @@ -470,9 +440,6 @@ class DAV extends Common { $this->client->proppatch($this->encodePath($path), ['{DAV:}lastmodified' => $mtime]); // non-owncloud clients might not have accepted the property, need to recheck it $response = $this->client->propfind($this->encodePath($path), ['{DAV:}getlastmodified'], 0); - if ($response === false) { - return false; - } if (isset($response['{DAV:}getlastmodified'])) { $remoteMtime = strtotime($response['{DAV:}getlastmodified']); if ($remoteMtime !== $mtime) { @@ -911,9 +878,6 @@ class DAV extends Common { self::PROPFIND_PROPS, 1 ); - if ($responses === false) { - return; - } array_shift($responses); //the first entry is the current directory if (!$this->statCache->hasKey($directory)) { @@ -921,7 +885,7 @@ class DAV extends Common { } foreach ($responses as $file => $response) { - $file = urldecode($file); + $file = rawurldecode($file); $file = substr($file, strlen($this->root)); $file = $this->cleanPath($file); $this->statCache->set($file, $response); diff --git a/lib/private/Files/Storage/FailedStorage.php b/lib/private/Files/Storage/FailedStorage.php index 07b3b21d965..87a6e7f5041 100644 --- a/lib/private/Files/Storage/FailedStorage.php +++ b/lib/private/Files/Storage/FailedStorage.php @@ -1,27 +1,9 @@ <?php + /** - * @copyright Copyright (c) 2016, ownCloud, Inc. - * - * @author Lukas Reschke <lukas@statuscode.ch> - * @author Robin Appelman <robin@icewind.nl> - * @author Robin McCorkell <robin@mccorkell.me.uk> - * @author Roeland Jago Douma <roeland@famdouma.nl> - * @author Vincent Petry <vincent@nextcloud.com> - * - * @license AGPL-3.0 - * - * This code is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License, version 3, - * as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License, version 3, - * along with this program. If not, see <http://www.gnu.org/licenses/> - * + * SPDX-FileCopyrightText: 2016-2024 Nextcloud GmbH and Nextcloud contributors + * SPDX-FileCopyrightText: 2016 ownCloud, Inc. + * SPDX-License-Identifier: AGPL-3.0-only */ namespace OC\Files\Storage; diff --git a/lib/private/Files/Storage/Home.php b/lib/private/Files/Storage/Home.php index 5427bc425c2..9a336d2efcc 100644 --- a/lib/private/Files/Storage/Home.php +++ b/lib/private/Files/Storage/Home.php @@ -1,31 +1,14 @@ <?php + /** - * @copyright Copyright (c) 2016, ownCloud, Inc. - * - * @author Björn Schießle <bjoern@schiessle.org> - * @author Morris Jobke <hey@morrisjobke.de> - * @author Robin Appelman <robin@icewind.nl> - * @author Thomas Müller <thomas.mueller@tmit.eu> - * @author Vincent Petry <vincent@nextcloud.com> - * - * @license AGPL-3.0 - * - * This code is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License, version 3, - * as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License, version 3, - * along with this program. If not, see <http://www.gnu.org/licenses/> - * + * SPDX-FileCopyrightText: 2016-2024 Nextcloud GmbH and Nextcloud contributors + * SPDX-FileCopyrightText: 2016 ownCloud, Inc. + * SPDX-License-Identifier: AGPL-3.0-only */ namespace OC\Files\Storage; use OC\Files\Cache\HomePropagator; +use OCP\IUser; /** * Specialized version of Local storage for home directory usage @@ -67,7 +50,7 @@ class Home extends Local implements \OCP\Files\IHomeStorage { $storage = $this; } if (!isset($this->cache)) { - $this->cache = new \OC\Files\Cache\HomeCache($storage); + $this->cache = new \OC\Files\Cache\HomeCache($storage, $this->getCacheDependencies()); } return $this->cache; } @@ -94,7 +77,7 @@ class Home extends Local implements \OCP\Files\IHomeStorage { * * @return \OC\User\User owner of this home storage */ - public function getUser() { + public function getUser(): IUser { return $this->user; } diff --git a/lib/private/Files/Storage/Local.php b/lib/private/Files/Storage/Local.php index 02708ed4f7d..ac3696118f7 100644 --- a/lib/private/Files/Storage/Local.php +++ b/lib/private/Files/Storage/Local.php @@ -1,45 +1,9 @@ <?php + /** - * @copyright Copyright (c) 2016, ownCloud, Inc. - * - * @author aler9 <46489434+aler9@users.noreply.github.com> - * @author Arthur Schiwon <blizzz@arthur-schiwon.de> - * @author Bart Visscher <bartv@thisnet.nl> - * @author Boris Rybalkin <ribalkin@gmail.com> - * @author Brice Maron <brice@bmaron.net> - * @author Christoph Wurst <christoph@winzerhof-wurst.at> - * @author J0WI <J0WI@users.noreply.github.com> - * @author Jakob Sack <mail@jakobsack.de> - * @author Joas Schilling <coding@schilljs.com> - * @author Johannes Leuker <j.leuker@hosting.de> - * @author Jörn Friedrich Dreyer <jfd@butonic.de> - * @author Klaas Freitag <freitag@owncloud.com> - * @author Lukas Reschke <lukas@statuscode.ch> - * @author Martin Brugnara <martin@0x6d62.eu> - * @author Michael Gapczynski <GapczynskiM@gmail.com> - * @author Morris Jobke <hey@morrisjobke.de> - * @author Robin Appelman <robin@icewind.nl> - * @author Roeland Jago Douma <roeland@famdouma.nl> - * @author Sjors van der Pluijm <sjors@desjors.nl> - * @author Stefan Weil <sw@weilnetz.de> - * @author Thomas Müller <thomas.mueller@tmit.eu> - * @author Tigran Mkrtchyan <tigran.mkrtchyan@desy.de> - * @author Vincent Petry <vincent@nextcloud.com> - * - * @license AGPL-3.0 - * - * This code is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License, version 3, - * as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License, version 3, - * along with this program. If not, see <http://www.gnu.org/licenses/> - * + * SPDX-FileCopyrightText: 2016-2024 Nextcloud GmbH and Nextcloud contributors + * SPDX-FileCopyrightText: 2016 ownCloud, Inc. + * SPDX-License-Identifier: AGPL-3.0-only */ namespace OC\Files\Storage; @@ -51,6 +15,7 @@ use OCP\Files\ForbiddenException; use OCP\Files\GenericFileException; use OCP\Files\IMimeTypeDetector; use OCP\Files\Storage\IStorage; +use OCP\Files\StorageNotAvailableException; use OCP\IConfig; use OCP\Util; use Psr\Log\LoggerInterface; @@ -73,6 +38,8 @@ class Local extends \OC\Files\Storage\Common { protected bool $unlinkOnTruncate; + protected bool $caseInsensitive = false; + public function __construct($arguments) { if (!isset($arguments['datadir']) || !is_string($arguments['datadir'])) { throw new \InvalidArgumentException('No data directory set for local storage'); @@ -85,16 +52,23 @@ class Local extends \OC\Files\Storage\Common { $realPath = realpath($this->datadir) ?: $this->datadir; $this->realDataDir = rtrim($realPath, '/') . '/'; } - if (substr($this->datadir, -1) !== '/') { + if (!str_ends_with($this->datadir, '/')) { $this->datadir .= '/'; } $this->dataDirLength = strlen($this->realDataDir); $this->config = \OC::$server->get(IConfig::class); $this->mimeTypeDetector = \OC::$server->get(IMimeTypeDetector::class); $this->defUMask = $this->config->getSystemValue('localstorage.umask', 0022); + $this->caseInsensitive = $this->config->getSystemValueBool('localstorage.case_insensitive', false); // support Write-Once-Read-Many file systems $this->unlinkOnTruncate = $this->config->getSystemValueBool('localstorage.unlink_on_truncate', false); + + if (isset($arguments['isExternal']) && $arguments['isExternal'] && !$this->stat('')) { + // data dir not accessible or available, can happen when using an external storage of type Local + // on an unmounted system mount point + throw new StorageNotAvailableException('Local storage path does not exist "' . $this->getSourcePath('') . '"'); + } } public function __destruct() { @@ -155,13 +129,19 @@ class Local extends \OC\Files\Storage\Common { } public function is_dir($path) { - if (substr($path, -1) == '/') { + if ($this->caseInsensitive && !$this->file_exists($path)) { + return false; + } + if (str_ends_with($path, '/')) { $path = substr($path, 0, -1); } return is_dir($this->getSourcePath($path)); } public function is_file($path) { + if ($this->caseInsensitive && !$this->file_exists($path)) { + return false; + } return is_file($this->getSourcePath($path)); } @@ -264,7 +244,17 @@ class Local extends \OC\Files\Storage\Common { } public function file_exists($path) { - return file_exists($this->getSourcePath($path)); + if ($this->caseInsensitive) { + $fullPath = $this->getSourcePath($path); + $parentPath = dirname($fullPath); + if (!is_dir($parentPath)) { + return false; + } + $content = scandir($parentPath, SCANDIR_SORT_NONE); + return is_array($content) && array_search(basename($fullPath), $content) !== false; + } else { + return file_exists($this->getSourcePath($path)); + } } public function filemtime($path) { @@ -365,6 +355,11 @@ class Local extends \OC\Files\Storage\Common { } if (@rename($this->getSourcePath($source), $this->getSourcePath($target))) { + if ($this->caseInsensitive) { + if (mb_strtolower($target) === mb_strtolower($source) && !$this->file_exists($target)) { + return false; + } + } return true; } @@ -381,6 +376,11 @@ class Local extends \OC\Files\Storage\Common { } $result = copy($this->getSourcePath($source), $this->getSourcePath($target)); umask($oldMask); + if ($this->caseInsensitive) { + if (mb_strtolower($target) === mb_strtolower($source) && !$this->file_exists($target)) { + return false; + } + } return $result; } } @@ -488,6 +488,7 @@ class Local extends \OC\Files\Storage\Common { $realPath = realpath($pathToResolve); while ($realPath === false) { // for non existing files check the parent directory $currentPath = dirname($currentPath); + /** @psalm-suppress TypeDoesNotContainType Let's be extra cautious and still check for empty string */ if ($currentPath === '' || $currentPath === '.') { return $fullPath; } @@ -548,11 +549,14 @@ class Local extends \OC\Files\Storage\Common { } private function canDoCrossStorageMove(IStorage $sourceStorage) { + /** @psalm-suppress UndefinedClass */ return $sourceStorage->instanceOfStorage(Local::class) // Don't treat ACLStorageWrapper like local storage where copy can be done directly. // Instead, use the slower recursive copying in php from Common::copyFromStorage with // more permissions checks. && !$sourceStorage->instanceOfStorage('OCA\GroupFolders\ACL\ACLStorageWrapper') + // Same for access control + && !$sourceStorage->instanceOfStorage(\OCA\FilesAccessControl\StorageWrapper::class) // when moving encrypted files we have to handle keys and the target might not be encrypted && !$sourceStorage->instanceOfStorage(Encryption::class); } @@ -606,7 +610,7 @@ class Local extends \OC\Files\Storage\Common { } } - public function writeStream(string $path, $stream, int $size = null): int { + public function writeStream(string $path, $stream, ?int $size = null): int { /** @var int|false $result We consider here that returned size will never be a float because we write less than 4GB */ $result = $this->file_put_contents($path, $stream); if (is_resource($stream)) { diff --git a/lib/private/Files/Storage/LocalRootStorage.php b/lib/private/Files/Storage/LocalRootStorage.php index 71584afef08..ae0575fe043 100644 --- a/lib/private/Files/Storage/LocalRootStorage.php +++ b/lib/private/Files/Storage/LocalRootStorage.php @@ -3,25 +3,8 @@ declare(strict_types=1); /** - * @copyright Copyright (c) 2020 Robin Appelman <robin@icewind.nl> - * - * @author Robin Appelman <robin@icewind.nl> - * - * @license GNU AGPL version 3 or any later version - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as - * published by the Free Software Foundation, either version 3 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - * + * SPDX-FileCopyrightText: 2020 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later */ namespace OC\Files\Storage; diff --git a/lib/private/Files/Storage/LocalTempFileTrait.php b/lib/private/Files/Storage/LocalTempFileTrait.php index 314e38cb277..26fbcc238fd 100644 --- a/lib/private/Files/Storage/LocalTempFileTrait.php +++ b/lib/private/Files/Storage/LocalTempFileTrait.php @@ -1,25 +1,9 @@ <?php + /** - * @copyright Copyright (c) 2016, ownCloud, Inc. - * - * @author Lukas Reschke <lukas@statuscode.ch> - * @author Morris Jobke <hey@morrisjobke.de> - * @author Thomas Müller <thomas.mueller@tmit.eu> - * - * @license AGPL-3.0 - * - * This code is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License, version 3, - * as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License, version 3, - * along with this program. If not, see <http://www.gnu.org/licenses/> - * + * SPDX-FileCopyrightText: 2016-2024 Nextcloud GmbH and Nextcloud contributors + * SPDX-FileCopyrightText: 2016 ownCloud, Inc. + * SPDX-License-Identifier: AGPL-3.0-only */ namespace OC\Files\Storage; diff --git a/lib/private/Files/Storage/PolyFill/CopyDirectory.php b/lib/private/Files/Storage/PolyFill/CopyDirectory.php index ff05eecb134..4b3e367da78 100644 --- a/lib/private/Files/Storage/PolyFill/CopyDirectory.php +++ b/lib/private/Files/Storage/PolyFill/CopyDirectory.php @@ -1,25 +1,9 @@ <?php + /** - * @copyright Copyright (c) 2016, ownCloud, Inc. - * - * @author Martin Mattel <martin.mattel@diemattels.at> - * @author Robin Appelman <robin@icewind.nl> - * @author Stefan Weil <sw@weilnetz.de> - * - * @license AGPL-3.0 - * - * This code is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License, version 3, - * as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License, version 3, - * along with this program. If not, see <http://www.gnu.org/licenses/> - * + * SPDX-FileCopyrightText: 2016-2024 Nextcloud GmbH and Nextcloud contributors + * SPDX-FileCopyrightText: 2016 ownCloud, Inc. + * SPDX-License-Identifier: AGPL-3.0-only */ namespace OC\Files\Storage\PolyFill; diff --git a/lib/private/Files/Storage/Storage.php b/lib/private/Files/Storage/Storage.php index 0a2511de164..44b1a52cfc0 100644 --- a/lib/private/Files/Storage/Storage.php +++ b/lib/private/Files/Storage/Storage.php @@ -1,26 +1,9 @@ <?php + /** - * @copyright Copyright (c) 2016, ownCloud, Inc. - * - * @author Christoph Wurst <christoph@winzerhof-wurst.at> - * @author Morris Jobke <hey@morrisjobke.de> - * @author Robin Appelman <robin@icewind.nl> - * @author Thomas Müller <thomas.mueller@tmit.eu> - * - * @license AGPL-3.0 - * - * This code is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License, version 3, - * as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License, version 3, - * along with this program. If not, see <http://www.gnu.org/licenses/> - * + * SPDX-FileCopyrightText: 2016-2024 Nextcloud GmbH and Nextcloud contributors + * SPDX-FileCopyrightText: 2016 ownCloud, Inc. + * SPDX-License-Identifier: AGPL-3.0-only */ namespace OC\Files\Storage; diff --git a/lib/private/Files/Storage/StorageFactory.php b/lib/private/Files/Storage/StorageFactory.php index cab739c4a81..18fca1d28e3 100644 --- a/lib/private/Files/Storage/StorageFactory.php +++ b/lib/private/Files/Storage/StorageFactory.php @@ -1,26 +1,9 @@ <?php + /** - * @copyright Copyright (c) 2016, ownCloud, Inc. - * - * @author Jörn Friedrich Dreyer <jfd@butonic.de> - * @author Morris Jobke <hey@morrisjobke.de> - * @author Robin Appelman <robin@icewind.nl> - * @author Vincent Petry <vincent@nextcloud.com> - * - * @license AGPL-3.0 - * - * This code is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License, version 3, - * as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License, version 3, - * along with this program. If not, see <http://www.gnu.org/licenses/> - * + * SPDX-FileCopyrightText: 2016-2024 Nextcloud GmbH and Nextcloud contributors + * SPDX-FileCopyrightText: 2016 ownCloud, Inc. + * SPDX-License-Identifier: AGPL-3.0-only */ namespace OC\Files\Storage; diff --git a/lib/private/Files/Storage/Temporary.php b/lib/private/Files/Storage/Temporary.php index 393a37f834a..95ae84cb7d9 100644 --- a/lib/private/Files/Storage/Temporary.php +++ b/lib/private/Files/Storage/Temporary.php @@ -1,27 +1,9 @@ <?php + /** - * @copyright Copyright (c) 2016, ownCloud, Inc. - * - * @author Christoph Wurst <christoph@winzerhof-wurst.at> - * @author Morris Jobke <hey@morrisjobke.de> - * @author Robin Appelman <robin@icewind.nl> - * @author Thomas Müller <thomas.mueller@tmit.eu> - * @author Vincent Petry <vincent@nextcloud.com> - * - * @license AGPL-3.0 - * - * This code is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License, version 3, - * as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License, version 3, - * along with this program. If not, see <http://www.gnu.org/licenses/> - * + * SPDX-FileCopyrightText: 2016-2024 Nextcloud GmbH and Nextcloud contributors + * SPDX-FileCopyrightText: 2016 ownCloud, Inc. + * SPDX-License-Identifier: AGPL-3.0-only */ namespace OC\Files\Storage; diff --git a/lib/private/Files/Storage/Wrapper/Availability.php b/lib/private/Files/Storage/Wrapper/Availability.php index 693d943f0dc..149cbbaa33b 100644 --- a/lib/private/Files/Storage/Wrapper/Availability.php +++ b/lib/private/Files/Storage/Wrapper/Availability.php @@ -1,28 +1,9 @@ <?php + /** - * @copyright Copyright (c) 2016, ownCloud, Inc. - * - * @author Arthur Schiwon <blizzz@arthur-schiwon.de> - * @author Christoph Wurst <christoph@winzerhof-wurst.at> - * @author Lukas Reschke <lukas@statuscode.ch> - * @author Robin Appelman <robin@icewind.nl> - * @author Robin McCorkell <robin@mccorkell.me.uk> - * @author Roeland Jago Douma <roeland@famdouma.nl> - * - * @license AGPL-3.0 - * - * This code is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License, version 3, - * as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License, version 3, - * along with this program. If not, see <http://www.gnu.org/licenses/> - * + * SPDX-FileCopyrightText: 2016-2024 Nextcloud GmbH and Nextcloud contributors + * SPDX-FileCopyrightText: 2016 ownCloud, Inc. + * SPDX-License-Identifier: AGPL-3.0-only */ namespace OC\Files\Storage\Wrapper; diff --git a/lib/private/Files/Storage/Wrapper/Encoding.php b/lib/private/Files/Storage/Wrapper/Encoding.php index 6633cbf41e3..11f21eb828e 100644 --- a/lib/private/Files/Storage/Wrapper/Encoding.php +++ b/lib/private/Files/Storage/Wrapper/Encoding.php @@ -1,35 +1,14 @@ <?php + /** - * @copyright Copyright (c) 2016, ownCloud, Inc. - * - * @author Christoph Wurst <christoph@winzerhof-wurst.at> - * @author J0WI <J0WI@users.noreply.github.com> - * @author Lukas Reschke <lukas@statuscode.ch> - * @author Morris Jobke <hey@morrisjobke.de> - * @author Robin Appelman <robin@icewind.nl> - * @author Roeland Jago Douma <roeland@famdouma.nl> - * @author Tigran Mkrtchyan <tigran.mkrtchyan@desy.de> - * @author Vincent Petry <vincent@nextcloud.com> - * - * @license AGPL-3.0 - * - * This code is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License, version 3, - * as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License, version 3, - * along with this program. If not, see <http://www.gnu.org/licenses/> - * + * SPDX-FileCopyrightText: 2016-2024 Nextcloud GmbH and Nextcloud contributors + * SPDX-FileCopyrightText: 2016 ownCloud, Inc. + * SPDX-License-Identifier: AGPL-3.0-only */ namespace OC\Files\Storage\Wrapper; -use OCP\Cache\CappedMemoryCache; use OC\Files\Filesystem; +use OCP\Cache\CappedMemoryCache; use OCP\Files\Storage\IStorage; use OCP\ICache; diff --git a/lib/private/Files/Storage/Wrapper/EncodingDirectoryWrapper.php b/lib/private/Files/Storage/Wrapper/EncodingDirectoryWrapper.php index 3cda399f22d..26b32d9aa62 100644 --- a/lib/private/Files/Storage/Wrapper/EncodingDirectoryWrapper.php +++ b/lib/private/Files/Storage/Wrapper/EncodingDirectoryWrapper.php @@ -1,26 +1,9 @@ <?php + /** - * @copyright Copyright (c) 2021, Nextcloud GmbH. - * - * @author Robin Appelman <robin@icewind.nl> - * @author Vincent Petry <vincent@nextcloud.com> - * - * @license AGPL-3.0 - * - * This code is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License, version 3, - * as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License, version 3, - * along with this program. If not, see <http://www.gnu.org/licenses/> - * + * SPDX-FileCopyrightText: 2021 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-only */ - namespace OC\Files\Storage\Wrapper; use Icewind\Streams\DirectoryWrapper; diff --git a/lib/private/Files/Storage/Wrapper/Encryption.php b/lib/private/Files/Storage/Wrapper/Encryption.php index a27f499a210..5c7862e8226 100644 --- a/lib/private/Files/Storage/Wrapper/Encryption.php +++ b/lib/private/Files/Storage/Wrapper/Encryption.php @@ -1,40 +1,10 @@ <?php + /** - * @copyright Copyright (c) 2016, ownCloud, Inc. - * - * @author Arthur Schiwon <blizzz@arthur-schiwon.de> - * @author Bjoern Schiessle <bjoern@schiessle.org> - * @author Björn Schießle <bjoern@schiessle.org> - * @author Christoph Wurst <christoph@winzerhof-wurst.at> - * @author J0WI <J0WI@users.noreply.github.com> - * @author jknockaert <jasper@knockaert.nl> - * @author Joas Schilling <coding@schilljs.com> - * @author Lukas Reschke <lukas@statuscode.ch> - * @author Morris Jobke <hey@morrisjobke.de> - * @author Piotr M <mrow4a@yahoo.com> - * @author Robin Appelman <robin@icewind.nl> - * @author Roeland Jago Douma <roeland@famdouma.nl> - * @author Thomas Müller <thomas.mueller@tmit.eu> - * @author Tigran Mkrtchyan <tigran.mkrtchyan@desy.de> - * @author Vincent Petry <vincent@nextcloud.com> - * @author Richard Steinmetz <richard@steinmetz.cloud> - * - * @license AGPL-3.0 - * - * This code is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License, version 3, - * as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License, version 3, - * along with this program. If not, see <http://www.gnu.org/licenses/> - * + * SPDX-FileCopyrightText: 2016-2024 Nextcloud GmbH and Nextcloud contributors + * SPDX-FileCopyrightText: 2016 ownCloud, Inc. + * SPDX-License-Identifier: AGPL-3.0-only */ - namespace OC\Files\Storage\Wrapper; use OC\Encryption\Exceptions\ModuleDoesNotExistsException; @@ -100,20 +70,22 @@ class Encryption extends Wrapper { /** @var CappedMemoryCache<bool> */ private CappedMemoryCache $encryptedPaths; + private $enabled = true; + /** * @param array $parameters */ public function __construct( $parameters, - IManager $encryptionManager = null, - Util $util = null, - LoggerInterface $logger = null, - IFile $fileHelper = null, + ?IManager $encryptionManager = null, + ?Util $util = null, + ?LoggerInterface $logger = null, + ?IFile $fileHelper = null, $uid = null, - IStorage $keyStorage = null, - Update $update = null, - Manager $mountManager = null, - ArrayCache $arrayCache = null + ?IStorage $keyStorage = null, + ?Update $update = null, + ?Manager $mountManager = null, + ?ArrayCache $arrayCache = null ) { $this->mountPoint = $parameters['mountPoint']; $this->mount = $parameters['mount']; @@ -392,6 +364,10 @@ class Encryption extends Wrapper { return $this->storage->fopen($path, $mode); } + if (!$this->enabled) { + return $this->storage->fopen($path, $mode); + } + $encryptionEnabled = $this->encryptionManager->isEnabled(); $shouldEncrypt = false; $encryptionModule = null; @@ -938,34 +914,6 @@ class Encryption extends Wrapper { } /** - * parse raw header to array - * - * @param string $rawHeader - * @return array - */ - protected function parseRawHeader($rawHeader) { - $result = []; - if (str_starts_with($rawHeader, Util::HEADER_START)) { - $header = $rawHeader; - $endAt = strpos($header, Util::HEADER_END); - if ($endAt !== false) { - $header = substr($header, 0, $endAt + strlen(Util::HEADER_END)); - - // +1 to not start with an ':' which would result in empty element at the beginning - $exploded = explode(':', substr($header, strlen(Util::HEADER_START) + 1)); - - $element = array_shift($exploded); - while ($element !== Util::HEADER_END) { - $result[$element] = array_shift($exploded); - $element = array_shift($exploded); - } - } - } - - return $result; - } - - /** * read header from file * * @param string $path @@ -988,7 +936,7 @@ class Encryption extends Wrapper { if ($isEncrypted) { $firstBlock = $this->readFirstBlock($path); - $result = $this->parseRawHeader($firstBlock); + $result = $this->util->parseRawHeader($firstBlock); // if the header doesn't contain a encryption module we check if it is a // legacy file. If true, we add the default encryption module @@ -1084,7 +1032,7 @@ class Encryption extends Wrapper { return $encryptionModule->shouldEncrypt($fullPath); } - public function writeStream(string $path, $stream, int $size = null): int { + public function writeStream(string $path, $stream, ?int $size = null): int { // always fall back to fopen $target = $this->fopen($path, 'w'); [$count, $result] = \OC_Helper::streamCopy($stream, $target); @@ -1093,7 +1041,7 @@ class Encryption extends Wrapper { // object store, stores the size after write and doesn't update this during scan // manually store the unencrypted size - if ($result && $this->getWrapperStorage()->instanceOfStorage(ObjectStoreStorage::class)) { + if ($result && $this->getWrapperStorage()->instanceOfStorage(ObjectStoreStorage::class) && $this->shouldEncrypt($path)) { $this->getCache()->put($path, ['unencrypted_size' => $count]); } @@ -1103,4 +1051,14 @@ class Encryption extends Wrapper { public function clearIsEncryptedCache(): void { $this->encryptedPaths->clear(); } + + /** + * Allow temporarily disabling the wrapper + * + * @param bool $enabled + * @return void + */ + public function setEnabled(bool $enabled): void { + $this->enabled = $enabled; + } } diff --git a/lib/private/Files/Storage/Wrapper/Jail.php b/lib/private/Files/Storage/Wrapper/Jail.php index 1921ac27848..2e8306dc705 100644 --- a/lib/private/Files/Storage/Wrapper/Jail.php +++ b/lib/private/Files/Storage/Wrapper/Jail.php @@ -1,35 +1,15 @@ <?php + /** - * @copyright Copyright (c) 2016, ownCloud, Inc. - * - * @author Christoph Wurst <christoph@winzerhof-wurst.at> - * @author J0WI <J0WI@users.noreply.github.com> - * @author Julius Härtl <jus@bitgrid.net> - * @author Lukas Reschke <lukas@statuscode.ch> - * @author Morris Jobke <hey@morrisjobke.de> - * @author Robin Appelman <robin@icewind.nl> - * @author Roeland Jago Douma <roeland@famdouma.nl> - * @author Tigran Mkrtchyan <tigran.mkrtchyan@desy.de> - * - * @license AGPL-3.0 - * - * This code is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License, version 3, - * as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License, version 3, - * along with this program. If not, see <http://www.gnu.org/licenses/> - * + * SPDX-FileCopyrightText: 2016-2024 Nextcloud GmbH and Nextcloud contributors + * SPDX-FileCopyrightText: 2016 ownCloud, Inc. + * SPDX-License-Identifier: AGPL-3.0-only */ namespace OC\Files\Storage\Wrapper; use OC\Files\Cache\Wrapper\CacheJail; use OC\Files\Cache\Wrapper\JailPropagator; +use OC\Files\Cache\Wrapper\JailWatcher; use OC\Files\Filesystem; use OCP\Files\Storage\IStorage; use OCP\Files\Storage\IWriteStreamStorage; @@ -396,10 +376,7 @@ class Jail extends Wrapper { * @return \OC\Files\Cache\Cache */ public function getCache($path = '', $storage = null) { - if (!$storage) { - $storage = $this->getWrapperStorage(); - } - $sourceCache = $this->getWrapperStorage()->getCache($this->getUnjailedPath($path), $storage); + $sourceCache = $this->getWrapperStorage()->getCache($this->getUnjailedPath($path)); return new CacheJail($sourceCache, $this->rootPath); } @@ -421,10 +398,8 @@ class Jail extends Wrapper { * @return \OC\Files\Cache\Watcher */ public function getWatcher($path = '', $storage = null) { - if (!$storage) { - $storage = $this; - } - return $this->getWrapperStorage()->getWatcher($this->getUnjailedPath($path), $storage); + $sourceWatcher = $this->getWrapperStorage()->getWatcher($this->getUnjailedPath($path), $this->getWrapperStorage()); + return new JailWatcher($sourceWatcher, $this->rootPath); } /** @@ -517,7 +492,7 @@ class Jail extends Wrapper { return $this->propagator; } - public function writeStream(string $path, $stream, int $size = null): int { + public function writeStream(string $path, $stream, ?int $size = null): int { $storage = $this->getWrapperStorage(); if ($storage->instanceOfStorage(IWriteStreamStorage::class)) { /** @var IWriteStreamStorage $storage */ diff --git a/lib/private/Files/Storage/Wrapper/KnownMtime.php b/lib/private/Files/Storage/Wrapper/KnownMtime.php new file mode 100644 index 00000000000..a7360ae1346 --- /dev/null +++ b/lib/private/Files/Storage/Wrapper/KnownMtime.php @@ -0,0 +1,146 @@ +<?php + +/** + * SPDX-FileCopyrightText: 2023 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later + */ +namespace OC\Files\Storage\Wrapper; + +use OCP\Cache\CappedMemoryCache; +use OCP\Files\Storage\IStorage; +use Psr\Clock\ClockInterface; + +/** + * Wrapper that overwrites the mtime return by stat/getMetaData if the returned value + * is lower than when we last modified the file. + * + * This is useful because some storage servers can return an outdated mtime right after writes + */ +class KnownMtime extends Wrapper { + private CappedMemoryCache $knowMtimes; + private ClockInterface $clock; + + public function __construct($arguments) { + parent::__construct($arguments); + $this->knowMtimes = new CappedMemoryCache(); + $this->clock = $arguments['clock']; + } + + public function file_put_contents($path, $data) { + $result = parent::file_put_contents($path, $data); + if ($result) { + $now = $this->clock->now()->getTimestamp(); + $this->knowMtimes->set($path, $this->clock->now()->getTimestamp()); + } + return $result; + } + + public function stat($path) { + $stat = parent::stat($path); + if ($stat) { + $this->applyKnownMtime($path, $stat); + } + return $stat; + } + + public function getMetaData($path) { + $stat = parent::getMetaData($path); + if ($stat) { + $this->applyKnownMtime($path, $stat); + } + return $stat; + } + + private function applyKnownMtime(string $path, array &$stat) { + if (isset($stat['mtime'])) { + $knownMtime = $this->knowMtimes->get($path) ?? 0; + $stat['mtime'] = max($stat['mtime'], $knownMtime); + } + } + + public function filemtime($path) { + $knownMtime = $this->knowMtimes->get($path) ?? 0; + return max(parent::filemtime($path), $knownMtime); + } + + public function mkdir($path) { + $result = parent::mkdir($path); + if ($result) { + $this->knowMtimes->set($path, $this->clock->now()->getTimestamp()); + } + return $result; + } + + public function rmdir($path) { + $result = parent::rmdir($path); + if ($result) { + $this->knowMtimes->set($path, $this->clock->now()->getTimestamp()); + } + return $result; + } + + public function unlink($path) { + $result = parent::unlink($path); + if ($result) { + $this->knowMtimes->set($path, $this->clock->now()->getTimestamp()); + } + return $result; + } + + public function rename($source, $target) { + $result = parent::rename($source, $target); + if ($result) { + $this->knowMtimes->set($target, $this->clock->now()->getTimestamp()); + $this->knowMtimes->set($source, $this->clock->now()->getTimestamp()); + } + return $result; + } + + public function copy($source, $target) { + $result = parent::copy($source, $target); + if ($result) { + $this->knowMtimes->set($target, $this->clock->now()->getTimestamp()); + } + return $result; + } + + public function fopen($path, $mode) { + $result = parent::fopen($path, $mode); + if ($result && $mode === 'w') { + $this->knowMtimes->set($path, $this->clock->now()->getTimestamp()); + } + return $result; + } + + public function touch($path, $mtime = null) { + $result = parent::touch($path, $mtime); + if ($result) { + $this->knowMtimes->set($path, $mtime ?? $this->clock->now()->getTimestamp()); + } + return $result; + } + + public function copyFromStorage(IStorage $sourceStorage, $sourceInternalPath, $targetInternalPath) { + $result = parent::copyFromStorage($sourceStorage, $sourceInternalPath, $targetInternalPath); + if ($result) { + $this->knowMtimes->set($targetInternalPath, $this->clock->now()->getTimestamp()); + } + return $result; + } + + public function moveFromStorage(IStorage $sourceStorage, $sourceInternalPath, $targetInternalPath) { + $result = parent::moveFromStorage($sourceStorage, $sourceInternalPath, $targetInternalPath); + if ($result) { + $this->knowMtimes->set($targetInternalPath, $this->clock->now()->getTimestamp()); + } + return $result; + } + + public function writeStream(string $path, $stream, ?int $size = null): int { + $result = parent::writeStream($path, $stream, $size); + if ($result) { + $this->knowMtimes->set($path, $this->clock->now()->getTimestamp()); + } + return $result; + } +} diff --git a/lib/private/Files/Storage/Wrapper/PermissionsMask.php b/lib/private/Files/Storage/Wrapper/PermissionsMask.php index 0d140e0a39d..c502de41a86 100644 --- a/lib/private/Files/Storage/Wrapper/PermissionsMask.php +++ b/lib/private/Files/Storage/Wrapper/PermissionsMask.php @@ -1,29 +1,9 @@ <?php + /** - * @copyright Copyright (c) 2016, ownCloud, Inc. - * - * @author Joas Schilling <coding@schilljs.com> - * @author Jörn Friedrich Dreyer <jfd@butonic.de> - * @author Morris Jobke <hey@morrisjobke.de> - * @author Robin Appelman <robin@icewind.nl> - * @author Robin McCorkell <robin@mccorkell.me.uk> - * @author Roeland Jago Douma <roeland@famdouma.nl> - * @author Stefan Weil <sw@weilnetz.de> - * - * @license AGPL-3.0 - * - * This code is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License, version 3, - * as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License, version 3, - * along with this program. If not, see <http://www.gnu.org/licenses/> - * + * SPDX-FileCopyrightText: 2016-2024 Nextcloud GmbH and Nextcloud contributors + * SPDX-FileCopyrightText: 2016 ownCloud, Inc. + * SPDX-License-Identifier: AGPL-3.0-only */ namespace OC\Files\Storage\Wrapper; @@ -140,7 +120,7 @@ class PermissionsMask extends Wrapper { $data = parent::getMetaData($path); if ($data && isset($data['permissions'])) { - $data['scan_permissions'] = isset($data['scan_permissions']) ? $data['scan_permissions'] : $data['permissions']; + $data['scan_permissions'] = $data['scan_permissions'] ?? $data['permissions']; $data['permissions'] &= $this->mask; } return $data; @@ -155,7 +135,7 @@ class PermissionsMask extends Wrapper { public function getDirectoryContent($directory): \Traversable { foreach ($this->getWrapperStorage()->getDirectoryContent($directory) as $data) { - $data['scan_permissions'] = isset($data['scan_permissions']) ? $data['scan_permissions'] : $data['permissions']; + $data['scan_permissions'] = $data['scan_permissions'] ?? $data['permissions']; $data['permissions'] &= $this->mask; yield $data; diff --git a/lib/private/Files/Storage/Wrapper/Quota.php b/lib/private/Files/Storage/Wrapper/Quota.php index 35dbc9fcd26..8c6799fdd2d 100644 --- a/lib/private/Files/Storage/Wrapper/Quota.php +++ b/lib/private/Files/Storage/Wrapper/Quota.php @@ -1,34 +1,9 @@ <?php + /** - * @copyright Copyright (c) 2016, ownCloud, Inc. - * - * @author Christoph Wurst <christoph@winzerhof-wurst.at> - * @author J0WI <J0WI@users.noreply.github.com> - * @author John Molakvoæ <skjnldsv@protonmail.com> - * @author Jörn Friedrich Dreyer <jfd@butonic.de> - * @author Julius Härtl <jus@bitgrid.net> - * @author Lukas Reschke <lukas@statuscode.ch> - * @author Morris Jobke <hey@morrisjobke.de> - * @author Robin Appelman <robin@icewind.nl> - * @author Robin McCorkell <robin@mccorkell.me.uk> - * @author Roeland Jago Douma <roeland@famdouma.nl> - * @author Tigran Mkrtchyan <tigran.mkrtchyan@desy.de> - * @author Vincent Petry <vincent@nextcloud.com> - * - * @license AGPL-3.0 - * - * This code is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License, version 3, - * as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License, version 3, - * along with this program. If not, see <http://www.gnu.org/licenses/> - * + * SPDX-FileCopyrightText: 2016-2024 Nextcloud GmbH and Nextcloud contributors + * SPDX-FileCopyrightText: 2016 ownCloud, Inc. + * SPDX-License-Identifier: AGPL-3.0-only */ namespace OC\Files\Storage\Wrapper; diff --git a/lib/private/Files/Storage/Wrapper/Wrapper.php b/lib/private/Files/Storage/Wrapper/Wrapper.php index 9f5564b4490..29acc9ad1c2 100644 --- a/lib/private/Files/Storage/Wrapper/Wrapper.php +++ b/lib/private/Files/Storage/Wrapper/Wrapper.php @@ -1,41 +1,20 @@ <?php + /** - * @copyright Copyright (c) 2016, ownCloud, Inc. - * - * @author Christoph Wurst <christoph@winzerhof-wurst.at> - * @author J0WI <J0WI@users.noreply.github.com> - * @author Joas Schilling <coding@schilljs.com> - * @author Lukas Reschke <lukas@statuscode.ch> - * @author Morris Jobke <hey@morrisjobke.de> - * @author Robin Appelman <robin@icewind.nl> - * @author Robin McCorkell <robin@mccorkell.me.uk> - * @author Thomas Müller <thomas.mueller@tmit.eu> - * @author Tigran Mkrtchyan <tigran.mkrtchyan@desy.de> - * @author Vincent Petry <vincent@nextcloud.com> - * @author Vinicius Cubas Brand <vinicius@eita.org.br> - * - * @license AGPL-3.0 - * - * This code is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License, version 3, - * as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License, version 3, - * along with this program. If not, see <http://www.gnu.org/licenses/> - * + * SPDX-FileCopyrightText: 2016-2024 Nextcloud GmbH and Nextcloud contributors + * SPDX-FileCopyrightText: 2016 ownCloud, Inc. + * SPDX-License-Identifier: AGPL-3.0-only */ namespace OC\Files\Storage\Wrapper; +use OC\Files\Storage\FailedStorage; use OCP\Files\InvalidPathException; use OCP\Files\Storage\ILockingStorage; use OCP\Files\Storage\IStorage; use OCP\Files\Storage\IWriteStreamStorage; use OCP\Lock\ILockingProvider; +use OCP\Server; +use Psr\Log\LoggerInterface; class Wrapper implements \OC\Files\Storage\Storage, ILockingStorage, IWriteStreamStorage { /** @@ -60,6 +39,12 @@ class Wrapper implements \OC\Files\Storage\Storage, ILockingStorage, IWriteStrea * @return \OC\Files\Storage\Storage */ public function getWrapperStorage() { + if (!$this->storage) { + $message = "storage wrapper " . get_class($this) . " doesn't have a wrapped storage set"; + $logger = Server::get(LoggerInterface::class); + $logger->error($message); + $this->storage = new FailedStorage(['exception' => new \Exception($message)]); + } return $this->storage; } @@ -637,7 +622,7 @@ class Wrapper implements \OC\Files\Storage\Storage, ILockingStorage, IWriteStrea return $this->getWrapperStorage()->needsPartFile(); } - public function writeStream(string $path, $stream, int $size = null): int { + public function writeStream(string $path, $stream, ?int $size = null): int { $storage = $this->getWrapperStorage(); if ($storage->instanceOfStorage(IWriteStreamStorage::class)) { /** @var IWriteStreamStorage $storage */ @@ -654,4 +639,19 @@ class Wrapper implements \OC\Files\Storage\Storage, ILockingStorage, IWriteStrea public function getDirectoryContent($directory): \Traversable { return $this->getWrapperStorage()->getDirectoryContent($directory); } + + public function isWrapperOf(IStorage $storage) { + $wrapped = $this->getWrapperStorage(); + if ($wrapped === $storage) { + return true; + } + if ($wrapped instanceof Wrapper) { + return $wrapped->isWrapperOf($storage); + } + return false; + } + + public function setOwner(?string $user): void { + $this->getWrapperStorage()->setOwner($user); + } } diff --git a/lib/private/Files/Stream/Encryption.php b/lib/private/Files/Stream/Encryption.php index bcf0a10740b..32c0021cd23 100644 --- a/lib/private/Files/Stream/Encryption.php +++ b/lib/private/Files/Stream/Encryption.php @@ -1,33 +1,9 @@ <?php + /** - * @copyright Copyright (c) 2016, ownCloud, Inc. - * - * @author Bjoern Schiessle <bjoern@schiessle.org> - * @author Björn Schießle <bjoern@schiessle.org> - * @author Christoph Wurst <christoph@winzerhof-wurst.at> - * @author jknockaert <jasper@knockaert.nl> - * @author Lukas Reschke <lukas@statuscode.ch> - * @author martink-p <47943787+martink-p@users.noreply.github.com> - * @author Morris Jobke <hey@morrisjobke.de> - * @author Robin Appelman <robin@icewind.nl> - * @author Roeland Jago Douma <roeland@famdouma.nl> - * @author Thomas Müller <thomas.mueller@tmit.eu> - * @author Vincent Petry <vincent@nextcloud.com> - * - * @license AGPL-3.0 - * - * This code is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License, version 3, - * as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License, version 3, - * along with this program. If not, see <http://www.gnu.org/licenses/> - * + * SPDX-FileCopyrightText: 2016-2024 Nextcloud GmbH and Nextcloud contributors + * SPDX-FileCopyrightText: 2016 ownCloud, Inc. + * SPDX-License-Identifier: AGPL-3.0-only */ namespace OC\Files\Stream; @@ -153,18 +129,18 @@ class Encryption extends Wrapper { * @throws \BadMethodCallException */ public static function wrap($source, $internalPath, $fullPath, array $header, - $uid, - \OCP\Encryption\IEncryptionModule $encryptionModule, - \OC\Files\Storage\Storage $storage, - \OC\Files\Storage\Wrapper\Encryption $encStorage, - \OC\Encryption\Util $util, - \OC\Encryption\File $file, - $mode, - $size, - $unencryptedSize, - $headerSize, - $signed, - $wrapper = Encryption::class) { + $uid, + \OCP\Encryption\IEncryptionModule $encryptionModule, + \OC\Files\Storage\Storage $storage, + \OC\Files\Storage\Wrapper\Encryption $encStorage, + \OC\Encryption\Util $util, + \OC\Encryption\File $file, + $mode, + $size, + $unencryptedSize, + $headerSize, + $signed, + $wrapper = Encryption::class) { $context = stream_context_create([ 'ocencryption' => [ 'source' => $source, @@ -321,7 +297,7 @@ class Encryption extends Wrapper { $result .= substr($this->cache, $blockPosition, $remainingLength); $this->position += $remainingLength; $count = 0; - // otherwise remainder of current block is fetched, the block is flushed and the position updated + // otherwise remainder of current block is fetched, the block is flushed and the position updated } else { $result .= substr($this->cache, $blockPosition); $this->flush(); @@ -389,8 +365,8 @@ class Encryption extends Wrapper { $this->position += $remainingLength; $length += $remainingLength; $data = ''; - // if $data doesn't fit the current block, the fill the current block and reiterate - // after the block is filled, it is flushed and $data is updatedxxx + // if $data doesn't fit the current block, the fill the current block and reiterate + // after the block is filled, it is flushed and $data is updatedxxx } else { $this->cache = substr($this->cache, 0, $blockPosition) . substr($data, 0, $this->unencryptedBlockSize - $blockPosition); diff --git a/lib/private/Files/Stream/HashWrapper.php b/lib/private/Files/Stream/HashWrapper.php index 4060d74de7d..5956ad92549 100644 --- a/lib/private/Files/Stream/HashWrapper.php +++ b/lib/private/Files/Stream/HashWrapper.php @@ -3,25 +3,8 @@ declare(strict_types=1); /** - * @copyright Copyright (c) 2020 Robin Appelman <robin@icewind.nl> - * - * @author Robin Appelman <robin@icewind.nl> - * - * @license GNU AGPL version 3 or any later version - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as - * published by the Free Software Foundation, either version 3 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - * + * SPDX-FileCopyrightText: 2020 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later */ namespace OC\Files\Stream; diff --git a/lib/private/Files/Stream/Quota.php b/lib/private/Files/Stream/Quota.php index 1549d95eba8..d8e7ae6dff0 100644 --- a/lib/private/Files/Stream/Quota.php +++ b/lib/private/Files/Stream/Quota.php @@ -1,27 +1,9 @@ <?php + /** - * @copyright Copyright (c) 2016, ownCloud, Inc. - * - * @author Christoph Wurst <christoph@winzerhof-wurst.at> - * @author Jörn Friedrich Dreyer <jfd@butonic.de> - * @author Morris Jobke <hey@morrisjobke.de> - * @author Robin Appelman <robin@icewind.nl> - * @author Vincent Petry <vincent@nextcloud.com> - * - * @license AGPL-3.0 - * - * This code is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License, version 3, - * as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License, version 3, - * along with this program. If not, see <http://www.gnu.org/licenses/> - * + * SPDX-FileCopyrightText: 2016-2024 Nextcloud GmbH and Nextcloud contributors + * SPDX-FileCopyrightText: 2016 ownCloud, Inc. + * SPDX-License-Identifier: AGPL-3.0-only */ namespace OC\Files\Stream; diff --git a/lib/private/Files/Stream/SeekableHttpStream.php b/lib/private/Files/Stream/SeekableHttpStream.php index 66f94768e62..02ed1470fbd 100644 --- a/lib/private/Files/Stream/SeekableHttpStream.php +++ b/lib/private/Files/Stream/SeekableHttpStream.php @@ -1,25 +1,7 @@ <?php /** - * @copyright Copyright (c) 2020, Lukas Stabe (lukas@stabe.de) - * - * @author Lukas Stabe <lukas@stabe.de> - * @author Robin Appelman <robin@icewind.nl> - * - * @license GNU AGPL version 3 or any later version - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as - * published by the Free Software Foundation, either version 3 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - * + * SPDX-FileCopyrightText: 2020 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later */ namespace OC\Files\Stream; diff --git a/lib/private/Files/Template/TemplateManager.php b/lib/private/Files/Template/TemplateManager.php index bf72e9e23e8..8362298b831 100644 --- a/lib/private/Files/Template/TemplateManager.php +++ b/lib/private/Files/Template/TemplateManager.php @@ -3,27 +3,8 @@ declare(strict_types=1); /** - * @copyright Copyright (c) 2021 Julius Härtl <jus@bitgrid.net> - * - * @author Christoph Wurst <christoph@winzerhof-wurst.at> - * @author John Molakvoæ <skjnldsv@protonmail.com> - * @author Julius Härtl <jus@bitgrid.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/>. - * + * SPDX-FileCopyrightText: 2021 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later */ namespace OC\Files\Template; @@ -31,8 +12,8 @@ use OC\AppFramework\Bootstrap\Coordinator; use OC\Files\Cache\Scanner; use OC\Files\Filesystem; use OCP\EventDispatcher\IEventDispatcher; -use OCP\Files\Folder; use OCP\Files\File; +use OCP\Files\Folder; use OCP\Files\GenericFileException; use OCP\Files\IRootFolder; use OCP\Files\Node; @@ -40,6 +21,7 @@ use OCP\Files\NotFoundException; use OCP\Files\Template\FileCreatedFromTemplateEvent; use OCP\Files\Template\ICustomTemplateProvider; use OCP\Files\Template\ITemplateManager; +use OCP\Files\Template\RegisterTemplateCreatorEvent; use OCP\Files\Template\Template; use OCP\Files\Template\TemplateFileCreator; use OCP\IConfig; @@ -119,6 +101,7 @@ class TemplateManager implements ITemplateManager { if (!empty($this->types)) { return $this->types; } + $this->eventDispatcher->dispatchTyped(new RegisterTemplateCreatorEvent($this)); foreach ($this->registeredTypes as $registeredType) { $this->types[] = $registeredType(); } @@ -240,7 +223,8 @@ class TemplateManager implements ITemplateManager { 'mime' => $file->getMimetype(), 'size' => $file->getSize(), 'type' => $file->getType(), - 'hasPreview' => $this->previewManager->isAvailable($file) + 'hasPreview' => $this->previewManager->isAvailable($file), + 'permissions' => $file->getPermissions(), ]; } @@ -261,7 +245,7 @@ class TemplateManager implements ITemplateManager { return $this->config->getUserValue($this->userId, 'core', 'templateDirectory', ''); } - public function initializeTemplateDirectory(string $path = null, string $userId = null, $copyTemplates = true): string { + public function initializeTemplateDirectory(?string $path = null, ?string $userId = null, $copyTemplates = true): string { if ($userId !== null) { $this->userId = $userId; } @@ -274,6 +258,11 @@ class TemplateManager implements ITemplateManager { $isDefaultTemplates = $skeletonTemplatePath === $defaultTemplateDirectory; $userLang = $this->l10nFactory->getUserLanguage($this->userManager->get($this->userId)); + if ($skeletonTemplatePath === '') { + $this->setTemplatePath(''); + return ''; + } + try { $l10n = $this->l10nFactory->get('lib', $userLang); $userFolder = $this->rootFolder->getUserFolder($this->userId); diff --git a/lib/private/Files/Type/Detection.php b/lib/private/Files/Type/Detection.php index 9a61aa93b95..b1e4c098e54 100644 --- a/lib/private/Files/Type/Detection.php +++ b/lib/private/Files/Type/Detection.php @@ -1,43 +1,10 @@ <?php declare(strict_types=1); - /** - * @copyright Copyright (c) 2016, ownCloud, Inc. - * - * @author Andreas Fischer <bantu@owncloud.com> - * @author bladewing <lukas@ifflaender-family.de> - * @author Christoph Wurst <christoph@winzerhof-wurst.at> - * @author Daniel Kesselberg <mail@danielkesselberg.de> - * @author Hendrik Leppelsack <hendrik@leppelsack.de> - * @author Jens-Christian Fischer <jens-christian.fischer@switch.ch> - * @author Joas Schilling <coding@schilljs.com> - * @author Julius Härtl <jus@bitgrid.net> - * @author lui87kw <lukas.ifflaender@uni-wuerzburg.de> - * @author Lukas Reschke <lukas@statuscode.ch> - * @author Magnus Walbeck <mw@mwalbeck.org> - * @author Morris Jobke <hey@morrisjobke.de> - * @author Robin Appelman <robin@icewind.nl> - * @author Robin McCorkell <robin@mccorkell.me.uk> - * @author Roeland Jago Douma <roeland@famdouma.nl> - * @author Thomas Tanghus <thomas@tanghus.net> - * @author Vincent Petry <vincent@nextcloud.com> - * @author Xheni Myrtaj <myrtajxheni@gmail.com> - * - * @license AGPL-3.0 - * - * This code is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License, version 3, - * as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License, version 3, - * along with this program. If not, see <http://www.gnu.org/licenses/> - * + * SPDX-FileCopyrightText: 2016-2024 Nextcloud GmbH and Nextcloud contributors + * SPDX-FileCopyrightText: 2016 ownCloud, Inc. + * SPDX-License-Identifier: AGPL-3.0-only */ namespace OC\Files\Type; @@ -56,32 +23,19 @@ class Detection implements IMimeTypeDetector { private const CUSTOM_MIMETYPEMAPPING = 'mimetypemapping.json'; private const CUSTOM_MIMETYPEALIASES = 'mimetypealiases.json'; - protected $mimetypes = []; - protected $secureMimeTypes = []; - - protected $mimetypeIcons = []; - /** @var string[] */ - protected $mimeTypeAlias = []; - - /** @var IURLGenerator */ - private $urlGenerator; - - private LoggerInterface $logger; - - /** @var string */ - private $customConfigDir; + protected array $mimetypes = []; + protected array $secureMimeTypes = []; - /** @var string */ - private $defaultConfigDir; + protected array $mimetypeIcons = []; + /** @var array<string,string> */ + protected array $mimeTypeAlias = []; - public function __construct(IURLGenerator $urlGenerator, - LoggerInterface $logger, - string $customConfigDir, - string $defaultConfigDir) { - $this->urlGenerator = $urlGenerator; - $this->logger = $logger; - $this->customConfigDir = $customConfigDir; - $this->defaultConfigDir = $defaultConfigDir; + public function __construct( + private IURLGenerator $urlGenerator, + private LoggerInterface $logger, + private string $customConfigDir, + private string $defaultConfigDir, + ) { } /** @@ -96,8 +50,8 @@ class Detection implements IMimeTypeDetector { * @param string|null $secureMimeType */ public function registerType(string $extension, - string $mimetype, - ?string $secureMimeType = null): void { + string $mimetype, + ?string $secureMimeType = null): void { $this->mimetypes[$extension] = [$mimetype, $secureMimeType]; $this->secureMimeTypes[$mimetype] = $secureMimeType ?: $mimetype; } @@ -151,7 +105,7 @@ class Detection implements IMimeTypeDetector { } /** - * @return string[] + * @return array<string,string> */ public function getAllAliases(): array { $this->loadAliases(); diff --git a/lib/private/Files/Type/Loader.php b/lib/private/Files/Type/Loader.php index 20c298f21b3..247acf0141a 100644 --- a/lib/private/Files/Type/Loader.php +++ b/lib/private/Files/Type/Loader.php @@ -1,26 +1,9 @@ <?php + /** - * @copyright Copyright (c) 2016, ownCloud, Inc. - * - * @author Joas Schilling <coding@schilljs.com> - * @author Rello <Rello@users.noreply.github.com> - * @author Robin Appelman <robin@icewind.nl> - * @author Robin McCorkell <robin@mccorkell.me.uk> - * - * @license AGPL-3.0 - * - * This code is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License, version 3, - * as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License, version 3, - * along with this program. If not, see <http://www.gnu.org/licenses/> - * + * SPDX-FileCopyrightText: 2016-2024 Nextcloud GmbH and Nextcloud contributors + * SPDX-FileCopyrightText: 2016 ownCloud, Inc. + * SPDX-License-Identifier: AGPL-3.0-only */ namespace OC\Files\Type; @@ -38,14 +21,13 @@ use OCP\IDBConnection; class Loader implements IMimeTypeLoader { use TTransactional; - /** @var IDBConnection */ - private $dbConnection; + private IDBConnection $dbConnection; - /** @var array [id => mimetype] */ - protected $mimetypes; + /** @psalm-var array<int, string> */ + protected array $mimetypes; - /** @var array [mimetype => id] */ - protected $mimetypeIds; + /** @psalm-var array<string, int> */ + protected array $mimetypeIds; /** * @param IDBConnection $dbConnection @@ -58,11 +40,8 @@ class Loader implements IMimeTypeLoader { /** * Get a mimetype from its ID - * - * @param int $id - * @return string|null */ - public function getMimetypeById($id) { + public function getMimetypeById(int $id): ?string { if (!$this->mimetypes) { $this->loadMimetypes(); } @@ -74,11 +53,8 @@ class Loader implements IMimeTypeLoader { /** * Get a mimetype ID, adding the mimetype to the DB if it does not exist - * - * @param string $mimetype - * @return int */ - public function getId($mimetype) { + public function getId(string $mimetype): int { if (!$this->mimetypeIds) { $this->loadMimetypes(); } @@ -90,11 +66,8 @@ class Loader implements IMimeTypeLoader { /** * Test if a mimetype exists in the database - * - * @param string $mimetype - * @return bool */ - public function exists($mimetype) { + public function exists(string $mimetype): bool { if (!$this->mimetypeIds) { $this->loadMimetypes(); } @@ -104,7 +77,7 @@ class Loader implements IMimeTypeLoader { /** * Clear all loaded mimetypes, allow for re-loading */ - public function reset() { + public function reset(): void { $this->mimetypes = []; $this->mimetypeIds = []; } @@ -115,9 +88,9 @@ class Loader implements IMimeTypeLoader { * @param string $mimetype * @return int inserted ID */ - protected function store($mimetype) { - $mimetypeId = $this->atomic(function () use ($mimetype) { - try { + protected function store(string $mimetype): int { + try { + $mimetypeId = $this->atomic(function () use ($mimetype) { $insert = $this->dbConnection->getQueryBuilder(); $insert->insert('mimetypes') ->values([ @@ -125,26 +98,24 @@ class Loader implements IMimeTypeLoader { ]) ->executeStatement(); return $insert->getLastInsertId(); - } catch (DbalException $e) { - if ($e->getReason() !== DBException::REASON_UNIQUE_CONSTRAINT_VIOLATION) { - throw $e; - } - $qb = $this->dbConnection->getQueryBuilder(); - $qb->select('id') - ->from('mimetypes') - ->where($qb->expr()->eq('mimetype', $qb->createNamedParameter($mimetype))); - $result = $qb->executeQuery(); - $id = $result->fetchOne(); - $result->closeCursor(); - if ($id !== false) { - return (int) $id; - } + }, $this->dbConnection); + } catch (DbalException $e) { + if ($e->getReason() !== DBException::REASON_UNIQUE_CONSTRAINT_VIOLATION) { + throw $e; + } + + $qb = $this->dbConnection->getQueryBuilder(); + $qb->select('id') + ->from('mimetypes') + ->where($qb->expr()->eq('mimetype', $qb->createNamedParameter($mimetype))); + $result = $qb->executeQuery(); + $id = $result->fetchOne(); + $result->closeCursor(); + if ($id === false) { throw new \Exception("Database threw an unique constraint on inserting a new mimetype, but couldn't return the ID for this very mimetype"); } - }, $this->dbConnection); - if (!$mimetypeId) { - throw new \Exception("Failed to get mimetype id for $mimetype after trying to store it"); + $mimetypeId = (int) $id; } $this->mimetypes[$mimetypeId] = $mimetype; @@ -155,29 +126,27 @@ class Loader implements IMimeTypeLoader { /** * Load all mimetypes from DB */ - private function loadMimetypes() { + private function loadMimetypes(): void { $qb = $this->dbConnection->getQueryBuilder(); $qb->select('id', 'mimetype') ->from('mimetypes'); - $result = $qb->execute(); + $result = $qb->executeQuery(); $results = $result->fetchAll(); $result->closeCursor(); foreach ($results as $row) { - $this->mimetypes[$row['id']] = $row['mimetype']; - $this->mimetypeIds[$row['mimetype']] = $row['id']; + $this->mimetypes[(int) $row['id']] = $row['mimetype']; + $this->mimetypeIds[$row['mimetype']] = (int) $row['id']; } } /** * Update filecache mimetype based on file extension * - * @param string $ext file extension - * @param int $mimeTypeId * @return int number of changed rows */ - public function updateFilecache($ext, $mimeTypeId) { + public function updateFilecache(string $ext, int $mimeTypeId): int { $folderMimeTypeId = $this->getId('httpd/unix-directory'); $update = $this->dbConnection->getQueryBuilder(); $update->update('filecache') diff --git a/lib/private/Files/Type/TemplateManager.php b/lib/private/Files/Type/TemplateManager.php index 6210edaaf05..a0c0e9fac9f 100644 --- a/lib/private/Files/Type/TemplateManager.php +++ b/lib/private/Files/Type/TemplateManager.php @@ -1,27 +1,9 @@ <?php + /** - * @copyright Copyright (c) 2016, ownCloud, Inc. - * - * @author Christoph Wurst <christoph@winzerhof-wurst.at> - * @author Julius Härtl <jus@bitgrid.net> - * @author Morris Jobke <hey@morrisjobke.de> - * @author Robin Appelman <robin@icewind.nl> - * @author Robin McCorkell <robin@mccorkell.me.uk> - * - * @license AGPL-3.0 - * - * This code is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License, version 3, - * as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License, version 3, - * along with this program. If not, see <http://www.gnu.org/licenses/> - * + * SPDX-FileCopyrightText: 2016-2024 Nextcloud GmbH and Nextcloud contributors + * SPDX-FileCopyrightText: 2016 ownCloud, Inc. + * SPDX-License-Identifier: AGPL-3.0-only */ namespace OC\Files\Type; diff --git a/lib/private/Files/Utils/PathHelper.php b/lib/private/Files/Utils/PathHelper.php index 0fb7ba663cc..a6ae029b957 100644 --- a/lib/private/Files/Utils/PathHelper.php +++ b/lib/private/Files/Utils/PathHelper.php @@ -2,25 +2,9 @@ declare(strict_types=1); /** - * @copyright Copyright (c) 2022 Robin Appelman <robin@icewind.nl> - * - * @license GNU AGPL version 3 or any later version - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as - * published by the Free Software Foundation, either version 3 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - * + * SPDX-FileCopyrightText: 2022 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later */ - namespace OC\Files\Utils; class PathHelper { diff --git a/lib/private/Files/Utils/Scanner.php b/lib/private/Files/Utils/Scanner.php index b7f6972ee10..bcc54dea0dc 100644 --- a/lib/private/Files/Utils/Scanner.php +++ b/lib/private/Files/Utils/Scanner.php @@ -1,33 +1,10 @@ <?php + /** - * @copyright Copyright (c) 2016, ownCloud, Inc. - * - * @author Blaok <i@blaok.me> - * @author Christoph Wurst <christoph@winzerhof-wurst.at> - * @author Jörn Friedrich Dreyer <jfd@butonic.de> - * @author Morris Jobke <hey@morrisjobke.de> - * @author Olivier Paroz <github@oparoz.com> - * @author Robin Appelman <robin@icewind.nl> - * @author Roeland Jago Douma <roeland@famdouma.nl> - * @author Thomas Müller <thomas.mueller@tmit.eu> - * @author Vincent Petry <vincent@nextcloud.com> - * - * @license AGPL-3.0 - * - * This code is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License, version 3, - * as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License, version 3, - * along with this program. If not, see <http://www.gnu.org/licenses/> - * + * SPDX-FileCopyrightText: 2016-2024 Nextcloud GmbH and Nextcloud contributors + * SPDX-FileCopyrightText: 2016 ownCloud, Inc. + * SPDX-License-Identifier: AGPL-3.0-only */ - namespace OC\Files\Utils; use OC\Files\Cache\Cache; @@ -41,15 +18,16 @@ use OCA\Files_Sharing\SharedStorage; use OCP\EventDispatcher\IEventDispatcher; use OCP\Files\Events\BeforeFileScannedEvent; use OCP\Files\Events\BeforeFolderScannedEvent; -use OCP\Files\Events\NodeAddedToCache; use OCP\Files\Events\FileCacheUpdated; -use OCP\Files\Events\NodeRemovedFromCache; use OCP\Files\Events\FileScannedEvent; use OCP\Files\Events\FolderScannedEvent; +use OCP\Files\Events\NodeAddedToCache; +use OCP\Files\Events\NodeRemovedFromCache; use OCP\Files\NotFoundException; use OCP\Files\Storage\IStorage; use OCP\Files\StorageNotAvailableException; use OCP\IDBConnection; +use OCP\Lock\ILockingProvider; use Psr\Log\LoggerInterface; /** @@ -100,7 +78,7 @@ class Scanner extends PublicEmitter { $this->dispatcher = $dispatcher; $this->logger = $logger; // when DB locking is used, no DB transactions will be used - $this->useTransaction = !(\OC::$server->getLockingProvider() instanceof DBLockingProvider); + $this->useTransaction = !(\OC::$server->get(ILockingProvider::class) instanceof DBLockingProvider); } /** @@ -156,33 +134,37 @@ class Scanner extends PublicEmitter { public function backgroundScan($dir) { $mounts = $this->getMounts($dir); foreach ($mounts as $mount) { - $storage = $mount->getStorage(); - if (is_null($storage)) { - continue; - } + try { + $storage = $mount->getStorage(); + if (is_null($storage)) { + continue; + } - // don't bother scanning failed storages (shortcut for same result) - if ($storage->instanceOfStorage(FailedStorage::class)) { - continue; - } + // don't bother scanning failed storages (shortcut for same result) + if ($storage->instanceOfStorage(FailedStorage::class)) { + continue; + } - $scanner = $storage->getScanner(); - $this->attachListener($mount); + $scanner = $storage->getScanner(); + $this->attachListener($mount); - $scanner->listen('\OC\Files\Cache\Scanner', 'removeFromCache', function ($path) use ($storage) { - $this->triggerPropagator($storage, $path); - }); - $scanner->listen('\OC\Files\Cache\Scanner', 'updateCache', function ($path) use ($storage) { - $this->triggerPropagator($storage, $path); - }); - $scanner->listen('\OC\Files\Cache\Scanner', 'addToCache', function ($path) use ($storage) { - $this->triggerPropagator($storage, $path); - }); + $scanner->listen('\OC\Files\Cache\Scanner', 'removeFromCache', function ($path) use ($storage) { + $this->triggerPropagator($storage, $path); + }); + $scanner->listen('\OC\Files\Cache\Scanner', 'updateCache', function ($path) use ($storage) { + $this->triggerPropagator($storage, $path); + }); + $scanner->listen('\OC\Files\Cache\Scanner', 'addToCache', function ($path) use ($storage) { + $this->triggerPropagator($storage, $path); + }); - $propagator = $storage->getPropagator(); - $propagator->beginBatch(); - $scanner->backgroundScan(); - $propagator->commitBatch(); + $propagator = $storage->getPropagator(); + $propagator->beginBatch(); + $scanner->backgroundScan(); + $propagator->commitBatch(); + } catch (\Exception $e) { + $this->logger->error("Error while trying to scan mount as {$mount->getMountPoint()}:" . $e->getMessage(), ['exception' => $e, 'app' => 'files']); + } } } @@ -193,7 +175,7 @@ class Scanner extends PublicEmitter { * @throws ForbiddenException * @throws NotFoundException */ - public function scan($dir = '', $recursive = \OC\Files\Cache\Scanner::SCAN_RECURSIVE, callable $mountFilter = null) { + public function scan($dir = '', $recursive = \OC\Files\Cache\Scanner::SCAN_RECURSIVE, ?callable $mountFilter = null) { if (!Filesystem::isValidPath($dir)) { throw new \InvalidArgumentException('Invalid path to scan'); } diff --git a/lib/private/Files/View.php b/lib/private/Files/View.php index 71815939310..0150a3e117a 100644 --- a/lib/private/Files/View.php +++ b/lib/private/Files/View.php @@ -1,73 +1,38 @@ <?php + /** - * @copyright Copyright (c) 2016, ownCloud, Inc. - * - * @author Arthur Schiwon <blizzz@arthur-schiwon.de> - * @author Ashod Nakashian <ashod.nakashian@collabora.co.uk> - * @author Bart Visscher <bartv@thisnet.nl> - * @author Björn Schießle <bjoern@schiessle.org> - * @author Christoph Wurst <christoph@winzerhof-wurst.at> - * @author Florin Peter <github@florin-peter.de> - * @author Jesús Macias <jmacias@solidgear.es> - * @author Joas Schilling <coding@schilljs.com> - * @author Jörn Friedrich Dreyer <jfd@butonic.de> - * @author Julius Härtl <jus@bitgrid.net> - * @author karakayasemi <karakayasemi@itu.edu.tr> - * @author Klaas Freitag <freitag@owncloud.com> - * @author korelstar <korelstar@users.noreply.github.com> - * @author Lukas Reschke <lukas@statuscode.ch> - * @author Luke Policinski <lpolicinski@gmail.com> - * @author Michael Gapczynski <GapczynskiM@gmail.com> - * @author Morris Jobke <hey@morrisjobke.de> - * @author Piotr Filiciak <piotr@filiciak.pl> - * @author Robin Appelman <robin@icewind.nl> - * @author Robin McCorkell <robin@mccorkell.me.uk> - * @author Roeland Jago Douma <roeland@famdouma.nl> - * @author Sam Tuke <mail@samtuke.com> - * @author Scott Dutton <exussum12@users.noreply.github.com> - * @author Thomas Müller <thomas.mueller@tmit.eu> - * @author Thomas Tanghus <thomas@tanghus.net> - * @author Vincent Petry <vincent@nextcloud.com> - * - * @license AGPL-3.0 - * - * This code is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License, version 3, - * as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License, version 3, - * along with this program. If not, see <http://www.gnu.org/licenses/> - * + * SPDX-FileCopyrightText: 2016-2024 Nextcloud GmbH and Nextcloud contributors + * SPDX-FileCopyrightText: 2016 ownCloud, Inc. + * SPDX-License-Identifier: AGPL-3.0-only */ namespace OC\Files; use Icewind\Streams\CallbackWrapper; use OC\Files\Mount\MoveableMount; use OC\Files\Storage\Storage; -use OC\User\LazyUser; use OC\Share\Share; -use OC\User\User; +use OC\User\LazyUser; use OC\User\Manager as UserManager; +use OC\User\User; use OCA\Files_Sharing\SharedMount; use OCP\Constants; use OCP\Files\Cache\ICacheEntry; +use OCP\Files\ConnectionLostException; use OCP\Files\EmptyFileNameException; use OCP\Files\FileNameTooLongException; +use OCP\Files\ForbiddenException; use OCP\Files\InvalidCharacterInPathException; use OCP\Files\InvalidDirectoryException; use OCP\Files\InvalidPathException; use OCP\Files\Mount\IMountPoint; use OCP\Files\NotFoundException; use OCP\Files\ReservedWordException; -use OCP\Files\Storage\IStorage; use OCP\IUser; use OCP\Lock\ILockingProvider; use OCP\Lock\LockedException; +use OCP\Server; +use OCP\Share\IManager; +use OCP\Share\IShare; use Psr\Log\LoggerInterface; /** @@ -103,7 +68,7 @@ class View { } $this->fakeRoot = $root; - $this->lockingProvider = \OC::$server->getLockingProvider(); + $this->lockingProvider = \OC::$server->get(ILockingProvider::class); $this->lockingEnabled = !($this->lockingProvider instanceof \OC\Lock\NoopLockingProvider); $this->userManager = \OC::$server->getUserManager(); $this->logger = \OC::$server->get(LoggerInterface::class); @@ -286,12 +251,12 @@ class View { $this->updaterEnabled = true; } - protected function writeUpdate(Storage $storage, string $internalPath, ?int $time = null): void { + protected function writeUpdate(Storage $storage, string $internalPath, ?int $time = null, ?int $sizeDifference = null): void { if ($this->updaterEnabled) { if (is_null($time)) { $time = time(); } - $storage->getUpdater()->update($internalPath, $time); + $storage->getUpdater()->update($internalPath, $time, $sizeDifference); } } @@ -397,10 +362,11 @@ class View { } $handle = $this->fopen($path, 'rb'); if ($handle) { - $chunkSize = 524288; // 512 kB chunks + $chunkSize = 524288; // 512 kiB chunks while (!feof($handle)) { echo fread($handle, $chunkSize); flush(); + $this->checkConnectionStatus(); } fclose($handle); return $this->filesize($path); @@ -423,7 +389,7 @@ class View { } $handle = $this->fopen($path, 'rb'); if ($handle) { - $chunkSize = 524288; // 512 kB chunks + $chunkSize = 524288; // 512 kiB chunks $startReading = true; if ($from !== 0 && $from !== '0' && fseek($handle, $from) !== 0) { @@ -453,6 +419,7 @@ class View { } echo fread($handle, $len); flush(); + $this->checkConnectionStatus(); } return ftell($handle) - $from; } @@ -462,6 +429,13 @@ class View { return false; } + private function checkConnectionStatus(): void { + $connectionStatus = \connection_status(); + if ($connectionStatus !== CONNECTION_NORMAL) { + throw new ConnectionLostException("Connection lost. Status: $connectionStatus"); + } + } + /** * @param string $path * @return mixed @@ -721,6 +695,13 @@ class View { public function rename($source, $target) { $absolutePath1 = Filesystem::normalizePath($this->getAbsolutePath($source)); $absolutePath2 = Filesystem::normalizePath($this->getAbsolutePath($target)); + + if (str_starts_with($absolutePath2, $absolutePath1 . '/')) { + throw new ForbiddenException("Moving a folder into a child folder is forbidden", false); + } + + $targetParts = explode('/', $absolutePath2); + $targetUser = $targetParts[1] ?? null; $result = false; if ( Filesystem::isValidPath($target) @@ -775,7 +756,7 @@ class View { if ($internalPath1 === '') { if ($mount1 instanceof MoveableMount) { $sourceParentMount = $this->getMount(dirname($source)); - if ($sourceParentMount === $mount2 && $this->targetIsNotShared($storage2, $internalPath2)) { + if ($sourceParentMount === $mount2 && $this->targetIsNotShared($targetUser, $absolutePath2)) { /** * @var \OC\Files\Mount\MountPoint | \OC\Files\Mount\MoveableMount $mount1 */ @@ -788,14 +769,14 @@ class View { } else { $result = false; } - // moving a file/folder within the same mount point + // moving a file/folder within the same mount point } elseif ($storage1 === $storage2) { if ($storage1) { $result = $storage1->rename($internalPath1, $internalPath2); } else { $result = false; } - // moving a file/folder between storages (from $storage1 to $storage2) + // moving a file/folder between storages (from $storage1 to $storage2) } else { $result = $storage2->moveFromStorage($storage1, $internalPath1, $internalPath2); } @@ -1163,10 +1144,12 @@ class View { $this->removeUpdate($storage, $internalPath); } if ($result !== false && in_array('write', $hooks, true) && $operation !== 'fopen' && $operation !== 'touch') { - $this->writeUpdate($storage, $internalPath); + $isCreateOperation = $operation === 'mkdir' || ($operation === 'file_put_contents' && in_array('create', $hooks, true)); + $sizeDifference = $operation === 'mkdir' ? 0 : $result; + $this->writeUpdate($storage, $internalPath, null, $isCreateOperation ? $sizeDifference : null); } if ($result !== false && in_array('touch', $hooks)) { - $this->writeUpdate($storage, $internalPath, $extraParam); + $this->writeUpdate($storage, $internalPath, $extraParam, 0); } if ((in_array('write', $hooks) || in_array('delete', $hooks)) && ($operation !== 'fopen' || $result === false)) { @@ -1412,7 +1395,7 @@ class View { * @param string $mimetype_filter limit returned content to this mimetype or mimepart * @return FileInfo[] */ - public function getDirectoryContent($directory, $mimetype_filter = '', \OCP\Files\FileInfo $directoryInfo = null) { + public function getDirectoryContent($directory, $mimetype_filter = '', ?\OCP\Files\FileInfo $directoryInfo = null) { $this->assertPathLength($directory); if (!Filesystem::isValidPath($directory)) { return []; @@ -1465,6 +1448,13 @@ class View { //add a folder for any mountpoint in this directory and add the sizes of other mountpoints to the folders $mounts = Filesystem::getMountManager()->findIn($path); + + // make sure nested mounts are sorted after their parent mounts + // otherwise doesn't propagate the etag across storage boundaries correctly + usort($mounts, function (IMountPoint $a, IMountPoint $b) { + return $a->getMountPoint() <=> $b->getMountPoint(); + }); + $dirLength = strlen($path); foreach ($mounts as $mount) { $mountPoint = $mount->getMountPoint(); @@ -1515,7 +1505,7 @@ class View { $rootEntry['path'] = substr(Filesystem::normalizePath($path . '/' . $rootEntry['name']), strlen($user) + 2); // full path without /$user/ // if sharing was disabled for the user we remove the share permissions - if (\OCP\Util::isSharingDisabledForUser()) { + if ($sharingDisabled) { $rootEntry['permissions'] = $rootEntry['permissions'] & ~\OCP\Constants::PERMISSION_SHARE; } @@ -1710,7 +1700,7 @@ class View { * @return string * @throws NotFoundException */ - public function getPath($id, int $storageId = null) { + public function getPath($id, ?int $storageId = null) { $id = (int)$id; $manager = Filesystem::getMountManager(); $mounts = $manager->findIn($this->fakeRoot); @@ -1769,28 +1759,29 @@ class View { * It is not allowed to move a mount point into a different mount point or * into an already shared folder */ - private function targetIsNotShared(IStorage $targetStorage, string $targetInternalPath): bool { - // note: cannot use the view because the target is already locked - $fileId = $targetStorage->getCache()->getId($targetInternalPath); - if ($fileId === -1) { - // target might not exist, need to check parent instead - $fileId = $targetStorage->getCache()->getId(dirname($targetInternalPath)); - } - - // check if any of the parents were shared by the current owner (include collections) - $shares = Share::getItemShared( - 'folder', - (string)$fileId, - \OC\Share\Constants::FORMAT_NONE, - null, - true - ); - - if (count($shares) > 0) { - $this->logger->debug( - 'It is not allowed to move one mount point into a shared folder', - ['app' => 'files']); - return false; + private function targetIsNotShared(string $user, string $targetPath): bool { + $providers = [ + IShare::TYPE_USER, + IShare::TYPE_GROUP, + IShare::TYPE_EMAIL, + IShare::TYPE_CIRCLE, + IShare::TYPE_ROOM, + IShare::TYPE_DECK, + IShare::TYPE_SCIENCEMESH + ]; + $shareManager = Server::get(IManager::class); + /** @var IShare[] $shares */ + $shares = array_merge(...array_map(function (int $type) use ($shareManager, $user) { + return $shareManager->getSharesBy($user, $type); + }, $providers)); + + foreach ($shares as $share) { + if (str_starts_with($targetPath, $share->getNode()->getPath())) { + $this->logger->debug( + 'It is not allowed to move one mount point into a shared folder', + ['app' => 'files']); + return false; + } } return true; @@ -1834,19 +1825,19 @@ class View { [$storage, $internalPath] = $this->resolvePath($path); $storage->verifyPath($internalPath, $fileName); } catch (ReservedWordException $ex) { - $l = \OC::$server->getL10N('lib'); + $l = \OCP\Util::getL10N('lib'); throw new InvalidPathException($l->t('File name is a reserved word')); } catch (InvalidCharacterInPathException $ex) { - $l = \OC::$server->getL10N('lib'); + $l = \OCP\Util::getL10N('lib'); throw new InvalidPathException($l->t('File name contains at least one invalid character')); } catch (FileNameTooLongException $ex) { - $l = \OC::$server->getL10N('lib'); + $l = \OCP\Util::getL10N('lib'); throw new InvalidPathException($l->t('File name is too long')); } catch (InvalidDirectoryException $ex) { - $l = \OC::$server->getL10N('lib'); + $l = \OCP\Util::getL10N('lib'); throw new InvalidPathException($l->t('Dot files are not allowed')); } catch (EmptyFileNameException $ex) { - $l = \OC::$server->getL10N('lib'); + $l = \OCP\Util::getL10N('lib'); throw new InvalidPathException($l->t('Empty filename is not allowed')); } } diff --git a/lib/private/FilesMetadata/FilesMetadataManager.php b/lib/private/FilesMetadata/FilesMetadataManager.php new file mode 100644 index 00000000000..231fb2ec98f --- /dev/null +++ b/lib/private/FilesMetadata/FilesMetadataManager.php @@ -0,0 +1,303 @@ +<?php + +declare(strict_types=1); +/** + * SPDX-FileCopyrightText: 2023 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later + */ + +namespace OC\FilesMetadata; + +use JsonException; +use OC\FilesMetadata\Job\UpdateSingleMetadata; +use OC\FilesMetadata\Listener\MetadataDelete; +use OC\FilesMetadata\Listener\MetadataUpdate; +use OC\FilesMetadata\Model\FilesMetadata; +use OC\FilesMetadata\Service\IndexRequestService; +use OC\FilesMetadata\Service\MetadataRequestService; +use OCP\BackgroundJob\IJobList; +use OCP\DB\Exception; +use OCP\DB\Exception as DBException; +use OCP\DB\QueryBuilder\IQueryBuilder; +use OCP\EventDispatcher\IEventDispatcher; +use OCP\Files\Cache\CacheEntryRemovedEvent; +use OCP\Files\Events\Node\NodeWrittenEvent; +use OCP\Files\InvalidPathException; +use OCP\Files\Node; +use OCP\Files\NotFoundException; +use OCP\FilesMetadata\Event\MetadataBackgroundEvent; +use OCP\FilesMetadata\Event\MetadataLiveEvent; +use OCP\FilesMetadata\Event\MetadataNamedEvent; +use OCP\FilesMetadata\Exceptions\FilesMetadataException; +use OCP\FilesMetadata\Exceptions\FilesMetadataNotFoundException; +use OCP\FilesMetadata\IFilesMetadataManager; +use OCP\FilesMetadata\IMetadataQuery; +use OCP\FilesMetadata\Model\IFilesMetadata; +use OCP\FilesMetadata\Model\IMetadataValueWrapper; +use OCP\IAppConfig; +use Psr\Log\LoggerInterface; + +/** + * @inheritDoc + * @since 28.0.0 + */ +class FilesMetadataManager implements IFilesMetadataManager { + public const CONFIG_KEY = 'files_metadata'; + public const MIGRATION_DONE = 'files_metadata_installed'; + private const JSON_MAXSIZE = 100000; + + private ?IFilesMetadata $all = null; + + public function __construct( + private IEventDispatcher $eventDispatcher, + private IJobList $jobList, + private IAppConfig $appConfig, + private LoggerInterface $logger, + private MetadataRequestService $metadataRequestService, + private IndexRequestService $indexRequestService, + ) { + } + + /** + * @inheritDoc + * + * @param Node $node related node + * @param int $process type of process + * + * @return IFilesMetadata + * @throws FilesMetadataException if metadata are invalid + * @throws InvalidPathException if path to file is not valid + * @throws NotFoundException if file cannot be found + * @see self::PROCESS_BACKGROUND + * @see self::PROCESS_LIVE + * @since 28.0.0 + */ + public function refreshMetadata( + Node $node, + int $process = self::PROCESS_LIVE, + string $namedEvent = '' + ): IFilesMetadata { + try { + $metadata = $this->metadataRequestService->getMetadataFromFileId($node->getId()); + } catch (FilesMetadataNotFoundException) { + $metadata = new FilesMetadata($node->getId()); + } + + // if $process is LIVE, we enforce LIVE + // if $process is NAMED, we go NAMED + // else BACKGROUND + if ((self::PROCESS_LIVE & $process) !== 0) { + $event = new MetadataLiveEvent($node, $metadata); + } elseif ((self::PROCESS_NAMED & $process) !== 0) { + $event = new MetadataNamedEvent($node, $metadata, $namedEvent); + } else { + $event = new MetadataBackgroundEvent($node, $metadata); + } + + $this->eventDispatcher->dispatchTyped($event); + $this->saveMetadata($event->getMetadata()); + + // if requested, we add a new job for next cron to refresh metadata out of main thread + // if $process was set to LIVE+BACKGROUND, we run background process directly + if ($event instanceof MetadataLiveEvent && $event->isRunAsBackgroundJobRequested()) { + if ((self::PROCESS_BACKGROUND & $process) !== 0) { + return $this->refreshMetadata($node, self::PROCESS_BACKGROUND); + } + + $this->jobList->add(UpdateSingleMetadata::class, [$node->getOwner()->getUID(), $node->getId()]); + } + + return $metadata; + } + + /** + * @param int $fileId file id + * @param boolean $generate Generate if metadata does not exists + * + * @inheritDoc + * @return IFilesMetadata + * @throws FilesMetadataNotFoundException if not found + * @since 28.0.0 + */ + public function getMetadata(int $fileId, bool $generate = false): IFilesMetadata { + try { + return $this->metadataRequestService->getMetadataFromFileId($fileId); + } catch (FilesMetadataNotFoundException $ex) { + if ($generate) { + return new FilesMetadata($fileId); + } + + throw $ex; + } + } + + /** + * returns metadata of multiple file ids + * + * @param int[] $fileIds file ids + * + * @return array File ID is the array key, files without metadata are not returned in the array + * @psalm-return array<int, IFilesMetadata> + * @since 28.0.0 + */ + public function getMetadataForFiles(array $fileIds): array { + return $this->metadataRequestService->getMetadataFromFileIds($fileIds); + } + + /** + * @param IFilesMetadata $filesMetadata metadata + * + * @inheritDoc + * @throws FilesMetadataException if metadata seems malformed + * @since 28.0.0 + */ + public function saveMetadata(IFilesMetadata $filesMetadata): void { + if ($filesMetadata->getFileId() === 0 || !$filesMetadata->updated()) { + return; + } + + $json = json_encode($filesMetadata->jsonSerialize()); + if (strlen($json) > self::JSON_MAXSIZE) { + $this->logger->debug('huge metadata content detected: ' . $json); + throw new FilesMetadataException('json cannot exceed ' . self::JSON_MAXSIZE . ' characters long; fileId: ' . $filesMetadata->getFileId() . '; size: ' . strlen($json)); + } + + try { + if ($filesMetadata->getSyncToken() === '') { + $this->metadataRequestService->store($filesMetadata); + } else { + $this->metadataRequestService->updateMetadata($filesMetadata); + } + } catch (DBException $e) { + // most of the logged exception are the result of race condition + // between 2 simultaneous process trying to create/update metadata + $this->logger->warning('issue while saveMetadata', ['exception' => $e, 'metadata' => $filesMetadata]); + + return; + } + + // update indexes + foreach ($filesMetadata->getIndexes() as $index) { + try { + $this->indexRequestService->updateIndex($filesMetadata, $index); + } catch (DBException $e) { + $this->logger->warning('issue while updateIndex', ['exception' => $e]); + } + } + + // update metadata types list + $current = $this->getKnownMetadata(); + $current->import($filesMetadata->jsonSerialize(true)); + $this->appConfig->setValueArray('core', self::CONFIG_KEY, $current->jsonSerialize(), lazy: true); + } + + /** + * @param int $fileId file id + * + * @inheritDoc + * @since 28.0.0 + */ + public function deleteMetadata(int $fileId): void { + try { + $this->metadataRequestService->dropMetadata($fileId); + } catch (Exception $e) { + $this->logger->warning('issue while deleteMetadata', ['exception' => $e, 'fileId' => $fileId]); + } + + try { + $this->indexRequestService->dropIndex($fileId); + } catch (Exception $e) { + $this->logger->warning('issue while deleteMetadata', ['exception' => $e, 'fileId' => $fileId]); + } + } + + /** + * @param IQueryBuilder $qb + * @param string $fileTableAlias alias of the table that contains data about files + * @param string $fileIdField alias of the field that contains file ids + * + * @inheritDoc + * @return IMetadataQuery + * @see IMetadataQuery + * @since 28.0.0 + */ + public function getMetadataQuery( + IQueryBuilder $qb, + string $fileTableAlias, + string $fileIdField + ): IMetadataQuery { + return new MetadataQuery($qb, $this, $fileTableAlias, $fileIdField); + } + + /** + * @inheritDoc + * @return IFilesMetadata + * @since 28.0.0 + */ + public function getKnownMetadata(): IFilesMetadata { + if ($this->all !== null) { + return $this->all; + } + $this->all = new FilesMetadata(); + + try { + $this->all->import($this->appConfig->getValueArray('core', self::CONFIG_KEY, lazy: true)); + } catch (JsonException) { + $this->logger->warning('issue while reading stored list of metadata. Advised to run ./occ files:scan --all --generate-metadata'); + } + + return $this->all; + } + + /** + * @param string $key metadata key + * @param string $type metadata type + * @param bool $indexed TRUE if metadata can be search + * @param int $editPermission remote edit permission via Webdav PROPPATCH + * + * @inheritDoc + * @since 28.0.0 + * @see IMetadataValueWrapper::TYPE_INT + * @see IMetadataValueWrapper::TYPE_FLOAT + * @see IMetadataValueWrapper::TYPE_BOOL + * @see IMetadataValueWrapper::TYPE_ARRAY + * @see IMetadataValueWrapper::TYPE_STRING_LIST + * @see IMetadataValueWrapper::TYPE_INT_LIST + * @see IMetadataValueWrapper::TYPE_STRING + * @see IMetadataValueWrapper::EDIT_FORBIDDEN + * @see IMetadataValueWrapper::EDIT_REQ_OWNERSHIP + * @see IMetadataValueWrapper::EDIT_REQ_WRITE_PERMISSION + * @see IMetadataValueWrapper::EDIT_REQ_READ_PERMISSION + */ + public function initMetadata( + string $key, + string $type, + bool $indexed = false, + int $editPermission = IMetadataValueWrapper::EDIT_FORBIDDEN + ): void { + $current = $this->getKnownMetadata(); + try { + if ($current->getType($key) === $type + && $indexed === $current->isIndex($key) + && $editPermission === $current->getEditPermission($key)) { + return; // if key exists, with same type and indexed, we do nothing. + } + } catch (FilesMetadataNotFoundException) { + // if value does not exist, we keep on the writing of course + } + + $current->import([$key => ['type' => $type, 'indexed' => $indexed, 'editPermission' => $editPermission]]); + $this->appConfig->setValueArray('core', self::CONFIG_KEY, $current->jsonSerialize(), lazy: true); + $this->all = $current; + } + + /** + * load listeners + * + * @param IEventDispatcher $eventDispatcher + */ + public static function loadListeners(IEventDispatcher $eventDispatcher): void { + $eventDispatcher->addServiceListener(NodeWrittenEvent::class, MetadataUpdate::class); + $eventDispatcher->addServiceListener(CacheEntryRemovedEvent::class, MetadataDelete::class); + } +} diff --git a/lib/private/FilesMetadata/Job/UpdateSingleMetadata.php b/lib/private/FilesMetadata/Job/UpdateSingleMetadata.php new file mode 100644 index 00000000000..c4b0117db13 --- /dev/null +++ b/lib/private/FilesMetadata/Job/UpdateSingleMetadata.php @@ -0,0 +1,49 @@ +<?php + +declare(strict_types=1); +/** + * SPDX-FileCopyrightText: 2023 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later + */ + +namespace OC\FilesMetadata\Job; + +use OC\FilesMetadata\FilesMetadataManager; +use OCP\AppFramework\Utility\ITimeFactory; +use OCP\BackgroundJob\QueuedJob; +use OCP\Files\IRootFolder; +use OCP\FilesMetadata\Event\MetadataLiveEvent; +use OCP\FilesMetadata\IFilesMetadataManager; +use Psr\Log\LoggerInterface; + +/** + * Simple background job, created when requested by an app during the + * dispatch of MetadataLiveEvent. + * This background job will re-run the event to refresh metadata on a non-live thread. + * + * @see MetadataLiveEvent::requestBackgroundJob() + * @since 28.0.0 + */ +class UpdateSingleMetadata extends QueuedJob { + public function __construct( + ITimeFactory $time, + private IRootFolder $rootFolder, + private FilesMetadataManager $filesMetadataManager, + private LoggerInterface $logger + ) { + parent::__construct($time); + } + + protected function run($argument) { + [$userId, $fileId] = $argument; + + try { + $node = $this->rootFolder->getUserFolder($userId)->getFirstNodeById($fileId); + if ($node) { + $this->filesMetadataManager->refreshMetadata($node, IFilesMetadataManager::PROCESS_BACKGROUND); + } + } catch (\Exception $e) { + $this->logger->warning('issue while running UpdateSingleMetadata', ['exception' => $e, 'userId' => $userId, 'fileId' => $fileId]); + } + } +} diff --git a/lib/private/FilesMetadata/Listener/MetadataDelete.php b/lib/private/FilesMetadata/Listener/MetadataDelete.php new file mode 100644 index 00000000000..c0a6fc758c1 --- /dev/null +++ b/lib/private/FilesMetadata/Listener/MetadataDelete.php @@ -0,0 +1,44 @@ +<?php + +declare(strict_types=1); +/** + * SPDX-FileCopyrightText: 2023 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later + */ + +namespace OC\FilesMetadata\Listener; + +use Exception; +use OCP\EventDispatcher\Event; +use OCP\EventDispatcher\IEventListener; +use OCP\Files\Cache\CacheEntryRemovedEvent; +use OCP\FilesMetadata\IFilesMetadataManager; +use Psr\Log\LoggerInterface; + +/** + * Handle file deletion event and remove stored metadata related to the deleted file + * + * @template-implements IEventListener<CacheEntryRemovedEvent> + */ +class MetadataDelete implements IEventListener { + public function __construct( + private IFilesMetadataManager $filesMetadataManager, + private LoggerInterface $logger + ) { + } + + public function handle(Event $event): void { + if (!($event instanceof CacheEntryRemovedEvent)) { + return; + } + + try { + $nodeId = $event->getFileId(); + if ($nodeId > 0) { + $this->filesMetadataManager->deleteMetadata($nodeId); + } + } catch (Exception $e) { + $this->logger->warning('issue while running MetadataDelete', ['exception' => $e]); + } + } +} diff --git a/lib/private/FilesMetadata/Listener/MetadataUpdate.php b/lib/private/FilesMetadata/Listener/MetadataUpdate.php new file mode 100644 index 00000000000..05422ba5aba --- /dev/null +++ b/lib/private/FilesMetadata/Listener/MetadataUpdate.php @@ -0,0 +1,47 @@ +<?php + +declare(strict_types=1); +/** + * SPDX-FileCopyrightText: 2023 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later + */ + +namespace OC\FilesMetadata\Listener; + +use Exception; +use OCP\EventDispatcher\Event; +use OCP\EventDispatcher\IEventListener; +use OCP\Files\Events\Node\NodeCreatedEvent; +use OCP\Files\Events\Node\NodeWrittenEvent; +use OCP\FilesMetadata\IFilesMetadataManager; +use Psr\Log\LoggerInterface; + +/** + * Handle file creation/modification events and initiate a new event related to the created/edited file. + * The generated new event is broadcast in order to obtain file related metadata from other apps. + * metadata will be stored in database. + * + * @template-implements IEventListener<NodeCreatedEvent|NodeWrittenEvent> + */ +class MetadataUpdate implements IEventListener { + public function __construct( + private IFilesMetadataManager $filesMetadataManager, + private LoggerInterface $logger + ) { + } + + /** + * @param Event $event + */ + public function handle(Event $event): void { + if (!($event instanceof NodeWrittenEvent)) { + return; + } + + try { + $this->filesMetadataManager->refreshMetadata($event->getNode()); + } catch (Exception $e) { + $this->logger->warning('issue while running MetadataUpdate', ['exception' => $e]); + } + } +} diff --git a/lib/private/FilesMetadata/MetadataQuery.php b/lib/private/FilesMetadata/MetadataQuery.php new file mode 100644 index 00000000000..aac93a23446 --- /dev/null +++ b/lib/private/FilesMetadata/MetadataQuery.php @@ -0,0 +1,176 @@ +<?php + +declare(strict_types=1); +/** + * SPDX-FileCopyrightText: 2023 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later + */ + +namespace OC\FilesMetadata; + +use OC\FilesMetadata\Model\FilesMetadata; +use OC\FilesMetadata\Service\IndexRequestService; +use OC\FilesMetadata\Service\MetadataRequestService; +use OCP\DB\QueryBuilder\IQueryBuilder; +use OCP\FilesMetadata\Exceptions\FilesMetadataNotFoundException; +use OCP\FilesMetadata\Exceptions\FilesMetadataTypeException; +use OCP\FilesMetadata\IFilesMetadataManager; +use OCP\FilesMetadata\IMetadataQuery; +use OCP\FilesMetadata\Model\IFilesMetadata; +use OCP\FilesMetadata\Model\IMetadataValueWrapper; +use Psr\Log\LoggerInterface; + +/** + * @inheritDoc + * @since 28.0.0 + */ +class MetadataQuery implements IMetadataQuery { + private array $knownJoinedIndex = []; + public function __construct( + private IQueryBuilder $queryBuilder, + private IFilesMetadata|IFilesMetadataManager $manager, + private string $fileTableAlias = 'fc', + private string $fileIdField = 'fileid', + private string $alias = 'meta', + private string $aliasIndexPrefix = 'meta_index' + ) { + if ($manager instanceof IFilesMetadata) { + /** + * Since 29, because knownMetadata is stored in lazy appconfig, it seems smarter + * to not call getKnownMetadata() at the load of this class as it is only needed + * in {@see getMetadataValueField}. + * + * FIXME: remove support for IFilesMetadata + */ + $logger = \OCP\Server::get(LoggerInterface::class); + $logger->debug('It is deprecated to use IFilesMetadata as second parameter when calling MetadataQuery::__construct()'); + } + } + + /** + * @inheritDoc + * @see self::extractMetadata() + * @since 28.0.0 + */ + public function retrieveMetadata(): void { + $this->queryBuilder->selectAlias($this->alias . '.json', 'meta_json'); + $this->queryBuilder->selectAlias($this->alias . '.sync_token', 'meta_sync_token'); + $this->queryBuilder->leftJoin( + $this->fileTableAlias, MetadataRequestService::TABLE_METADATA, $this->alias, + $this->queryBuilder->expr()->eq($this->fileTableAlias . '.' . $this->fileIdField, $this->alias . '.file_id') + ); + } + + /** + * @param array $row result row + * + * @inheritDoc + * @return IFilesMetadata metadata + * @see self::retrieveMetadata() + * @since 28.0.0 + */ + public function extractMetadata(array $row): IFilesMetadata { + $fileId = (array_key_exists($this->fileIdField, $row)) ? $row[$this->fileIdField] : 0; + $metadata = new FilesMetadata((int)$fileId); + try { + $metadata->importFromDatabase($row, $this->alias . '_'); + } catch (FilesMetadataNotFoundException) { + // can be ignored as files' metadata are optional and might not exist in database + } + + return $metadata; + } + + /** + * @param string $metadataKey metadata key + * @param bool $enforce limit the request only to existing metadata + * + * @inheritDoc + * @since 28.0.0 + */ + public function joinIndex(string $metadataKey, bool $enforce = false): string { + if (array_key_exists($metadataKey, $this->knownJoinedIndex)) { + return $this->knownJoinedIndex[$metadataKey]; + } + + $aliasIndex = $this->aliasIndexPrefix . '_' . count($this->knownJoinedIndex); + $this->knownJoinedIndex[$metadataKey] = $aliasIndex; + + $expr = $this->queryBuilder->expr(); + $andX = $expr->andX($expr->eq($aliasIndex . '.file_id', $this->fileTableAlias . '.' . $this->fileIdField)); + $andX->add($expr->eq($this->getMetadataKeyField($metadataKey), $this->queryBuilder->createNamedParameter($metadataKey))); + + if ($enforce) { + $this->queryBuilder->innerJoin( + $this->fileTableAlias, + IndexRequestService::TABLE_METADATA_INDEX, + $aliasIndex, + $andX + ); + } else { + $this->queryBuilder->leftJoin( + $this->fileTableAlias, + IndexRequestService::TABLE_METADATA_INDEX, + $aliasIndex, + $andX + ); + } + + return $aliasIndex; + } + + /** + * @throws FilesMetadataNotFoundException + */ + private function joinedTableAlias(string $metadataKey): string { + if (!array_key_exists($metadataKey, $this->knownJoinedIndex)) { + throw new FilesMetadataNotFoundException('table related to ' . $metadataKey . ' not initiated, you need to use leftJoin() first.'); + } + + return $this->knownJoinedIndex[$metadataKey]; + } + + /** + * @inheritDoc + * + * @param string $metadataKey metadata key + * + * @return string table field + * @throws FilesMetadataNotFoundException + * @since 28.0.0 + */ + public function getMetadataKeyField(string $metadataKey): string { + return $this->joinedTableAlias($metadataKey) . '.meta_key'; + } + + /** + * @inheritDoc + * + * @param string $metadataKey metadata key + * + * @return string table field + * @throws FilesMetadataNotFoundException if metadataKey is not known + * @throws FilesMetadataTypeException is metadataKey is not set as indexed + * @since 28.0.0 + */ + public function getMetadataValueField(string $metadataKey): string { + if ($this->manager instanceof IFilesMetadataManager) { + /** + * Since 29, because knownMetadata is stored in lazy appconfig, it seems smarter + * to not call getKnownMetadata() at the load of this class as it is only needed + * in this method. + * + * FIXME: keep only this line and remove support for previous IFilesMetadata in constructor + */ + $knownMetadata = $this->manager->getKnownMetadata(); + } else { + $knownMetadata = $this->manager; + } + + return match ($knownMetadata->getType($metadataKey)) { + IMetadataValueWrapper::TYPE_STRING => $this->joinedTableAlias($metadataKey) . '.meta_value_string', + IMetadataValueWrapper::TYPE_INT, IMetadataValueWrapper::TYPE_BOOL => $this->joinedTableAlias($metadataKey) . '.meta_value_int', + default => throw new FilesMetadataTypeException('metadata is not set as indexed'), + }; + } +} diff --git a/lib/private/FilesMetadata/Model/FilesMetadata.php b/lib/private/FilesMetadata/Model/FilesMetadata.php new file mode 100644 index 00000000000..dfeb429507a --- /dev/null +++ b/lib/private/FilesMetadata/Model/FilesMetadata.php @@ -0,0 +1,621 @@ +<?php + +declare(strict_types=1); +/** + * SPDX-FileCopyrightText: 2023 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later + */ + +namespace OC\FilesMetadata\Model; + +use JsonException; +use OCP\FilesMetadata\Exceptions\FilesMetadataKeyFormatException; +use OCP\FilesMetadata\Exceptions\FilesMetadataNotFoundException; +use OCP\FilesMetadata\Exceptions\FilesMetadataTypeException; +use OCP\FilesMetadata\Model\IFilesMetadata; +use OCP\FilesMetadata\Model\IMetadataValueWrapper; + +/** + * Model that represent metadata linked to a specific file. + * + * @inheritDoc + * @since 28.0.0 + */ +class FilesMetadata implements IFilesMetadata { + /** @var array<string, MetadataValueWrapper> */ + private array $metadata = []; + private bool $updated = false; + private int $lastUpdate = 0; + private string $syncToken = ''; + + public function __construct( + private int $fileId = 0 + ) { + } + + /** + * @inheritDoc + * @return int related file id + * @since 28.0.0 + */ + public function getFileId(): int { + return $this->fileId; + } + + /** + * @inheritDoc + * @return int timestamp + * @since 28.0.0 + */ + public function lastUpdateTimestamp(): int { + return $this->lastUpdate; + } + + /** + * @inheritDoc + * @return string token + * @since 28.0.0 + */ + public function getSyncToken(): string { + return $this->syncToken; + } + + /** + * @inheritDoc + * @return string[] list of keys + * @since 28.0.0 + */ + public function getKeys(): array { + return array_keys($this->metadata); + } + + /** + * @param string $needle metadata key to search + * + * @inheritDoc + * @return bool TRUE if key exist + * @since 28.0.0 + */ + public function hasKey(string $needle): bool { + return (in_array($needle, $this->getKeys())); + } + + /** + * @inheritDoc + * @return string[] list of indexes + * @since 28.0.0 + */ + public function getIndexes(): array { + $indexes = []; + foreach ($this->getKeys() as $key) { + if ($this->metadata[$key]->isIndexed()) { + $indexes[] = $key; + } + } + + return $indexes; + } + + /** + * @param string $key metadata key + * + * @inheritDoc + * @return bool TRUE if key exists and is set as indexed + * @since 28.0.0 + */ + public function isIndex(string $key): bool { + return $this->metadata[$key]?->isIndexed() ?? false; + } + + /** + * @param string $key metadata key + * + * @inheritDoc + * @return int edit permission + * @throws FilesMetadataNotFoundException + * @since 28.0.0 + */ + public function getEditPermission(string $key): int { + if (!array_key_exists($key, $this->metadata)) { + throw new FilesMetadataNotFoundException(); + } + + return $this->metadata[$key]->getEditPermission(); + } + + /** + * @param string $key metadata key + * @param int $permission edit permission + * + * @inheritDoc + * @throws FilesMetadataNotFoundException + * @since 28.0.0 + */ + public function setEditPermission(string $key, int $permission): void { + if (!array_key_exists($key, $this->metadata)) { + throw new FilesMetadataNotFoundException(); + } + + $this->metadata[$key]->setEditPermission($permission); + } + + + public function getEtag(string $key): string { + if (!array_key_exists($key, $this->metadata)) { + return ''; + } + + return $this->metadata[$key]->getEtag(); + } + + public function setEtag(string $key, string $etag): void { + if (!array_key_exists($key, $this->metadata)) { + throw new FilesMetadataNotFoundException(); + } + + $this->metadata[$key]->setEtag($etag); + } + + /** + * @param string $key metadata key + * + * @inheritDoc + * @return string metadata value + * @throws FilesMetadataNotFoundException + * @throws FilesMetadataTypeException + * @since 28.0.0 + */ + public function getString(string $key): string { + if (!array_key_exists($key, $this->metadata)) { + throw new FilesMetadataNotFoundException(); + } + + return $this->metadata[$key]->getValueString(); + } + + /** + * @param string $key metadata key + * + * @inheritDoc + * @return int metadata value + * @throws FilesMetadataNotFoundException + * @throws FilesMetadataTypeException + * @since 28.0.0 + */ + public function getInt(string $key): int { + if (!array_key_exists($key, $this->metadata)) { + throw new FilesMetadataNotFoundException(); + } + + return $this->metadata[$key]->getValueInt(); + } + + /** + * @param string $key metadata key + * + * @inheritDoc + * @return float metadata value + * @throws FilesMetadataNotFoundException + * @throws FilesMetadataTypeException + * @since 28.0.0 + */ + public function getFloat(string $key): float { + if (!array_key_exists($key, $this->metadata)) { + throw new FilesMetadataNotFoundException(); + } + + return $this->metadata[$key]->getValueFloat(); + } + + /** + * @param string $key metadata key + * + * @inheritDoc + * @return bool metadata value + * @throws FilesMetadataNotFoundException + * @throws FilesMetadataTypeException + * @since 28.0.0 + */ + public function getBool(string $key): bool { + if (!array_key_exists($key, $this->metadata)) { + throw new FilesMetadataNotFoundException(); + } + + return $this->metadata[$key]->getValueBool(); + } + + /** + * @param string $key metadata key + * + * @inheritDoc + * @return array metadata value + * @throws FilesMetadataNotFoundException + * @throws FilesMetadataTypeException + * @since 28.0.0 + */ + public function getArray(string $key): array { + if (!array_key_exists($key, $this->metadata)) { + throw new FilesMetadataNotFoundException(); + } + + return $this->metadata[$key]->getValueArray(); + } + + /** + * @param string $key metadata key + * + * @inheritDoc + * @return string[] metadata value + * @throws FilesMetadataNotFoundException + * @throws FilesMetadataTypeException + * @since 28.0.0 + */ + public function getStringList(string $key): array { + if (!array_key_exists($key, $this->metadata)) { + throw new FilesMetadataNotFoundException(); + } + + return $this->metadata[$key]->getValueStringList(); + } + + /** + * @param string $key metadata key + * + * @inheritDoc + * @return int[] metadata value + * @throws FilesMetadataNotFoundException + * @throws FilesMetadataTypeException + * @since 28.0.0 + */ + public function getIntList(string $key): array { + if (!array_key_exists($key, $this->metadata)) { + throw new FilesMetadataNotFoundException(); + } + + return $this->metadata[$key]->getValueIntList(); + } + + /** + * @param string $key metadata key + * + * @inheritDoc + * @return string value type + * @throws FilesMetadataNotFoundException + * @see IMetadataValueWrapper::TYPE_STRING + * @see IMetadataValueWrapper::TYPE_INT + * @see IMetadataValueWrapper::TYPE_FLOAT + * @see IMetadataValueWrapper::TYPE_BOOL + * @see IMetadataValueWrapper::TYPE_ARRAY + * @see IMetadataValueWrapper::TYPE_STRING_LIST + * @see IMetadataValueWrapper::TYPE_INT_LIST + * @since 28.0.0 + */ + public function getType(string $key): string { + if (!array_key_exists($key, $this->metadata)) { + throw new FilesMetadataNotFoundException(); + } + + return $this->metadata[$key]->getType(); + } + + /** + * @param string $key metadata key + * @param string $value metadata value + * @param bool $index set TRUE if value must be indexed + * + * @inheritDoc + * @return self + * @throws FilesMetadataKeyFormatException + * @since 28.0.0 + */ + public function setString(string $key, string $value, bool $index = false): IFilesMetadata { + $this->confirmKeyFormat($key); + try { + if ($this->getString($key) === $value && $index === $this->isIndex($key)) { + return $this; // we ignore if value and index have not changed + } + } catch (FilesMetadataNotFoundException|FilesMetadataTypeException $e) { + // if value does not exist, or type has changed, we keep on the writing + } + + $meta = new MetadataValueWrapper(IMetadataValueWrapper::TYPE_STRING); + $this->updated = true; + $this->metadata[$key] = $meta->setValueString($value)->setIndexed($index); + + return $this; + } + + /** + * @param string $key metadata key + * @param int $value metadata value + * @param bool $index set TRUE if value must be indexed + * + * @inheritDoc + * @return self + * @throws FilesMetadataKeyFormatException + * @since 28.0.0 + */ + public function setInt(string $key, int $value, bool $index = false): IFilesMetadata { + $this->confirmKeyFormat($key); + try { + if ($this->getInt($key) === $value && $index === $this->isIndex($key)) { + return $this; // we ignore if value have not changed + } + } catch (FilesMetadataNotFoundException|FilesMetadataTypeException $e) { + // if value does not exist, or type has changed, we keep on the writing + } + + $meta = new MetadataValueWrapper(IMetadataValueWrapper::TYPE_INT); + $this->metadata[$key] = $meta->setValueInt($value)->setIndexed($index); + $this->updated = true; + + return $this; + } + + /** + * @param string $key metadata key + * @param float $value metadata value + * + * @inheritDoc + * @return self + * @throws FilesMetadataKeyFormatException + * @since 28.0.0 + */ + public function setFloat(string $key, float $value, bool $index = false): IFilesMetadata { + $this->confirmKeyFormat($key); + try { + if ($this->getFloat($key) === $value && $index === $this->isIndex($key)) { + return $this; // we ignore if value have not changed + } + } catch (FilesMetadataNotFoundException|FilesMetadataTypeException $e) { + // if value does not exist, or type has changed, we keep on the writing + } + + $meta = new MetadataValueWrapper(IMetadataValueWrapper::TYPE_FLOAT); + $this->metadata[$key] = $meta->setValueFloat($value)->setIndexed($index); + $this->updated = true; + + return $this; + } + + + /** + * @param string $key metadata key + * @param bool $value metadata value + * @param bool $index set TRUE if value must be indexed + * + * @inheritDoc + * @return self + * @throws FilesMetadataKeyFormatException + * @since 28.0.0 + */ + public function setBool(string $key, bool $value, bool $index = false): IFilesMetadata { + $this->confirmKeyFormat($key); + try { + if ($this->getBool($key) === $value && $index === $this->isIndex($key)) { + return $this; // we ignore if value have not changed + } + } catch (FilesMetadataNotFoundException|FilesMetadataTypeException $e) { + // if value does not exist, or type has changed, we keep on the writing + } + + $meta = new MetadataValueWrapper(IMetadataValueWrapper::TYPE_BOOL); + $this->metadata[$key] = $meta->setValueBool($value)->setIndexed($index); + $this->updated = true; + + return $this; + } + + + /** + * @param string $key metadata key + * @param array $value metadata value + * + * @inheritDoc + * @return self + * @throws FilesMetadataKeyFormatException + * @since 28.0.0 + */ + public function setArray(string $key, array $value): IFilesMetadata { + $this->confirmKeyFormat($key); + try { + if ($this->getArray($key) === $value) { + return $this; // we ignore if value have not changed + } + } catch (FilesMetadataNotFoundException|FilesMetadataTypeException $e) { + // if value does not exist, or type has changed, we keep on the writing + } + + $meta = new MetadataValueWrapper(IMetadataValueWrapper::TYPE_ARRAY); + $this->metadata[$key] = $meta->setValueArray($value); + $this->updated = true; + + return $this; + } + + /** + * @param string $key metadata key + * @param string[] $value metadata value + * @param bool $index set TRUE if each values from the list must be indexed + * + * @inheritDoc + * @return self + * @throws FilesMetadataKeyFormatException + * @since 28.0.0 + */ + public function setStringList(string $key, array $value, bool $index = false): IFilesMetadata { + $this->confirmKeyFormat($key); + try { + if ($this->getStringList($key) === $value) { + return $this; // we ignore if value have not changed + } + } catch (FilesMetadataNotFoundException|FilesMetadataTypeException $e) { + // if value does not exist, or type has changed, we keep on the writing + } + + $meta = new MetadataValueWrapper(IMetadataValueWrapper::TYPE_STRING_LIST); + $this->metadata[$key] = $meta->setValueStringList($value)->setIndexed($index); + $this->updated = true; + + return $this; + } + + /** + * @param string $key metadata key + * @param int[] $value metadata value + * @param bool $index set TRUE if each values from the list must be indexed + * + * @inheritDoc + * @return self + * @throws FilesMetadataKeyFormatException + * @since 28.0.0 + */ + public function setIntList(string $key, array $value, bool $index = false): IFilesMetadata { + $this->confirmKeyFormat($key); + try { + if ($this->getIntList($key) === $value) { + return $this; // we ignore if value have not changed + } + } catch (FilesMetadataNotFoundException|FilesMetadataTypeException $e) { + // if value does not exist, or type has changed, we keep on the writing + } + + $valueWrapper = new MetadataValueWrapper(IMetadataValueWrapper::TYPE_INT_LIST); + $this->metadata[$key] = $valueWrapper->setValueIntList($value)->setIndexed($index); + $this->updated = true; + + return $this; + } + + /** + * @param string $key metadata key + * + * @inheritDoc + * @return self + * @since 28.0.0 + */ + public function unset(string $key): IFilesMetadata { + if (!array_key_exists($key, $this->metadata)) { + return $this; + } + + unset($this->metadata[$key]); + $this->updated = true; + + return $this; + } + + /** + * @param string $keyPrefix metadata key prefix + * + * @inheritDoc + * @return self + * @since 28.0.0 + */ + public function removeStartsWith(string $keyPrefix): IFilesMetadata { + if ($keyPrefix === '') { + return $this; + } + + foreach ($this->getKeys() as $key) { + if (str_starts_with($key, $keyPrefix)) { + $this->unset($key); + } + } + + return $this; + } + + /** + * @param string $key + * + * @return void + * @throws FilesMetadataKeyFormatException + */ + private function confirmKeyFormat(string $key): void { + $acceptedChars = ['-', '_']; + if (ctype_alnum(str_replace($acceptedChars, '', $key))) { + return; + } + + throw new FilesMetadataKeyFormatException('key can only contains alphanumerical characters, and dash (-, _)'); + } + + /** + * @inheritDoc + * @return bool TRUE if metadata have been modified + * @since 28.0.0 + */ + public function updated(): bool { + return $this->updated; + } + + public function jsonSerialize(bool $emptyValues = false): array { + $data = []; + foreach ($this->metadata as $metaKey => $metaValueWrapper) { + $data[$metaKey] = $metaValueWrapper->jsonSerialize($emptyValues); + } + + return $data; + } + + /** + * @return array<string, string|int|bool|float|string[]|int[]> + */ + public function asArray(): array { + $data = []; + foreach ($this->metadata as $metaKey => $metaValueWrapper) { + try { + $data[$metaKey] = $metaValueWrapper->getValueAny(); + } catch (FilesMetadataNotFoundException $e) { + // ignore exception + } + } + + return $data; + } + + /** + * @param array $data + * + * @inheritDoc + * @return IFilesMetadata + * @since 28.0.0 + */ + public function import(array $data): IFilesMetadata { + foreach ($data as $k => $v) { + $valueWrapper = new MetadataValueWrapper(); + $this->metadata[$k] = $valueWrapper->import($v); + } + $this->updated = false; + + return $this; + } + + /** + * import data from database to configure this model + * + * @param array $data + * @param string $prefix + * + * @return IFilesMetadata + * @throws FilesMetadataNotFoundException + * @since 28.0.0 + */ + public function importFromDatabase(array $data, string $prefix = ''): IFilesMetadata { + try { + $this->syncToken = $data[$prefix . 'sync_token'] ?? ''; + + return $this->import( + json_decode( + $data[$prefix . 'json'] ?? '[]', + true, + 512, + JSON_THROW_ON_ERROR + ) + ); + } catch (JsonException) { + throw new FilesMetadataNotFoundException(); + } + } +} diff --git a/lib/private/FilesMetadata/Model/MetadataValueWrapper.php b/lib/private/FilesMetadata/Model/MetadataValueWrapper.php new file mode 100644 index 00000000000..710a8129340 --- /dev/null +++ b/lib/private/FilesMetadata/Model/MetadataValueWrapper.php @@ -0,0 +1,428 @@ +<?php + +declare(strict_types=1); +/** + * SPDX-FileCopyrightText: 2023 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later + */ + +namespace OC\FilesMetadata\Model; + +use OCP\FilesMetadata\Exceptions\FilesMetadataNotFoundException; +use OCP\FilesMetadata\Exceptions\FilesMetadataTypeException; +use OCP\FilesMetadata\Model\IMetadataValueWrapper; + +/** + * @inheritDoc + * @see IFilesMetadata + * @since 28.0.0 + */ +class MetadataValueWrapper implements IMetadataValueWrapper { + private string $type; + /** @var string|int|float|bool|array|string[]|int[] */ + private mixed $value = null; + private string $etag = ''; + private bool $indexed = false; + private int $editPermission = self::EDIT_FORBIDDEN; + + /** + * @param string $type value type + * + * @inheritDoc + * @see self::TYPE_INT + * @see self::TYPE_FLOAT + * @see self::TYPE_BOOL + * @see self::TYPE_ARRAY + * @see self::TYPE_STRING_LIST + * @see self::TYPE_INT_LIST + * @see self::TYPE_STRING + * @since 28.0.0 + */ + public function __construct(string $type = '') { + $this->type = $type; + } + + /** + * @inheritDoc + * @return string value type + * @see self::TYPE_INT + * @see self::TYPE_FLOAT + * @see self::TYPE_BOOL + * @see self::TYPE_ARRAY + * @see self::TYPE_STRING_LIST + * @see self::TYPE_INT_LIST + * @see self::TYPE_STRING + * @since 28.0.0 + */ + public function getType(): string { + return $this->type; + } + + /** + * @param string $type value type + * + * @inheritDoc + * @return bool + * @see self::TYPE_INT + * @see self::TYPE_FLOAT + * @see self::TYPE_BOOL + * @see self::TYPE_ARRAY + * @see self::TYPE_STRING_LIST + * @see self::TYPE_INT_LIST + * @see self::TYPE_STRING + * @since 28.0.0 + */ + public function isType(string $type): bool { + return (strtolower($type) === strtolower($this->type)); + } + + /** + * @param string $type value type + * + * @inheritDoc + * @return self + * @throws FilesMetadataTypeException if type cannot be confirmed + * @see self::TYPE_INT + * @see self::TYPE_BOOL + * @see self::TYPE_ARRAY + * @see self::TYPE_STRING_LIST + * @see self::TYPE_INT_LIST + * @see self::TYPE_STRING + * @see self::TYPE_FLOAT + * @since 28.0.0 + */ + public function assertType(string $type): self { + if (!$this->isType($type)) { + throw new FilesMetadataTypeException('type is \'' . $this->getType() . '\', expecting \'' . $type . '\''); + } + + return $this; + } + + /** + * @param string $value string to be set as value + * + * @inheritDoc + * @return self + * @throws FilesMetadataTypeException if wrapper was not set to store a string + * @since 28.0.0 + */ + public function setValueString(string $value): self { + $this->assertType(self::TYPE_STRING); + $this->value = $value; + + return $this; + } + + /** + * @param int $value int to be set as value + * + * @inheritDoc + * @return self + * @throws FilesMetadataTypeException if wrapper was not set to store an int + * @since 28.0.0 + */ + public function setValueInt(int $value): self { + $this->assertType(self::TYPE_INT); + $this->value = $value; + + return $this; + } + + /** + * @param float $value float to be set as value + * + * @inheritDoc + * @return self + * @throws FilesMetadataTypeException if wrapper was not set to store a float + * @since 28.0.0 + */ + public function setValueFloat(float $value): self { + $this->assertType(self::TYPE_FLOAT); + $this->value = $value; + + return $this; + } + + /** + * @param bool $value bool to be set as value + * + * @inheritDoc + * @return self + * @throws FilesMetadataTypeException if wrapper was not set to store a bool + * @since 28.0.0 + */ + public function setValueBool(bool $value): self { + $this->assertType(self::TYPE_BOOL); + $this->value = $value; + + + return $this; + } + + /** + * @param array $value array to be set as value + * + * @inheritDoc + * @return self + * @throws FilesMetadataTypeException if wrapper was not set to store an array + * @since 28.0.0 + */ + public function setValueArray(array $value): self { + $this->assertType(self::TYPE_ARRAY); + $this->value = $value; + + return $this; + } + + /** + * @param string[] $value string list to be set as value + * + * @inheritDoc + * @return self + * @throws FilesMetadataTypeException if wrapper was not set to store a string list + * @since 28.0.0 + */ + public function setValueStringList(array $value): self { + $this->assertType(self::TYPE_STRING_LIST); + // TODO confirm value is an array or string ? + $this->value = $value; + + return $this; + } + + /** + * @param int[] $value int list to be set as value + * + * @inheritDoc + * @return self + * @throws FilesMetadataTypeException if wrapper was not set to store an int list + * @since 28.0.0 + */ + public function setValueIntList(array $value): self { + $this->assertType(self::TYPE_INT_LIST); + // TODO confirm value is an array of int ? + $this->value = $value; + + return $this; + } + + + /** + * @inheritDoc + * @return string set value + * @throws FilesMetadataTypeException if wrapper was not set to store a string + * @throws FilesMetadataNotFoundException if value is not set + * @since 28.0.0 + */ + public function getValueString(): string { + $this->assertType(self::TYPE_STRING); + if ($this->value === null) { + throw new FilesMetadataNotFoundException('value is not set'); + } + + return (string)$this->value; + } + + /** + * @inheritDoc + * @return int set value + * @throws FilesMetadataTypeException if wrapper was not set to store an int + * @throws FilesMetadataNotFoundException if value is not set + * @since 28.0.0 + */ + public function getValueInt(): int { + $this->assertType(self::TYPE_INT); + if ($this->value === null) { + throw new FilesMetadataNotFoundException('value is not set'); + } + + return (int)$this->value; + } + + /** + * @inheritDoc + * @return float set value + * @throws FilesMetadataTypeException if wrapper was not set to store a float + * @throws FilesMetadataNotFoundException if value is not set + * @since 28.0.0 + */ + public function getValueFloat(): float { + $this->assertType(self::TYPE_FLOAT); + if ($this->value === null) { + throw new FilesMetadataNotFoundException('value is not set'); + } + + return (float)$this->value; + } + + /** + * @inheritDoc + * @return bool set value + * @throws FilesMetadataTypeException if wrapper was not set to store a bool + * @throws FilesMetadataNotFoundException if value is not set + * @since 28.0.0 + */ + public function getValueBool(): bool { + $this->assertType(self::TYPE_BOOL); + if ($this->value === null) { + throw new FilesMetadataNotFoundException('value is not set'); + } + + return (bool)$this->value; + } + + /** + * @inheritDoc + * @return array set value + * @throws FilesMetadataTypeException if wrapper was not set to store an array + * @throws FilesMetadataNotFoundException if value is not set + * @since 28.0.0 + */ + public function getValueArray(): array { + $this->assertType(self::TYPE_ARRAY); + if ($this->value === null) { + throw new FilesMetadataNotFoundException('value is not set'); + } + + return (array)$this->value; + } + + /** + * @inheritDoc + * @return string[] set value + * @throws FilesMetadataTypeException if wrapper was not set to store a string list + * @throws FilesMetadataNotFoundException if value is not set + * @since 28.0.0 + */ + public function getValueStringList(): array { + $this->assertType(self::TYPE_STRING_LIST); + if ($this->value === null) { + throw new FilesMetadataNotFoundException('value is not set'); + } + + return (array)$this->value; + } + + /** + * @inheritDoc + * @return int[] set value + * @throws FilesMetadataTypeException if wrapper was not set to store an int list + * @throws FilesMetadataNotFoundException if value is not set + * @since 28.0.0 + */ + public function getValueIntList(): array { + $this->assertType(self::TYPE_INT_LIST); + if ($this->value === null) { + throw new FilesMetadataNotFoundException('value is not set'); + } + + return (array)$this->value; + } + + /** + * @inheritDoc + * @return string|int|float|bool|array|string[]|int[] set value + * @throws FilesMetadataNotFoundException if value is not set + * @since 28.0.0 + */ + public function getValueAny(): mixed { + if ($this->value === null) { + throw new FilesMetadataNotFoundException('value is not set'); + } + + return $this->value; + } + + /** + * @inheritDoc + * @return string stored etag + * @since 29.0.0 + */ + public function getEtag(): string { + return $this->etag; + } + + /** + * @param string $etag etag value + * + * @inheritDoc + * @return self + * @since 29.0.0 + */ + public function setEtag(string $etag): self { + $this->etag = $etag; + return $this; + } + + /** + * @param bool $indexed TRUE to set the stored value as an indexed value + * + * @inheritDoc + * @return self + * @since 28.0.0 + */ + public function setIndexed(bool $indexed): self { + $this->indexed = $indexed; + + return $this; + } + + /** + * @inheritDoc + * @return bool TRUE if value is an indexed value + * @since 28.0.0 + */ + public function isIndexed(): bool { + return $this->indexed; + } + + /** + * @param int $permission edit permission + * + * @inheritDoc + * @return self + * @since 28.0.0 + */ + public function setEditPermission(int $permission): self { + $this->editPermission = $permission; + + return $this; + } + + /** + * @inheritDoc + * @return int edit permission + * @since 28.0.0 + */ + public function getEditPermission(): int { + return $this->editPermission; + } + + /** + * @param array $data serialized version of the object + * + * @inheritDoc + * @return self + * @see jsonSerialize + * @since 28.0.0 + */ + public function import(array $data): self { + $this->value = $data['value'] ?? null; + $this->type = $data['type'] ?? ''; + $this->setEtag($data['etag'] ?? ''); + $this->setIndexed($data['indexed'] ?? false); + $this->setEditPermission($data['editPermission'] ?? self::EDIT_FORBIDDEN); + return $this; + } + + public function jsonSerialize(bool $emptyValues = false): array { + return [ + 'value' => ($emptyValues) ? null : $this->value, + 'type' => $this->getType(), + 'etag' => $this->getEtag(), + 'indexed' => $this->isIndexed(), + 'editPermission' => $this->getEditPermission() + ]; + } +} diff --git a/lib/private/FilesMetadata/Service/IndexRequestService.php b/lib/private/FilesMetadata/Service/IndexRequestService.php new file mode 100644 index 00000000000..32248ff5c24 --- /dev/null +++ b/lib/private/FilesMetadata/Service/IndexRequestService.php @@ -0,0 +1,178 @@ +<?php + +declare(strict_types=1); +/** + * SPDX-FileCopyrightText: 2023 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later + */ + +namespace OC\FilesMetadata\Service; + +use OCP\DB\Exception as DbException; +use OCP\DB\QueryBuilder\IQueryBuilder; +use OCP\FilesMetadata\Exceptions\FilesMetadataNotFoundException; +use OCP\FilesMetadata\Exceptions\FilesMetadataTypeException; +use OCP\FilesMetadata\Model\IFilesMetadata; +use OCP\FilesMetadata\Model\IMetadataValueWrapper; +use OCP\IDBConnection; +use Psr\Log\LoggerInterface; + +/** + * manage sql request to the metadata_index table + */ +class IndexRequestService { + public const TABLE_METADATA_INDEX = 'files_metadata_index'; + + public function __construct( + private IDBConnection $dbConnection, + private LoggerInterface $logger + ) { + } + + /** + * update the index for a specific metadata key + * + * @param IFilesMetadata $filesMetadata metadata + * @param string $key metadata key to update + * + * @throws DbException + */ + public function updateIndex(IFilesMetadata $filesMetadata, string $key): void { + $fileId = $filesMetadata->getFileId(); + try { + $metadataType = $filesMetadata->getType($key); + } catch (FilesMetadataNotFoundException $e) { + return; + } + + /** + * might look harsh, but a lot simpler than comparing current indexed data, as we can expect + * conflict with a change of types. + * We assume that each time one random metadata were modified we can drop all index for this + * key and recreate them. + * To make it slightly cleaner, we'll use transaction + */ + $this->dbConnection->beginTransaction(); + try { + $this->dropIndex($fileId, $key); + match ($metadataType) { + IMetadataValueWrapper::TYPE_STRING => $this->insertIndexString($fileId, $key, $filesMetadata->getString($key)), + IMetadataValueWrapper::TYPE_INT => $this->insertIndexInt($fileId, $key, $filesMetadata->getInt($key)), + IMetadataValueWrapper::TYPE_BOOL => $this->insertIndexBool($fileId, $key, $filesMetadata->getBool($key)), + IMetadataValueWrapper::TYPE_STRING_LIST => $this->insertIndexStringList($fileId, $key, $filesMetadata->getStringList($key)), + IMetadataValueWrapper::TYPE_INT_LIST => $this->insertIndexIntList($fileId, $key, $filesMetadata->getIntList($key)) + }; + } catch (FilesMetadataNotFoundException|FilesMetadataTypeException|DbException $e) { + $this->dbConnection->rollBack(); + $this->logger->warning('issue while updateIndex', ['exception' => $e, 'fileId' => $fileId, 'key' => $key]); + } + + $this->dbConnection->commit(); + } + + /** + * insert a new entry in the metadata_index table for a string value + * + * @param int $fileId file id + * @param string $key metadata key + * @param string $value metadata value + * + * @throws DbException + */ + private function insertIndexString(int $fileId, string $key, string $value): void { + $qb = $this->dbConnection->getQueryBuilder(); + $qb->insert(self::TABLE_METADATA_INDEX) + ->setValue('meta_key', $qb->createNamedParameter($key)) + ->setValue('meta_value_string', $qb->createNamedParameter($value)) + ->setValue('file_id', $qb->createNamedParameter($fileId, IQueryBuilder::PARAM_INT)); + $qb->executeStatement(); + } + + /** + * insert a new entry in the metadata_index table for an int value + * + * @param int $fileId file id + * @param string $key metadata key + * @param int $value metadata value + * + * @throws DbException + */ + public function insertIndexInt(int $fileId, string $key, int $value): void { + $qb = $this->dbConnection->getQueryBuilder(); + $qb->insert(self::TABLE_METADATA_INDEX) + ->setValue('meta_key', $qb->createNamedParameter($key)) + ->setValue('meta_value_int', $qb->createNamedParameter($value, IQueryBuilder::PARAM_INT)) + ->setValue('file_id', $qb->createNamedParameter($fileId, IQueryBuilder::PARAM_INT)); + $qb->executeStatement(); + } + + /** + * insert a new entry in the metadata_index table for a bool value + * + * @param int $fileId file id + * @param string $key metadata key + * @param bool $value metadata value + * + * @throws DbException + */ + public function insertIndexBool(int $fileId, string $key, bool $value): void { + $qb = $this->dbConnection->getQueryBuilder(); + $qb->insert(self::TABLE_METADATA_INDEX) + ->setValue('meta_key', $qb->createNamedParameter($key)) + ->setValue('meta_value_int', $qb->createNamedParameter(($value) ? '1' : '0', IQueryBuilder::PARAM_INT)) + ->setValue('file_id', $qb->createNamedParameter($fileId, IQueryBuilder::PARAM_INT)); + $qb->executeStatement(); + } + + /** + * insert entries in the metadata_index table for list of string + * + * @param int $fileId file id + * @param string $key metadata key + * @param string[] $values metadata values + * + * @throws DbException + */ + public function insertIndexStringList(int $fileId, string $key, array $values): void { + foreach ($values as $value) { + $this->insertIndexString($fileId, $key, $value); + } + } + + /** + * insert entries in the metadata_index table for list of int + * + * @param int $fileId file id + * @param string $key metadata key + * @param int[] $values metadata values + * + * @throws DbException + */ + public function insertIndexIntList(int $fileId, string $key, array $values): void { + foreach ($values as $value) { + $this->insertIndexInt($fileId, $key, $value); + } + } + + /** + * drop indexes related to a file id + * if a key is specified, only drop entries related to it + * + * @param int $fileId file id + * @param string $key metadata key + * + * @throws DbException + */ + public function dropIndex(int $fileId, string $key = ''): void { + $qb = $this->dbConnection->getQueryBuilder(); + $expr = $qb->expr(); + $qb->delete(self::TABLE_METADATA_INDEX) + ->where($expr->eq('file_id', $qb->createNamedParameter($fileId, IQueryBuilder::PARAM_INT))); + + if ($key !== '') { + $qb->andWhere($expr->eq('meta_key', $qb->createNamedParameter($key))); + } + + $qb->executeStatement(); + } +} diff --git a/lib/private/FilesMetadata/Service/MetadataRequestService.php b/lib/private/FilesMetadata/Service/MetadataRequestService.php new file mode 100644 index 00000000000..08982a2a659 --- /dev/null +++ b/lib/private/FilesMetadata/Service/MetadataRequestService.php @@ -0,0 +1,169 @@ +<?php + +declare(strict_types=1); +/** + * SPDX-FileCopyrightText: 2023 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later + */ + +namespace OC\FilesMetadata\Service; + +use OC\FilesMetadata\Model\FilesMetadata; +use OCP\DB\Exception; +use OCP\DB\QueryBuilder\IQueryBuilder; +use OCP\FilesMetadata\Exceptions\FilesMetadataNotFoundException; +use OCP\FilesMetadata\Model\IFilesMetadata; +use OCP\IDBConnection; +use Psr\Log\LoggerInterface; + +/** + * manage sql request to the metadata table + */ +class MetadataRequestService { + public const TABLE_METADATA = 'files_metadata'; + + public function __construct( + private IDBConnection $dbConnection, + private LoggerInterface $logger + ) { + } + + /** + * store metadata into database + * + * @param IFilesMetadata $filesMetadata + * + * @throws Exception + */ + public function store(IFilesMetadata $filesMetadata): void { + $qb = $this->dbConnection->getQueryBuilder(); + $qb->insert(self::TABLE_METADATA) + ->setValue('file_id', $qb->createNamedParameter($filesMetadata->getFileId(), IQueryBuilder::PARAM_INT)) + ->setValue('json', $qb->createNamedParameter(json_encode($filesMetadata->jsonSerialize()))) + ->setValue('sync_token', $qb->createNamedParameter($this->generateSyncToken())) + ->setValue('last_update', (string) $qb->createFunction('NOW()')); + $qb->executeStatement(); + } + + /** + * returns metadata for a file id + * + * @param int $fileId file id + * + * @return IFilesMetadata + * @throws FilesMetadataNotFoundException if no metadata are found in database + */ + public function getMetadataFromFileId(int $fileId): IFilesMetadata { + try { + $qb = $this->dbConnection->getQueryBuilder(); + $qb->select('json', 'sync_token')->from(self::TABLE_METADATA); + $qb->where($qb->expr()->eq('file_id', $qb->createNamedParameter($fileId, IQueryBuilder::PARAM_INT))); + $result = $qb->executeQuery(); + $data = $result->fetch(); + $result->closeCursor(); + } catch (Exception $e) { + $this->logger->warning('exception while getMetadataFromDatabase()', ['exception' => $e, 'fileId' => $fileId]); + throw new FilesMetadataNotFoundException(); + } + + if ($data === false) { + throw new FilesMetadataNotFoundException(); + } + + $metadata = new FilesMetadata($fileId); + $metadata->importFromDatabase($data); + + return $metadata; + } + + /** + * returns metadata for multiple file ids + * + * @param array $fileIds file ids + * + * @return array File ID is the array key, files without metadata are not returned in the array + * @psalm-return array<int, IFilesMetadata> + */ + public function getMetadataFromFileIds(array $fileIds): array { + $qb = $this->dbConnection->getQueryBuilder(); + $qb->select('file_id', 'json', 'sync_token')->from(self::TABLE_METADATA); + $qb->where($qb->expr()->in('file_id', $qb->createNamedParameter($fileIds, IQueryBuilder::PARAM_INT_ARRAY))); + + $list = []; + $result = $qb->executeQuery(); + while ($data = $result->fetch()) { + $fileId = (int) $data['file_id']; + $metadata = new FilesMetadata($fileId); + try { + $metadata->importFromDatabase($data); + } catch (FilesMetadataNotFoundException) { + continue; + } + $list[$fileId] = $metadata; + } + $result->closeCursor(); + + return $list; + } + + /** + * drop metadata related to a file id + * + * @param int $fileId file id + * + * @return void + * @throws Exception + */ + public function dropMetadata(int $fileId): void { + $qb = $this->dbConnection->getQueryBuilder(); + $qb->delete(self::TABLE_METADATA) + ->where($qb->expr()->eq('file_id', $qb->createNamedParameter($fileId, IQueryBuilder::PARAM_INT))); + $qb->executeStatement(); + } + + /** + * update metadata in the database + * + * @param IFilesMetadata $filesMetadata metadata + * + * @return int number of affected rows + * @throws Exception + */ + public function updateMetadata(IFilesMetadata $filesMetadata): int { + $qb = $this->dbConnection->getQueryBuilder(); + $expr = $qb->expr(); + + $qb->update(self::TABLE_METADATA) + ->set('json', $qb->createNamedParameter(json_encode($filesMetadata->jsonSerialize()))) + ->set('sync_token', $qb->createNamedParameter($this->generateSyncToken())) + ->set('last_update', $qb->createFunction('NOW()')) + ->where( + $expr->andX( + $expr->eq('file_id', $qb->createNamedParameter($filesMetadata->getFileId(), IQueryBuilder::PARAM_INT)), + $expr->eq('sync_token', $qb->createNamedParameter($filesMetadata->getSyncToken())) + ) + ); + + return $qb->executeStatement(); + } + + /** + * generate a random token + * @return string + */ + private function generateSyncToken(): string { + $chars = 'qwertyuiopasdfghjklzxcvbnmQWERTYUIOPASDFGHJKLZXCVBNM1234567890'; + + $str = ''; + $max = strlen($chars); + for ($i = 0; $i < 7; $i++) { + try { + $str .= $chars[random_int(0, $max - 2)]; + } catch (\Exception $e) { + $this->logger->warning('exception during generateSyncToken', ['exception' => $e]); + } + } + + return $str; + } +} diff --git a/lib/private/ForbiddenException.php b/lib/private/ForbiddenException.php index 8cc9a698253..e65576d27ad 100644 --- a/lib/private/ForbiddenException.php +++ b/lib/private/ForbiddenException.php @@ -1,24 +1,9 @@ <?php + /** - * @copyright Copyright (c) 2016, ownCloud, Inc. - * - * @author Morris Jobke <hey@morrisjobke.de> - * @author Vincent Petry <vincent@nextcloud.com> - * - * @license AGPL-3.0 - * - * This code is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License, version 3, - * as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License, version 3, - * along with this program. If not, see <http://www.gnu.org/licenses/> - * + * SPDX-FileCopyrightText: 2016-2024 Nextcloud GmbH and Nextcloud contributors + * SPDX-FileCopyrightText: 2016 ownCloud, Inc. + * SPDX-License-Identifier: AGPL-3.0-only */ namespace OC; diff --git a/lib/private/FullTextSearch/FullTextSearchManager.php b/lib/private/FullTextSearch/FullTextSearchManager.php index 8d850513949..3ef8547ad3f 100644 --- a/lib/private/FullTextSearch/FullTextSearchManager.php +++ b/lib/private/FullTextSearch/FullTextSearchManager.php @@ -3,25 +3,8 @@ declare(strict_types=1); /** - * @copyright 2018, Maxence Lange <maxence@artificial-owl.com> - * - * @author Maxence Lange <maxence@artificial-owl.com> - * - * @license GNU AGPL version 3 or any later version - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as - * published by the Free Software Foundation, either version 3 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - * + * SPDX-FileCopyrightText: 2018 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later */ namespace OC\FullTextSearch; @@ -39,47 +22,35 @@ use OCP\FullTextSearch\Service\ISearchService; * @package OC\FullTextSearch */ class FullTextSearchManager implements IFullTextSearchManager { - /** @var IProviderService */ - private $providerService; + private ?IProviderService $providerService = null; - /** @var IIndexService */ - private $indexService; - - /** @var ISearchService */ - private $searchService; + private ?IIndexService $indexService = null; + private ?ISearchService $searchService = null; /** * @since 15.0.0 - * - * @param IProviderService $providerService */ - public function registerProviderService(IProviderService $providerService) { + public function registerProviderService(IProviderService $providerService): void { $this->providerService = $providerService; } /** * @since 15.0.0 - * - * @param IIndexService $indexService */ - public function registerIndexService(IIndexService $indexService) { + public function registerIndexService(IIndexService $indexService): void { $this->indexService = $indexService; } /** * @since 15.0.0 - * - * @param ISearchService $searchService */ - public function registerSearchService(ISearchService $searchService) { + public function registerSearchService(ISearchService $searchService): void { $this->searchService = $searchService; } /** * @since 16.0.0 - * - * @return bool */ public function isAvailable(): bool { if ($this->indexService === null || @@ -93,7 +64,6 @@ class FullTextSearchManager implements IFullTextSearchManager { /** - * @return IProviderService * @throws FullTextSearchAppNotAvailableException */ private function getProviderService(): IProviderService { @@ -106,7 +76,6 @@ class FullTextSearchManager implements IFullTextSearchManager { /** - * @return IIndexService * @throws FullTextSearchAppNotAvailableException */ private function getIndexService(): IIndexService { @@ -119,7 +88,6 @@ class FullTextSearchManager implements IFullTextSearchManager { /** - * @return ISearchService * @throws FullTextSearchAppNotAvailableException */ private function getSearchService(): ISearchService { @@ -134,15 +102,12 @@ class FullTextSearchManager implements IFullTextSearchManager { /** * @throws FullTextSearchAppNotAvailableException */ - public function addJavascriptAPI() { + public function addJavascriptAPI(): void { $this->getProviderService()->addJavascriptAPI(); } /** - * @param string $providerId - * - * @return bool * @throws FullTextSearchAppNotAvailableException */ public function isProviderIndexed(string $providerId): bool { @@ -151,9 +116,6 @@ class FullTextSearchManager implements IFullTextSearchManager { /** - * @param string $providerId - * @param string $documentId - * @return IIndex * @throws FullTextSearchAppNotAvailableException */ public function getIndex(string $providerId, string $documentId): IIndex { @@ -161,46 +123,45 @@ class FullTextSearchManager implements IFullTextSearchManager { } /** - * @param string $providerId - * @param string $documentId - * @param string $userId - * @param int $status - * * @see IIndex for available value for $status. * - * @return IIndex * @throws FullTextSearchAppNotAvailableException */ - public function createIndex(string $providerId, string $documentId, string $userId, int $status = 0): IIndex { + public function createIndex( + string $providerId, + string $documentId, + string $userId, + int $status = 0, + ): IIndex { return $this->getIndexService()->createIndex($providerId, $documentId, $userId, $status); } /** - * @param string $providerId - * @param string $documentId - * @param int $status - * @param bool $reset - * * @see IIndex for available value for $status. * * @throws FullTextSearchAppNotAvailableException */ - public function updateIndexStatus(string $providerId, string $documentId, int $status, bool $reset = false) { + public function updateIndexStatus( + string $providerId, + string $documentId, + int $status, + bool $reset = false, + ): void { $this->getIndexService()->updateIndexStatus($providerId, $documentId, $status, $reset); } /** - * @param string $providerId - * @param array $documentIds - * @param int $status - * @param bool $reset - * * @see IIndex for available value for $status. * * @throws FullTextSearchAppNotAvailableException */ - public function updateIndexesStatus(string $providerId, array $documentIds, int $status, bool $reset = false) { + public function updateIndexesStatus( + string $providerId, + array $documentIds, + int $status, + bool $reset = false, + ): void { $this->getIndexService()->updateIndexesStatus($providerId, $documentIds, $status, $reset); } @@ -210,15 +171,12 @@ class FullTextSearchManager implements IFullTextSearchManager { * * @throws FullTextSearchAppNotAvailableException */ - public function updateIndexes(array $indexes) { + public function updateIndexes(array $indexes): void { $this->getIndexService()->updateIndexes($indexes); } /** - * @param array $request - * @param string $userId - * * @return ISearchResult[] * @throws FullTextSearchAppNotAvailableException */ diff --git a/lib/private/FullTextSearch/Model/DocumentAccess.php b/lib/private/FullTextSearch/Model/DocumentAccess.php index cb2b95284f1..9efffeaee88 100644 --- a/lib/private/FullTextSearch/Model/DocumentAccess.php +++ b/lib/private/FullTextSearch/Model/DocumentAccess.php @@ -1,27 +1,9 @@ <?php declare(strict_types=1); - /** - * @copyright 2018 - * - * @author Maxence Lange <maxence@artificial-owl.com> - * - * @license GNU AGPL version 3 or any later version - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as - * published by the Free Software Foundation, either version 3 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - * + * SPDX-FileCopyrightText: 2019 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later */ namespace OC\FullTextSearch\Model; @@ -49,23 +31,17 @@ use OCP\FullTextSearch\Model\IDocumentAccess; * @package OC\FullTextSearch\Model */ final class DocumentAccess implements IDocumentAccess, JsonSerializable { - /** @var string */ - private $ownerId; + private string $ownerId; - /** @var string */ - private $viewerId = ''; + private string $viewerId = ''; - /** @var array */ - private $users = []; + private array $users = []; - /** @var array */ - private $groups = []; + private array $groups = []; - /** @var array */ - private $circles = []; + private array $circles = []; - /** @var array */ - private $links = []; + private array $links = []; /** @@ -74,8 +50,6 @@ final class DocumentAccess implements IDocumentAccess, JsonSerializable { * @since 16.0.0 * * IDocumentAccess constructor. - * - * @param string $ownerId */ public function __construct(string $ownerId = '') { $this->setOwnerId($ownerId); @@ -86,10 +60,6 @@ final class DocumentAccess implements IDocumentAccess, JsonSerializable { * Set the Owner of the document. * * @since 16.0.0 - * - * @param string $ownerId - * - * @return IDocumentAccess */ public function setOwnerId(string $ownerId): IDocumentAccess { $this->ownerId = $ownerId; @@ -101,8 +71,6 @@ final class DocumentAccess implements IDocumentAccess, JsonSerializable { * Get the Owner of the document. * * @since 16.0.0 - * - * @return string */ public function getOwnerId(): string { return $this->ownerId; @@ -113,10 +81,6 @@ final class DocumentAccess implements IDocumentAccess, JsonSerializable { * Set the viewer of the document. * * @since 16.0.0 - * - * @param string $viewerId - * - * @return IDocumentAccess */ public function setViewerId(string $viewerId): IDocumentAccess { $this->viewerId = $viewerId; @@ -128,8 +92,6 @@ final class DocumentAccess implements IDocumentAccess, JsonSerializable { * Get the viewer of the document. * * @since 16.0.0 - * - * @return string */ public function getViewerId(): string { return $this->viewerId; @@ -140,10 +102,6 @@ final class DocumentAccess implements IDocumentAccess, JsonSerializable { * Set the list of users that have read access to the document. * * @since 16.0.0 - * - * @param array $users - * - * @return IDocumentAccess */ public function setUsers(array $users): IDocumentAccess { $this->users = $users; @@ -155,10 +113,6 @@ final class DocumentAccess implements IDocumentAccess, JsonSerializable { * Add an entry to the list of users that have read access to the document. * * @since 16.0.0 - * - * @param string $user - * - * @return IDocumentAccess */ public function addUser(string $user): IDocumentAccess { $this->users[] = $user; @@ -171,10 +125,6 @@ final class DocumentAccess implements IDocumentAccess, JsonSerializable { * document. * * @since 16.0.0 - * - * @param array $users - * - * @return IDocumentAccess */ public function addUsers($users): IDocumentAccess { $this->users = array_merge($this->users, $users); @@ -186,8 +136,6 @@ final class DocumentAccess implements IDocumentAccess, JsonSerializable { * Get the complete list of users that have read access to the document. * * @since 16.0.0 - * - * @return array */ public function getUsers(): array { return $this->users; @@ -198,10 +146,6 @@ final class DocumentAccess implements IDocumentAccess, JsonSerializable { * Set the list of groups that have read access to the document. * * @since 16.0.0 - * - * @param array $groups - * - * @return IDocumentAccess */ public function setGroups(array $groups): IDocumentAccess { $this->groups = $groups; @@ -213,10 +157,6 @@ final class DocumentAccess implements IDocumentAccess, JsonSerializable { * Add an entry to the list of groups that have read access to the document. * * @since 16.0.0 - * - * @param string $group - * - * @return IDocumentAccess */ public function addGroup(string $group): IDocumentAccess { $this->groups[] = $group; @@ -229,12 +169,8 @@ final class DocumentAccess implements IDocumentAccess, JsonSerializable { * document. * * @since 16.0.0 - * - * @param array $groups - * - * @return IDocumentAccess */ - public function addGroups(array $groups) { + public function addGroups(array $groups): IDocumentAccess { $this->groups = array_merge($this->groups, $groups); return $this; @@ -244,8 +180,6 @@ final class DocumentAccess implements IDocumentAccess, JsonSerializable { * Get the complete list of groups that have read access to the document. * * @since 16.0.0 - * - * @return array */ public function getGroups(): array { return $this->groups; @@ -256,10 +190,6 @@ final class DocumentAccess implements IDocumentAccess, JsonSerializable { * Set the list of circles that have read access to the document. * * @since 16.0.0 - * - * @param array $circles - * - * @return IDocumentAccess */ public function setCircles(array $circles): IDocumentAccess { $this->circles = $circles; @@ -271,10 +201,6 @@ final class DocumentAccess implements IDocumentAccess, JsonSerializable { * Add an entry to the list of circles that have read access to the document. * * @since 16.0.0 - * - * @param string $circle - * - * @return IDocumentAccess */ public function addCircle(string $circle): IDocumentAccess { $this->circles[] = $circle; @@ -287,10 +213,6 @@ final class DocumentAccess implements IDocumentAccess, JsonSerializable { * document. * * @since 16.0.0 - * - * @param array $circles - * - * @return IDocumentAccess */ public function addCircles(array $circles): IDocumentAccess { $this->circles = array_merge($this->circles, $circles); @@ -302,8 +224,6 @@ final class DocumentAccess implements IDocumentAccess, JsonSerializable { * Get the complete list of circles that have read access to the document. * * @since 16.0.0 - * - * @return array */ public function getCircles(): array { return $this->circles; @@ -314,10 +234,6 @@ final class DocumentAccess implements IDocumentAccess, JsonSerializable { * Set the list of links that have read access to the document. * * @since 16.0.0 - * - * @param array $links - * - * @return IDocumentAccess */ public function setLinks(array $links): IDocumentAccess { $this->links = $links; @@ -329,8 +245,6 @@ final class DocumentAccess implements IDocumentAccess, JsonSerializable { * Get the list of links that have read access to the document. * * @since 16.0.0 - * - * @return array */ public function getLinks(): array { return $this->links; @@ -339,8 +253,6 @@ final class DocumentAccess implements IDocumentAccess, JsonSerializable { /** * @since 16.0.0 - * - * @return array */ public function jsonSerialize(): array { return [ diff --git a/lib/private/FullTextSearch/Model/IndexDocument.php b/lib/private/FullTextSearch/Model/IndexDocument.php index 74788463693..6f9da9416fa 100644 --- a/lib/private/FullTextSearch/Model/IndexDocument.php +++ b/lib/private/FullTextSearch/Model/IndexDocument.php @@ -1,31 +1,14 @@ <?php declare(strict_types=1); - /** - * @copyright 2018 - * - * @author Maxence Lange <maxence@artificial-owl.com> - * - * @license GNU AGPL version 3 or any later version - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as - * published by the Free Software Foundation, either version 3 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - * + * SPDX-FileCopyrightText: 2019 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later */ namespace OC\FullTextSearch\Model; use JsonSerializable; +use OCP\FullTextSearch\Exceptions\FullTextSearchIndexNotAvailableException; use OCP\FullTextSearch\Model\IDocumentAccess; use OCP\FullTextSearch\Model\IIndex; use OCP\FullTextSearch\Model\IIndexDocument; @@ -47,62 +30,41 @@ use OCP\FullTextSearch\Model\IIndexDocument; * @package OC\FullTextSearch\Model */ class IndexDocument implements IIndexDocument, JsonSerializable { - /** @var string */ - protected $id = ''; - - /** @var string */ - protected $providerId = ''; + protected string $id = ''; - /** @var DocumentAccess */ - protected $access; + protected DocumentAccess $access; - /** @var IIndex */ - protected $index; + protected ?IIndex $index = null; - /** @var int */ - protected $modifiedTime = 0; + protected int $modifiedTime = 0; - /** @var string */ - protected $source = ''; + protected string $source = ''; - /** @var array */ - protected $tags = []; + protected array $tags = []; - /** @var array */ - protected $metaTags = []; + protected array $metaTags = []; - /** @var array */ - protected $subTags = []; + protected array $subTags = []; - /** @var string */ - protected $title = ''; + protected string $title = ''; - /** @var string */ - protected $content = ''; + protected string $content = ''; - /** @var string */ - protected $hash = ''; + protected string $hash = ''; - /** @var array */ - protected $parts = []; + protected array $parts = []; - /** @var string */ - protected $link = ''; + protected string $link = ''; - /** @var array */ - protected $more = []; + protected array $more = []; - /** @var array */ - protected $excerpts = []; + protected array $excerpts = []; - /** @var string */ - protected $score = ''; + protected string $score = ''; - /** @var array */ - protected $info = []; + protected array $info = []; - /** @var int */ - protected $contentEncoded = 0; + protected int $contentEncoded = 0; /** @@ -112,12 +74,11 @@ class IndexDocument implements IIndexDocument, JsonSerializable { * and the Id of the original document. * * @since 15.0.0 - * - * @param string $providerId - * @param string $documentId */ - public function __construct(string $providerId, string $documentId) { - $this->providerId = $providerId; + public function __construct( + protected string $providerId, + string $documentId, + ) { $this->id = $documentId; } @@ -126,8 +87,6 @@ class IndexDocument implements IIndexDocument, JsonSerializable { * Returns the Id of the original document. * * @since 15.0.0 - * - * @return string */ final public function getId(): string { return $this->id; @@ -138,8 +97,6 @@ class IndexDocument implements IIndexDocument, JsonSerializable { * Returns the Id of the provider. * * @since 15.0.0 - * - * @return string */ final public function getProviderId(): string { return $this->providerId; @@ -152,10 +109,6 @@ class IndexDocument implements IIndexDocument, JsonSerializable { * @see IIndex * * @since 15.0.0 - * - * @param IIndex $index - * - * @return IIndexDocument */ final public function setIndex(IIndex $index): IIndexDocument { $this->index = $index; @@ -166,11 +119,14 @@ class IndexDocument implements IIndexDocument, JsonSerializable { /** * Get the Index. * + * @throws FullTextSearchIndexNotAvailableException * @since 15.0.0 - * - * @return IIndex */ final public function getIndex(): IIndex { + if ($this->index === null) { + throw new FullTextSearchIndexNotAvailableException('No IIndex generated'); + } + return $this->index; } @@ -178,22 +134,15 @@ class IndexDocument implements IIndexDocument, JsonSerializable { * return if Index is defined. * * @since 16.0.0 - * - * @return bool */ final public function hasIndex(): bool { - return ($this->index !== null); + return $this->index !== null; } - /** * Set the modified time of the original document. * * @since 15.0.0 - * - * @param int $modifiedTime - * - * @return IIndexDocument */ final public function setModifiedTime(int $modifiedTime): IIndexDocument { $this->modifiedTime = $modifiedTime; @@ -205,8 +154,6 @@ class IndexDocument implements IIndexDocument, JsonSerializable { * Get the modified time of the original document. * * @since 15.0.0 - * - * @return int */ final public function getModifiedTime(): int { return $this->modifiedTime; @@ -216,10 +163,6 @@ class IndexDocument implements IIndexDocument, JsonSerializable { * Check if the original document of the IIndexDocument is older than $time. * * @since 15.0.0 - * - * @param int $time - * - * @return bool */ final public function isOlderThan(int $time): bool { return ($this->modifiedTime < $time); @@ -232,10 +175,6 @@ class IndexDocument implements IIndexDocument, JsonSerializable { * @see IDocumentAccess * * @since 15.0.0 - * - * @param IDocumentAccess $access - * - * @return $this */ final public function setAccess(IDocumentAccess $access): IIndexDocument { $this->access = $access; @@ -247,8 +186,6 @@ class IndexDocument implements IIndexDocument, JsonSerializable { * Get the IDocumentAccess related to the original document. * * @since 15.0.0 - * - * @return IDocumentAccess */ final public function getAccess(): IDocumentAccess { return $this->access; @@ -259,10 +196,6 @@ class IndexDocument implements IIndexDocument, JsonSerializable { * Add a tag to the list. * * @since 15.0.0 - * - * @param string $tag - * - * @return IIndexDocument */ final public function addTag(string $tag): IIndexDocument { $this->tags[] = $tag; @@ -274,10 +207,6 @@ class IndexDocument implements IIndexDocument, JsonSerializable { * Set the list of tags assigned to the original document. * * @since 15.0.0 - * - * @param array $tags - * - * @return IIndexDocument */ final public function setTags(array $tags): IIndexDocument { $this->tags = $tags; @@ -289,8 +218,6 @@ class IndexDocument implements IIndexDocument, JsonSerializable { * Get the list of tags assigned to the original document. * * @since 15.0.0 - * - * @return array */ final public function getTags(): array { return $this->tags; @@ -301,10 +228,6 @@ class IndexDocument implements IIndexDocument, JsonSerializable { * Add a meta tag to the list. * * @since 15.0.0 - * - * @param string $tag - * - * @return IIndexDocument */ final public function addMetaTag(string $tag): IIndexDocument { $this->metaTags[] = $tag; @@ -316,10 +239,6 @@ class IndexDocument implements IIndexDocument, JsonSerializable { * Set the list of meta tags assigned to the original document. * * @since 15.0.0 - * - * @param array $tags - * - * @return IIndexDocument */ final public function setMetaTags(array $tags): IIndexDocument { $this->metaTags = $tags; @@ -331,8 +250,6 @@ class IndexDocument implements IIndexDocument, JsonSerializable { * Get the list of meta tags assigned to the original document. * * @since 15.0.0 - * - * @return array */ final public function getMetaTags(): array { return $this->metaTags; @@ -343,11 +260,6 @@ class IndexDocument implements IIndexDocument, JsonSerializable { * Add a sub tag to the list. * * @since 15.0.0 - * - * @param string $sub - * @param string $tag - * - * @return IIndexDocument */ final public function addSubTag(string $sub, string $tag): IIndexDocument { if (!array_key_exists($sub, $this->subTags)) { @@ -364,10 +276,6 @@ class IndexDocument implements IIndexDocument, JsonSerializable { * Set the list of sub tags assigned to the original document. * * @since 15.0.0 - * - * @param array $tags - * - * @return IIndexDocument */ final public function setSubTags(array $tags): IIndexDocument { $this->subTags = $tags; @@ -381,10 +289,6 @@ class IndexDocument implements IIndexDocument, JsonSerializable { * dimensional array. * * @since 15.0.0 - * - * @param bool $formatted - * - * @return array */ final public function getSubTags(bool $formatted = false): array { if ($formatted === false) { @@ -408,10 +312,6 @@ class IndexDocument implements IIndexDocument, JsonSerializable { * Set the source of the original document. * * @since 15.0.0 - * - * @param string $source - * - * @return IIndexDocument */ final public function setSource(string $source): IIndexDocument { $this->source = $source; @@ -423,8 +323,6 @@ class IndexDocument implements IIndexDocument, JsonSerializable { * Get the source of the original document. * * @since 15.0.0 - * - * @return string */ final public function getSource(): string { return $this->source; @@ -435,10 +333,6 @@ class IndexDocument implements IIndexDocument, JsonSerializable { * Set the title of the original document. * * @since 15.0.0 - * - * @param string $title - * - * @return IIndexDocument */ final public function setTitle(string $title): IIndexDocument { $this->title = $title; @@ -450,8 +344,6 @@ class IndexDocument implements IIndexDocument, JsonSerializable { * Get the title of the original document. * * @since 15.0.0 - * - * @return string */ final public function getTitle(): string { return $this->title; @@ -464,11 +356,6 @@ class IndexDocument implements IIndexDocument, JsonSerializable { * encoded in base64. * * @since 15.0.0 - * - * @param string $content - * @param int $encoded - * - * @return IIndexDocument */ final public function setContent(string $content, int $encoded = 0): IIndexDocument { $this->content = $content; @@ -481,8 +368,6 @@ class IndexDocument implements IIndexDocument, JsonSerializable { * Get the content of the original document. * * @since 15.0.0 - * - * @return string */ final public function getContent(): string { return $this->content; @@ -492,8 +377,6 @@ class IndexDocument implements IIndexDocument, JsonSerializable { * Returns the type of the encoding on the content. * * @since 15.0.0 - * - * @return int */ final public function isContentEncoded(): int { return $this->contentEncoded; @@ -503,8 +386,6 @@ class IndexDocument implements IIndexDocument, JsonSerializable { * Return the size of the content. * * @since 15.0.0 - * - * @return int */ final public function getContentSize(): int { return strlen($this->getContent()); @@ -512,11 +393,9 @@ class IndexDocument implements IIndexDocument, JsonSerializable { /** - * Generate an hash, based on the content of the original document. + * Generate a hash, based on the content of the original document. * * @since 15.0.0 - * - * @return IIndexDocument */ final public function initHash(): IIndexDocument { if ($this->getContent() === '' || is_null($this->getContent())) { @@ -532,10 +411,6 @@ class IndexDocument implements IIndexDocument, JsonSerializable { * Set the hash of the original document. * * @since 15.0.0 - * - * @param string $hash - * - * @return IIndexDocument */ final public function setHash(string $hash): IIndexDocument { $this->hash = $hash; @@ -547,8 +422,6 @@ class IndexDocument implements IIndexDocument, JsonSerializable { * Get the hash of the original document. * * @since 15.0.0 - * - * @return string */ final public function getHash(): string { return $this->hash; @@ -562,11 +435,6 @@ class IndexDocument implements IIndexDocument, JsonSerializable { * $part string. * * @since 15.0.0 - * - * @param string $part - * @param string $content - * - * @return IIndexDocument */ final public function addPart(string $part, string $content): IIndexDocument { $this->parts[$part] = $content; @@ -578,10 +446,6 @@ class IndexDocument implements IIndexDocument, JsonSerializable { * Set all parts and their content. * * @since 15.0.0 - * - * @param array $parts - * - * @return IIndexDocument */ final public function setParts(array $parts): IIndexDocument { $this->parts = $parts; @@ -593,8 +457,6 @@ class IndexDocument implements IIndexDocument, JsonSerializable { * Get all parts of the IIndexDocument. * * @since 15.0.0 - * - * @return array */ final public function getParts(): array { return $this->parts; @@ -605,10 +467,6 @@ class IndexDocument implements IIndexDocument, JsonSerializable { * Add a link, usable by the frontend. * * @since 15.0.0 - * - * @param string $link - * - * @return IIndexDocument */ final public function setLink(string $link): IIndexDocument { $this->link = $link; @@ -620,8 +478,6 @@ class IndexDocument implements IIndexDocument, JsonSerializable { * Get the link. * * @since 15.0.0 - * - * @return string */ final public function getLink(): string { return $this->link; @@ -632,10 +488,6 @@ class IndexDocument implements IIndexDocument, JsonSerializable { * Set more information that couldn't be set using other method. * * @since 15.0.0 - * - * @param array $more - * - * @return IIndexDocument */ final public function setMore(array $more): IIndexDocument { $this->more = $more; @@ -647,8 +499,6 @@ class IndexDocument implements IIndexDocument, JsonSerializable { * Get more information. * * @since 15.0.0 - * - * @return array */ final public function getMore(): array { return $this->more; @@ -660,11 +510,6 @@ class IndexDocument implements IIndexDocument, JsonSerializable { * on the search request. * * @since 16.0.0 - * - * @param string $source - * @param string $excerpt - * - * @return IIndexDocument */ final public function addExcerpt(string $source, string $excerpt): IIndexDocument { $this->excerpts[] = @@ -681,10 +526,6 @@ class IndexDocument implements IIndexDocument, JsonSerializable { * Set all excerpts of the content of the original document. * * @since 16.0.0 - * - * @param array $excerpts - * - * @return IIndexDocument */ final public function setExcerpts(array $excerpts): IIndexDocument { $new = []; @@ -704,8 +545,6 @@ class IndexDocument implements IIndexDocument, JsonSerializable { * Get all excerpts of the content of the original document. * * @since 15.0.0 - * - * @return array */ final public function getExcerpts(): array { return $this->excerpts; @@ -715,9 +554,6 @@ class IndexDocument implements IIndexDocument, JsonSerializable { * Clean excerpt. * * @since 16.0.0 - * - * @param string $excerpt - * @return string */ private function cleanExcerpt(string $excerpt): string { $excerpt = str_replace("\\n", ' ', $excerpt); @@ -736,10 +572,6 @@ class IndexDocument implements IIndexDocument, JsonSerializable { * request. * * @since 15.0.0 - * - * @param string $score - * - * @return IIndexDocument */ final public function setScore(string $score): IIndexDocument { $this->score = $score; @@ -751,8 +583,6 @@ class IndexDocument implements IIndexDocument, JsonSerializable { * Get the score. * * @since 15.0.0 - * - * @return string */ final public function getScore(): string { return $this->score; @@ -767,11 +597,6 @@ class IndexDocument implements IIndexDocument, JsonSerializable { * indexing. * * @since 15.0.0 - * - * @param string $info - * @param string $value - * - * @return IIndexDocument */ final public function setInfo(string $info, string $value): IIndexDocument { $this->info[$info] = $value; @@ -783,11 +608,6 @@ class IndexDocument implements IIndexDocument, JsonSerializable { * Get an information about a document. (string) * * @since 15.0.0 - * - * @param string $info - * @param string $default - * - * @return string */ final public function getInfo(string $info, string $default = ''): string { if (!key_exists($info, $this->info)) { @@ -805,11 +625,6 @@ class IndexDocument implements IIndexDocument, JsonSerializable { * indexing. * * @since 15.0.0 - * - * @param string $info - * @param array $value - * - * @return IIndexDocument */ final public function setInfoArray(string $info, array $value): IIndexDocument { $this->info[$info] = $value; @@ -821,11 +636,6 @@ class IndexDocument implements IIndexDocument, JsonSerializable { * Get an information about a document. (array) * * @since 15.0.0 - * - * @param string $info - * @param array $default - * - * @return array */ final public function getInfoArray(string $info, array $default = []): array { if (!key_exists($info, $this->info)) { @@ -843,11 +653,6 @@ class IndexDocument implements IIndexDocument, JsonSerializable { * indexing. * * @since 15.0.0 - * - * @param string $info - * @param int $value - * - * @return IIndexDocument */ final public function setInfoInt(string $info, int $value): IIndexDocument { $this->info[$info] = $value; @@ -859,11 +664,6 @@ class IndexDocument implements IIndexDocument, JsonSerializable { * Get an information about a document. (int) * * @since 15.0.0 - * - * @param string $info - * @param int $default - * - * @return int */ final public function getInfoInt(string $info, int $default = 0): int { if (!key_exists($info, $this->info)) { @@ -881,11 +681,6 @@ class IndexDocument implements IIndexDocument, JsonSerializable { * indexing. * * @since 15.0.0 - * - * @param string $info - * @param bool $value - * - * @return IIndexDocument */ final public function setInfoBool(string $info, bool $value): IIndexDocument { $this->info[$info] = $value; @@ -897,11 +692,6 @@ class IndexDocument implements IIndexDocument, JsonSerializable { * Get an information about a document. (bool) * * @since 15.0.0 - * - * @param string $info - * @param bool $default - * - * @return bool */ final public function getInfoBool(string $info, bool $default = false): bool { if (!key_exists($info, $this->info)) { @@ -915,13 +705,11 @@ class IndexDocument implements IIndexDocument, JsonSerializable { * Get all info. * * @since 15.0.0 - * - * @return array */ final public function getInfoAll(): array { $info = []; foreach ($this->info as $k => $v) { - if (substr($k, 0, 1) === '_') { + if (str_starts_with($k, '_')) { continue; } diff --git a/lib/private/FullTextSearch/Model/SearchOption.php b/lib/private/FullTextSearch/Model/SearchOption.php index 91f45db5fb4..c7769a62138 100644 --- a/lib/private/FullTextSearch/Model/SearchOption.php +++ b/lib/private/FullTextSearch/Model/SearchOption.php @@ -1,27 +1,9 @@ <?php declare(strict_types=1); - /** - * @copyright 2018 - * - * @author Maxence Lange <maxence@artificial-owl.com> - * - * @license GNU AGPL version 3 or any later version - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as - * published by the Free Software Foundation, either version 3 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - * + * SPDX-FileCopyrightText: 2019 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later */ namespace OC\FullTextSearch\Model; @@ -36,22 +18,6 @@ use OCP\FullTextSearch\Model\ISearchOption; * @package OC\FullTextSearch\Model */ final class SearchOption implements ISearchOption, JsonSerializable { - /** @var string */ - private $name = ''; - - /** @var string */ - private $title = ''; - - /** @var string */ - private $type = ''; - - /** @var string */ - private $size = ''; - - /** @var string */ - private $placeholder = ''; - - /** * * * @@ -104,37 +70,28 @@ final class SearchOption implements ISearchOption, JsonSerializable { /** * ISearchOption constructor. * - * Some value can be setduring the creation of the object. + * Some value can be set during the creation of the object. * * @since 15.0.0 - * - * @param string $name - * @param string $title - * @param string $type - * @param string $size - * @param string $placeholder */ - public function __construct(string $name = '', string $title = '', string $type = '', string $size = '', string $placeholder = '') { - $this->name = $name; - $this->title = $title; - $this->type = $type; - $this->size = $size; - $this->placeholder = $placeholder; + public function __construct( + private string $name = '', + private string $title = '', + private string $type = '', + private string $size = '', + private string $placeholder = '', + ) { } /** * Set the name/key of the option. - * The string should only contains alphanumerical chars and underscore. - * The key can be retrieve when using ISearchRequest::getOption + * The string should only contain alphanumerical chars and underscore. + * The key can be retrieved when using ISearchRequest::getOption * * @see ISearchRequest::getOption * * @since 15.0.0 - * - * @param string $name - * - * @return ISearchOption */ public function setName(string $name): ISearchOption { $this->name = $name; @@ -146,8 +103,6 @@ final class SearchOption implements ISearchOption, JsonSerializable { * Get the name/key of the option. * * @since 15.0.0 - * - * @return string */ public function getName(): string { return $this->name; @@ -158,10 +113,6 @@ final class SearchOption implements ISearchOption, JsonSerializable { * Set the title/display name of the option. * * @since 15.0.0 - * - * @param string $title - * - * @return ISearchOption */ public function setTitle(string $title): ISearchOption { $this->title = $title; @@ -173,8 +124,6 @@ final class SearchOption implements ISearchOption, JsonSerializable { * Get the title of the option. * * @since 15.0.0 - * - * @return string */ public function getTitle(): string { return $this->title; @@ -186,10 +135,6 @@ final class SearchOption implements ISearchOption, JsonSerializable { * $type can be ISearchOption::CHECKBOX or ISearchOption::INPUT * * @since 15.0.0 - * - * @param string $type - * - * @return ISearchOption */ public function setType(string $type): ISearchOption { $this->type = $type; @@ -201,8 +146,6 @@ final class SearchOption implements ISearchOption, JsonSerializable { * Get the type of the option. * * @since 15.0.0 - * - * @return string */ public function getType(): string { return $this->type; @@ -214,10 +157,6 @@ final class SearchOption implements ISearchOption, JsonSerializable { * Value can be ISearchOption::INPUT_SMALL or not defined. * * @since 15.0.0 - * - * @param string $size - * - * @return ISearchOption */ public function setSize(string $size): ISearchOption { $this->size = $size; @@ -229,23 +168,16 @@ final class SearchOption implements ISearchOption, JsonSerializable { * Get the size of the INPUT. * * @since 15.0.0 - * - * @return string */ public function getSize(): string { return $this->size; } - /** * In case of Type is , set the placeholder to be displayed in the input * field. * * @since 15.0.0 - * - * @param string $placeholder - * - * @return ISearchOption */ public function setPlaceholder(string $placeholder): ISearchOption { $this->placeholder = $placeholder; @@ -257,18 +189,13 @@ final class SearchOption implements ISearchOption, JsonSerializable { * Get the placeholder. * * @since 15.0.0 - * - * @return string */ public function getPlaceholder(): string { return $this->placeholder; } - /** * @since 15.0.0 - * - * @return array */ public function jsonSerialize(): array { return [ diff --git a/lib/private/FullTextSearch/Model/SearchRequestSimpleQuery.php b/lib/private/FullTextSearch/Model/SearchRequestSimpleQuery.php index c58d55b9b55..5b075daf7e6 100644 --- a/lib/private/FullTextSearch/Model/SearchRequestSimpleQuery.php +++ b/lib/private/FullTextSearch/Model/SearchRequestSimpleQuery.php @@ -1,28 +1,9 @@ <?php declare(strict_types=1); - /** - * @copyright 2018 - * - * @author Christoph Wurst <christoph@winzerhof-wurst.at> - * @author Maxence Lange <maxence@artificial-owl.com> - * - * @license GNU AGPL version 3 or any later version - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as - * published by the Free Software Foundation, either version 3 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - * + * SPDX-FileCopyrightText: 2019 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later */ namespace OC\FullTextSearch\Model; @@ -37,34 +18,24 @@ use OCP\FullTextSearch\Model\ISearchRequestSimpleQuery; * @package OC\FullTextSearch\Model */ final class SearchRequestSimpleQuery implements ISearchRequestSimpleQuery, JsonSerializable { - /** @var int */ - private $type = 0; - - /** @var string */ - private $field = ''; - - /** @var array */ - private $values = []; + private array $values = []; /** * SearchRequestQuery constructor. * - * @param $type - * @param $field - * * @since 17.0.0 */ - public function __construct(string $field, int $type) { - $this->field = $field; - $this->type = $type; + public function __construct( + private string $field, + private int $type, + ) { } /** * Get the compare type of the query * - * @return int * @since 17.0.0 */ public function getType(): int { @@ -75,7 +46,6 @@ final class SearchRequestSimpleQuery implements ISearchRequestSimpleQuery, JsonS /** * Get the field to apply query * - * @return string * @since 17.0.0 */ public function getField(): string { @@ -85,9 +55,6 @@ final class SearchRequestSimpleQuery implements ISearchRequestSimpleQuery, JsonS /** * Set the field to apply query * - * @param string $field - * - * @return ISearchRequestSimpleQuery * @since 17.0.0 */ public function setField(string $field): ISearchRequestSimpleQuery { @@ -100,7 +67,6 @@ final class SearchRequestSimpleQuery implements ISearchRequestSimpleQuery, JsonS /** * Get the value to compare (string) * - * @return array * @since 17.0.0 */ public function getValues(): array { @@ -111,9 +77,6 @@ final class SearchRequestSimpleQuery implements ISearchRequestSimpleQuery, JsonS /** * Add value to compare (string) * - * @param string $value - * - * @return ISearchRequestSimpleQuery * @since 17.0.0 */ public function addValue(string $value): ISearchRequestSimpleQuery { @@ -125,9 +88,6 @@ final class SearchRequestSimpleQuery implements ISearchRequestSimpleQuery, JsonS /** * Add value to compare (int) * - * @param int $value - * - * @return ISearchRequestSimpleQuery * @since 17.0.0 */ public function addValueInt(int $value): ISearchRequestSimpleQuery { @@ -139,9 +99,6 @@ final class SearchRequestSimpleQuery implements ISearchRequestSimpleQuery, JsonS /** * Add value to compare (array) * - * @param array $value - * - * @return ISearchRequestSimpleQuery * @since 17.0.0 */ public function addValueArray(array $value): ISearchRequestSimpleQuery { @@ -153,9 +110,6 @@ final class SearchRequestSimpleQuery implements ISearchRequestSimpleQuery, JsonS /** * Add value to compare (bool) * - * @param bool $value - * - * @return ISearchRequestSimpleQuery * @since 17.0.0 */ public function addValueBool(bool $value): ISearchRequestSimpleQuery { diff --git a/lib/private/FullTextSearch/Model/SearchTemplate.php b/lib/private/FullTextSearch/Model/SearchTemplate.php index 4bb56f24b58..c2929bea139 100644 --- a/lib/private/FullTextSearch/Model/SearchTemplate.php +++ b/lib/private/FullTextSearch/Model/SearchTemplate.php @@ -3,25 +3,8 @@ declare(strict_types=1); /** - * @copyright 2018 - * - * @author Maxence Lange <maxence@artificial-owl.com> - * - * @license GNU AGPL version 3 or any later version - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as - * published by the Free Software Foundation, either version 3 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - * + * SPDX-FileCopyrightText: 2019 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later */ namespace OC\FullTextSearch\Model; @@ -56,21 +39,13 @@ use OCP\FullTextSearch\Model\ISearchTemplate; * @package OC\FullTextSearch\Model */ final class SearchTemplate implements ISearchTemplate, JsonSerializable { - /** @var string */ - private $icon = ''; - - /** @var string */ - private $css = ''; - - /** @var string */ - private $template = ''; + private string $template = ''; /** @var SearchOption[] */ - private $panelOptions = []; + private array $panelOptions = []; /** @var SearchOption[] */ - private $navigationOptions = []; - + private array $navigationOptions = []; /** * ISearchTemplate constructor. @@ -79,13 +54,11 @@ final class SearchTemplate implements ISearchTemplate, JsonSerializable { * creation of the object. * * @since 15.0.0 - * - * @param string $icon - * @param string $css */ - public function __construct(string $icon = '', string $css = '') { - $this->icon = $icon; - $this->css = $css; + public function __construct( + private string $icon = '', + private string $css = '', + ) { } @@ -94,10 +67,6 @@ final class SearchTemplate implements ISearchTemplate, JsonSerializable { * FullTextSearch navigation page, in front of the related Content Provider. * * @since 15.0.0 - * - * @param string $class - * - * @return ISearchTemplate */ public function setIcon(string $class): ISearchTemplate { $this->icon = $class; @@ -107,10 +76,6 @@ final class SearchTemplate implements ISearchTemplate, JsonSerializable { /** * Get the class of the icon. - * - * @since 15.0.0 - * - * @return string */ public function getIcon(): string { return $this->icon; @@ -121,10 +86,6 @@ final class SearchTemplate implements ISearchTemplate, JsonSerializable { * Set the path of a CSS file that will be loaded when needed. * * @since 15.0.0 - * - * @param string $css - * - * @return ISearchTemplate */ public function setCss(string $css): ISearchTemplate { $this->css = $css; @@ -136,8 +97,6 @@ final class SearchTemplate implements ISearchTemplate, JsonSerializable { * Get the path of the CSS file. * * @since 15.0.0 - * - * @return string */ public function getCss(): string { return $this->css; @@ -151,10 +110,6 @@ final class SearchTemplate implements ISearchTemplate, JsonSerializable { * a way not generated by FullTextSearch * * @since 15.0.0 - * - * @param string $template - * - * @return ISearchTemplate */ public function setTemplate(string $template): ISearchTemplate { $this->template = $template; @@ -166,8 +121,6 @@ final class SearchTemplate implements ISearchTemplate, JsonSerializable { * Get the path of the template file. * * @since 15.0.0 - * - * @return string */ public function getTemplate(): string { return $this->template; @@ -181,10 +134,6 @@ final class SearchTemplate implements ISearchTemplate, JsonSerializable { * @see ISearchOption * * @since 15.0.0 - * - * @param ISearchOption $option - * - * @return ISearchTemplate */ public function addPanelOption(ISearchOption $option): ISearchTemplate { $this->panelOptions[] = $option; @@ -210,10 +159,6 @@ final class SearchTemplate implements ISearchTemplate, JsonSerializable { * @see ISearchOption * * @since 15.0.0 - * - * @param ISearchOption $option - * - * @return ISearchTemplate */ public function addNavigationOption(ISearchOption $option): ISearchTemplate { $this->navigationOptions[] = $option; @@ -225,8 +170,6 @@ final class SearchTemplate implements ISearchTemplate, JsonSerializable { * Get all options to be displayed in the FullTextSearch navigation page. * * @since 15.0.0 - * - * @return array */ public function getNavigationOptions(): array { return $this->navigationOptions; @@ -235,8 +178,6 @@ final class SearchTemplate implements ISearchTemplate, JsonSerializable { /** * @since 15.0.0 - * - * @return array */ public function jsonSerialize(): array { return [ diff --git a/lib/private/GlobalScale/Config.php b/lib/private/GlobalScale/Config.php index 66abbfc15db..02acc1ab50a 100644 --- a/lib/private/GlobalScale/Config.php +++ b/lib/private/GlobalScale/Config.php @@ -1,24 +1,7 @@ <?php /** - * @copyright Copyright (c) 2017 Bjoern Schiessle <bjoern@schiessle.org> - * - * @author Bjoern Schiessle <bjoern@schiessle.org> - * - * @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/>. - * + * SPDX-FileCopyrightText: 2017 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later */ namespace OC\GlobalScale; diff --git a/lib/private/Group/Backend.php b/lib/private/Group/Backend.php index 1e3e5ef1164..f6844308a15 100644 --- a/lib/private/Group/Backend.php +++ b/lib/private/Group/Backend.php @@ -1,26 +1,9 @@ <?php + /** - * @copyright Copyright (c) 2016, ownCloud, Inc. - * - * @author Christoph Wurst <christoph@winzerhof-wurst.at> - * @author Knut Ahlers <knut@ahlers.me> - * @author Roeland Jago Douma <roeland@famdouma.nl> - * @author Vincent Petry <vincent@nextcloud.com> - * - * @license AGPL-3.0 - * - * This code is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License, version 3, - * as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License, version 3, - * along with this program. If not, see <http://www.gnu.org/licenses/> - * + * SPDX-FileCopyrightText: 2016-2024 Nextcloud GmbH and Nextcloud contributors + * SPDX-FileCopyrightText: 2016 ownCloud, Inc. + * SPDX-License-Identifier: AGPL-3.0-only */ namespace OC\Group; diff --git a/lib/private/Group/Database.php b/lib/private/Group/Database.php index ef5641d8137..925b5bb0bdd 100644 --- a/lib/private/Group/Database.php +++ b/lib/private/Group/Database.php @@ -1,51 +1,30 @@ <?php + /** - * @copyright Copyright (c) 2016, ownCloud, Inc. - * - * @author Christoph Wurst <christoph@winzerhof-wurst.at> - * @author Joas Schilling <coding@schilljs.com> - * @author Johannes Leuker <j.leuker@hosting.de> - * @author John Molakvoæ <skjnldsv@protonmail.com> - * @author Loki3000 <github@labcms.ru> - * @author Morris Jobke <hey@morrisjobke.de> - * @author Roeland Jago Douma <roeland@famdouma.nl> - * @author tgrant <tom.grant760@gmail.com> - * @author Tom Grant <TomG736@users.noreply.github.com> - * - * @license AGPL-3.0 - * - * This code is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License, version 3, - * as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License, version 3, - * along with this program. If not, see <http://www.gnu.org/licenses/> - * + * SPDX-FileCopyrightText: 2016-2024 Nextcloud GmbH and Nextcloud contributors + * SPDX-FileCopyrightText: 2016 ownCloud, Inc. + * SPDX-License-Identifier: AGPL-3.0-only */ namespace OC\Group; use Doctrine\DBAL\Exception\UniqueConstraintViolationException; +use OC\User\LazyUser; use OCP\DB\QueryBuilder\IQueryBuilder; use OCP\Group\Backend\ABackend; use OCP\Group\Backend\IAddToGroupBackend; +use OCP\Group\Backend\IBatchMethodsBackend; use OCP\Group\Backend\ICountDisabledInGroup; use OCP\Group\Backend\ICountUsersBackend; -use OCP\Group\Backend\ICreateGroupBackend; +use OCP\Group\Backend\ICreateNamedGroupBackend; use OCP\Group\Backend\IDeleteGroupBackend; use OCP\Group\Backend\IGetDisplayNameBackend; use OCP\Group\Backend\IGroupDetailsBackend; +use OCP\Group\Backend\INamedBackend; use OCP\Group\Backend\IRemoveFromGroupBackend; use OCP\Group\Backend\ISearchableGroupBackend; use OCP\Group\Backend\ISetDisplayNameBackend; -use OCP\Group\Backend\INamedBackend; use OCP\IDBConnection; use OCP\IUserManager; -use OC\User\LazyUser; /** * Class for group management in a SQL Database (e.g. MySQL, SQLite) @@ -54,26 +33,25 @@ class Database extends ABackend implements IAddToGroupBackend, ICountDisabledInGroup, ICountUsersBackend, - ICreateGroupBackend, + ICreateNamedGroupBackend, IDeleteGroupBackend, IGetDisplayNameBackend, IGroupDetailsBackend, IRemoveFromGroupBackend, ISetDisplayNameBackend, ISearchableGroupBackend, + IBatchMethodsBackend, INamedBackend { - /** @var string[] */ + /** @var array<string, array{gid: string, displayname: string}> */ private $groupCache = []; - - /** @var IDBConnection */ - private $dbConn; + private ?IDBConnection $dbConn; /** * \OC\Group\Database constructor. * * @param IDBConnection|null $dbConn */ - public function __construct(IDBConnection $dbConn = null) { + public function __construct(?IDBConnection $dbConn = null) { $this->dbConn = $dbConn; } @@ -86,35 +64,28 @@ class Database extends ABackend implements } } - /** - * Try to create a new group - * @param string $gid The name of the group to create - * @return bool - * - * Tries to create a new group. If the group name already exists, false will - * be returned. - */ - public function createGroup(string $gid): bool { + public function createGroup(string $name): ?string { $this->fixDI(); + $gid = $this->computeGid($name); try { // Add group $builder = $this->dbConn->getQueryBuilder(); $result = $builder->insert('groups') ->setValue('gid', $builder->createNamedParameter($gid)) - ->setValue('displayname', $builder->createNamedParameter($gid)) + ->setValue('displayname', $builder->createNamedParameter($name)) ->execute(); } catch (UniqueConstraintViolationException $e) { - $result = 0; + return null; } // Add to cache $this->groupCache[$gid] = [ 'gid' => $gid, - 'displayname' => $gid + 'displayname' => $name ]; - return $result === 1; + return $gid; } /** @@ -270,7 +241,7 @@ class Database extends ABackend implements $this->fixDI(); $query = $this->dbConn->getQueryBuilder(); - $query->select('gid') + $query->select('gid', 'displayname') ->from('groups') ->orderBy('gid', 'ASC'); @@ -293,6 +264,10 @@ class Database extends ABackend implements $groups = []; while ($row = $result->fetch()) { + $this->groupCache[$row['gid']] = [ + 'displayname' => $row['displayname'], + 'gid' => $row['gid'], + ]; $groups[] = $row['gid']; } $result->closeCursor(); @@ -332,6 +307,43 @@ class Database extends ABackend implements } /** + * {@inheritdoc} + */ + public function groupsExists(array $gids): array { + $notFoundGids = []; + $existingGroups = []; + + // In case the data is already locally accessible, not need to do SQL query + // or do a SQL query but with a smaller in clause + foreach ($gids as $gid) { + if (isset($this->groupCache[$gid])) { + $existingGroups[] = $gid; + } else { + $notFoundGids[] = $gid; + } + } + + $qb = $this->dbConn->getQueryBuilder(); + $qb->select('gid', 'displayname') + ->from('groups') + ->where($qb->expr()->in('gid', $qb->createParameter('ids'))); + foreach (array_chunk($notFoundGids, 1000) as $chunk) { + $qb->setParameter('ids', $chunk, IQueryBuilder::PARAM_STR_ARRAY); + $result = $qb->executeQuery(); + while ($row = $result->fetch()) { + $this->groupCache[(string)$row['gid']] = [ + 'displayname' => (string)$row['displayname'], + 'gid' => (string)$row['gid'], + ]; + $existingGroups[] = (string)$row['gid']; + } + $result->closeCursor(); + } + + return $existingGroups; + } + + /** * Get a list of all users in a group * @param string $gid * @param string $search @@ -488,6 +500,43 @@ class Database extends ABackend implements return []; } + /** + * {@inheritdoc} + */ + public function getGroupsDetails(array $gids): array { + $notFoundGids = []; + $details = []; + + // In case the data is already locally accessible, not need to do SQL query + // or do a SQL query but with a smaller in clause + foreach ($gids as $gid) { + if (isset($this->groupCache[$gid])) { + $details[$gid] = ['displayName' => $this->groupCache[$gid]['displayname']]; + } else { + $notFoundGids[] = $gid; + } + } + + foreach (array_chunk($notFoundGids, 1000) as $chunk) { + $query = $this->dbConn->getQueryBuilder(); + $query->select('gid', 'displayname') + ->from('groups') + ->where($query->expr()->in('gid', $query->createNamedParameter($chunk, IQueryBuilder::PARAM_STR_ARRAY))); + + $result = $query->executeQuery(); + while ($row = $result->fetch()) { + $details[(string)$row['gid']] = ['displayName' => (string)$row['displayname']]; + $this->groupCache[(string)$row['gid']] = [ + 'displayname' => (string)$row['displayname'], + 'gid' => (string)$row['gid'], + ]; + } + $result->closeCursor(); + } + + return $details; + } + public function setDisplayName(string $gid, string $displayName): bool { if (!$this->groupExists($gid)) { return false; @@ -517,4 +566,13 @@ class Database extends ABackend implements public function getBackendName(): string { return 'Database'; } + + /** + * Compute group ID from display name (GIDs are limited to 64 characters in database) + */ + private function computeGid(string $displayName): string { + return mb_strlen($displayName) > 64 + ? hash('sha256', $displayName) + : $displayName; + } } diff --git a/lib/private/Group/DisplayNameCache.php b/lib/private/Group/DisplayNameCache.php index 4eb8211be6e..fef8d40a05f 100644 --- a/lib/private/Group/DisplayNameCache.php +++ b/lib/private/Group/DisplayNameCache.php @@ -1,28 +1,10 @@ <?php declare(strict_types=1); - /** - * @copyright 2022 Anna Larch <anna.larch@gmx.net> - * @author Anna Larch <anna.larch@gmx.net> - * - * @license AGPL-3.0-or-later - * - * This code is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License, version 3, - * as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License, version 3, - * along with this program. If not, see <http://www.gnu.org/licenses/> - * + * SPDX-FileCopyrightText: 2022 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later */ - - namespace OC\Group; use OCP\Cache\CappedMemoryCache; @@ -38,6 +20,7 @@ use OCP\IGroupManager; * Class that cache the relation Group ID -> Display name * * This saves fetching the group from the backend for "just" the display name + * @template-implements IEventListener<GroupChangedEvent|GroupDeletedEvent> */ class DisplayNameCache implements IEventListener { private CappedMemoryCache $cache; diff --git a/lib/private/Group/Group.php b/lib/private/Group/Group.php index 441ee64604d..dcda7c29bb5 100644 --- a/lib/private/Group/Group.php +++ b/lib/private/Group/Group.php @@ -1,47 +1,15 @@ <?php + /** - * @copyright Copyright (c) 2016, ownCloud, Inc. - * - * @author Arthur Schiwon <blizzz@arthur-schiwon.de> - * @author Bart Visscher <bartv@thisnet.nl> - * @author Christoph Wurst <christoph@winzerhof-wurst.at> - * @author Joas Schilling <coding@schilljs.com> - * @author Johannes Leuker <j.leuker@hosting.de> - * @author John Molakvoæ <skjnldsv@protonmail.com> - * @author Lukas Reschke <lukas@statuscode.ch> - * @author Morris Jobke <hey@morrisjobke.de> - * @author Robin Appelman <robin@icewind.nl> - * @author Robin McCorkell <robin@mccorkell.me.uk> - * @author Roeland Jago Douma <roeland@famdouma.nl> - * @author Vincent Petry <vincent@nextcloud.com> - * - * @license AGPL-3.0 - * - * This code is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License, version 3, - * as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License, version 3, - * along with this program. If not, see <http://www.gnu.org/licenses/> - * + * SPDX-FileCopyrightText: 2016-2024 Nextcloud GmbH and Nextcloud contributors + * SPDX-FileCopyrightText: 2016 ownCloud, Inc. + * SPDX-License-Identifier: AGPL-3.0-only */ namespace OC\Group; use OC\Hooks\PublicEmitter; use OC\User\LazyUser; use OCP\EventDispatcher\IEventDispatcher; -use OCP\Group\Events\BeforeGroupDeletedEvent; -use OCP\Group\Events\BeforeUserAddedEvent; -use OCP\Group\Events\BeforeUserRemovedEvent; -use OCP\Group\Events\GroupDeletedEvent; -use OCP\Group\Events\UserAddedEvent; -use OCP\Group\Events\UserRemovedEvent; -use OCP\GroupInterface; use OCP\Group\Backend\ICountDisabledInGroup; use OCP\Group\Backend\IGetDisplayNameBackend; use OCP\Group\Backend\IHideFromCollaborationBackend; @@ -49,7 +17,14 @@ use OCP\Group\Backend\INamedBackend; use OCP\Group\Backend\ISearchableGroupBackend; use OCP\Group\Backend\ISetDisplayNameBackend; use OCP\Group\Events\BeforeGroupChangedEvent; +use OCP\Group\Events\BeforeGroupDeletedEvent; +use OCP\Group\Events\BeforeUserAddedEvent; +use OCP\Group\Events\BeforeUserRemovedEvent; use OCP\Group\Events\GroupChangedEvent; +use OCP\Group\Events\GroupDeletedEvent; +use OCP\Group\Events\UserAddedEvent; +use OCP\Group\Events\UserRemovedEvent; +use OCP\GroupInterface; use OCP\IGroup; use OCP\IUser; use OCP\IUserManager; @@ -76,7 +51,7 @@ class Group implements IGroup { /** @var PublicEmitter */ private $emitter; - public function __construct(string $gid, array $backends, IEventDispatcher $dispatcher, IUserManager $userManager, PublicEmitter $emitter = null, ?string $displayName = null) { + public function __construct(string $gid, array $backends, IEventDispatcher $dispatcher, IUserManager $userManager, ?PublicEmitter $emitter = null, ?string $displayName = null) { $this->gid = $gid; $this->backends = $backends; $this->dispatcher = $dispatcher; @@ -85,11 +60,11 @@ class Group implements IGroup { $this->displayName = $displayName; } - public function getGID() { + public function getGID(): string { return $this->gid; } - public function getDisplayName() { + public function getDisplayName(): string { if (is_null($this->displayName)) { foreach ($this->backends as $backend) { if ($backend instanceof IGetDisplayNameBackend) { @@ -126,7 +101,7 @@ class Group implements IGroup { * * @return \OC\User\User[] */ - public function getUsers() { + public function getUsers(): array { if ($this->usersLoaded) { return $this->users; } @@ -153,7 +128,7 @@ class Group implements IGroup { * @param IUser $user * @return bool */ - public function inGroup(IUser $user) { + public function inGroup(IUser $user): bool { if (isset($this->users[$user->getUID()])) { return true; } @@ -171,7 +146,7 @@ class Group implements IGroup { * * @param IUser $user */ - public function addUser(IUser $user) { + public function addUser(IUser $user): void { if ($this->inGroup($user)) { return; } @@ -184,9 +159,7 @@ class Group implements IGroup { foreach ($this->backends as $backend) { if ($backend->implementsActions(\OC\Group\Backend::ADD_TO_GROUP)) { $backend->addToGroup($user->getUID(), $this->gid); - if ($this->users) { - $this->users[$user->getUID()] = $user; - } + $this->users[$user->getUID()] = $user; $this->dispatcher->dispatchTyped(new UserAddedEvent($this, $user)); @@ -200,10 +173,8 @@ class Group implements IGroup { /** * remove a user from the group - * - * @param \OC\User\User $user */ - public function removeUser($user) { + public function removeUser(IUser $user): void { $result = false; $this->dispatcher->dispatchTyped(new BeforeUserRemovedEvent($this, $user)); if ($this->emitter) { @@ -262,7 +233,7 @@ class Group implements IGroup { * @param string $search * @return int|bool */ - public function count($search = '') { + public function count($search = ''): int|bool { $users = false; foreach ($this->backends as $backend) { if ($backend->implementsActions(\OC\Group\Backend::COUNT_USERS)) { @@ -282,7 +253,7 @@ class Group implements IGroup { * * @return int|bool */ - public function countDisabled() { + public function countDisabled(): int|bool { $users = false; foreach ($this->backends as $backend) { if ($backend instanceof ICountDisabledInGroup) { @@ -306,7 +277,7 @@ class Group implements IGroup { * @return IUser[] * @deprecated 27.0.0 Use searchUsers instead (same implementation) */ - public function searchDisplayName($search, $limit = null, $offset = null) { + public function searchDisplayName(string $search, ?int $limit = null, ?int $offset = null): array { return $this->searchUsers($search, $limit, $offset); } @@ -315,7 +286,7 @@ class Group implements IGroup { * * @return string[] */ - public function getBackendNames() { + public function getBackendNames(): array { $backends = []; foreach ($this->backends as $backend) { if ($backend instanceof INamedBackend) { @@ -329,11 +300,11 @@ class Group implements IGroup { } /** - * delete the group + * Delete the group * * @return bool */ - public function delete() { + public function delete(): bool { // Prevent users from deleting group admin if ($this->getGID() === 'admin') { return false; @@ -378,7 +349,7 @@ class Group implements IGroup { * @return bool * @since 14.0.0 */ - public function canRemoveUser() { + public function canRemoveUser(): bool { foreach ($this->backends as $backend) { if ($backend->implementsActions(GroupInterface::REMOVE_FROM_GOUP)) { return true; @@ -391,7 +362,7 @@ class Group implements IGroup { * @return bool * @since 14.0.0 */ - public function canAddUser() { + public function canAddUser(): bool { foreach ($this->backends as $backend) { if ($backend->implementsActions(GroupInterface::ADD_TO_GROUP)) { return true; diff --git a/lib/private/Group/Manager.php b/lib/private/Group/Manager.php index c43b5165a79..0ab64907c8b 100644 --- a/lib/private/Group/Manager.php +++ b/lib/private/Group/Manager.php @@ -1,46 +1,17 @@ <?php + /** - * @copyright Copyright (c) 2016, ownCloud, Inc. - * - * @author Arthur Schiwon <blizzz@arthur-schiwon.de> - * @author Bart Visscher <bartv@thisnet.nl> - * @author Bernhard Posselt <dev@bernhard-posselt.com> - * @author Christoph Wurst <christoph@winzerhof-wurst.at> - * @author Joas Schilling <coding@schilljs.com> - * @author John Molakvoæ <skjnldsv@protonmail.com> - * @author Jörn Friedrich Dreyer <jfd@butonic.de> - * @author Knut Ahlers <knut@ahlers.me> - * @author Lukas Reschke <lukas@statuscode.ch> - * @author macjohnny <estebanmarin@gmx.ch> - * @author Morris Jobke <hey@morrisjobke.de> - * @author Robin Appelman <robin@icewind.nl> - * @author Robin McCorkell <robin@mccorkell.me.uk> - * @author Roeland Jago Douma <roeland@famdouma.nl> - * @author Roman Kreisel <mail@romankreisel.de> - * @author Thomas Müller <thomas.mueller@tmit.eu> - * @author Vincent Petry <vincent@nextcloud.com> - * @author Vinicius Cubas Brand <vinicius@eita.org.br> - * @author voxsim "Simon Vocella" - * - * @license AGPL-3.0 - * - * This code is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License, version 3, - * as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License, version 3, - * along with this program. If not, see <http://www.gnu.org/licenses/> - * + * SPDX-FileCopyrightText: 2016-2024 Nextcloud GmbH and Nextcloud contributors + * SPDX-FileCopyrightText: 2016 ownCloud, Inc. + * SPDX-License-Identifier: AGPL-3.0-only */ namespace OC\Group; use OC\Hooks\PublicEmitter; use OCP\EventDispatcher\IEventDispatcher; +use OCP\Group\Backend\IBatchMethodsBackend; +use OCP\Group\Backend\ICreateNamedGroupBackend; +use OCP\Group\Backend\IGroupDetailsBackend; use OCP\Group\Events\BeforeGroupCreatedEvent; use OCP\Group\Events\GroupCreatedEvent; use OCP\GroupInterface; @@ -49,6 +20,7 @@ use OCP\IGroup; use OCP\IGroupManager; use OCP\IUser; use Psr\Log\LoggerInterface; +use function is_string; /** * Class Manager @@ -74,10 +46,10 @@ class Manager extends PublicEmitter implements IGroupManager { private IEventDispatcher $dispatcher; private LoggerInterface $logger; - /** @var \OC\Group\Group[] */ + /** @var array<string, IGroup> */ private $cachedGroups = []; - /** @var (string[])[] */ + /** @var array<string, list<string>> */ private $cachedUserGroups = []; /** @var \OC\SubAdmin */ @@ -85,35 +57,26 @@ class Manager extends PublicEmitter implements IGroupManager { private DisplayNameCache $displayNameCache; + private const MAX_GROUP_LENGTH = 255; + public function __construct(\OC\User\Manager $userManager, - IEventDispatcher $dispatcher, - LoggerInterface $logger, - ICacheFactory $cacheFactory) { + IEventDispatcher $dispatcher, + LoggerInterface $logger, + ICacheFactory $cacheFactory) { $this->userManager = $userManager; $this->dispatcher = $dispatcher; $this->logger = $logger; $this->displayNameCache = new DisplayNameCache($cacheFactory, $this); - $cachedGroups = &$this->cachedGroups; - $cachedUserGroups = &$this->cachedUserGroups; - $this->listen('\OC\Group', 'postDelete', function ($group) use (&$cachedGroups, &$cachedUserGroups) { - /** - * @var \OC\Group\Group $group - */ - unset($cachedGroups[$group->getGID()]); - $cachedUserGroups = []; + $this->listen('\OC\Group', 'postDelete', function (IGroup $group): void { + unset($this->cachedGroups[$group->getGID()]); + $this->cachedUserGroups = []; }); - $this->listen('\OC\Group', 'postAddUser', function ($group) use (&$cachedUserGroups) { - /** - * @var \OC\Group\Group $group - */ - $cachedUserGroups = []; + $this->listen('\OC\Group', 'postAddUser', function (IGroup $group): void { + $this->cachedUserGroups = []; }); - $this->listen('\OC\Group', 'postRemoveUser', function ($group) use (&$cachedUserGroups) { - /** - * @var \OC\Group\Group $group - */ - $cachedUserGroups = []; + $this->listen('\OC\Group', 'postRemoveUser', function (IGroup $group): void { + $this->cachedUserGroups = []; }); } @@ -185,7 +148,7 @@ class Manager extends PublicEmitter implements IGroupManager { if ($backend->implementsActions(Backend::GROUP_DETAILS)) { $groupData = $backend->getGroupDetails($gid); if (is_array($groupData) && !empty($groupData)) { - // take the display name from the first backend that has a non-null one + // take the display name from the last backend that has a non-null one if (is_null($displayName) && isset($groupData['displayName'])) { $displayName = $groupData['displayName']; } @@ -198,11 +161,69 @@ class Manager extends PublicEmitter implements IGroupManager { if (count($backends) === 0) { return null; } + /** @var GroupInterface[] $backends */ $this->cachedGroups[$gid] = new Group($gid, $backends, $this->dispatcher, $this->userManager, $this, $displayName); return $this->cachedGroups[$gid]; } /** + * @brief Batch method to create group objects + * + * @param list<string> $gids List of groupIds for which we want to create a IGroup object + * @param array<string, string> $displayNames Array containing already know display name for a groupId + * @return array<string, IGroup> + */ + protected function getGroupsObjects(array $gids, array $displayNames = []): array { + $backends = []; + $groups = []; + foreach ($gids as $gid) { + $backends[$gid] = []; + if (!isset($displayNames[$gid])) { + $displayNames[$gid] = null; + } + } + foreach ($this->backends as $backend) { + if ($backend instanceof IGroupDetailsBackend || $backend->implementsActions(GroupInterface::GROUP_DETAILS)) { + /** @var IGroupDetailsBackend $backend */ + if ($backend instanceof IBatchMethodsBackend) { + $groupDatas = $backend->getGroupsDetails($gids); + } else { + $groupDatas = []; + foreach ($gids as $gid) { + $groupDatas[$gid] = $backend->getGroupDetails($gid); + } + } + foreach ($groupDatas as $gid => $groupData) { + if (!empty($groupData)) { + // take the display name from the last backend that has a non-null one + if (isset($groupData['displayName'])) { + $displayNames[$gid] = $groupData['displayName']; + } + $backends[$gid][] = $backend; + } + } + } else { + if ($backend instanceof IBatchMethodsBackend) { + $existingGroups = $backend->groupsExists($gids); + } else { + $existingGroups = array_filter($gids, fn (string $gid): bool => $backend->groupExists($gid)); + } + foreach ($existingGroups as $group) { + $backends[$group][] = $backend; + } + } + } + foreach ($gids as $gid) { + if (count($backends[$gid]) === 0) { + continue; + } + $this->cachedGroups[$gid] = new Group($gid, $backends[$gid], $this->dispatcher, $this->userManager, $this, $displayNames[$gid]); + $groups[$gid] = $this->cachedGroups[$gid]; + } + return $groups; + } + + /** * @param string $gid * @return bool */ @@ -219,12 +240,22 @@ class Manager extends PublicEmitter implements IGroupManager { return null; } elseif ($group = $this->get($gid)) { return $group; + } elseif (mb_strlen($gid) > self::MAX_GROUP_LENGTH) { + throw new \Exception('Group name is limited to '. self::MAX_GROUP_LENGTH.' characters'); } else { $this->dispatcher->dispatchTyped(new BeforeGroupCreatedEvent($gid)); $this->emit('\OC\Group', 'preCreate', [$gid]); foreach ($this->backends as $backend) { if ($backend->implementsActions(Backend::CREATE_GROUP)) { - if ($backend->createGroup($gid)) { + if ($backend instanceof ICreateNamedGroupBackend) { + $groupName = $gid; + if (($gid = $backend->createGroup($groupName)) !== null) { + $group = $this->getGroupObject($gid); + $this->dispatcher->dispatchTyped(new GroupCreatedEvent($group)); + $this->emit('\OC\Group', 'postCreate', [$group]); + return $group; + } + } elseif ($backend->createGroup($gid)) { $group = $this->getGroupObject($gid); $this->dispatcher->dispatchTyped(new GroupCreatedEvent($group)); $this->emit('\OC\Group', 'postCreate', [$group]); @@ -246,13 +277,9 @@ class Manager extends PublicEmitter implements IGroupManager { $groups = []; foreach ($this->backends as $backend) { $groupIds = $backend->getGroups($search, $limit ?? -1, $offset ?? 0); - foreach ($groupIds as $groupId) { - $aGroup = $this->get($groupId); - if ($aGroup instanceof IGroup) { - $groups[$groupId] = $aGroup; - } else { - $this->logger->debug('Group "' . $groupId . '" was returned by search but not found through direct access', ['app' => 'core']); - } + $newGroups = $this->getGroupsObjects($groupIds); + foreach ($newGroups as $groupId => $group) { + $groups[$groupId] = $group; } if (!is_null($limit) and $limit <= 0) { return array_values($groups); @@ -265,7 +292,7 @@ class Manager extends PublicEmitter implements IGroupManager { * @param IUser|null $user * @return \OC\Group\Group[] */ - public function getUserGroups(IUser $user = null) { + public function getUserGroups(?IUser $user = null) { if (!$user instanceof IUser) { return []; } @@ -299,7 +326,7 @@ class Manager extends PublicEmitter implements IGroupManager { */ public function isAdmin($userId) { foreach ($this->backends as $backend) { - if ($backend->implementsActions(Backend::IS_ADMIN) && $backend->isAdmin($userId)) { + if (is_string($userId) && $backend->implementsActions(Backend::IS_ADMIN) && $backend->isAdmin($userId)) { return true; } } @@ -314,7 +341,7 @@ class Manager extends PublicEmitter implements IGroupManager { * @return bool if in group */ public function isInGroup($userId, $group) { - return array_search($group, $this->getUserIdGroupIds($userId)) !== false; + return in_array($group, $this->getUserIdGroupIds($userId)); } /** diff --git a/lib/private/Group/MetaData.php b/lib/private/Group/MetaData.php index a58d7e78bfc..638dc184812 100644 --- a/lib/private/Group/MetaData.php +++ b/lib/private/Group/MetaData.php @@ -1,31 +1,9 @@ <?php + /** - * @copyright Copyright (c) 2016, ownCloud, Inc. - * - * @author Arthur Schiwon <blizzz@arthur-schiwon.de> - * @author Christoph Wurst <christoph@winzerhof-wurst.at> - * @author Daniel Kesselberg <mail@danielkesselberg.de> - * @author Joas Schilling <coding@schilljs.com> - * @author John Molakvoæ <skjnldsv@protonmail.com> - * @author Lukas Reschke <lukas@statuscode.ch> - * @author Roeland Jago Douma <roeland@famdouma.nl> - * @author Stephan Peijnik <speijnik@anexia-it.com> - * @author Thomas Müller <thomas.mueller@tmit.eu> - * - * @license AGPL-3.0 - * - * This code is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License, version 3, - * as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License, version 3, - * along with this program. If not, see <http://www.gnu.org/licenses/> - * + * SPDX-FileCopyrightText: 2016-2024 Nextcloud GmbH and Nextcloud contributors + * SPDX-FileCopyrightText: 2016 ownCloud, Inc. + * SPDX-License-Identifier: AGPL-3.0-only */ namespace OC\Group; @@ -57,11 +35,11 @@ class MetaData { * @param bool $isAdmin whether the current users is an admin */ public function __construct( - string $user, - bool $isAdmin, - IGroupManager $groupManager, - IUserSession $userSession - ) { + string $user, + bool $isAdmin, + IGroupManager $groupManager, + IUserSession $userSession + ) { $this->user = $user; $this->isAdmin = $isAdmin; $this->groupManager = $groupManager; diff --git a/lib/private/HintException.php b/lib/private/HintException.php index 20f7142d1c0..c63e4b76ae5 100644 --- a/lib/private/HintException.php +++ b/lib/private/HintException.php @@ -2,25 +2,8 @@ declare(strict_types=1); /** - * @copyright Copyright (c) 2021 Gary Kim <gary@garykim.dev> - * - * @author Gary Kim <gary@garykim.dev> - * - * @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/>. - * + * SPDX-FileCopyrightText: 2021 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later */ namespace OC; diff --git a/lib/private/Hooks/BasicEmitter.php b/lib/private/Hooks/BasicEmitter.php index 61b5184a804..c9444b40473 100644 --- a/lib/private/Hooks/BasicEmitter.php +++ b/lib/private/Hooks/BasicEmitter.php @@ -1,24 +1,8 @@ <?php /** - * @copyright Copyright (c) 2016, ownCloud, Inc. - * - * @author Christoph Wurst <christoph@winzerhof-wurst.at> - * @author Robin Appelman <robin@icewind.nl> - * - * @license AGPL-3.0 - * - * This code is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License, version 3, - * as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License, version 3, - * along with this program. If not, see <http://www.gnu.org/licenses/> - * + * SPDX-FileCopyrightText: 2016-2024 Nextcloud GmbH and Nextcloud contributors + * SPDX-FileCopyrightText: 2016 ownCloud, Inc. + * SPDX-License-Identifier: AGPL-3.0-only */ namespace OC\Hooks; diff --git a/lib/private/Hooks/Emitter.php b/lib/private/Hooks/Emitter.php index bc5af2b60a2..8a63ac9ed3a 100644 --- a/lib/private/Hooks/Emitter.php +++ b/lib/private/Hooks/Emitter.php @@ -1,26 +1,8 @@ <?php /** - * @copyright Copyright (c) 2016, ownCloud, Inc. - * - * @author Christoph Wurst <christoph@winzerhof-wurst.at> - * @author Jörn Friedrich Dreyer <jfd@butonic.de> - * @author Morris Jobke <hey@morrisjobke.de> - * @author Robin Appelman <robin@icewind.nl> - * - * @license AGPL-3.0 - * - * This code is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License, version 3, - * as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License, version 3, - * along with this program. If not, see <http://www.gnu.org/licenses/> - * + * SPDX-FileCopyrightText: 2016-2024 Nextcloud GmbH and Nextcloud contributors + * SPDX-FileCopyrightText: 2016 ownCloud, Inc. + * SPDX-License-Identifier: AGPL-3.0-only */ namespace OC\Hooks; @@ -49,5 +31,5 @@ interface Emitter { * @return void * @deprecated 18.0.0 use \OCP\EventDispatcher\IEventDispatcher::removeListener */ - public function removeListener($scope = null, $method = null, callable $callback = null); + public function removeListener($scope = null, $method = null, ?callable $callback = null); } diff --git a/lib/private/Hooks/EmitterTrait.php b/lib/private/Hooks/EmitterTrait.php index da4e3da2bd6..8bffb6f7c3b 100644 --- a/lib/private/Hooks/EmitterTrait.php +++ b/lib/private/Hooks/EmitterTrait.php @@ -1,25 +1,8 @@ <?php /** - * @copyright Copyright (c) 2016, ownCloud, Inc. - * - * @author Christoph Wurst <christoph@winzerhof-wurst.at> - * @author Morris Jobke <hey@morrisjobke.de> - * @author Robin Appelman <robin@icewind.nl> - * - * @license AGPL-3.0 - * - * This code is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License, version 3, - * as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License, version 3, - * along with this program. If not, see <http://www.gnu.org/licenses/> - * + * SPDX-FileCopyrightText: 2016-2024 Nextcloud GmbH and Nextcloud contributors + * SPDX-FileCopyrightText: 2016 ownCloud, Inc. + * SPDX-License-Identifier: AGPL-3.0-only */ namespace OC\Hooks; @@ -43,7 +26,7 @@ trait EmitterTrait { if (!isset($this->listeners[$eventName])) { $this->listeners[$eventName] = []; } - if (array_search($callback, $this->listeners[$eventName], true) === false) { + if (!in_array($callback, $this->listeners[$eventName], true)) { $this->listeners[$eventName][] = $callback; } } @@ -54,7 +37,7 @@ trait EmitterTrait { * @param callable $callback optional * @deprecated 18.0.0 use \OCP\EventDispatcher\IEventDispatcher::removeListener */ - public function removeListener($scope = null, $method = null, callable $callback = null) { + public function removeListener($scope = null, $method = null, ?callable $callback = null) { $names = []; $allNames = array_keys($this->listeners); if ($scope and $method) { diff --git a/lib/private/Hooks/PublicEmitter.php b/lib/private/Hooks/PublicEmitter.php index b72b12dbbbf..042b616e264 100644 --- a/lib/private/Hooks/PublicEmitter.php +++ b/lib/private/Hooks/PublicEmitter.php @@ -1,26 +1,8 @@ <?php /** - * @copyright Copyright (c) 2016, ownCloud, Inc. - * - * @author Christoph Wurst <christoph@winzerhof-wurst.at> - * @author Lukas Reschke <lukas@statuscode.ch> - * @author Morris Jobke <hey@morrisjobke.de> - * @author Robin Appelman <robin@icewind.nl> - * - * @license AGPL-3.0 - * - * This code is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License, version 3, - * as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License, version 3, - * along with this program. If not, see <http://www.gnu.org/licenses/> - * + * SPDX-FileCopyrightText: 2016-2024 Nextcloud GmbH and Nextcloud contributors + * SPDX-FileCopyrightText: 2016 ownCloud, Inc. + * SPDX-License-Identifier: AGPL-3.0-only */ namespace OC\Hooks; diff --git a/lib/private/Http/Client/Client.php b/lib/private/Http/Client/Client.php index 3bf43e6c07e..7cadf3fdf6e 100644 --- a/lib/private/Http/Client/Client.php +++ b/lib/private/Http/Client/Client.php @@ -1,35 +1,10 @@ <?php declare(strict_types=1); - /** - * @copyright Copyright (c) 2016, ownCloud, Inc. - * - * @author Carlos Ferreira <carlos@reendex.com> - * @author Christoph Wurst <christoph@winzerhof-wurst.at> - * @author Daniel Kesselberg <mail@danielkesselberg.de> - * @author Joas Schilling <coding@schilljs.com> - * @author Lukas Reschke <lukas@statuscode.ch> - * @author Mohammed Abdellatif <m.latief@gmail.com> - * @author Morris Jobke <hey@morrisjobke.de> - * @author Robin Appelman <robin@icewind.nl> - * @author Roeland Jago Douma <roeland@famdouma.nl> - * @author Scott Shambarger <devel@shambarger.net> - * - * @license AGPL-3.0 - * - * This code is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License, version 3, - * as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License, version 3, - * along with this program. If not, see <http://www.gnu.org/licenses/> - * + * SPDX-FileCopyrightText: 2016-2024 Nextcloud GmbH and Nextcloud contributors + * SPDX-FileCopyrightText: 2016 ownCloud, Inc. + * SPDX-License-Identifier: AGPL-3.0-only */ namespace OC\Http\Client; @@ -192,7 +167,7 @@ class Client implements IClient { throw new LocalServerException('Could not detect any host'); } if (!$this->remoteHostValidator->isValid($host)) { - throw new LocalServerException('Host violates local access rules'); + throw new LocalServerException('Host "'.$host.'" violates local access rules'); } } @@ -339,6 +314,41 @@ class Client implements IClient { } /** + * Sends a PATCH request + * + * @param string $uri + * @param array $options Array such as + * 'body' => [ + * 'field' => 'abc', + * 'other_field' => '123', + * 'file_name' => fopen('/path/to/file', 'r'), + * ], + * 'headers' => [ + * 'foo' => 'bar', + * ], + * 'cookies' => [ + * 'foo' => 'bar', + * ], + * 'allow_redirects' => [ + * 'max' => 10, // allow at most 10 redirects. + * 'strict' => true, // use "strict" RFC compliant redirects. + * 'referer' => true, // add a Referer header + * 'protocols' => ['https'] // only allow https URLs + * ], + * 'sink' => '/path/to/file', // save to a file or a stream + * 'verify' => true, // bool or string to CA file + * 'debug' => true, + * 'timeout' => 5, + * @return IResponse + * @throws \Exception If the request could not get completed + */ + public function patch(string $uri, array $options = []): IResponse { + $this->preventLocalAddress($uri, $options); + $response = $this->client->request('patch', $uri, $this->buildRequestOptions($options)); + return new Response($response); + } + + /** * Sends a DELETE request * * @param string $uri @@ -408,6 +418,59 @@ class Client implements IClient { return new Response($response); } + /** + * Get the response of a Throwable thrown by the request methods when possible + * + * @param \Throwable $e + * @return IResponse + * @throws \Throwable When $e did not have a response + * @since 29.0.0 + */ + public function getResponseFromThrowable(\Throwable $e): IResponse { + if (method_exists($e, 'hasResponse') && method_exists($e, 'getResponse') && $e->hasResponse()) { + return new Response($e->getResponse()); + } + + throw $e; + } + + /** + * Sends a HTTP request + * + * @param string $method The HTTP method to use + * @param string $uri + * @param array $options Array such as + * 'query' => [ + * 'field' => 'abc', + * 'other_field' => '123', + * 'file_name' => fopen('/path/to/file', 'r'), + * ], + * 'headers' => [ + * 'foo' => 'bar', + * ], + * 'cookies' => [ + * 'foo' => 'bar', + * ], + * 'allow_redirects' => [ + * 'max' => 10, // allow at most 10 redirects. + * 'strict' => true, // use "strict" RFC compliant redirects. + * 'referer' => true, // add a Referer header + * 'protocols' => ['https'] // only allow https URLs + * ], + * 'sink' => '/path/to/file', // save to a file or a stream + * 'verify' => true, // bool or string to CA file + * 'debug' => true, + * 'timeout' => 5, + * @return IResponse + * @throws \Exception If the request could not get completed + */ + public function request(string $method, string $uri, array $options = []): IResponse { + $this->preventLocalAddress($uri, $options); + $response = $this->client->request($method, $uri, $this->buildRequestOptions($options)); + $isStream = isset($options['stream']) && $options['stream']; + return new Response($response, $isStream); + } + protected function wrapGuzzlePromise(PromiseInterface $promise): IPromise { return new GuzzlePromiseAdapter( $promise, diff --git a/lib/private/Http/Client/ClientService.php b/lib/private/Http/Client/ClientService.php index 532aa7f566a..9a170be8752 100644 --- a/lib/private/Http/Client/ClientService.php +++ b/lib/private/Http/Client/ClientService.php @@ -1,34 +1,16 @@ <?php declare(strict_types=1); - /** - * @copyright Copyright (c) 2016, ownCloud, Inc. - * - * @author Joas Schilling <coding@schilljs.com> - * @author Lukas Reschke <lukas@statuscode.ch> - * @author Roeland Jago Douma <roeland@famdouma.nl> - * - * @license AGPL-3.0 - * - * This code is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License, version 3, - * as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License, version 3, - * along with this program. If not, see <http://www.gnu.org/licenses/> - * + * SPDX-FileCopyrightText: 2016-2024 Nextcloud GmbH and Nextcloud contributors + * SPDX-FileCopyrightText: 2016 ownCloud, Inc. + * SPDX-License-Identifier: AGPL-3.0-only */ namespace OC\Http\Client; use GuzzleHttp\Client as GuzzleClient; -use GuzzleHttp\HandlerStack; use GuzzleHttp\Handler\CurlHandler; +use GuzzleHttp\HandlerStack; use GuzzleHttp\Middleware; use OCP\Diagnostics\IEventLogger; use OCP\Http\Client\IClient; @@ -75,7 +57,9 @@ class ClientService implements IClientService { public function newClient(): IClient { $handler = new CurlHandler(); $stack = HandlerStack::create($handler); - $stack->push($this->dnsPinMiddleware->addDnsPinning()); + if ($this->config->getSystemValueBool('dns_pinning', true)) { + $stack->push($this->dnsPinMiddleware->addDnsPinning()); + } $stack->push(Middleware::tap(function (RequestInterface $request) { $this->eventLogger->start('http:request', $request->getMethod() . " request to " . $request->getRequestTarget()); }, function () { diff --git a/lib/private/Http/Client/DnsPinMiddleware.php b/lib/private/Http/Client/DnsPinMiddleware.php index c6a58972fdd..fcf0818ebb9 100644 --- a/lib/private/Http/Client/DnsPinMiddleware.php +++ b/lib/private/Http/Client/DnsPinMiddleware.php @@ -3,25 +3,8 @@ declare(strict_types=1); /** - * @copyright Copyright (c) 2021, Lukas Reschke <lukas@statuscode.ch> - * - * @author Lukas Reschke <lukas@statuscode.ch> - * - * @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/>. - * + * SPDX-FileCopyrightText: 2021 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later */ namespace OC\Http\Client; @@ -55,7 +38,7 @@ class DnsPinMiddleware { $second = array_pop($labels); $hostname = $second . '.' . $top; - $responses = dns_get_record($hostname, DNS_SOA); + $responses = $this->dnsGetRecord($hostname, DNS_SOA); if ($responses === false || count($responses) === 0) { return null; @@ -75,13 +58,15 @@ class DnsPinMiddleware { $soaDnsEntry = $this->soaRecord($target); $dnsNegativeTtl = $soaDnsEntry['minimum-ttl'] ?? null; - $dnsTypes = [DNS_A, DNS_AAAA, DNS_CNAME]; + $dnsTypes = \defined('AF_INET6') || @inet_pton('::1') + ? [DNS_A, DNS_AAAA, DNS_CNAME] + : [DNS_A, DNS_CNAME]; foreach ($dnsTypes as $dnsType) { if ($this->negativeDnsCache->isNegativeCached($target, $dnsType)) { continue; } - $dnsResponses = dns_get_record($target, $dnsType); + $dnsResponses = $this->dnsGetRecord($target, $dnsType); $canHaveCnameRecord = true; if ($dnsResponses !== false && count($dnsResponses) > 0) { foreach ($dnsResponses as $dnsResponse) { @@ -104,6 +89,13 @@ class DnsPinMiddleware { return $targetIps; } + /** + * Wrapper for dns_get_record + */ + protected function dnsGetRecord(string $hostname, int $type): array|false { + return \dns_get_record($hostname, $type); + } + public function addDnsPinning() { return function (callable $handler) { return function ( @@ -128,6 +120,10 @@ class DnsPinMiddleware { $targetIps = $this->dnsResolve(idn_to_utf8($hostName), 0); + if (empty($targetIps)) { + throw new LocalServerException('No DNS record found for ' . $hostName); + } + $curlResolves = []; foreach ($ports as $port) { @@ -136,7 +132,7 @@ class DnsPinMiddleware { foreach ($targetIps as $ip) { if ($this->ipAddressClassifier->isLocalAddress($ip)) { // TODO: continue with all non-local IPs? - throw new LocalServerException('Host violates local access rules'); + throw new LocalServerException('Host "'.$ip.'" ('.$hostName.':'.$port.') violates local access rules'); } $curlResolves["$hostName:$port"][] = $ip; } diff --git a/lib/private/Http/Client/GuzzlePromiseAdapter.php b/lib/private/Http/Client/GuzzlePromiseAdapter.php index 9d0b89bfd17..dc8be9bd2e0 100644 --- a/lib/private/Http/Client/GuzzlePromiseAdapter.php +++ b/lib/private/Http/Client/GuzzlePromiseAdapter.php @@ -3,23 +3,8 @@ declare(strict_types=1); /** - * @copyright Copyright (c) 2023, Joas Schilling <coding@schilljs.com> - * - * @license GNU AGPL version 3 or any later version - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as - * published by the Free Software Foundation, either version 3 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - * + * SPDX-FileCopyrightText: 2023 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later */ namespace OC\Http\Client; diff --git a/lib/private/Http/Client/NegativeDnsCache.php b/lib/private/Http/Client/NegativeDnsCache.php index 6c7585b11e3..d5e32fa7c2d 100644 --- a/lib/private/Http/Client/NegativeDnsCache.php +++ b/lib/private/Http/Client/NegativeDnsCache.php @@ -3,26 +3,8 @@ declare(strict_types=1); /** - * @copyright Copyright (c) 2021, Lukas Reschke <lukas@statuscode.ch> - * - * @author Arthur Schiwon <blizzz@arthur-schiwon.de> - * @author Lukas Reschke <lukas@statuscode.ch> - * - * @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/>. - * + * SPDX-FileCopyrightText: 2021 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later */ namespace OC\Http\Client; diff --git a/lib/private/Http/Client/Response.php b/lib/private/Http/Client/Response.php index 054c902fcc5..adf83306d07 100644 --- a/lib/private/Http/Client/Response.php +++ b/lib/private/Http/Client/Response.php @@ -1,28 +1,10 @@ <?php declare(strict_types=1); - /** - * @copyright Copyright (c) 2016, ownCloud, Inc. - * - * @author Lukas Reschke <lukas@statuscode.ch> - * @author Robin Appelman <robin@icewind.nl> - * @author Roeland Jago Douma <roeland@famdouma.nl> - * - * @license AGPL-3.0 - * - * This code is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License, version 3, - * as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License, version 3, - * along with this program. If not, see <http://www.gnu.org/licenses/> - * + * SPDX-FileCopyrightText: 2016-2024 Nextcloud GmbH and Nextcloud contributors + * SPDX-FileCopyrightText: 2016 ownCloud, Inc. + * SPDX-License-Identifier: AGPL-3.0-only */ namespace OC\Http\Client; diff --git a/lib/private/Http/CookieHelper.php b/lib/private/Http/CookieHelper.php index 720a1e9185d..9d07ff4534c 100644 --- a/lib/private/Http/CookieHelper.php +++ b/lib/private/Http/CookieHelper.php @@ -3,27 +3,8 @@ declare(strict_types=1); /** - * @copyright Copyright (c) 2018, Roeland Jago Douma <roeland@famdouma.nl> - * - * @author Christoph Wurst <christoph@winzerhof-wurst.at> - * @author Marco Ziech <marco@ziech.net> - * @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/>. - * + * SPDX-FileCopyrightText: 2018 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later */ namespace OC\Http; @@ -33,13 +14,13 @@ class CookieHelper { public const SAMESITE_STRICT = 2; public static function setCookie(string $name, - string $value = '', - int $maxAge = 0, - string $path = '', - string $domain = '', - bool $secure = false, - bool $httponly = false, - int $samesite = self::SAMESITE_NONE) { + string $value = '', + int $maxAge = 0, + string $path = '', + string $domain = '', + bool $secure = false, + bool $httponly = false, + int $samesite = self::SAMESITE_NONE) { $header = sprintf( 'Set-Cookie: %s=%s', $name, diff --git a/lib/private/Http/WellKnown/RequestManager.php b/lib/private/Http/WellKnown/RequestManager.php index b83ff2ada50..38dde0eade2 100644 --- a/lib/private/Http/WellKnown/RequestManager.php +++ b/lib/private/Http/WellKnown/RequestManager.php @@ -3,25 +3,8 @@ declare(strict_types=1); /** - * @copyright 2020 Christoph Wurst <christoph@winzerhof-wurst.at> - * - * @author Christoph Wurst <christoph@winzerhof-wurst.at> - * - * @license GNU AGPL version 3 or any later version - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as - * published by the Free Software Foundation, either version 3 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - * + * SPDX-FileCopyrightText: 2020 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later */ namespace OC\Http\WellKnown; @@ -49,8 +32,8 @@ class RequestManager { private $logger; public function __construct(Coordinator $coordinator, - IServerContainer $container, - LoggerInterface $logger) { + IServerContainer $container, + LoggerInterface $logger) { $this->coordinator = $coordinator; $this->container = $container; $this->logger = $logger; diff --git a/lib/private/InitialStateService.php b/lib/private/InitialStateService.php index c10442a5267..8d364a90f84 100644 --- a/lib/private/InitialStateService.php +++ b/lib/private/InitialStateService.php @@ -3,28 +3,8 @@ declare(strict_types=1); /** - * @copyright Copyright (c) 2019, Roeland Jago Douma <roeland@famdouma.nl> - * - * @author Christoph Wurst <christoph@winzerhof-wurst.at> - * @author Joas Schilling <coding@schilljs.com> - * @author John Molakvoæ <skjnldsv@protonmail.com> - * @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/>. - * + * SPDX-FileCopyrightText: 2019 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later */ namespace OC; diff --git a/lib/private/Installer.php b/lib/private/Installer.php index dc81135b644..ad80b26d8bc 100644 --- a/lib/private/Installer.php +++ b/lib/private/Installer.php @@ -1,41 +1,11 @@ <?php + +declare(strict_types=1); + /** - * @copyright Copyright (c) 2016, ownCloud, Inc. - * @copyright Copyright (c) 2016, Lukas Reschke <lukas@statuscode.ch> - * - * @author acsfer <carlos@reendex.com> - * @author Arthur Schiwon <blizzz@arthur-schiwon.de> - * @author Brice Maron <brice@bmaron.net> - * @author Christoph Wurst <christoph@winzerhof-wurst.at> - * @author Daniel Kesselberg <mail@danielkesselberg.de> - * @author Frank Karlitschek <frank@karlitschek.de> - * @author Georg Ehrke <oc.list@georgehrke.com> - * @author Joas Schilling <coding@schilljs.com> - * @author John Molakvoæ <skjnldsv@protonmail.com> - * @author Julius Härtl <jus@bitgrid.net> - * @author Kamil Domanski <kdomanski@kdemail.net> - * @author Lukas Reschke <lukas@statuscode.ch> - * @author Morris Jobke <hey@morrisjobke.de> - * @author Robin Appelman <robin@icewind.nl> - * @author Roeland Jago Douma <roeland@famdouma.nl> - * @author root "root@oc.(none)" - * @author Thomas Müller <thomas.mueller@tmit.eu> - * @author Thomas Tanghus <thomas@tanghus.net> - * - * @license AGPL-3.0 - * - * This code is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License, version 3, - * as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License, version 3, - * along with this program. If not, see <http://www.gnu.org/licenses/> - * + * SPDX-FileCopyrightText: 2016 Nextcloud GmbH and Nextcloud contributors + * SPDX-FileCopyrightText: 2016 ownCloud, Inc. + * SPDX-License-Identifier: AGPL-3.0-only */ namespace OC; @@ -53,6 +23,7 @@ use OCP\HintException; use OCP\Http\Client\IClientService; use OCP\IConfig; use OCP\ITempManager; +use OCP\Migration\IOutput; use phpseclib\File\X509; use Psr\Log\LoggerInterface; @@ -60,37 +31,17 @@ use Psr\Log\LoggerInterface; * This class provides the functionality needed to install, update and remove apps */ class Installer { - /** @var AppFetcher */ - private $appFetcher; - /** @var IClientService */ - private $clientService; - /** @var ITempManager */ - private $tempManager; - /** @var LoggerInterface */ - private $logger; - /** @var IConfig */ - private $config; - /** @var array - for caching the result of app fetcher */ - private $apps = null; - /** @var bool|null - for caching the result of the ready status */ - private $isInstanceReadyForUpdates = null; - /** @var bool */ - private $isCLI; + private ?bool $isInstanceReadyForUpdates = null; + private ?array $apps = null; public function __construct( - AppFetcher $appFetcher, - IClientService $clientService, - ITempManager $tempManager, - LoggerInterface $logger, - IConfig $config, - bool $isCLI + private AppFetcher $appFetcher, + private IClientService $clientService, + private ITempManager $tempManager, + private LoggerInterface $logger, + private IConfig $config, + private bool $isCLI, ) { - $this->appFetcher = $appFetcher; - $this->clientService = $clientService; - $this->tempManager = $tempManager; - $this->logger = $logger; - $this->config = $config; - $this->isCLI = $isCLI; } /** @@ -113,7 +64,7 @@ class Installer { throw new \Exception('The appinfo/database.xml file is not longer supported. Used in ' . $appId); } - $l = \OC::$server->getL10N('core'); + $l = \OCP\Util::getL10N('core'); $info = \OCP\Server::get(IAppManager::class)->getAppInfo($basedir . '/appinfo/info.xml', true, $l->getLanguageCode()); if (!is_array($info)) { @@ -150,7 +101,7 @@ class Installer { } //install the database - $ms = new MigrationService($info['id'], \OC::$server->get(Connection::class)); + $ms = new MigrationService($info['id'], \OCP\Server::get(Connection::class)); $ms->migrate('latest', !$previousVersion); if ($previousVersion) { @@ -164,16 +115,17 @@ class Installer { OC_App::executeRepairSteps($appId, $info['repair-steps']['install']); + $config = \OCP\Server::get(IConfig::class); //set the installed version - \OC::$server->getConfig()->setAppValue($info['id'], 'installed_version', \OCP\Server::get(IAppManager::class)->getAppVersion($info['id'], false)); - \OC::$server->getConfig()->setAppValue($info['id'], 'enabled', 'no'); + $config->setAppValue($info['id'], 'installed_version', \OCP\Server::get(IAppManager::class)->getAppVersion($info['id'], false)); + $config->setAppValue($info['id'], 'enabled', 'no'); //set remote/public handlers foreach ($info['remote'] as $name => $path) { - \OC::$server->getConfig()->setAppValue('core', 'remote_'.$name, $info['id'].'/'.$path); + $config->setAppValue('core', 'remote_'.$name, $info['id'].'/'.$path); } foreach ($info['public'] as $name => $path) { - \OC::$server->getConfig()->setAppValue('core', 'public_'.$name, $info['id'].'/'.$path); + $config->setAppValue('core', 'public_'.$name, $info['id'].'/'.$path); } OC_App::setAppTypes($info['id']); @@ -184,11 +136,9 @@ class Installer { /** * Updates the specified app from the appstore * - * @param string $appId - * @param bool [$allowUnstable] Allow unstable releases - * @return bool + * @param bool $allowUnstable Allow unstable releases */ - public function updateAppstoreApp($appId, $allowUnstable = false) { + public function updateAppstoreApp(string $appId, bool $allowUnstable = false): bool { if ($this->isUpdateAvailable($appId, $allowUnstable)) { try { $this->downloadApp($appId, $allowUnstable); @@ -224,7 +174,7 @@ class Installer { * * @throws \Exception If the installation was not successful */ - public function downloadApp($appId, $allowUnstable = false) { + public function downloadApp(string $appId, bool $allowUnstable = false): void { $appId = strtolower($appId); $apps = $this->appFetcher->get($allowUnstable); @@ -297,11 +247,7 @@ class Installer { // Check if the signature actually matches the downloaded content $certificate = openssl_get_publickey($app['certificate']); - $verified = (bool)openssl_verify(file_get_contents($tempFile), base64_decode($app['releases'][0]['signature']), $certificate, OPENSSL_ALGO_SHA512); - // PHP 8+ deprecates openssl_free_key and automatically destroys the key instance when it goes out of scope - if ((PHP_VERSION_ID < 80000)) { - openssl_free_key($certificate); - } + $verified = openssl_verify(file_get_contents($tempFile), base64_decode($app['releases'][0]['signature']), $certificate, OPENSSL_ALGO_SHA512) === 1; if ($verified === true) { // Seems to match, let's proceed @@ -322,6 +268,15 @@ class Installer { $folders = array_diff($allFiles, ['.', '..']); $folders = array_values($folders); + if (count($folders) < 1) { + throw new \Exception( + sprintf( + 'Extracted app %s has no folders', + $appId + ) + ); + } + if (count($folders) > 1) { throw new \Exception( sprintf( @@ -332,13 +287,17 @@ class Installer { } // Check if appinfo/info.xml has the same app ID as well - if ((PHP_VERSION_ID < 80000)) { - $loadEntities = libxml_disable_entity_loader(false); - $xml = simplexml_load_string(file_get_contents($extractDir . '/' . $folders[0] . '/appinfo/info.xml')); - libxml_disable_entity_loader($loadEntities); - } else { - $xml = simplexml_load_string(file_get_contents($extractDir . '/' . $folders[0] . '/appinfo/info.xml')); + $xml = simplexml_load_string(file_get_contents($extractDir . '/' . $folders[0] . '/appinfo/info.xml')); + + if ($xml === false) { + throw new \Exception( + sprintf( + 'Failed to load info.xml for app id %s', + $appId, + ) + ); } + if ((string)$xml->id !== $appId) { throw new \Exception( sprintf( @@ -400,10 +359,10 @@ class Installer { * @param bool $allowUnstable * @return string|false false or the version number of the update */ - public function isUpdateAvailable($appId, $allowUnstable = false) { + public function isUpdateAvailable($appId, $allowUnstable = false): string|false { if ($this->isInstanceReadyForUpdates === null) { $installPath = OC_App::getInstallPath(); - if ($installPath === false || $installPath === null) { + if ($installPath === null) { $this->isInstanceReadyForUpdates = false; } else { $this->isInstanceReadyForUpdates = true; @@ -443,12 +402,10 @@ class Installer { /** * Check if app has been installed from git - * @param string $name name of the application to remove - * @return boolean * * The function will check if the path contains a .git folder */ - private function isInstalledFromGit($appId) { + private function isInstalledFromGit(string $appId): bool { $app = \OC_App::findAppInDirectories($appId); if ($app === false) { return false; @@ -459,12 +416,10 @@ class Installer { /** * Check if app is already downloaded - * @param string $name name of the application to remove - * @return boolean * * The function will check if the app is already downloaded in the apps repository */ - public function isDownloaded($name) { + public function isDownloaded(string $name): bool { foreach (\OC::$APPSROOTS as $dir) { $dirToTest = $dir['path']; $dirToTest .= '/'; @@ -481,9 +436,6 @@ class Installer { /** * Removes an app - * @param string $appId ID of the application to remove - * @return boolean - * * * This function works as follows * -# call uninstall repair steps @@ -492,9 +444,9 @@ class Installer { * The function will not delete preferences, tables and the configuration, * this has to be done by the function oc_app_uninstall(). */ - public function removeApp($appId) { + public function removeApp(string $appId): bool { if ($this->isDownloaded($appId)) { - if (\OC::$server->getAppManager()->isShipped($appId)) { + if (\OCP\Server::get(IAppManager::class)->isShipped($appId)) { return false; } $appDir = OC_App::getInstallPath() . '/' . $appId; @@ -510,10 +462,9 @@ class Installer { /** * Installs the app within the bundle and marks the bundle as installed * - * @param Bundle $bundle * @throws \Exception If app could not get installed */ - public function installAppBundle(Bundle $bundle) { + public function installAppBundle(Bundle $bundle): void { $appIds = $bundle->getAppIdentifiers(); foreach ($appIds as $appId) { if (!$this->isDownloaded($appId)) { @@ -536,9 +487,12 @@ class Installer { * working ownCloud at the end instead of an aborted update. * @return array Array of error messages (appid => Exception) */ - public static function installShippedApps($softErrors = false) { - $appManager = \OC::$server->getAppManager(); - $config = \OC::$server->getConfig(); + public static function installShippedApps(bool $softErrors = false, ?IOutput $output = null): array { + if ($output instanceof IOutput) { + $output->debug('Installing shipped apps'); + } + $appManager = \OCP\Server::get(IAppManager::class); + $config = \OCP\Server::get(IConfig::class); $errors = []; foreach (\OC::$APPSROOTS as $app_dir) { if ($dir = opendir($app_dir['path'])) { @@ -551,7 +505,7 @@ class Installer { && $config->getAppValue($filename, 'enabled') !== 'no') { if ($softErrors) { try { - Installer::installShippedApp($filename); + Installer::installShippedApp($filename, $output); } catch (HintException $e) { if ($e->getPrevious() instanceof TableExistsException) { $errors[$filename] = $e; @@ -560,7 +514,7 @@ class Installer { throw $e; } } else { - Installer::installShippedApp($filename); + Installer::installShippedApp($filename, $output); } $config->setAppValue($filename, 'enabled', 'yes'); } @@ -577,17 +531,22 @@ class Installer { /** * install an app already placed in the app folder - * @param string $app id of the app to install - * @return integer */ - public static function installShippedApp($app) { - //install the database - $appPath = OC_App::getAppPath($app); - \OC_App::registerAutoloading($app, $appPath); + public static function installShippedApp(string $app, ?IOutput $output = null): string|false { + if ($output instanceof IOutput) { + $output->debug('Installing ' . $app); + } + + $appManager = \OCP\Server::get(IAppManager::class); + $config = \OCP\Server::get(IConfig::class); - $config = \OC::$server->getConfig(); + $appPath = $appManager->getAppPath($app); + \OC_App::registerAutoloading($app, $appPath); - $ms = new MigrationService($app, \OC::$server->get(Connection::class)); + $ms = new MigrationService($app, \OCP\Server::get(Connection::class)); + if ($output instanceof IOutput) { + $ms->setOutput($output); + } $previousVersion = $config->getAppValue($app, 'installed_version', false); $ms->migrate('latest', !$previousVersion); @@ -598,6 +557,9 @@ class Installer { if (is_null($info)) { return false; } + if ($output instanceof IOutput) { + $output->debug('Registering tasks of ' . $app); + } \OC_App::setupBackgroundJobs($info['background-jobs']); OC_App::executeRepairSteps($app, $info['repair-steps']['install']); @@ -620,10 +582,7 @@ class Installer { return $info['id']; } - /** - * @param string $script - */ - private static function includeAppScript($script) { + private static function includeAppScript(string $script): void { if (file_exists($script)) { include $script; } diff --git a/lib/private/IntegrityCheck/Checker.php b/lib/private/IntegrityCheck/Checker.php index a2ff62e4070..a6de3cf6030 100644 --- a/lib/private/IntegrityCheck/Checker.php +++ b/lib/private/IntegrityCheck/Checker.php @@ -1,33 +1,10 @@ <?php declare(strict_types=1); - /** - * @copyright Copyright (c) 2016, ownCloud, Inc. - * - * @author Christoph Wurst <christoph@winzerhof-wurst.at> - * @author Joas Schilling <coding@schilljs.com> - * @author Lukas Reschke <lukas@statuscode.ch> - * @author Morris Jobke <hey@morrisjobke.de> - * @author Roeland Jago Douma <roeland@famdouma.nl> - * @author Victor Dubiniuk <dubiniuk@owncloud.com> - * @author Vincent Petry <vincent@nextcloud.com> - * @author Xheni Myrtaj <myrtajxheni@gmail.com> - * - * @license AGPL-3.0 - * - * This code is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License, version 3, - * as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License, version 3, - * along with this program. If not, see <http://www.gnu.org/licenses/> - * + * SPDX-FileCopyrightText: 2016-2024 Nextcloud GmbH and Nextcloud contributors + * SPDX-FileCopyrightText: 2016 ownCloud, Inc. + * SPDX-License-Identifier: AGPL-3.0-only */ namespace OC\IntegrityCheck; @@ -40,6 +17,7 @@ use OC\IntegrityCheck\Iterator\ExcludeFileByNameFilterIterator; use OC\IntegrityCheck\Iterator\ExcludeFoldersByPathFilterIterator; use OCP\App\IAppManager; use OCP\Files\IMimeTypeDetector; +use OCP\IAppConfig; use OCP\ICache; use OCP\ICacheFactory; use OCP\IConfig; @@ -58,44 +36,20 @@ use phpseclib\File\X509; */ class Checker { public const CACHE_KEY = 'oc.integritycheck.checker'; - /** @var EnvironmentHelper */ - private $environmentHelper; - /** @var AppLocator */ - private $appLocator; - /** @var FileAccessHelper */ - private $fileAccessHelper; - /** @var IConfig|null */ - private $config; - /** @var ICache */ - private $cache; - /** @var IAppManager|null */ - private $appManager; - /** @var IMimeTypeDetector */ - private $mimeTypeDetector; - /** - * @param EnvironmentHelper $environmentHelper - * @param FileAccessHelper $fileAccessHelper - * @param AppLocator $appLocator - * @param IConfig|null $config - * @param ICacheFactory $cacheFactory - * @param IAppManager|null $appManager - * @param IMimeTypeDetector $mimeTypeDetector - */ - public function __construct(EnvironmentHelper $environmentHelper, - FileAccessHelper $fileAccessHelper, - AppLocator $appLocator, - ?IConfig $config, - ICacheFactory $cacheFactory, - ?IAppManager $appManager, - IMimeTypeDetector $mimeTypeDetector) { - $this->environmentHelper = $environmentHelper; - $this->fileAccessHelper = $fileAccessHelper; - $this->appLocator = $appLocator; - $this->config = $config; + private ICache $cache; + + public function __construct( + private EnvironmentHelper $environmentHelper, + private FileAccessHelper $fileAccessHelper, + private AppLocator $appLocator, + private ?IConfig $config, + private ?IAppConfig $appConfig, + ICacheFactory $cacheFactory, + private ?IAppManager $appManager, + private IMimeTypeDetector $mimeTypeDetector, + ) { $this->cache = $cacheFactory->createDistributed(self::CACHE_KEY); - $this->appManager = $appManager; - $this->mimeTypeDetector = $mimeTypeDetector; } /** @@ -114,15 +68,7 @@ class Checker { * applicable for very specific scenarios and we should not advertise it * too prominent. So please do not add it to config.sample.php. */ - $isIntegrityCheckDisabled = false; - if ($this->config !== null) { - $isIntegrityCheckDisabled = $this->config->getSystemValueBool('integrity.check.disabled', false); - } - if ($isIntegrityCheckDisabled) { - return false; - } - - return true; + return !($this->config?->getSystemValueBool('integrity.check.disabled', false) ?? false); } /** @@ -161,7 +107,7 @@ class Checker { * @return array Array of hashes. */ private function generateHashes(\RecursiveIteratorIterator $iterator, - string $path): array { + string $path): array { $hashes = []; $baseDirectoryLength = \strlen($path); @@ -223,8 +169,8 @@ class Checker { * @return array */ private function createSignatureData(array $hashes, - X509 $certificate, - RSA $privateKey): array { + X509 $certificate, + RSA $privateKey): array { ksort($hashes); $privateKey->setSignatureMode(RSA::SIGNATURE_PSS); @@ -249,8 +195,8 @@ class Checker { * @throws \Exception */ public function writeAppSignature($path, - X509 $certificate, - RSA $privateKey) { + X509 $certificate, + RSA $privateKey) { $appInfoDir = $path . '/appinfo'; try { $this->fileAccessHelper->assertDirectoryExists($appInfoDir); @@ -279,8 +225,8 @@ class Checker { * @throws \Exception */ public function writeCoreSignature(X509 $certificate, - RSA $rsa, - $path) { + RSA $rsa, + $path) { $coreDir = $path . '/core'; try { $this->fileAccessHelper->assertDirectoryExists($coreDir); @@ -443,10 +389,7 @@ class Checker { return json_decode($cachedResults, true); } - if ($this->config !== null) { - return json_decode($this->config->getAppValue('core', self::CACHE_KEY, '{}'), true); - } - return []; + return $this->appConfig?->getValueArray('core', self::CACHE_KEY, lazy: true) ?? []; } /** @@ -461,9 +404,7 @@ class Checker { if (!empty($result)) { $resultArray[$scope] = $result; } - if ($this->config !== null) { - $this->config->setAppValue('core', self::CACHE_KEY, json_encode($resultArray)); - } + $this->appConfig?->setValueArray('core', self::CACHE_KEY, $resultArray, lazy: true); $this->cache->set(self::CACHE_KEY, json_encode($resultArray)); } @@ -472,7 +413,7 @@ class Checker { * Clean previous results for a proper rescanning. Otherwise */ private function cleanResults() { - $this->config->deleteAppValue('core', self::CACHE_KEY); + $this->appConfig->deleteKey('core', self::CACHE_KEY); $this->cache->remove(self::CACHE_KEY); } diff --git a/lib/private/IntegrityCheck/Exceptions/InvalidSignatureException.php b/lib/private/IntegrityCheck/Exceptions/InvalidSignatureException.php index 18a4ba40e1a..25f8b0dcbf7 100644 --- a/lib/private/IntegrityCheck/Exceptions/InvalidSignatureException.php +++ b/lib/private/IntegrityCheck/Exceptions/InvalidSignatureException.php @@ -1,24 +1,9 @@ <?php + /** - * @copyright Copyright (c) 2016, ownCloud, Inc. - * - * @author Christoph Wurst <christoph@winzerhof-wurst.at> - * @author Lukas Reschke <lukas@statuscode.ch> - * - * @license AGPL-3.0 - * - * This code is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License, version 3, - * as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License, version 3, - * along with this program. If not, see <http://www.gnu.org/licenses/> - * + * SPDX-FileCopyrightText: 2016-2024 Nextcloud GmbH and Nextcloud contributors + * SPDX-FileCopyrightText: 2016 ownCloud, Inc. + * SPDX-License-Identifier: AGPL-3.0-only */ namespace OC\IntegrityCheck\Exceptions; diff --git a/lib/private/IntegrityCheck/Helpers/AppLocator.php b/lib/private/IntegrityCheck/Helpers/AppLocator.php index 9980fbdf497..3da5cc13227 100644 --- a/lib/private/IntegrityCheck/Helpers/AppLocator.php +++ b/lib/private/IntegrityCheck/Helpers/AppLocator.php @@ -1,28 +1,10 @@ <?php declare(strict_types=1); - /** - * @copyright Copyright (c) 2016, ownCloud, Inc. - * - * @author Christoph Wurst <christoph@winzerhof-wurst.at> - * @author Lukas Reschke <lukas@statuscode.ch> - * @author Roeland Jago Douma <roeland@famdouma.nl> - * - * @license AGPL-3.0 - * - * This code is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License, version 3, - * as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License, version 3, - * along with this program. If not, see <http://www.gnu.org/licenses/> - * + * SPDX-FileCopyrightText: 2016-2024 Nextcloud GmbH and Nextcloud contributors + * SPDX-FileCopyrightText: 2016 ownCloud, Inc. + * SPDX-License-Identifier: AGPL-3.0-only */ namespace OC\IntegrityCheck\Helpers; diff --git a/lib/private/IntegrityCheck/Helpers/EnvironmentHelper.php b/lib/private/IntegrityCheck/Helpers/EnvironmentHelper.php index 5b6d0448703..dcdc7839f8e 100644 --- a/lib/private/IntegrityCheck/Helpers/EnvironmentHelper.php +++ b/lib/private/IntegrityCheck/Helpers/EnvironmentHelper.php @@ -1,27 +1,10 @@ <?php declare(strict_types=1); - /** - * @copyright Copyright (c) 2016, ownCloud, Inc. - * - * @author Lukas Reschke <lukas@statuscode.ch> - * @author Roeland Jago Douma <roeland@famdouma.nl> - * - * @license AGPL-3.0 - * - * This code is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License, version 3, - * as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License, version 3, - * along with this program. If not, see <http://www.gnu.org/licenses/> - * + * SPDX-FileCopyrightText: 2016-2024 Nextcloud GmbH and Nextcloud contributors + * SPDX-FileCopyrightText: 2016 ownCloud, Inc. + * SPDX-License-Identifier: AGPL-3.0-only */ namespace OC\IntegrityCheck\Helpers; diff --git a/lib/private/IntegrityCheck/Helpers/FileAccessHelper.php b/lib/private/IntegrityCheck/Helpers/FileAccessHelper.php index cd16a619915..c6e57d5d935 100644 --- a/lib/private/IntegrityCheck/Helpers/FileAccessHelper.php +++ b/lib/private/IntegrityCheck/Helpers/FileAccessHelper.php @@ -1,29 +1,10 @@ <?php declare(strict_types=1); - /** - * @copyright Copyright (c) 2016, ownCloud, Inc. - * - * @author Christoph Wurst <christoph@winzerhof-wurst.at> - * @author Lukas Reschke <lukas@statuscode.ch> - * @author Roeland Jago Douma <roeland@famdouma.nl> - * @author Victor Dubiniuk <dubiniuk@owncloud.com> - * - * @license AGPL-3.0 - * - * This code is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License, version 3, - * as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License, version 3, - * along with this program. If not, see <http://www.gnu.org/licenses/> - * + * SPDX-FileCopyrightText: 2016-2024 Nextcloud GmbH and Nextcloud contributors + * SPDX-FileCopyrightText: 2016 ownCloud, Inc. + * SPDX-License-Identifier: AGPL-3.0-only */ namespace OC\IntegrityCheck\Helpers; diff --git a/lib/private/IntegrityCheck/Iterator/ExcludeFileByNameFilterIterator.php b/lib/private/IntegrityCheck/Iterator/ExcludeFileByNameFilterIterator.php index 6d26a40aef4..e2e68008a35 100644 --- a/lib/private/IntegrityCheck/Iterator/ExcludeFileByNameFilterIterator.php +++ b/lib/private/IntegrityCheck/Iterator/ExcludeFileByNameFilterIterator.php @@ -1,28 +1,10 @@ <?php declare(strict_types=1); - /** - * @copyright Copyright (c) 2016, ownCloud, Inc. - * - * @author Daniel Kesselberg <mail@danielkesselberg.de> - * @author Lukas Reschke <lukas@statuscode.ch> - * @author Romain Rivière <lecoyote@lecoyote.org> - * - * @license AGPL-3.0 - * - * This code is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License, version 3, - * as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License, version 3, - * along with this program. If not, see <http://www.gnu.org/licenses/> - * + * SPDX-FileCopyrightText: 2016-2024 Nextcloud GmbH and Nextcloud contributors + * SPDX-FileCopyrightText: 2016 ownCloud, Inc. + * SPDX-License-Identifier: AGPL-3.0-only */ namespace OC\IntegrityCheck\Iterator; diff --git a/lib/private/IntegrityCheck/Iterator/ExcludeFoldersByPathFilterIterator.php b/lib/private/IntegrityCheck/Iterator/ExcludeFoldersByPathFilterIterator.php index a0b48158b9f..f6d4c7afd4a 100644 --- a/lib/private/IntegrityCheck/Iterator/ExcludeFoldersByPathFilterIterator.php +++ b/lib/private/IntegrityCheck/Iterator/ExcludeFoldersByPathFilterIterator.php @@ -1,29 +1,10 @@ <?php declare(strict_types=1); - /** - * @copyright Copyright (c) 2016, ownCloud, Inc. - * - * @author Christoph Wurst <christoph@winzerhof-wurst.at> - * @author Lukas Reschke <lukas@statuscode.ch> - * @author RealRancor <Fisch.666@gmx.de> - * @author Roeland Jago Douma <roeland@famdouma.nl> - * - * @license AGPL-3.0 - * - * This code is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License, version 3, - * as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License, version 3, - * along with this program. If not, see <http://www.gnu.org/licenses/> - * + * SPDX-FileCopyrightText: 2016-2024 Nextcloud GmbH and Nextcloud contributors + * SPDX-FileCopyrightText: 2016 ownCloud, Inc. + * SPDX-License-Identifier: AGPL-3.0-only */ namespace OC\IntegrityCheck\Iterator; diff --git a/lib/private/KnownUser/KnownUser.php b/lib/private/KnownUser/KnownUser.php index 532d3ccb565..808c0400c6e 100644 --- a/lib/private/KnownUser/KnownUser.php +++ b/lib/private/KnownUser/KnownUser.php @@ -3,25 +3,8 @@ declare(strict_types=1); /** - * @copyright Copyright (c) 2021 Joas Schilling <coding@schilljs.com> - * - * @author Joas Schilling <coding@schilljs.com> - * - * @license GNU AGPL version 3 or any later version - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as - * published by the Free Software Foundation, either version 3 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - * + * SPDX-FileCopyrightText: 2021 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later */ namespace OC\KnownUser; diff --git a/lib/private/KnownUser/KnownUserMapper.php b/lib/private/KnownUser/KnownUserMapper.php index fd8f22cd0aa..4c3bbb0b436 100644 --- a/lib/private/KnownUser/KnownUserMapper.php +++ b/lib/private/KnownUser/KnownUserMapper.php @@ -3,25 +3,8 @@ declare(strict_types=1); /** - * @copyright Copyright (c) 2021 Joas Schilling <coding@schilljs.com> - * - * @author Joas Schilling <coding@schilljs.com> - * - * @license GNU AGPL version 3 or any later version - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as - * published by the Free Software Foundation, either version 3 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - * + * SPDX-FileCopyrightText: 2021 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later */ namespace OC\KnownUser; diff --git a/lib/private/KnownUser/KnownUserService.php b/lib/private/KnownUser/KnownUserService.php index fec24542007..aff0c097d98 100644 --- a/lib/private/KnownUser/KnownUserService.php +++ b/lib/private/KnownUser/KnownUserService.php @@ -3,26 +3,8 @@ declare(strict_types=1); /** - * @copyright Copyright (c) 2021 Joas Schilling <coding@schilljs.com> - * - * @author Joas Schilling <coding@schilljs.com> - * @author Vincent Petry <vincent@nextcloud.com> - * - * @license GNU AGPL version 3 or any later version - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as - * published by the Free Software Foundation, either version 3 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - * + * SPDX-FileCopyrightText: 2021 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later */ namespace OC\KnownUser; diff --git a/lib/private/L10N/Factory.php b/lib/private/L10N/Factory.php index 778124c4c38..1825719fb75 100644 --- a/lib/private/L10N/Factory.php +++ b/lib/private/L10N/Factory.php @@ -1,45 +1,15 @@ <?php declare(strict_types=1); - /** - * @copyright Copyright (c) 2016, ownCloud, Inc. - * @copyright 2016 Roeland Jago Douma <roeland@famdouma.nl> - * @copyright 2016 Lukas Reschke <lukas@statuscode.ch> - * - * @author Arthur Schiwon <blizzz@arthur-schiwon.de> - * @author Bart Visscher <bartv@thisnet.nl> - * @author Bjoern Schiessle <bjoern@schiessle.org> - * @author Christoph Wurst <christoph@winzerhof-wurst.at> - * @author Georg Ehrke <oc.list@georgehrke.com> - * @author GretaD <gretadoci@gmail.com> - * @author Joas Schilling <coding@schilljs.com> - * @author John Molakvoæ <skjnldsv@protonmail.com> - * @author Lukas Reschke <lukas@statuscode.ch> - * @author Morris Jobke <hey@morrisjobke.de> - * @author Robin Appelman <robin@icewind.nl> - * @author Robin McCorkell <robin@mccorkell.me.uk> - * @author Roeland Jago Douma <roeland@famdouma.nl> - * @author Thomas Citharel <nextcloud@tcit.fr> - * - * @license AGPL-3.0 - * - * This code is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License, version 3, - * as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License, version 3, - * along with this program. If not, see <http://www.gnu.org/licenses/> - * + * SPDX-FileCopyrightText: 2016 Nextcloud GmbH and Nextcloud contributors + * SPDX-FileCopyrightText: 2016 ownCloud, Inc. + * SPDX-License-Identifier: AGPL-3.0-only */ - namespace OC\L10N; +use OCP\App\AppPathNotFoundException; +use OCP\App\IAppManager; use OCP\ICache; use OCP\ICacheFactory; use OCP\IConfig; @@ -88,38 +58,17 @@ class Factory implements IFactory { 'pt_BR', 'pt_PT', 'da', 'fi_FI', 'nb_NO', 'sv', 'tr', 'zh_CN', 'ko' ]; - /** @var IConfig */ - protected $config; - - /** @var IRequest */ - protected $request; - - /** @var IUserSession */ - protected IUserSession $userSession; - private ICache $cache; - /** @var string */ - protected $serverRoot; - - /** - * @param IConfig $config - * @param IRequest $request - * @param IUserSession $userSession - * @param string $serverRoot - */ public function __construct( - IConfig $config, - IRequest $request, - IUserSession $userSession, + protected IConfig $config, + protected IRequest $request, + protected IUserSession $userSession, ICacheFactory $cacheFactory, - $serverRoot + protected string $serverRoot, + protected IAppManager $appManager, ) { - $this->config = $config; - $this->request = $request; - $this->userSession = $userSession; $this->cache = $cacheFactory->createLocal('L10NFactory'); - $this->serverRoot = $serverRoot; } /** @@ -233,7 +182,7 @@ class Factory implements IFactory { return 'en'; } - public function findGenericLanguage(string $appId = null): string { + public function findGenericLanguage(?string $appId = null): string { // Step 1: Forced language always has precedence over anything else $forcedLanguage = $this->config->getSystemValue('force_language', false); if ($forcedLanguage !== false) { @@ -283,9 +232,9 @@ class Factory implements IFactory { } if ($this->config->getSystemValueBool('installed', false)) { - $userId = null !== $this->userSession->getUser() ? $this->userSession->getUser()->getUID() : null; + $userId = $this->userSession->getUser() !== null ? $this->userSession->getUser()->getUID() : null; $userLocale = null; - if (null !== $userId) { + if ($userId !== null) { $userLocale = $this->config->getUserValue($userId, 'core', 'locale', null); } } else { @@ -304,7 +253,7 @@ class Factory implements IFactory { } // If no user locale set, use lang as locale - if (null !== $lang && $this->localeExists($lang)) { + if ($lang !== null && $this->localeExists($lang)) { return $lang; } @@ -319,7 +268,7 @@ class Factory implements IFactory { * @param string $locale * @return null|string */ - public function findLanguageFromLocale(string $app = 'core', string $locale = null) { + public function findLanguageFromLocale(string $app = 'core', ?string $locale = null) { if ($this->languageExists($app, $locale)) { return $locale; } @@ -358,7 +307,7 @@ class Factory implements IFactory { $files = scandir($dir); if ($files !== false) { foreach ($files as $file) { - if (substr($file, -5) === '.json' && substr($file, 0, 4) !== 'l10n') { + if (str_ends_with($file, '.json') && !str_starts_with($file, 'l10n')) { $available[] = substr($file, 0, -5); } } @@ -374,7 +323,7 @@ class Factory implements IFactory { $files = scandir($themeDir); if ($files !== false) { foreach ($files as $file) { - if (substr($file, -5) === '.json' && substr($file, 0, 4) !== 'l10n') { + if (str_ends_with($file, '.json') && !str_starts_with($file, 'l10n')) { $available[] = substr($file, 0, -5); } } @@ -415,7 +364,7 @@ class Factory implements IFactory { return in_array($lang, $languages); } - public function getLanguageIterator(IUser $user = null): ILanguageIterator { + public function getLanguageIterator(?IUser $user = null): ILanguageIterator { $user = $user ?? $this->userSession->getUser(); if ($user === null) { throw new \RuntimeException('Failed to get an IUser instance'); @@ -430,7 +379,7 @@ class Factory implements IFactory { * @return string * @since 20.0.0 */ - public function getUserLanguage(IUser $user = null): string { + public function getUserLanguage(?IUser $user = null): string { $language = $this->config->getSystemValue('force_language', false); if ($language !== false) { return $language; @@ -490,10 +439,14 @@ class Factory implements IFactory { [$preferred_language] = explode(';', $preference); $preferred_language = str_replace('-', '_', $preferred_language); + $preferred_language_parts = explode('_', $preferred_language); foreach ($available as $available_language) { if ($preferred_language === strtolower($available_language)) { return $this->respectDefaultLanguage($app, $available_language); } + if (strtolower($available_language) === $preferred_language_parts[0].'_'.end($preferred_language_parts)) { + return $available_language; + } } // Fallback from de_De to de @@ -554,13 +507,9 @@ class Factory implements IFactory { /** * Get a list of language files that should be loaded * - * @param string $app - * @param string $lang * @return string[] */ - // FIXME This method is only public, until OC_L10N does not need it anymore, - // FIXME This is also the reason, why it is not in the public interface - public function getL10nFilesForApp($app, $lang) { + private function getL10nFilesForApp(string $app, string $lang): array { $languageFiles = []; $i18nDir = $this->findL10nDir($app); @@ -568,7 +517,7 @@ class Factory implements IFactory { if (($this->isSubDirectory($transFile, $this->serverRoot . '/core/l10n/') || $this->isSubDirectory($transFile, $this->serverRoot . '/lib/l10n/') - || $this->isSubDirectory($transFile, \OC_App::getAppPath($app) . '/l10n/')) + || $this->isSubDirectory($transFile, $this->appManager->getAppPath($app) . '/l10n/')) && file_exists($transFile) ) { // load the translations file @@ -598,9 +547,12 @@ class Factory implements IFactory { if (file_exists($this->serverRoot . '/' . $app . '/l10n/')) { return $this->serverRoot . '/' . $app . '/l10n/'; } - } elseif ($app && \OC_App::getAppPath($app) !== false) { - // Check if the app is in the app folder - return \OC_App::getAppPath($app) . '/l10n/'; + } elseif ($app) { + try { + return $this->appManager->getAppPath($app) . '/l10n/'; + } catch (AppPathNotFoundException) { + /* App not found, continue */ + } } return $this->serverRoot . '/core/l10n/'; } diff --git a/lib/private/L10N/L10N.php b/lib/private/L10N/L10N.php index ea4aa0527bb..39d778f80d6 100644 --- a/lib/private/L10N/L10N.php +++ b/lib/private/L10N/L10N.php @@ -1,35 +1,16 @@ <?php declare(strict_types=1); - /** - * @copyright Copyright (c) 2016, ownCloud, Inc. - * - * @author Christoph Wurst <christoph@winzerhof-wurst.at> - * @author Georg Ehrke <oc.list@georgehrke.com> - * @author Joas Schilling <coding@schilljs.com> - * @author Roeland Jago Douma <roeland@famdouma.nl> - * @author Thomas Citharel <nextcloud@tcit.fr> - * - * @license AGPL-3.0 - * - * This code is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License, version 3, - * as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License, version 3, - * along with this program. If not, see <http://www.gnu.org/licenses/> - * + * SPDX-FileCopyrightText: 2016-2024 Nextcloud GmbH and Nextcloud contributors + * SPDX-FileCopyrightText: 2016 ownCloud, Inc. + * SPDX-License-Identifier: AGPL-3.0-only */ namespace OC\L10N; use OCP\IL10N; use OCP\L10N\IFactory; +use Psr\Log\LoggerInterface; use Punic\Calendar; use Symfony\Component\Translation\IdentityTranslator; @@ -156,7 +137,7 @@ class L10N implements IL10N { * - jsdate: Returns the short JS date format */ public function l(string $type, $data = null, array $options = []) { - if (null === $this->locale) { + if ($this->locale === null) { // Use the language of the instance $this->locale = $this->getLanguageCode(); } @@ -234,7 +215,7 @@ class L10N implements IL10N { $json = json_decode(file_get_contents($translationFile), true); if (!\is_array($json)) { $jsonError = json_last_error(); - \OC::$server->getLogger()->warning("Failed to load $translationFile - json error code: $jsonError", ['app' => 'l10n']); + \OCP\Server::get(LoggerInterface::class)->warning("Failed to load $translationFile - json error code: $jsonError", ['app' => 'l10n']); return false; } diff --git a/lib/private/L10N/L10NString.php b/lib/private/L10N/L10NString.php index ea64f17eff3..a6515e78f24 100644 --- a/lib/private/L10N/L10NString.php +++ b/lib/private/L10N/L10NString.php @@ -1,29 +1,9 @@ <?php + /** - * @copyright Copyright (c) 2016, ownCloud, Inc. - * - * @author Bart Visscher <bartv@thisnet.nl> - * @author Bernhard Posselt <dev@bernhard-posselt.com> - * @author Jakob Sack <mail@jakobsack.de> - * @author Joas Schilling <coding@schilljs.com> - * @author Jörn Friedrich Dreyer <jfd@butonic.de> - * @author Roeland Jago Douma <roeland@famdouma.nl> - * @author Thomas Müller <thomas.mueller@tmit.eu> - * - * @license AGPL-3.0 - * - * This code is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License, version 3, - * as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License, version 3, - * along with this program. If not, see <http://www.gnu.org/licenses/> - * + * SPDX-FileCopyrightText: 2016-2024 Nextcloud GmbH and Nextcloud contributors + * SPDX-FileCopyrightText: 2016 ownCloud, Inc. + * SPDX-License-Identifier: AGPL-3.0-only */ namespace OC\L10N; diff --git a/lib/private/L10N/LanguageIterator.php b/lib/private/L10N/LanguageIterator.php index 8b3aaf10210..531eea5866b 100644 --- a/lib/private/L10N/LanguageIterator.php +++ b/lib/private/L10N/LanguageIterator.php @@ -3,26 +3,8 @@ declare(strict_types=1); /** - * @copyright Copyright (c) 2018 Arthur Schiwon <blizzz@arthur-schiwon.de> - * - * @author Arthur Schiwon <blizzz@arthur-schiwon.de> - * @author Christoph Wurst <christoph@winzerhof-wurst.at> - * - * @license GNU AGPL version 3 or any later version - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as - * published by the Free Software Foundation, either version 3 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - * + * SPDX-FileCopyrightText: 2018 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later */ namespace OC\L10N; diff --git a/lib/private/L10N/LanguageNotFoundException.php b/lib/private/L10N/LanguageNotFoundException.php index d483fd799dd..31b08b2ec14 100644 --- a/lib/private/L10N/LanguageNotFoundException.php +++ b/lib/private/L10N/LanguageNotFoundException.php @@ -1,24 +1,7 @@ <?php /** - * @copyright Copyright (c) 2016 Roeland Jago Douma <roeland@famdouma.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/>. - * + * SPDX-FileCopyrightText: 2016 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later */ namespace OC\L10N; diff --git a/lib/private/L10N/LazyL10N.php b/lib/private/L10N/LazyL10N.php index 3226c1a604b..0d72f638632 100644 --- a/lib/private/L10N/LazyL10N.php +++ b/lib/private/L10N/LazyL10N.php @@ -3,25 +3,8 @@ declare(strict_types=1); /** - * @copyright Copyright (c) 2019, Roeland Jago Douma <roeland@famdouma.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/>. - * + * SPDX-FileCopyrightText: 2019 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later */ namespace OC\L10N; diff --git a/lib/private/LDAP/NullLDAPProviderFactory.php b/lib/private/LDAP/NullLDAPProviderFactory.php index 23dfe78952c..55561b5692e 100644 --- a/lib/private/LDAP/NullLDAPProviderFactory.php +++ b/lib/private/LDAP/NullLDAPProviderFactory.php @@ -3,25 +3,8 @@ declare(strict_types=1); /** - * @copyright Copyright (c) 2021 Robin Appelman <robin@icewind.nl> - * - * @author Robin Appelman <robin@icewind.nl> - * - * @license GNU AGPL version 3 or any later version - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as - * published by the Free Software Foundation, either version 3 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - * + * SPDX-FileCopyrightText: 2021 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later */ namespace OC\LDAP; diff --git a/lib/private/LargeFileHelper.php b/lib/private/LargeFileHelper.php index 40cf2ecd5ff..4d96e79ead4 100755 --- a/lib/private/LargeFileHelper.php +++ b/lib/private/LargeFileHelper.php @@ -1,31 +1,8 @@ <?php /** - * @copyright Copyright (c) 2016, ownCloud, Inc. - * @copyright Copyright (c) 2016, Lukas Reschke <lukas@statuscode.ch> - * - * @author Andreas Fischer <bantu@owncloud.com> - * @author Christoph Wurst <christoph@winzerhof-wurst.at> - * @author Joas Schilling <coding@schilljs.com> - * @author Lukas Reschke <lukas@statuscode.ch> - * @author marco44 <cousinmarc@gmail.com> - * @author Michael Roitzsch <reactorcontrol@icloud.com> - * @author Morris Jobke <hey@morrisjobke.de> - * @author Thomas Müller <thomas.mueller@tmit.eu> - * - * @license AGPL-3.0 - * - * This code is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License, version 3, - * as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License, version 3, - * along with this program. If not, see <http://www.gnu.org/licenses/> - * + * SPDX-FileCopyrightText: 2016 Nextcloud GmbH and Nextcloud contributors + * SPDX-FileCopyrightText: 2016 ownCloud, Inc. + * SPDX-License-Identifier: AGPL-3.0-only */ namespace OC; diff --git a/lib/private/Lock/AbstractLockingProvider.php b/lib/private/Lock/AbstractLockingProvider.php index 6e8289db12e..aa203f6a90b 100644 --- a/lib/private/Lock/AbstractLockingProvider.php +++ b/lib/private/Lock/AbstractLockingProvider.php @@ -1,28 +1,10 @@ <?php declare(strict_types=1); - /** - * @copyright Copyright (c) 2016, ownCloud, Inc. - * - * @author Christoph Wurst <christoph@winzerhof-wurst.at> - * @author Robin Appelman <robin@icewind.nl> - * @author Roeland Jago Douma <roeland@famdouma.nl> - * - * @license AGPL-3.0 - * - * This code is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License, version 3, - * as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License, version 3, - * along with this program. If not, see <http://www.gnu.org/licenses/> - * + * SPDX-FileCopyrightText: 2016-2024 Nextcloud GmbH and Nextcloud contributors + * SPDX-FileCopyrightText: 2016 ownCloud, Inc. + * SPDX-License-Identifier: AGPL-3.0-only */ namespace OC\Lock; @@ -33,14 +15,18 @@ use OCP\Lock\ILockingProvider; * to release any leftover locks at the end of the request */ abstract class AbstractLockingProvider implements ILockingProvider { - /** how long until we clear stray locks in seconds */ - protected int $ttl; - - protected $acquiredLocks = [ + protected array $acquiredLocks = [ 'shared' => [], 'exclusive' => [] ]; + /** + * + * @param int $ttl how long until we clear stray locks in seconds + */ + public function __construct(protected int $ttl) { + } + /** @inheritDoc */ protected function hasAcquiredLock(string $path, int $type): bool { if ($type === self::LOCK_SHARED) { diff --git a/lib/private/Lock/DBLockingProvider.php b/lib/private/Lock/DBLockingProvider.php index fb8af8ac55b..d19f2cf3791 100644 --- a/lib/private/Lock/DBLockingProvider.php +++ b/lib/private/Lock/DBLockingProvider.php @@ -1,30 +1,9 @@ <?php + /** - * @copyright Copyright (c) 2016, ownCloud, Inc. - * - * @author Christoph Wurst <christoph@winzerhof-wurst.at> - * @author Individual IT Services <info@individual-it.net> - * @author Joas Schilling <coding@schilljs.com> - * @author Morris Jobke <hey@morrisjobke.de> - * @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 - * - * This code is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License, version 3, - * as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License, version 3, - * along with this program. If not, see <http://www.gnu.org/licenses/> - * + * SPDX-FileCopyrightText: 2016-2024 Nextcloud GmbH and Nextcloud contributors + * SPDX-FileCopyrightText: 2016 ownCloud, Inc. + * SPDX-License-Identifier: AGPL-3.0-only */ namespace OC\Lock; @@ -39,21 +18,15 @@ use OCP\Lock\LockedException; * Locking provider that stores the locks in the database */ class DBLockingProvider extends AbstractLockingProvider { - private IDBConnection $connection; - private ITimeFactory $timeFactory; private array $sharedLocks = []; - private bool $cacheSharedLocks; public function __construct( - IDBConnection $connection, - ITimeFactory $timeFactory, + private IDBConnection $connection, + private ITimeFactory $timeFactory, int $ttl = 3600, - bool $cacheSharedLocks = true + private bool $cacheSharedLocks = true ) { - $this->connection = $connection; - $this->timeFactory = $timeFactory; - $this->ttl = $ttl; - $this->cacheSharedLocks = $cacheSharedLocks; + parent::__construct($ttl); } /** diff --git a/lib/private/Lock/MemcacheLockingProvider.php b/lib/private/Lock/MemcacheLockingProvider.php index d4eebd7c302..883abb5da98 100644 --- a/lib/private/Lock/MemcacheLockingProvider.php +++ b/lib/private/Lock/MemcacheLockingProvider.php @@ -1,47 +1,49 @@ <?php declare(strict_types=1); - /** - * @copyright Copyright (c) 2016, ownCloud, Inc. - * - * @author Christoph Wurst <christoph@winzerhof-wurst.at> - * @author Jaakko Salo <jaakkos@gmail.com> - * @author Robin Appelman <robin@icewind.nl> - * @author Roeland Jago Douma <roeland@famdouma.nl> - * - * @license AGPL-3.0 - * - * This code is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License, version 3, - * as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License, version 3, - * along with this program. If not, see <http://www.gnu.org/licenses/> - * + * SPDX-FileCopyrightText: 2016-2024 Nextcloud GmbH and Nextcloud contributors + * SPDX-FileCopyrightText: 2016 ownCloud, Inc. + * SPDX-License-Identifier: AGPL-3.0-only */ namespace OC\Lock; +use OCP\AppFramework\Utility\ITimeFactory; use OCP\IMemcache; use OCP\IMemcacheTTL; use OCP\Lock\LockedException; class MemcacheLockingProvider extends AbstractLockingProvider { - private IMemcache $memcache; + /** @var array<string, array{time: int, ttl: int}> */ + private array $oldTTLs = []; + + public function __construct( + private IMemcache $memcache, + private ITimeFactory $timeFactory, + int $ttl = 3600, + ) { + parent::__construct($ttl); + } - public function __construct(IMemcache $memcache, int $ttl = 3600) { - $this->memcache = $memcache; - $this->ttl = $ttl; + private function setTTL(string $path, ?int $ttl = null, ?int $compare = null): void { + if (is_null($ttl)) { + $ttl = $this->ttl; + } + if ($this->memcache instanceof IMemcacheTTL) { + if ($compare !== null) { + $this->memcache->compareSetTTL($path, $compare, $ttl); + } else { + $this->memcache->setTTL($path, $ttl); + } + } } - private function setTTL(string $path): void { + private function getTTL(string $path): int { if ($this->memcache instanceof IMemcacheTTL) { - $this->memcache->setTTL($path, $this->ttl); + $ttl = $this->memcache->getTTL($path); + return $ttl === false ? -1 : $ttl; + } else { + return -1; } } @@ -58,14 +60,22 @@ class MemcacheLockingProvider extends AbstractLockingProvider { public function acquireLock(string $path, int $type, ?string $readablePath = null): void { if ($type === self::LOCK_SHARED) { + // save the old TTL to for `restoreTTL` + $this->oldTTLs[$path] = [ + "ttl" => $this->getTTL($path), + "time" => $this->timeFactory->getTime() + ]; if (!$this->memcache->inc($path)) { throw new LockedException($path, null, $this->getExistingLockForException($path), $readablePath); } } else { + // when getting exclusive locks, we know there are no old TTLs to restore $this->memcache->add($path, 0); + // ttl is updated automatically when the `set` succeeds if (!$this->memcache->cas($path, 0, 'exclusive')) { throw new LockedException($path, null, $this->getExistingLockForException($path), $readablePath); } + unset($this->oldTTLs[$path]); } $this->setTTL($path); $this->markAcquire($path, $type); @@ -88,6 +98,12 @@ class MemcacheLockingProvider extends AbstractLockingProvider { $newValue = $this->memcache->dec($path); } + if ($newValue > 0) { + $this->restoreTTL($path); + } else { + unset($this->oldTTLs[$path]); + } + // if we somehow release more locks then exists, reset the lock if ($newValue < 0) { $this->memcache->cad($path, $newValue); @@ -106,13 +122,52 @@ class MemcacheLockingProvider extends AbstractLockingProvider { } elseif ($targetType === self::LOCK_EXCLUSIVE) { // we can only change a shared lock to an exclusive if there's only a single owner of the shared lock if (!$this->memcache->cas($path, 1, 'exclusive')) { + $this->restoreTTL($path); throw new LockedException($path, null, $this->getExistingLockForException($path)); } + unset($this->oldTTLs[$path]); } $this->setTTL($path); $this->markChange($path, $targetType); } + /** + * With shared locks, each time the lock is acquired, the ttl for the path is reset. + * + * Due to this "ttl extension" when a shared lock isn't freed correctly for any reason + * the lock won't expire until no shared locks are required for the path for 1h. + * This can lead to a client repeatedly trying to upload a file, and failing forever + * because the lock never gets the opportunity to expire. + * + * To help the lock expire in this case, we lower the TTL back to what it was before we + * took the shared lock *only* if nobody else got a shared lock after we did. + * + * This doesn't handle all cases where multiple requests are acquiring shared locks + * but it should handle some of the more common ones and not hurt things further + */ + private function restoreTTL(string $path): void { + if (isset($this->oldTTLs[$path])) { + $saved = $this->oldTTLs[$path]; + $elapsed = $this->timeFactory->getTime() - $saved['time']; + + // old value to compare to when setting ttl in case someone else changes the lock in the middle of this function + $value = $this->memcache->get($path); + + $currentTtl = $this->getTTL($path); + + // what the old ttl would be given the time elapsed since we acquired the lock + // note that if this gets negative the key will be expired directly when we set the ttl + $remainingOldTtl = $saved['ttl'] - $elapsed; + // what the currently ttl would be if nobody else acquired a lock since we did (+1 to cover rounding errors) + $expectedTtl = $this->ttl - $elapsed + 1; + + // check if another request has acquired a lock (and didn't release it yet) + if ($currentTtl <= $expectedTtl) { + $this->setTTL($path, $remainingOldTtl, $value); + } + } + } + private function getExistingLockForException(string $path): string { $existing = $this->memcache->get($path); if (!$existing) { diff --git a/lib/private/Lock/NoopLockingProvider.php b/lib/private/Lock/NoopLockingProvider.php index 542cb5c028e..42f1f9ae5ec 100644 --- a/lib/private/Lock/NoopLockingProvider.php +++ b/lib/private/Lock/NoopLockingProvider.php @@ -1,28 +1,10 @@ <?php declare(strict_types=1); - /** - * @copyright Copyright (c) 2016, ownCloud, Inc. - * - * @author Robin Appelman <robin@icewind.nl> - * @author Roeland Jago Douma <roeland@famdouma.nl> - * @author Vincent Petry <vincent@nextcloud.com> - * - * @license AGPL-3.0 - * - * This code is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License, version 3, - * as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License, version 3, - * along with this program. If not, see <http://www.gnu.org/licenses/> - * + * SPDX-FileCopyrightText: 2016-2024 Nextcloud GmbH and Nextcloud contributors + * SPDX-FileCopyrightText: 2016 ownCloud, Inc. + * SPDX-License-Identifier: AGPL-3.0-only */ namespace OC\Lock; diff --git a/lib/private/Lockdown/Filesystem/NullCache.php b/lib/private/Lockdown/Filesystem/NullCache.php index 4fde9668ded..e84ff40e00c 100644 --- a/lib/private/Lockdown/Filesystem/NullCache.php +++ b/lib/private/Lockdown/Filesystem/NullCache.php @@ -1,24 +1,7 @@ <?php /** - * @copyright Copyright (c) 2016, Robin Appelman <robin@icewind.nl> - * - * @author Robin Appelman <robin@icewind.nl> - * - * @license GNU AGPL version 3 or any later version - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as - * published by the Free Software Foundation, either version 3 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - * + * SPDX-FileCopyrightText: 2016 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later */ namespace OC\Lockdown\Filesystem; diff --git a/lib/private/Lockdown/Filesystem/NullStorage.php b/lib/private/Lockdown/Filesystem/NullStorage.php index a3976733b1a..7175594a01d 100644 --- a/lib/private/Lockdown/Filesystem/NullStorage.php +++ b/lib/private/Lockdown/Filesystem/NullStorage.php @@ -1,25 +1,7 @@ <?php /** - * @copyright Copyright (c) 2016, Robin Appelman <robin@icewind.nl> - * - * @author Lukas Reschke <lukas@statuscode.ch> - * @author Robin Appelman <robin@icewind.nl> - * - * @license GNU AGPL version 3 or any later version - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as - * published by the Free Software Foundation, either version 3 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - * + * SPDX-FileCopyrightText: 2016 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later */ namespace OC\Lockdown\Filesystem; diff --git a/lib/private/Lockdown/LockdownManager.php b/lib/private/Lockdown/LockdownManager.php index bb76d801725..779b1ea2650 100644 --- a/lib/private/Lockdown/LockdownManager.php +++ b/lib/private/Lockdown/LockdownManager.php @@ -1,24 +1,7 @@ <?php /** - * @copyright Copyright (c) 2016, Robin Appelman <robin@icewind.nl> - * - * @author Robin Appelman <robin@icewind.nl> - * - * @license GNU AGPL version 3 or any later version - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as - * published by the Free Software Foundation, either version 3 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - * + * SPDX-FileCopyrightText: 2016 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later */ namespace OC\Lockdown; diff --git a/lib/private/Log.php b/lib/private/Log.php index d6750491d92..c7684a1aefd 100644 --- a/lib/private/Log.php +++ b/lib/private/Log.php @@ -1,43 +1,17 @@ <?php declare(strict_types=1); - /** - * @copyright Copyright (c) 2016, ownCloud, Inc. - * - * @author Arthur Schiwon <blizzz@arthur-schiwon.de> - * @author Bart Visscher <bartv@thisnet.nl> - * @author Bernhard Posselt <dev@bernhard-posselt.com> - * @author Christoph Wurst <christoph@winzerhof-wurst.at> - * @author Joas Schilling <coding@schilljs.com> - * @author Julius Härtl <jus@bitgrid.net> - * @author Morris Jobke <hey@morrisjobke.de> - * @author Olivier Paroz <github@oparoz.com> - * @author Robin Appelman <robin@icewind.nl> - * @author Roeland Jago Douma <roeland@famdouma.nl> - * @author Thomas Citharel <nextcloud@tcit.fr> - * @author Thomas Müller <thomas.mueller@tmit.eu> - * @author Victor Dubiniuk <dubiniuk@owncloud.com> - * - * @license AGPL-3.0 - * - * This code is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License, version 3, - * as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License, version 3, - * along with this program. If not, see <http://www.gnu.org/licenses/> - * + * SPDX-FileCopyrightText: 2016-2024 Nextcloud GmbH and Nextcloud contributors + * SPDX-FileCopyrightText: 2016 ownCloud, Inc. + * SPDX-License-Identifier: AGPL-3.0-only */ namespace OC; use Exception; use Nextcloud\LogNormalizer\Normalizer; +use OC\AppFramework\Bootstrap\Coordinator; +use OC\Log\ExceptionSerializer; use OCP\EventDispatcher\IEventDispatcher; use OCP\ILogger; use OCP\IUserSession; @@ -46,8 +20,6 @@ use OCP\Log\IDataLogger; use OCP\Log\IFileBased; use OCP\Log\IWriter; use OCP\Support\CrashReport\IRegistry; -use OC\AppFramework\Bootstrap\Coordinator; -use OC\Log\ExceptionSerializer; use Throwable; use function array_merge; use function strtr; @@ -62,42 +34,22 @@ use function strtr; * MonoLog is an example implementing this interface. */ class Log implements ILogger, IDataLogger { - private IWriter $logger; - private ?SystemConfig $config; private ?bool $logConditionSatisfied = null; - private ?Normalizer $normalizer; - private ?IRegistry $crashReporters; - private ?IEventDispatcher $eventDispatcher; + private ?IEventDispatcher $eventDispatcher = null; - /** - * @param IWriter $logger The logger that should be used - * @param SystemConfig $config the system config object - * @param Normalizer|null $normalizer - * @param IRegistry|null $registry - */ public function __construct( - IWriter $logger, - SystemConfig $config = null, - Normalizer $normalizer = null, - IRegistry $registry = null + private IWriter $logger, + private SystemConfig $config, + private ?Normalizer $normalizer = null, + private ?IRegistry $crashReporters = null ) { - // FIXME: Add this for backwards compatibility, should be fixed at some point probably - if ($config === null) { - $config = \OC::$server->getSystemConfig(); - } - - $this->config = $config; - $this->logger = $logger; + // FIXME: php8.1 allows "private Normalizer $normalizer = new Normalizer()," in initializer if ($normalizer === null) { $this->normalizer = new Normalizer(); - } else { - $this->normalizer = $normalizer; } - $this->crashReporters = $registry; - $this->eventDispatcher = null; } - public function setEventDispatcher(IEventDispatcher $eventDispatcher) { + public function setEventDispatcher(IEventDispatcher $eventDispatcher): void { $this->eventDispatcher = $eventDispatcher; } @@ -106,9 +58,8 @@ class Log implements ILogger, IDataLogger { * * @param string $message * @param array $context - * @return void */ - public function emergency(string $message, array $context = []) { + public function emergency(string $message, array $context = []): void { $this->log(ILogger::FATAL, $message, $context); } @@ -120,9 +71,8 @@ class Log implements ILogger, IDataLogger { * * @param string $message * @param array $context - * @return void */ - public function alert(string $message, array $context = []) { + public function alert(string $message, array $context = []): void { $this->log(ILogger::ERROR, $message, $context); } @@ -133,9 +83,8 @@ class Log implements ILogger, IDataLogger { * * @param string $message * @param array $context - * @return void */ - public function critical(string $message, array $context = []) { + public function critical(string $message, array $context = []): void { $this->log(ILogger::ERROR, $message, $context); } @@ -145,9 +94,8 @@ class Log implements ILogger, IDataLogger { * * @param string $message * @param array $context - * @return void */ - public function error(string $message, array $context = []) { + public function error(string $message, array $context = []): void { $this->log(ILogger::ERROR, $message, $context); } @@ -159,9 +107,8 @@ class Log implements ILogger, IDataLogger { * * @param string $message * @param array $context - * @return void */ - public function warning(string $message, array $context = []) { + public function warning(string $message, array $context = []): void { $this->log(ILogger::WARN, $message, $context); } @@ -170,9 +117,8 @@ class Log implements ILogger, IDataLogger { * * @param string $message * @param array $context - * @return void */ - public function notice(string $message, array $context = []) { + public function notice(string $message, array $context = []): void { $this->log(ILogger::INFO, $message, $context); } @@ -183,9 +129,8 @@ class Log implements ILogger, IDataLogger { * * @param string $message * @param array $context - * @return void */ - public function info(string $message, array $context = []) { + public function info(string $message, array $context = []): void { $this->log(ILogger::INFO, $message, $context); } @@ -194,9 +139,8 @@ class Log implements ILogger, IDataLogger { * * @param string $message * @param array $context - * @return void */ - public function debug(string $message, array $context = []) { + public function debug(string $message, array $context = []): void { $this->log(ILogger::DEBUG, $message, $context); } @@ -207,19 +151,21 @@ class Log implements ILogger, IDataLogger { * @param int $level * @param string $message * @param array $context - * @return void */ - public function log(int $level, string $message, array $context = []) { + public function log(int $level, string $message, array $context = []): void { $minLevel = $this->getLogLevel($context); + if ($level < $minLevel + && (($this->crashReporters?->hasReporters() ?? false) === false) + && (($this->eventDispatcher?->hasListeners(BeforeMessageLoggedEvent::class) ?? false) === false)) { + return; // no crash reporter, no listeners, we can stop for lower log level + } array_walk($context, [$this->normalizer, 'format']); $app = $context['app'] ?? 'no app in context'; $entry = $this->interpolateMessage($context, $message); - if ($this->eventDispatcher) { - $this->eventDispatcher->dispatchTyped(new BeforeMessageLoggedEvent($app, $level, $entry)); - } + $this->eventDispatcher?->dispatchTyped(new BeforeMessageLoggedEvent($app, $level, $entry)); $hasBacktrace = isset($entry['exception']); $logBacktrace = $this->config->getValue('log.backtrace', false); @@ -241,16 +187,14 @@ class Log implements ILogger, IDataLogger { $this->crashReporters->delegateMessage($entry['message'], $messageContext); } } else { - if ($this->crashReporters !== null) { - $this->crashReporters->delegateBreadcrumb($entry['message'], 'log', $context); - } + $this->crashReporters?->delegateBreadcrumb($entry['message'], 'log', $context); } } catch (Throwable $e) { // make sure we dont hard crash if logging fails } } - public function getLogLevel($context) { + public function getLogLevel($context): int { $logCondition = $this->config->getValue('log.condition', []); /** @@ -300,20 +244,23 @@ class Log implements ILogger, IDataLogger { } if (isset($context['app'])) { - $app = $context['app']; - /** * check log condition based on the context of each log message * once this is met -> change the required log level to debug */ - if (!empty($logCondition) - && isset($logCondition['apps']) - && in_array($app, $logCondition['apps'], true)) { + if (in_array($context['app'], $logCondition['apps'] ?? [], true)) { return ILogger::DEBUG; } } - return min($this->config->getValue('loglevel', ILogger::WARN), ILogger::FATAL); + $configLogLevel = $this->config->getValue('loglevel', ILogger::WARN); + if (is_numeric($configLogLevel)) { + return min((int)$configLogLevel, ILogger::FATAL); + } + + // Invalid configuration, warn the user and fall back to default level of WARN + error_log('Nextcloud configuration: "loglevel" is not a valid integer'); + return ILogger::WARN; } /** @@ -324,13 +271,15 @@ class Log implements ILogger, IDataLogger { * @return void * @since 8.2.0 */ - public function logException(Throwable $exception, array $context = []) { + public function logException(Throwable $exception, array $context = []): void { $app = $context['app'] ?? 'no app in context'; $level = $context['level'] ?? ILogger::ERROR; $minLevel = $this->getLogLevel($context); - if ($level < $minLevel && ($this->crashReporters === null || !$this->crashReporters->hasReporters())) { - return; + if ($level < $minLevel + && (($this->crashReporters?->hasReporters() ?? false) === false) + && (($this->eventDispatcher?->hasListeners(BeforeMessageLoggedEvent::class) ?? false) === false)) { + return; // no crash reporter, no listeners, we can stop for lower log level } // if an error is raised before the autoloader is properly setup, we can't serialize exceptions @@ -344,14 +293,11 @@ class Log implements ILogger, IDataLogger { unset($data['app']); unset($data['level']); $data = array_merge($serializer->serializeException($exception), $data); - $data = $this->interpolateMessage($data, $context['message'] ?? '--', 'CustomMessage'); - + $data = $this->interpolateMessage($data, isset($context['message']) && $context['message'] !== '' ? $context['message'] : ('Exception thrown: ' . get_class($exception)), 'CustomMessage'); array_walk($context, [$this->normalizer, 'format']); - if ($this->eventDispatcher) { - $this->eventDispatcher->dispatchTyped(new BeforeMessageLoggedEvent($app, $level, $data)); - } + $this->eventDispatcher?->dispatchTyped(new BeforeMessageLoggedEvent($app, $level, $data)); try { if ($level >= $minLevel) { @@ -399,7 +345,7 @@ class Log implements ILogger, IDataLogger { * @param string|array $entry * @param int $level */ - protected function writeLog(string $app, $entry, int $level) { + protected function writeLog(string $app, $entry, int $level): void { $this->logger->write($app, $entry, $level); } diff --git a/lib/private/Log/ErrorHandler.php b/lib/private/Log/ErrorHandler.php index c4b9631e75a..e1faf336118 100644 --- a/lib/private/Log/ErrorHandler.php +++ b/lib/private/Log/ErrorHandler.php @@ -1,32 +1,10 @@ <?php declare(strict_types=1); - /** - * @copyright Copyright (c) 2016, ownCloud, Inc. - * - * @author Bart Visscher <bartv@thisnet.nl> - * @author Björn Schießle <bjoern@schiessle.org> - * @author Christoph Wurst <christoph@winzerhof-wurst.at> - * @author Joas Schilling <coding@schilljs.com> - * @author Julius Härtl <jus@bitgrid.net> - * @author Morris Jobke <hey@morrisjobke.de> - * @author Thomas Müller <thomas.mueller@tmit.eu> - * - * @license AGPL-3.0 - * - * This code is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License, version 3, - * as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License, version 3, - * along with this program. If not, see <http://www.gnu.org/licenses/> - * + * SPDX-FileCopyrightText: 2016-2024 Nextcloud GmbH and Nextcloud contributors + * SPDX-FileCopyrightText: 2016 ownCloud, Inc. + * SPDX-License-Identifier: AGPL-3.0-only */ namespace OC\Log; @@ -36,10 +14,9 @@ use Psr\Log\LoggerInterface; use Throwable; class ErrorHandler { - private LoggerInterface $logger; - - public function __construct(LoggerInterface $logger) { - $this->logger = $logger; + public function __construct( + private LoggerInterface $logger, + ) { } /** @@ -94,20 +71,11 @@ class ErrorHandler { } private static function errnoToLogLevel(int $errno): int { - switch ($errno) { - case E_USER_WARNING: - return ILogger::WARN; - - case E_DEPRECATED: - case E_USER_DEPRECATED: - return ILogger::DEBUG; - - case E_USER_NOTICE: - return ILogger::INFO; - - case E_USER_ERROR: - default: - return ILogger::ERROR; - } + return match ($errno) { + E_USER_WARNING => ILogger::WARN, + E_DEPRECATED, E_USER_DEPRECATED => ILogger::DEBUG, + E_USER_NOTICE => ILogger::INFO, + default => ILogger::ERROR, + }; } } diff --git a/lib/private/Log/Errorlog.php b/lib/private/Log/Errorlog.php index 72d11aa098c..d53b8b151a1 100644 --- a/lib/private/Log/Errorlog.php +++ b/lib/private/Log/Errorlog.php @@ -1,53 +1,31 @@ <?php declare(strict_types=1); - /** - * The MIT License (MIT) - * - * Copyright (c) 2014 Christian Kampka <christian@kampka.net> - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. + * SPDX-FileCopyrightText: 2016-2024 Nextcloud GmbH and Nextcloud contributors + * SPDX-FileCopyrightText: 2016 ownCloud, Inc. + * SPDX-FileCopyrightText: 2014 Christian Kampka <christian@kampka.net> + * SPDX-License-Identifier: MIT */ - namespace OC\Log; use OC\SystemConfig; use OCP\Log\IWriter; class Errorlog extends LogDetails implements IWriter { - /** @var string */ - protected $tag; - - public function __construct(SystemConfig $config, string $tag = 'nextcloud') { + public function __construct( + SystemConfig $config, + protected string $tag = 'nextcloud', + ) { parent::__construct($config); - $this->tag = $tag; } /** * Write a message in the log * - * @param string $app * @param string|array $message - * @param int $level */ - public function write(string $app, $message, int $level) { + public function write(string $app, $message, int $level): void { error_log('[' . $this->tag . ']['.$app.']['.$level.'] '.$this->logDetailsAsJSON($app, $message, $level)); } } diff --git a/lib/private/Log/ExceptionSerializer.php b/lib/private/Log/ExceptionSerializer.php index b585461e8d9..03beed7cce1 100644 --- a/lib/private/Log/ExceptionSerializer.php +++ b/lib/private/Log/ExceptionSerializer.php @@ -1,30 +1,7 @@ <?php /** - * @copyright Copyright (c) 2018 Robin Appelman <robin@icewind.nl> - * - * @author Arthur Schiwon <blizzz@arthur-schiwon.de> - * @author Christoph Wurst <christoph@winzerhof-wurst.at> - * @author Joas Schilling <coding@schilljs.com> - * @author Morris Jobke <hey@morrisjobke.de> - * @author Robin Appelman <robin@icewind.nl> - * @author Roeland Jago Douma <roeland@famdouma.nl> - * @author Vincent Petry <vincent@nextcloud.com> - * - * @license GNU AGPL version 3 or any later version - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as - * published by the Free Software Foundation, either version 3 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - * + * SPDX-FileCopyrightText: 2018 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later */ namespace OC\Log; @@ -112,11 +89,9 @@ class ExceptionSerializer { ]; - /** @var SystemConfig */ - private $systemConfig; - - public function __construct(SystemConfig $systemConfig) { - $this->systemConfig = $systemConfig; + public function __construct( + private SystemConfig $systemConfig, + ) { } protected array $methodsWithSensitiveParametersByClass = [ @@ -219,7 +194,7 @@ class ExceptionSerializer { }, $trace); } - private function removeValuesFromArgs($args, $values) { + private function removeValuesFromArgs($args, $values): array { $workArgs = []; foreach ($args as $arg) { if (in_array($arg, $values, true)) { @@ -279,7 +254,7 @@ class ExceptionSerializer { return $arg; } - public function serializeException(\Throwable $exception) { + public function serializeException(\Throwable $exception): array { $data = [ 'Exception' => get_class($exception), 'Message' => $exception->getMessage(), diff --git a/lib/private/Log/File.php b/lib/private/Log/File.php index a33667c9b68..28cc856b980 100644 --- a/lib/private/Log/File.php +++ b/lib/private/Log/File.php @@ -1,38 +1,9 @@ <?php + /** - * @copyright Copyright (c) 2016, ownCloud, Inc. - * - * @author Arthur Schiwon <blizzz@arthur-schiwon.de> - * @author Bart Visscher <bartv@thisnet.nl> - * @author Christoph Wurst <christoph@winzerhof-wurst.at> - * @author duritong <peter.meier+github@immerda.ch> - * @author Georg Ehrke <oc.list@georgehrke.com> - * @author J0WI <J0WI@users.noreply.github.com> - * @author Joas Schilling <coding@schilljs.com> - * @author Julius Härtl <jus@bitgrid.net> - * @author Lukas Reschke <lukas@statuscode.ch> - * @author Michael Gapczynski <GapczynskiM@gmail.com> - * @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> - * @author Thomas Müller <thomas.mueller@tmit.eu> - * @author Thomas Pulzer <t.pulzer@kniel.de> - * - * @license AGPL-3.0 - * - * This code is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License, version 3, - * as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License, version 3, - * along with this program. If not, see <http://www.gnu.org/licenses/> - * + * SPDX-FileCopyrightText: 2016-2024 Nextcloud GmbH and Nextcloud contributors + * SPDX-FileCopyrightText: 2016 ownCloud, Inc. + * SPDX-License-Identifier: AGPL-3.0-only */ namespace OC\Log; @@ -48,14 +19,15 @@ use OCP\Log\IWriter; */ class File extends LogDetails implements IWriter, IFileBased { - /** @var string */ - protected $logFile; - /** @var int */ - protected $logFileMode; - /** @var SystemConfig */ - private $config; + protected string $logFile; + + protected int $logFileMode; - public function __construct(string $path, string $fallbackPath, SystemConfig $config) { + public function __construct( + string $path, + string $fallbackPath, + private SystemConfig $config, + ) { parent::__construct($config); $this->logFile = $path; if (!file_exists($this->logFile)) { @@ -69,17 +41,14 @@ class File extends LogDetails implements IWriter, IFileBased { $this->logFile = $fallbackPath; } } - $this->config = $config; $this->logFileMode = $config->getValue('logfilemode', 0640); } /** * write a message in the log - * @param string $app * @param string|array $message - * @param int $level */ - public function write(string $app, $message, int $level) { + public function write(string $app, $message, int $level): void { $entry = $this->logDetailsAsJSON($app, $message, $level); $handle = @fopen($this->logFile, 'a'); if ($this->logFileMode > 0 && is_file($this->logFile) && (fileperms($this->logFile) & 0777) != $this->logFileMode) { @@ -102,11 +71,8 @@ class File extends LogDetails implements IWriter, IFileBased { /** * get entries from the log in reverse chronological order - * @param int $limit - * @param int $offset - * @return array */ - public function getEntries(int $limit = 50, int $offset = 0):array { + public function getEntries(int $limit = 50, int $offset = 0): array { $minLevel = $this->config->getValue("loglevel", ILogger::WARN); $entries = []; $handle = @fopen($this->logFile, 'rb'); @@ -148,9 +114,6 @@ class File extends LogDetails implements IWriter, IFileBased { return $entries; } - /** - * @return string - */ public function getLogFilePath():string { return $this->logFile; } diff --git a/lib/private/Log/LogDetails.php b/lib/private/Log/LogDetails.php index c82904d7cea..bf2c1a22c49 100644 --- a/lib/private/Log/LogDetails.php +++ b/lib/private/Log/LogDetails.php @@ -1,38 +1,16 @@ <?php /** - * @copyright Copyright (c) 2019 Julius Härtl <jus@bitgrid.net> - * - * @author Arthur Schiwon <blizzz@arthur-schiwon.de> - * @author Christoph Wurst <christoph@winzerhof-wurst.at> - * @author Julius Härtl <jus@bitgrid.net> - * @author Thomas Citharel <nextcloud@tcit.fr> - * - * @license GNU AGPL version 3 or any later version - * - * 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/>. - * + * SPDX-FileCopyrightText: 2019 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later */ namespace OC\Log; use OC\SystemConfig; abstract class LogDetails { - /** @var SystemConfig */ - private $config; - - public function __construct(SystemConfig $config) { - $this->config = $config; + public function __construct( + private SystemConfig $config, + ) { } public function logDetails(string $app, $message, int $level): array { @@ -108,7 +86,7 @@ abstract class LogDetails { if (is_string($value)) { $testEncode = json_encode($value, JSON_UNESCAPED_SLASHES); if ($testEncode === false) { - $entry[$key] = utf8_encode($value); + $entry[$key] = mb_convert_encoding($value, 'UTF-8', mb_detect_encoding($value)); } } } diff --git a/lib/private/Log/LogFactory.php b/lib/private/Log/LogFactory.php index a5008f5ef77..a5a7290bd9c 100644 --- a/lib/private/Log/LogFactory.php +++ b/lib/private/Log/LogFactory.php @@ -1,26 +1,7 @@ <?php /** - * @copyright Copyright (c) 2018 Arthur Schiwon <blizzz@arthur-schiwon.de> - * - * @author Arthur Schiwon <blizzz@arthur-schiwon.de> - * @author Christoph Wurst <christoph@winzerhof-wurst.at> - * @author Johannes Ernst <jernst@indiecomputing.com> - * - * @license GNU AGPL version 3 or any later version - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as - * published by the Free Software Foundation, either version 3 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - * + * SPDX-FileCopyrightText: 2018 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later */ namespace OC\Log; @@ -33,57 +14,37 @@ use OCP\Log\IWriter; use Psr\Log\LoggerInterface; class LogFactory implements ILogFactory { - /** @var IServerContainer */ - private $c; - /** @var SystemConfig */ - private $systemConfig; - - public function __construct(IServerContainer $c, SystemConfig $systemConfig) { - $this->c = $c; - $this->systemConfig = $systemConfig; + public function __construct( + private IServerContainer $c, + private SystemConfig $systemConfig, + ) { } /** * @throws \OCP\AppFramework\QueryException */ public function get(string $type):IWriter { - switch (strtolower($type)) { - case 'errorlog': - return new Errorlog($this->systemConfig); - case 'syslog': - return $this->c->resolve(Syslog::class); - case 'systemd': - return $this->c->resolve(Systemdlog::class); - case 'file': - return $this->buildLogFile(); - - // Backwards compatibility for old and fallback for unknown log types - case 'owncloud': - case 'nextcloud': - default: - return $this->buildLogFile(); - } + return match (strtolower($type)) { + 'errorlog' => new Errorlog($this->systemConfig), + 'syslog' => $this->c->resolve(Syslog::class), + 'systemd' => $this->c->resolve(Systemdlog::class), + 'file' => $this->buildLogFile(), + default => $this->buildLogFile(), + }; } - public function getCustomLogger(string $path):ILogger { + public function getCustomLogger(string $path): ILogger { $log = $this->buildLogFile($path); return new Log($log, $this->systemConfig); } protected function createNewLogger(string $type, string $tag, string $path): IWriter { - switch (strtolower($type)) { - case 'errorlog': - return new Errorlog($this->systemConfig, $tag); - case 'syslog': - return new Syslog($this->systemConfig, $tag); - case 'systemd': - return new Systemdlog($this->systemConfig, $tag); - case 'file': - case 'owncloud': - case 'nextcloud': - default: - return $this->buildLogFile($path); - } + return match (strtolower($type)) { + 'errorlog' => new Errorlog($this->systemConfig, $tag), + 'syslog' => new Syslog($this->systemConfig, $tag), + 'systemd' => new Systemdlog($this->systemConfig, $tag), + default => $this->buildLogFile($path), + }; } public function getCustomPsrLogger(string $path, string $type = 'file', string $tag = 'Nextcloud'): LoggerInterface { @@ -93,7 +54,7 @@ class LogFactory implements ILogFactory { ); } - protected function buildLogFile(string $logFile = ''):File { + protected function buildLogFile(string $logFile = ''): File { $defaultLogFile = $this->systemConfig->getValue('datadirectory', \OC::$SERVERROOT.'/data').'/nextcloud.log'; if ($logFile === '') { $logFile = $this->systemConfig->getValue('logfile', $defaultLogFile); diff --git a/lib/private/Log/PsrLoggerAdapter.php b/lib/private/Log/PsrLoggerAdapter.php index 07a898e2528..16e609eefdb 100644 --- a/lib/private/Log/PsrLoggerAdapter.php +++ b/lib/private/Log/PsrLoggerAdapter.php @@ -3,25 +3,8 @@ declare(strict_types=1); /** - * @copyright 2020 Christoph Wurst <christoph@winzerhof-wurst.at> - * - * @author Christoph Wurst <christoph@winzerhof-wurst.at> - * - * @license GNU AGPL version 3 or any later version - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as - * published by the Free Software Foundation, either version 3 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - * + * SPDX-FileCopyrightText: 2020 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later */ namespace OC\Log; @@ -36,14 +19,12 @@ use function array_key_exists; use function array_merge; final class PsrLoggerAdapter implements LoggerInterface, IDataLogger { - /** @var Log */ - private $logger; - - public function __construct(Log $logger) { - $this->logger = $logger; + public function __construct( + private Log $logger, + ) { } - public function setEventDispatcher(IEventDispatcher $eventDispatcher) { + public function setEventDispatcher(IEventDispatcher $eventDispatcher): void { $this->logger->setEventDispatcher($eventDispatcher); } @@ -54,22 +35,20 @@ final class PsrLoggerAdapter implements LoggerInterface, IDataLogger { /** * System is unusable. * - * @param string $message - * @param array $context - * - * @return void + * @param $message + * @param mixed[] $context */ public function emergency($message, array $context = []): void { if ($this->containsThrowable($context)) { $this->logger->logException($context['exception'], array_merge( [ - 'message' => $message, + 'message' => (string)$message, 'level' => ILogger::FATAL, ], $context )); } else { - $this->logger->emergency($message, $context); + $this->logger->emergency((string)$message, $context); } } @@ -79,22 +58,20 @@ final class PsrLoggerAdapter implements LoggerInterface, IDataLogger { * Example: Entire website down, database unavailable, etc. This should * trigger the SMS alerts and wake you up. * - * @param string $message - * @param array $context - * - * @return void + * @param $message + * @param mixed[] $context */ - public function alert($message, array $context = []) { + public function alert($message, array $context = []): void { if ($this->containsThrowable($context)) { $this->logger->logException($context['exception'], array_merge( [ - 'message' => $message, + 'message' => (string)$message, 'level' => ILogger::ERROR, ], $context )); } else { - $this->logger->alert($message, $context); + $this->logger->alert((string)$message, $context); } } @@ -103,22 +80,20 @@ final class PsrLoggerAdapter implements LoggerInterface, IDataLogger { * * Example: Application component unavailable, unexpected exception. * - * @param string $message - * @param array $context - * - * @return void + * @param $message + * @param mixed[] $context */ - public function critical($message, array $context = []) { + public function critical($message, array $context = []): void { if ($this->containsThrowable($context)) { $this->logger->logException($context['exception'], array_merge( [ - 'message' => $message, + 'message' => (string)$message, 'level' => ILogger::ERROR, ], $context )); } else { - $this->logger->critical($message, $context); + $this->logger->critical((string)$message, $context); } } @@ -126,22 +101,20 @@ final class PsrLoggerAdapter implements LoggerInterface, IDataLogger { * Runtime errors that do not require immediate action but should typically * be logged and monitored. * - * @param string $message - * @param array $context - * - * @return void + * @param $message + * @param mixed[] $context */ - public function error($message, array $context = []) { + public function error($message, array $context = []): void { if ($this->containsThrowable($context)) { $this->logger->logException($context['exception'], array_merge( [ - 'message' => $message, + 'message' => (string)$message, 'level' => ILogger::ERROR, ], $context )); } else { - $this->logger->error($message, $context); + $this->logger->error((string)$message, $context); } } @@ -151,44 +124,40 @@ final class PsrLoggerAdapter implements LoggerInterface, IDataLogger { * Example: Use of deprecated APIs, poor use of an API, undesirable things * that are not necessarily wrong. * - * @param string $message - * @param array $context - * - * @return void + * @param $message + * @param mixed[] $context */ - public function warning($message, array $context = []) { + public function warning($message, array $context = []): void { if ($this->containsThrowable($context)) { $this->logger->logException($context['exception'], array_merge( [ - 'message' => $message, + 'message' => (string)$message, 'level' => ILogger::WARN, ], $context )); } else { - $this->logger->warning($message, $context); + $this->logger->warning((string)$message, $context); } } /** * Normal but significant events. * - * @param string $message - * @param array $context - * - * @return void + * @param $message + * @param mixed[] $context */ - public function notice($message, array $context = []) { + public function notice($message, array $context = []): void { if ($this->containsThrowable($context)) { $this->logger->logException($context['exception'], array_merge( [ - 'message' => $message, + 'message' => (string)$message, 'level' => ILogger::INFO, ], $context )); } else { - $this->logger->notice($message, $context); + $this->logger->notice((string)$message, $context); } } @@ -197,44 +166,40 @@ final class PsrLoggerAdapter implements LoggerInterface, IDataLogger { * * Example: User logs in, SQL logs. * - * @param string $message - * @param array $context - * - * @return void + * @param $message + * @param mixed[] $context */ - public function info($message, array $context = []) { + public function info($message, array $context = []): void { if ($this->containsThrowable($context)) { $this->logger->logException($context['exception'], array_merge( [ - 'message' => $message, + 'message' => (string)$message, 'level' => ILogger::INFO, ], $context )); } else { - $this->logger->info($message, $context); + $this->logger->info((string)$message, $context); } } /** * Detailed debug information. * - * @param string $message - * @param array $context - * - * @return void + * @param $message + * @param mixed[] $context */ - public function debug($message, array $context = []) { + public function debug($message, array $context = []): void { if ($this->containsThrowable($context)) { $this->logger->logException($context['exception'], array_merge( [ - 'message' => $message, + 'message' => (string)$message, 'level' => ILogger::DEBUG, ], $context )); } else { - $this->logger->debug($message, $context); + $this->logger->debug((string)$message, $context); } } @@ -242,27 +207,25 @@ final class PsrLoggerAdapter implements LoggerInterface, IDataLogger { * Logs with an arbitrary level. * * @param mixed $level - * @param string $message - * @param array $context - * - * @return void + * @param $message + * @param mixed[] $context * * @throws InvalidArgumentException */ - public function log($level, $message, array $context = []) { + public function log($level, $message, array $context = []): void { if (!is_int($level) || $level < ILogger::DEBUG || $level > ILogger::FATAL) { throw new InvalidArgumentException('Nextcloud allows only integer log levels'); } if ($this->containsThrowable($context)) { $this->logger->logException($context['exception'], array_merge( [ - 'message' => $message, + 'message' => (string)$message, 'level' => $level, ], $context )); } else { - $this->logger->log($level, $message, $context); + $this->logger->log($level, (string)$message, $context); } } diff --git a/lib/private/Log/Rotate.php b/lib/private/Log/Rotate.php index dfb588837f3..f2fdb83623d 100644 --- a/lib/private/Log/Rotate.php +++ b/lib/private/Log/Rotate.php @@ -1,30 +1,15 @@ <?php + /** - * @copyright Copyright (c) 2016, ownCloud, Inc. - * - * @author Arthur Schiwon <blizzz@arthur-schiwon.de> - * @author Bart Visscher <bartv@thisnet.nl> - * @author Christoph Wurst <christoph@winzerhof-wurst.at> - * @author Robin Appelman <robin@icewind.nl> - * - * @license AGPL-3.0 - * - * This code is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License, version 3, - * as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License, version 3, - * along with this program. If not, see <http://www.gnu.org/licenses/> - * + * SPDX-FileCopyrightText: 2016-2024 Nextcloud GmbH and Nextcloud contributors + * SPDX-FileCopyrightText: 2016 ownCloud, Inc. + * SPDX-License-Identifier: AGPL-3.0-only */ namespace OC\Log; +use OCP\IConfig; use OCP\Log\RotationTrait; +use Psr\Log\LoggerInterface; /** * This rotates the current logfile to a new name, this way the total log usage @@ -35,15 +20,15 @@ use OCP\Log\RotationTrait; class Rotate extends \OCP\BackgroundJob\Job { use RotationTrait; - public function run($dummy) { - $systemConfig = \OC::$server->getSystemConfig(); - $this->filePath = $systemConfig->getValue('logfile', $systemConfig->getValue('datadirectory', \OC::$SERVERROOT . '/data') . '/nextcloud.log'); + public function run($argument): void { + $config = \OCP\Server::get(IConfig::class); + $this->filePath = $config->getSystemValueString('logfile', $config->getSystemValueString('datadirectory', \OC::$SERVERROOT . '/data') . '/nextcloud.log'); - $this->maxSize = \OC::$server->getConfig()->getSystemValueInt('log_rotate_size', 100 * 1024 * 1024); + $this->maxSize = $config->getSystemValueInt('log_rotate_size', 100 * 1024 * 1024); if ($this->shouldRotateBySize()) { $rotatedFile = $this->rotate(); $msg = 'Log file "'.$this->filePath.'" was over '.$this->maxSize.' bytes, moved to "'.$rotatedFile.'"'; - \OC::$server->getLogger()->warning($msg, ['app' => Rotate::class]); + \OCP\Server::get(LoggerInterface::class)->info($msg, ['app' => Rotate::class]); } } } diff --git a/lib/private/Log/Syslog.php b/lib/private/Log/Syslog.php index f4ba857742f..bd2c39509b1 100644 --- a/lib/private/Log/Syslog.php +++ b/lib/private/Log/Syslog.php @@ -1,27 +1,9 @@ <?php + /** - * @copyright Copyright (c) 2016, ownCloud, Inc. - * - * @author Arthur Schiwon <blizzz@arthur-schiwon.de> - * @author Bart Visscher <bartv@thisnet.nl> - * @author Julius Härtl <jus@bitgrid.net> - * @author Roeland Jago Douma <roeland@famdouma.nl> - * @author Thomas Müller <thomas.mueller@tmit.eu> - * - * @license AGPL-3.0 - * - * This code is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License, version 3, - * as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License, version 3, - * along with this program. If not, see <http://www.gnu.org/licenses/> - * + * SPDX-FileCopyrightText: 2016-2024 Nextcloud GmbH and Nextcloud contributors + * SPDX-FileCopyrightText: 2016 ownCloud, Inc. + * SPDX-License-Identifier: AGPL-3.0-only */ namespace OC\Log; @@ -30,7 +12,7 @@ use OCP\ILogger; use OCP\Log\IWriter; class Syslog extends LogDetails implements IWriter { - protected $levels = [ + protected array $levels = [ ILogger::DEBUG => LOG_DEBUG, ILogger::INFO => LOG_INFO, ILogger::WARN => LOG_WARNING, @@ -38,7 +20,10 @@ class Syslog extends LogDetails implements IWriter { ILogger::FATAL => LOG_CRIT, ]; - public function __construct(SystemConfig $config, ?string $tag = null) { + public function __construct( + SystemConfig $config, + ?string $tag = null, + ) { parent::__construct($config); if ($tag === null) { $tag = $config->getValue('syslog_tag', 'Nextcloud'); @@ -52,11 +37,9 @@ class Syslog extends LogDetails implements IWriter { /** * write a message in the log - * @param string $app * @param string|array $message - * @param int $level */ - public function write(string $app, $message, int $level) { + public function write(string $app, $message, int $level): void { $syslog_level = $this->levels[$level]; syslog($syslog_level, $this->logDetailsAsJSON($app, $message, $level)); } diff --git a/lib/private/Log/Systemdlog.php b/lib/private/Log/Systemdlog.php index 8619cb5e4dd..4e33a4e5419 100644 --- a/lib/private/Log/Systemdlog.php +++ b/lib/private/Log/Systemdlog.php @@ -1,27 +1,7 @@ <?php /** - * @copyright Copyright (c) 2018, Johannes Ernst - * - * @author Christoph Wurst <christoph@winzerhof-wurst.at> - * @author Johannes Ernst <jernst@indiecomputing.com> - * @author Julius Härtl <jus@bitgrid.net> - * @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/>. - * + * SPDX-FileCopyrightText: 2018 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later */ namespace OC\Log; @@ -46,7 +26,7 @@ use OCP\Log\IWriter; // Syslog compatibility fields class Systemdlog extends LogDetails implements IWriter { - protected $levels = [ + protected array $levels = [ ILogger::DEBUG => 7, ILogger::INFO => 6, ILogger::WARN => 4, @@ -54,9 +34,12 @@ class Systemdlog extends LogDetails implements IWriter { ILogger::FATAL => 2, ]; - protected $syslogId; + protected string $syslogId; - public function __construct(SystemConfig $config, ?string $tag = null) { + public function __construct( + SystemConfig $config, + ?string $tag = null, + ) { parent::__construct($config); if (!function_exists('sd_journal_send')) { throw new HintException( @@ -71,11 +54,9 @@ class Systemdlog extends LogDetails implements IWriter { /** * Write a message to the log. - * @param string $app * @param string|array $message - * @param int $level */ - public function write(string $app, $message, int $level) { + public function write(string $app, $message, int $level): void { $journal_level = $this->levels[$level]; sd_journal_send('PRIORITY='.$journal_level, 'SYSLOG_IDENTIFIER='.$this->syslogId, diff --git a/lib/private/Mail/Attachment.php b/lib/private/Mail/Attachment.php index a39161b2505..8dede9714f8 100644 --- a/lib/private/Mail/Attachment.php +++ b/lib/private/Mail/Attachment.php @@ -3,26 +3,8 @@ declare(strict_types=1); /** - * @copyright Copyright (c) 2017 Joas Schilling <coding@schilljs.com> - * - * @author Joas Schilling <coding@schilljs.com> - * @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/>. - * + * SPDX-FileCopyrightText: 2017 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later */ namespace OC\Mail; diff --git a/lib/private/Mail/EMailTemplate.php b/lib/private/Mail/EMailTemplate.php index bb60f761450..80740e14aca 100644 --- a/lib/private/Mail/EMailTemplate.php +++ b/lib/private/Mail/EMailTemplate.php @@ -3,38 +3,8 @@ declare(strict_types=1); /** - * @copyright 2017, Morris Jobke <hey@morrisjobke.de> - * @copyright 2017, Lukas Reschke <lukas@statuscode.ch> - * - * @author Bjoern Schiessle <bjoern@schiessle.org> - * @author brad2014 <brad2014@users.noreply.github.com> - * @author Brad Rubenstein <brad@wbr.tech> - * @author Christoph Wurst <christoph@winzerhof-wurst.at> - * @author Jan-Christoph Borchardt <hey@jancborchardt.net> - * @author Joas Schilling <coding@schilljs.com> - * @author Julius Härtl <jus@bitgrid.net> - * @author Liam JACK <liamjack@users.noreply.github.com> - * @author Lukas Reschke <lukas@statuscode.ch> - * @author medcloud <42641918+medcloud@users.noreply.github.com> - * @author Morris Jobke <hey@morrisjobke.de> - * @author Roeland Jago Douma <roeland@famdouma.nl> - * @author Tomasz Paluszkiewicz <tomasz.paluszkiewicz@gmail.com> - * - * @license GNU AGPL version 3 or any later version - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as - * published by the Free Software Foundation, either version 3 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - * + * SPDX-FileCopyrightText: 2017 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later */ namespace OC\Mail; diff --git a/lib/private/Mail/Mailer.php b/lib/private/Mail/Mailer.php index e4c3467f854..041ab8b0ff7 100644 --- a/lib/private/Mail/Mailer.php +++ b/lib/private/Mail/Mailer.php @@ -1,37 +1,10 @@ <?php declare(strict_types=1); - /** - * @copyright Copyright (c) 2016, ownCloud, Inc. - * - * @author Arne Hamann <kontakt+github@arne.email> - * @author Branko Kokanovic <branko@kokanovic.org> - * @author Carsten Wiedmann <carsten_sttgt@gmx.de> - * @author Christoph Wurst <christoph@winzerhof-wurst.at> - * @author Jared Boone <jared.boone@gmail.com> - * @author Joas Schilling <coding@schilljs.com> - * @author Julius Härtl <jus@bitgrid.net> - * @author kevin147147 <kevintamool@gmail.com> - * @author Lukas Reschke <lukas@statuscode.ch> - * @author Morris Jobke <hey@morrisjobke.de> - * @author Roeland Jago Douma <roeland@famdouma.nl> - * @author Tekhnee <info@tekhnee.org> - * - * @license AGPL-3.0 - * - * This code is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License, version 3, - * as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License, version 3, - * along with this program. If not, see <http://www.gnu.org/licenses/> - * + * SPDX-FileCopyrightText: 2016-2024 Nextcloud GmbH and Nextcloud contributors + * SPDX-FileCopyrightText: 2016 ownCloud, Inc. + * SPDX-License-Identifier: AGPL-3.0-only */ namespace OC\Mail; @@ -331,7 +304,7 @@ class Mailer implements IMailer { } $binaryParam = match ($this->config->getSystemValueString('mail_sendmailmode', 'smtp')) { - 'pipe' => ' -t', + 'pipe' => ' -t -i', default => ' -bs', }; diff --git a/lib/private/Mail/Message.php b/lib/private/Mail/Message.php index 15d4da793dd..faeba469e20 100644 --- a/lib/private/Mail/Message.php +++ b/lib/private/Mail/Message.php @@ -1,33 +1,10 @@ <?php declare(strict_types=1); - /** - * @copyright Copyright (c) 2016, ownCloud, Inc. - * - * @author Arne Hamann <kontakt+github@arne.email> - * @author Christoph Wurst <christoph@winzerhof-wurst.at> - * @author Jared Boone <jared.boone@gmail.com> - * @author Joas Schilling <coding@schilljs.com> - * @author Lukas Reschke <lukas@statuscode.ch> - * @author Morris Jobke <hey@morrisjobke.de> - * @author Roeland Jago Douma <roeland@famdouma.nl> - * @author Thomas Müller <thomas.mueller@tmit.eu> - * - * @license AGPL-3.0 - * - * This code is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License, version 3, - * as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License, version 3, - * along with this program. If not, see <http://www.gnu.org/licenses/> - * + * SPDX-FileCopyrightText: 2016-2024 Nextcloud GmbH and Nextcloud contributors + * SPDX-FileCopyrightText: 2016 ownCloud, Inc. + * SPDX-License-Identifier: AGPL-3.0-only */ namespace OC\Mail; @@ -73,7 +50,7 @@ class Message implements IMessage { * {@inheritDoc} * @since 26.0.0 */ - public function attachInline(string $body, string $name, string $contentType = null): IMessage { + public function attachInline(string $body, string $name, ?string $contentType = null): IMessage { # To be sure this works with iCalendar messages, we encode with 8bit instead of # quoted-printable encoding. We save the current encoder, replace the current # encoder with an 8bit encoder and after we've finished, we reset the encoder diff --git a/lib/private/Memcache/APCu.php b/lib/private/Memcache/APCu.php index f3221fc7b77..7f6a73354ee 100644 --- a/lib/private/Memcache/APCu.php +++ b/lib/private/Memcache/APCu.php @@ -1,29 +1,8 @@ <?php /** - * @copyright Copyright (c) 2016, ownCloud, Inc. - * - * @author Andreas Fischer <bantu@owncloud.com> - * @author Bart Visscher <bartv@thisnet.nl> - * @author Christoph Wurst <christoph@winzerhof-wurst.at> - * @author Joas Schilling <coding@schilljs.com> - * @author Lukas Reschke <lukas@statuscode.ch> - * @author Morris Jobke <hey@morrisjobke.de> - * @author Robin Appelman <robin@icewind.nl> - * - * @license AGPL-3.0 - * - * This code is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License, version 3, - * as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License, version 3, - * along with this program. If not, see <http://www.gnu.org/licenses/> - * + * SPDX-FileCopyrightText: 2016-2024 Nextcloud GmbH and Nextcloud contributors + * SPDX-FileCopyrightText: 2016 ownCloud, Inc. + * SPDX-License-Identifier: AGPL-3.0-only */ namespace OC\Memcache; diff --git a/lib/private/Memcache/ArrayCache.php b/lib/private/Memcache/ArrayCache.php index 8c0af399116..4cac60c272c 100644 --- a/lib/private/Memcache/ArrayCache.php +++ b/lib/private/Memcache/ArrayCache.php @@ -1,26 +1,8 @@ <?php /** - * @copyright Copyright (c) 2016, ownCloud, Inc. - * - * @author Christoph Wurst <christoph@winzerhof-wurst.at> - * @author Joas Schilling <coding@schilljs.com> - * @author Morris Jobke <hey@morrisjobke.de> - * @author Robin Appelman <robin@icewind.nl> - * - * @license AGPL-3.0 - * - * This code is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License, version 3, - * as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License, version 3, - * along with this program. If not, see <http://www.gnu.org/licenses/> - * + * SPDX-FileCopyrightText: 2016-2024 Nextcloud GmbH and Nextcloud contributors + * SPDX-FileCopyrightText: 2016 ownCloud, Inc. + * SPDX-License-Identifier: AGPL-3.0-only */ namespace OC\Memcache; diff --git a/lib/private/Memcache/CADTrait.php b/lib/private/Memcache/CADTrait.php index a0843fc7731..bb010e238dc 100644 --- a/lib/private/Memcache/CADTrait.php +++ b/lib/private/Memcache/CADTrait.php @@ -1,23 +1,8 @@ <?php /** - * @copyright Copyright (c) 2016, ownCloud, Inc. - * - * @author Robin Appelman <robin@icewind.nl> - * - * @license AGPL-3.0 - * - * This code is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License, version 3, - * as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License, version 3, - * along with this program. If not, see <http://www.gnu.org/licenses/> - * + * SPDX-FileCopyrightText: 2016-2024 Nextcloud GmbH and Nextcloud contributors + * SPDX-FileCopyrightText: 2016 ownCloud, Inc. + * SPDX-License-Identifier: AGPL-3.0-only */ namespace OC\Memcache; diff --git a/lib/private/Memcache/CASTrait.php b/lib/private/Memcache/CASTrait.php index 13688651043..945f1539f99 100644 --- a/lib/private/Memcache/CASTrait.php +++ b/lib/private/Memcache/CASTrait.php @@ -1,23 +1,8 @@ <?php /** - * @copyright Copyright (c) 2016, ownCloud, Inc. - * - * @author Robin Appelman <robin@icewind.nl> - * - * @license AGPL-3.0 - * - * This code is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License, version 3, - * as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License, version 3, - * along with this program. If not, see <http://www.gnu.org/licenses/> - * + * SPDX-FileCopyrightText: 2016-2024 Nextcloud GmbH and Nextcloud contributors + * SPDX-FileCopyrightText: 2016 ownCloud, Inc. + * SPDX-License-Identifier: AGPL-3.0-only */ namespace OC\Memcache; diff --git a/lib/private/Memcache/Cache.php b/lib/private/Memcache/Cache.php index 1d54a705098..2a2a6e2a23f 100644 --- a/lib/private/Memcache/Cache.php +++ b/lib/private/Memcache/Cache.php @@ -1,29 +1,14 @@ <?php /** - * @copyright Copyright (c) 2016, ownCloud, Inc. - * - * @author Lukas Reschke <lukas@statuscode.ch> - * @author Morris Jobke <hey@morrisjobke.de> - * @author Robin Appelman <robin@icewind.nl> - * @author Robin McCorkell <robin@mccorkell.me.uk> - * - * @license AGPL-3.0 - * - * This code is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License, version 3, - * as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License, version 3, - * along with this program. If not, see <http://www.gnu.org/licenses/> - * + * SPDX-FileCopyrightText: 2016-2024 Nextcloud GmbH and Nextcloud contributors + * SPDX-FileCopyrightText: 2016 ownCloud, Inc. + * SPDX-License-Identifier: AGPL-3.0-only */ namespace OC\Memcache; +/** + * @template-implements \ArrayAccess<string,mixed> + */ abstract class Cache implements \ArrayAccess, \OCP\ICache { /** * @var string $prefix diff --git a/lib/private/Memcache/Factory.php b/lib/private/Memcache/Factory.php index 788a7c2e8c9..c0f4f787200 100644 --- a/lib/private/Memcache/Factory.php +++ b/lib/private/Memcache/Factory.php @@ -1,40 +1,16 @@ <?php /** - * @copyright Copyright (c) 2016, ownCloud, Inc. - * - * @author Christoph Wurst <christoph@winzerhof-wurst.at> - * @author Joas Schilling <coding@schilljs.com> - * @author Lukas Reschke <lukas@statuscode.ch> - * @author Markus Goetz <markus@woboq.com> - * @author Morris Jobke <hey@morrisjobke.de> - * @author Richard Steinmetz <richard@steinmetz.cloud> - * @author Robin Appelman <robin@icewind.nl> - * @author Robin McCorkell <robin@mccorkell.me.uk> - * @author Roeland Jago Douma <roeland@famdouma.nl> - * @author Stefan Weil <sw@weilnetz.de> - * @author Vincent Petry <vincent@nextcloud.com> - * - * @license AGPL-3.0 - * - * This code is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License, version 3, - * as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License, version 3, - * along with this program. If not, see <http://www.gnu.org/licenses/> - * + * SPDX-FileCopyrightText: 2016-2024 Nextcloud GmbH and Nextcloud contributors + * SPDX-FileCopyrightText: 2016 ownCloud, Inc. + * SPDX-License-Identifier: AGPL-3.0-only */ namespace OC\Memcache; -use OCP\Profiler\IProfiler; +use OCP\Cache\CappedMemoryCache; use OCP\ICache; use OCP\ICacheFactory; use OCP\IMemcache; +use OCP\Profiler\IProfiler; use Psr\Log\LoggerInterface; class Factory implements ICacheFactory { @@ -184,13 +160,8 @@ class Factory implements ICacheFactory { return $this->distributedCacheClass !== self::NULL_CACHE; } - /** - * @see \OC\Memcache\Factory::createLocal() - * @param string $prefix - * @return ICache - */ - public function createLowLatency(string $prefix = ''): ICache { - return $this->createLocal($prefix); + public function createInMemory(int $capacity = 512): ICache { + return new CappedMemoryCache($capacity); } /** diff --git a/lib/private/Memcache/LoggerWrapperCache.php b/lib/private/Memcache/LoggerWrapperCache.php index 55c0e76db79..11497e2a5d8 100644 --- a/lib/private/Memcache/LoggerWrapperCache.php +++ b/lib/private/Memcache/LoggerWrapperCache.php @@ -2,25 +2,8 @@ declare(strict_types = 1); /** - * @copyright 2022 Carl Schwan <carl@carlschwan.eu> - * - * @author Carl Schwan <carl@carlschwan.eu> - * - * @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/>. - * + * SPDX-FileCopyrightText: 2022 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later */ namespace OC\Memcache; @@ -75,7 +58,7 @@ class LoggerWrapperCache extends Cache implements IMemcacheTTL { FILE_APPEND ); - return $this->wrappedCache->set($key, $value, $$ttl); + return $this->wrappedCache->set($key, $value, $ttl); } /** @inheritDoc */ @@ -167,10 +150,18 @@ class LoggerWrapperCache extends Cache implements IMemcacheTTL { } /** @inheritDoc */ - public function setTTL($key, $ttl) { + public function setTTL(string $key, int $ttl) { $this->wrappedCache->setTTL($key, $ttl); } + public function getTTL(string $key): int|false { + return $this->wrappedCache->getTTL($key); + } + + public function compareSetTTL(string $key, mixed $value, int $ttl): bool { + return $this->wrappedCache->compareSetTTL($key, $value, $ttl); + } + public static function isAvailable(): bool { return true; } diff --git a/lib/private/Memcache/Memcached.php b/lib/private/Memcache/Memcached.php index 7d512d4d1ae..620013feda6 100644 --- a/lib/private/Memcache/Memcached.php +++ b/lib/private/Memcache/Memcached.php @@ -1,33 +1,8 @@ <?php /** - * @copyright Copyright (c) 2016, ownCloud, Inc. - * - * @author Andreas Fischer <bantu@owncloud.com> - * @author Bart Visscher <bartv@thisnet.nl> - * @author Christoph Wurst <christoph@winzerhof-wurst.at> - * @author Joas Schilling <coding@schilljs.com> - * @author Jörn Friedrich Dreyer <jfd@butonic.de> - * @author Morris Jobke <hey@morrisjobke.de> - * @author Robin Appelman <robin@icewind.nl> - * @author Robin McCorkell <robin@mccorkell.me.uk> - * @author Roeland Jago Douma <roeland@famdouma.nl> - * @author Thomas Müller <thomas.mueller@tmit.eu> - * @author Victor Dubiniuk <dubiniuk@owncloud.com> - * - * @license AGPL-3.0 - * - * This code is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License, version 3, - * as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License, version 3, - * along with this program. If not, see <http://www.gnu.org/licenses/> - * + * SPDX-FileCopyrightText: 2016-2024 Nextcloud GmbH and Nextcloud contributors + * SPDX-FileCopyrightText: 2016 ownCloud, Inc. + * SPDX-License-Identifier: AGPL-3.0-only */ namespace OC\Memcache; diff --git a/lib/private/Memcache/NullCache.php b/lib/private/Memcache/NullCache.php index fc41595dfe1..ab5c491913a 100644 --- a/lib/private/Memcache/NullCache.php +++ b/lib/private/Memcache/NullCache.php @@ -1,28 +1,8 @@ <?php /** - * @copyright Copyright (c) 2016, ownCloud, Inc. - * - * @author Christoph Wurst <christoph@winzerhof-wurst.at> - * @author Morris Jobke <hey@morrisjobke.de> - * @author Robin Appelman <robin@icewind.nl> - * @author Robin McCorkell <robin@mccorkell.me.uk> - * @author Thomas Müller <thomas.mueller@tmit.eu> - * @author Vincent Petry <vincent@nextcloud.com> - * - * @license AGPL-3.0 - * - * This code is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License, version 3, - * as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License, version 3, - * along with this program. If not, see <http://www.gnu.org/licenses/> - * + * SPDX-FileCopyrightText: 2016-2024 Nextcloud GmbH and Nextcloud contributors + * SPDX-FileCopyrightText: 2016 ownCloud, Inc. + * SPDX-License-Identifier: AGPL-3.0-only */ namespace OC\Memcache; diff --git a/lib/private/Memcache/ProfilerWrapperCache.php b/lib/private/Memcache/ProfilerWrapperCache.php index 6e76989dddd..84e3d880a0c 100644 --- a/lib/private/Memcache/ProfilerWrapperCache.php +++ b/lib/private/Memcache/ProfilerWrapperCache.php @@ -2,25 +2,8 @@ declare(strict_types = 1); /** - * @copyright 2022 Carl Schwan <carl@carlschwan.eu> - * - * @author Carl Schwan <carl@carlschwan.eu> - * - * @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/>. - * + * SPDX-FileCopyrightText: 2022 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later */ namespace OC\Memcache; @@ -32,6 +15,7 @@ use OCP\IMemcacheTTL; /** * Cache wrapper that logs profiling information + * @template-implements \ArrayAccess<string,mixed> */ class ProfilerWrapperCache extends AbstractDataCollector implements IMemcacheTTL, \ArrayAccess { /** @var Redis $wrappedCache*/ @@ -183,10 +167,18 @@ class ProfilerWrapperCache extends AbstractDataCollector implements IMemcacheTTL } /** @inheritDoc */ - public function setTTL($key, $ttl) { + public function setTTL(string $key, int $ttl) { $this->wrappedCache->setTTL($key, $ttl); } + public function getTTL(string $key): int|false { + return $this->wrappedCache->getTTL($key); + } + + public function compareSetTTL(string $key, mixed $value, int $ttl): bool { + return $this->wrappedCache->compareSetTTL($key, $value, $ttl); + } + public function offsetExists($offset): bool { return $this->hasKey($offset); } @@ -207,7 +199,7 @@ class ProfilerWrapperCache extends AbstractDataCollector implements IMemcacheTTL $this->remove($offset); } - public function collect(Request $request, Response $response, \Throwable $exception = null): void { + public function collect(Request $request, Response $response, ?\Throwable $exception = null): void { // Nothing to do here $data is already ready } diff --git a/lib/private/Memcache/Redis.php b/lib/private/Memcache/Redis.php index bde25a3385a..cbafadc3b1b 100644 --- a/lib/private/Memcache/Redis.php +++ b/lib/private/Memcache/Redis.php @@ -1,31 +1,8 @@ <?php /** - * @copyright Copyright (c) 2016, ownCloud, Inc. - * - * @author Arthur Schiwon <blizzz@arthur-schiwon.de> - * @author Christoph Wurst <christoph@winzerhof-wurst.at> - * @author Joas Schilling <coding@schilljs.com> - * @author Jörn Friedrich Dreyer <jfd@butonic.de> - * @author Morris Jobke <hey@morrisjobke.de> - * @author Robin Appelman <robin@icewind.nl> - * @author Robin McCorkell <robin@mccorkell.me.uk> - * @author Roeland Jago Douma <roeland@famdouma.nl> - * @author Stefan Weil <sw@weilnetz.de> - * - * @license AGPL-3.0 - * - * This code is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License, version 3, - * as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License, version 3, - * along with this program. If not, see <http://www.gnu.org/licenses/> - * + * SPDX-FileCopyrightText: 2016-2024 Nextcloud GmbH and Nextcloud contributors + * SPDX-FileCopyrightText: 2016 ownCloud, Inc. + * SPDX-License-Identifier: AGPL-3.0-only */ namespace OC\Memcache; @@ -46,8 +23,15 @@ class Redis extends Cache implements IMemcacheTTL { 'if redis.call("get", KEYS[1]) == ARGV[1] then return redis.call("del", KEYS[1]) else return 0 end', 'cf0e94b2e9ffc7e04395cf88f7583fc309985910', ], + 'caSetTtl' => [ + 'if redis.call("get", KEYS[1]) == ARGV[1] then redis.call("expire", KEYS[1], ARGV[2]) return 1 else return 0 end', + 'fa4acbc946d23ef41d7d3910880b60e6e4972d72', + ], ]; + private const DEFAULT_TTL = 24 * 60 * 60; // 1 day + private const MAX_TTL = 30 * 24 * 60 * 60; // 1 month + /** * @var \Redis|\RedisCluster $cache */ @@ -63,7 +47,7 @@ class Redis extends Cache implements IMemcacheTTL { */ public function getCache() { if (is_null(self::$cache)) { - self::$cache = \OC::$server->getGetRedisFactory()->getInstance(); + self::$cache = \OC::$server->get('RedisFactory')->getInstance(); } return self::$cache; } @@ -79,11 +63,12 @@ class Redis extends Cache implements IMemcacheTTL { public function set($key, $value, $ttl = 0) { $value = self::encodeValue($value); - if ($ttl > 0) { - return $this->getCache()->setex($this->getPrefix() . $key, $ttl, $value); - } else { - return $this->getCache()->set($this->getPrefix() . $key, $value); + if ($ttl === 0) { + // having infinite TTL can lead to leaked keys as the prefix changes with version upgrades + $ttl = self::DEFAULT_TTL; } + $ttl = min($ttl, self::MAX_TTL); + return $this->getCache()->setex($this->getPrefix() . $key, $ttl, $value); } public function hasKey($key) { @@ -117,11 +102,14 @@ class Redis extends Cache implements IMemcacheTTL { */ public function add($key, $value, $ttl = 0) { $value = self::encodeValue($value); + if ($ttl === 0) { + // having infinite TTL can lead to leaked keys as the prefix changes with version upgrades + $ttl = self::DEFAULT_TTL; + } + $ttl = min($ttl, self::MAX_TTL); $args = ['nx']; - if ($ttl !== 0 && is_int($ttl)) { - $args['ex'] = $ttl; - } + $args['ex'] = $ttl; return $this->getCache()->set($this->getPrefix() . $key, $value, $args); } @@ -178,11 +166,27 @@ class Redis extends Cache implements IMemcacheTTL { } public function setTTL($key, $ttl) { + if ($ttl === 0) { + // having infinite TTL can lead to leaked keys as the prefix changes with version upgrades + $ttl = self::DEFAULT_TTL; + } + $ttl = min($ttl, self::MAX_TTL); $this->getCache()->expire($this->getPrefix() . $key, $ttl); } + public function getTTL(string $key): int|false { + $ttl = $this->getCache()->ttl($this->getPrefix() . $key); + return $ttl > 0 ? (int)$ttl : false; + } + + public function compareSetTTL(string $key, mixed $value, int $ttl): bool { + $value = self::encodeValue($value); + + return $this->evalLua('caSetTtl', [$key], [$value, $ttl]) > 0; + } + public static function isAvailable(): bool { - return \OC::$server->getGetRedisFactory()->isAvailable(); + return \OC::$server->get('RedisFactory')->isAvailable(); } protected function evalLua(string $scriptName, array $keys, array $args) { @@ -191,7 +195,7 @@ class Redis extends Cache implements IMemcacheTTL { $script = self::LUA_SCRIPTS[$scriptName]; $result = $this->getCache()->evalSha($script[1], $args, count($keys)); - if (false === $result) { + if ($result === false) { $result = $this->getCache()->eval($script[0], $args, count($keys)); } diff --git a/lib/private/Memcache/WithLocalCache.php b/lib/private/Memcache/WithLocalCache.php index 5b7ccc10e39..0fc5d310801 100644 --- a/lib/private/Memcache/WithLocalCache.php +++ b/lib/private/Memcache/WithLocalCache.php @@ -1,5 +1,9 @@ <?php +/** + * SPDX-FileCopyrightText: 2023 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later + */ namespace OC\Memcache; use OCP\Cache\CappedMemoryCache; diff --git a/lib/private/MemoryInfo.php b/lib/private/MemoryInfo.php index 266fb15a573..a90739a6643 100644 --- a/lib/private/MemoryInfo.php +++ b/lib/private/MemoryInfo.php @@ -3,26 +3,8 @@ declare(strict_types=1); /** - * @copyright Copyright (c) 2018, Michael Weimann (<mail@michael-weimann.eu>) - * - * @author Christoph Wurst <christoph@winzerhof-wurst.at> - * @author Michael Weimann <mail@michael-weimann.eu> - * - * @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/>. - * + * SPDX-FileCopyrightText: 2018 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later */ namespace OC; diff --git a/lib/private/Metadata/Capabilities.php b/lib/private/Metadata/Capabilities.php deleted file mode 100644 index 2fa0006f581..00000000000 --- a/lib/private/Metadata/Capabilities.php +++ /dev/null @@ -1,44 +0,0 @@ -<?php - -declare(strict_types=1); - -/** - * @copyright Copyright 2022 Carl Schwan <carl@carlschwan.eu> - * @license AGPL-3.0-or-later - * - * This code is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License, version 3, - * as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License, version 3, - * along with this program. If not, see <http://www.gnu.org/licenses/> - * - */ - -namespace OC\Metadata; - -use OCP\Capabilities\IPublicCapability; -use OCP\IConfig; - -class Capabilities implements IPublicCapability { - private IMetadataManager $manager; - private IConfig $config; - - public function __construct(IMetadataManager $manager, IConfig $config) { - $this->manager = $manager; - $this->config = $config; - } - - public function getCapabilities() { - if ($this->config->getSystemValueBool('enable_file_metadata', true)) { - return ['metadataAvailable' => $this->manager->getCapabilities()]; - } - - return []; - } -} diff --git a/lib/private/Metadata/FileEventListener.php b/lib/private/Metadata/FileEventListener.php deleted file mode 100644 index 162e85ff3aa..00000000000 --- a/lib/private/Metadata/FileEventListener.php +++ /dev/null @@ -1,110 +0,0 @@ -<?php - -declare(strict_types=1); -/** - * @copyright Copyright 2022 Carl Schwan <carl@carlschwan.eu> - * @license AGPL-3.0-or-later - * - * This code is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License, version 3, - * as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License, version 3, - * along with this program. If not, see <http://www.gnu.org/licenses/> - * - */ - -namespace OC\Metadata; - -use OC\Files\Filesystem; -use OCP\EventDispatcher\Event; -use OCP\EventDispatcher\IEventListener; -use OCP\Files\Events\Node\NodeDeletedEvent; -use OCP\Files\Events\Node\NodeWrittenEvent; -use OCP\Files\Events\NodeRemovedFromCache; -use OCP\Files\File; -use OCP\Files\Node; -use OCP\Files\NotFoundException; -use OCP\Files\FileInfo; -use Psr\Log\LoggerInterface; - -/** - * @template-implements IEventListener<NodeRemovedFromCache> - * @template-implements IEventListener<NodeDeletedEvent> - * @template-implements IEventListener<NodeWrittenEvent> - */ -class FileEventListener implements IEventListener { - private IMetadataManager $manager; - private LoggerInterface $logger; - - public function __construct(IMetadataManager $manager, LoggerInterface $logger) { - $this->manager = $manager; - $this->logger = $logger; - } - - private function shouldExtractMetadata(Node $node): bool { - try { - if ($node->getMimetype() === 'httpd/unix-directory') { - return false; - } - } catch (NotFoundException $e) { - return false; - } - if ($node->getSize(false) <= 0) { - return false; - } - - $path = $node->getPath(); - return $this->isCorrectPath($path); - } - - private function isCorrectPath(string $path): bool { - // TODO make this more dynamic, we have the same issue in other places - return !str_starts_with($path, 'appdata_') && !str_starts_with($path, 'files_versions/') && !str_starts_with($path, 'files_trashbin/'); - } - - public function handle(Event $event): void { - if ($event instanceof NodeRemovedFromCache) { - if (!$this->isCorrectPath($event->getPath())) { - // Don't listen to paths for which we don't extract metadata - return; - } - $view = Filesystem::getView(); - if (!$view) { - // Should not happen since a scan in the user folder should setup - // the file system. - $e = new \Exception(); // don't trigger, just get backtrace - $this->logger->error('Detecting deletion of a file with possible metadata but file system setup is not setup', [ - 'exception' => $e, - 'app' => 'metadata' - ]); - return; - } - $info = $view->getFileInfo($event->getPath()); - if ($info && $info->getType() === FileInfo::TYPE_FILE) { - $this->manager->clearMetadata($info->getId()); - } - } - - if ($event instanceof NodeDeletedEvent) { - $node = $event->getNode(); - if ($this->shouldExtractMetadata($node)) { - /** @var File $node */ - $this->manager->clearMetadata($event->getNode()->getId()); - } - } - - if ($event instanceof NodeWrittenEvent) { - $node = $event->getNode(); - if ($this->shouldExtractMetadata($node)) { - /** @var File $node */ - $this->manager->generateMetadata($event->getNode(), false); - } - } - } -} diff --git a/lib/private/Metadata/FileMetadata.php b/lib/private/Metadata/FileMetadata.php deleted file mode 100644 index a9808a86998..00000000000 --- a/lib/private/Metadata/FileMetadata.php +++ /dev/null @@ -1,51 +0,0 @@ -<?php - -declare(strict_types=1); -/** - * @copyright Copyright 2022 Carl Schwan <carl@carlschwan.eu> - * - * @license AGPL-3.0 - * - * This code is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License, version 3, - * as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License, version 3, - * along with this program. If not, see <http://www.gnu.org/licenses/> - * - */ - -namespace OC\Metadata; - -use OCP\AppFramework\Db\Entity; -use OCP\DB\Types; - -/** - * @method string getGroupName() - * @method void setGroupName(string $groupName) - * @method string getValue() - * @method void setValue(string $value) - * @see \OC\Core\Migrations\Version240000Date20220404230027 - */ -class FileMetadata extends Entity { - protected ?string $groupName = null; - protected ?string $value = null; - - public function __construct() { - $this->addType('groupName', 'string'); - $this->addType('value', Types::STRING); - } - - public function getDecodedValue(): array { - return json_decode($this->getValue(), true) ?? []; - } - - public function setArrayAsValue(array $value): void { - $this->setValue(json_encode($value, JSON_THROW_ON_ERROR)); - } -} diff --git a/lib/private/Metadata/FileMetadataMapper.php b/lib/private/Metadata/FileMetadataMapper.php deleted file mode 100644 index 003ab13126e..00000000000 --- a/lib/private/Metadata/FileMetadataMapper.php +++ /dev/null @@ -1,177 +0,0 @@ -<?php - -declare(strict_types=1); -/** - * @copyright Copyright 2022 Carl Schwan <carl@carlschwan.eu> - * @copyright Copyright 2022 Louis Chmn <louis@chmn.me> - * @license AGPL-3.0-or-later - * - * This code is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License, version 3, - * as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License, version 3, - * along with this program. If not, see <http://www.gnu.org/licenses/> - * - */ - -namespace OC\Metadata; - -use OCP\AppFramework\Db\DoesNotExistException; -use OCP\AppFramework\Db\MultipleObjectsReturnedException; -use OCP\AppFramework\Db\QBMapper; -use OCP\AppFramework\Db\Entity; -use OCP\DB\Exception; -use OCP\DB\QueryBuilder\IQueryBuilder; -use OCP\IDBConnection; - -/** - * @template-extends QBMapper<FileMetadata> - */ -class FileMetadataMapper extends QBMapper { - public function __construct(IDBConnection $db) { - parent::__construct($db, 'file_metadata', FileMetadata::class); - } - - /** - * @return FileMetadata[] - * @throws Exception - */ - public function findForFile(int $fileId): array { - $qb = $this->db->getQueryBuilder(); - $qb->select('*') - ->from($this->getTableName()) - ->where($qb->expr()->eq('id', $qb->createNamedParameter($fileId, IQueryBuilder::PARAM_INT))); - - return $this->findEntities($qb); - } - - /** - * @throws DoesNotExistException - * @throws MultipleObjectsReturnedException - * @throws Exception - */ - public function findForGroupForFile(int $fileId, string $groupName): FileMetadata { - $qb = $this->db->getQueryBuilder(); - $qb->select('*') - ->from($this->getTableName()) - ->where($qb->expr()->eq('id', $qb->createNamedParameter($fileId, IQueryBuilder::PARAM_INT))) - ->andWhere($qb->expr()->eq('group_name', $qb->createNamedParameter($groupName, IQueryBuilder::PARAM_STR))); - - return $this->findEntity($qb); - } - - /** - * @return array<int, FileMetadata> - * @throws Exception - */ - public function findForGroupForFiles(array $fileIds, string $groupName): array { - $qb = $this->db->getQueryBuilder(); - $qb->select('*') - ->from($this->getTableName()) - ->where($qb->expr()->in('id', $qb->createParameter('fileIds'))) - ->andWhere($qb->expr()->eq('group_name', $qb->createNamedParameter($groupName, IQueryBuilder::PARAM_STR))); - - $metadata = []; - foreach (array_chunk($fileIds, 1000) as $fileIdsChunk) { - $qb->setParameter('fileIds', $fileIdsChunk, IQueryBuilder::PARAM_INT_ARRAY); - /** @var FileMetadata[] $rawEntities */ - $rawEntities = $this->findEntities($qb); - foreach ($rawEntities as $entity) { - $metadata[$entity->getId()] = $entity; - } - } - - foreach ($fileIds as $id) { - if (isset($metadata[$id])) { - continue; - } - $empty = new FileMetadata(); - $empty->setValue(''); - $empty->setGroupName($groupName); - $empty->setId($id); - $metadata[$id] = $empty; - } - return $metadata; - } - - public function clear(int $fileId): void { - $qb = $this->db->getQueryBuilder(); - $qb->delete($this->getTableName()) - ->where($qb->expr()->eq('id', $qb->createNamedParameter($fileId, IQueryBuilder::PARAM_INT))); - - $qb->executeStatement(); - } - - /** - * Updates an entry in the db from an entity - * - * @param FileMetadata $entity the entity that should be created - * @return FileMetadata the saved entity with the set id - * @throws Exception - * @throws \InvalidArgumentException if entity has no id - */ - public function update(Entity $entity): FileMetadata { - if (!($entity instanceof FileMetadata)) { - throw new \Exception("Entity should be a FileMetadata entity"); - } - - // entity needs an id - $id = $entity->getId(); - if ($id === null) { - throw new \InvalidArgumentException('Entity which should be updated has no id'); - } - - // entity needs an group_name - $groupName = $entity->getGroupName(); - if ($groupName === null) { - throw new \InvalidArgumentException('Entity which should be updated has no group_name'); - } - - $idType = $this->getParameterTypeForProperty($entity, 'id'); - $groupNameType = $this->getParameterTypeForProperty($entity, 'groupName'); - $value = $entity->getValue(); - $valueType = $this->getParameterTypeForProperty($entity, 'value'); - - $qb = $this->db->getQueryBuilder(); - - $qb->update($this->tableName) - ->set('value', $qb->createNamedParameter($value, $valueType)) - ->where($qb->expr()->eq('id', $qb->createNamedParameter($id, $idType))) - ->andWhere($qb->expr()->eq('group_name', $qb->createNamedParameter($groupName, $groupNameType))) - ->executeStatement(); - - return $entity; - } - - /** - * Override the insertOrUpdate as we could be in a transaction in which case we can not afford on error. - * - * @param FileMetadata $entity the entity that should be created/updated - * @return FileMetadata the saved entity with the (new) id - * @throws Exception - * @throws \InvalidArgumentException if entity has no id - */ - public function insertOrUpdate(Entity $entity): FileMetadata { - try { - $existingEntity = $this->findForGroupForFile($entity->getId(), $entity->getGroupName()); - } catch (\Throwable) { - $existingEntity = null; - } - - if ($existingEntity !== null) { - if ($entity->getValue() !== $existingEntity->getValue()) { - return $this->update($entity); - } else { - return $existingEntity; - } - } else { - return parent::insertOrUpdate($entity); - } - } -} diff --git a/lib/private/Metadata/IMetadataManager.php b/lib/private/Metadata/IMetadataManager.php deleted file mode 100644 index fa0bcc22801..00000000000 --- a/lib/private/Metadata/IMetadataManager.php +++ /dev/null @@ -1,35 +0,0 @@ -<?php - -declare(strict_types=1); - -namespace OC\Metadata; - -use OCP\Files\File; - -/** - * Interface to manage additional metadata for files - */ -interface IMetadataManager { - /** - * @param class-string<IMetadataProvider> $className - */ - public function registerProvider(string $className): void; - - /** - * Generate the metadata for one file - */ - public function generateMetadata(File $file, bool $checkExisting = false): void; - - /** - * Clear the metadata for one file - */ - public function clearMetadata(int $fileId): void; - - /** @return array<int, FileMetadata> */ - public function fetchMetadataFor(string $group, array $fileIds): array; - - /** - * Get the capabilities as an array of mimetype regex to the type provided - */ - public function getCapabilities(): array; -} diff --git a/lib/private/Metadata/IMetadataProvider.php b/lib/private/Metadata/IMetadataProvider.php deleted file mode 100644 index 7cbe102a538..00000000000 --- a/lib/private/Metadata/IMetadataProvider.php +++ /dev/null @@ -1,41 +0,0 @@ -<?php - -namespace OC\Metadata; - -use OCP\Files\File; - -/** - * Interface for the metadata providers. If you want an application to provide - * some metadata, you can use this to store them. - */ -interface IMetadataProvider { - /** - * The list of groups that this metadata provider is able to provide. - * - * @return string[] - */ - public static function groupsProvided(): array; - - /** - * Check if the metadata provider is available. A metadata provider might be - * unavailable due to a php extension not being installed. - */ - public static function isAvailable(): bool; - - /** - * Get the mimetypes supported as a regex. - */ - public static function getMimetypesSupported(): string; - - /** - * Execute the extraction on the specified file. The metadata should be - * grouped by metadata - * - * Each group should be json serializable and the string representation - * shouldn't be longer than 4000 characters. - * - * @param File $file The file to extract the metadata from - * @param array<string, FileMetadata> An array containing all the metadata fetched. - */ - public function execute(File $file): array; -} diff --git a/lib/private/Metadata/MetadataManager.php b/lib/private/Metadata/MetadataManager.php deleted file mode 100644 index 6d96ff1ab68..00000000000 --- a/lib/private/Metadata/MetadataManager.php +++ /dev/null @@ -1,100 +0,0 @@ -<?php -/** - * @copyright Copyright 2022 Carl Schwan <carl@carlschwan.eu> - * @license AGPL-3.0-or-later - * - * This code is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License, version 3, - * as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License, version 3, - * along with this program. If not, see <http://www.gnu.org/licenses/> - * - */ - -namespace OC\Metadata; - -use OC\Metadata\Provider\ExifProvider; -use OCP\Files\File; - -class MetadataManager implements IMetadataManager { - /** @var array<string, IMetadataProvider> */ - private array $providers; - private array $providerClasses; - private FileMetadataMapper $fileMetadataMapper; - - public function __construct( - FileMetadataMapper $fileMetadataMapper - ) { - $this->providers = []; - $this->providerClasses = []; - $this->fileMetadataMapper = $fileMetadataMapper; - - // TODO move to another place, where? - $this->registerProvider(ExifProvider::class); - } - - /** - * @param class-string<IMetadataProvider> $className - */ - public function registerProvider(string $className):void { - if (in_array($className, $this->providerClasses)) { - return; - } - - if (call_user_func([$className, 'isAvailable'])) { - $this->providers[call_user_func([$className, 'getMimetypesSupported'])] = \OC::$server->get($className); - } - } - - public function generateMetadata(File $file, bool $checkExisting = false): void { - $existingMetadataGroups = []; - - if ($checkExisting) { - $existingMetadata = $this->fileMetadataMapper->findForFile($file->getId()); - foreach ($existingMetadata as $metadata) { - $existingMetadataGroups[] = $metadata->getGroupName(); - } - } - - foreach ($this->providers as $supportedMimetype => $provider) { - if (preg_match($supportedMimetype, $file->getMimeType())) { - if (count(array_diff($provider::groupsProvided(), $existingMetadataGroups)) > 0) { - $metaDataGroup = $provider->execute($file); - foreach ($metaDataGroup as $group => $metadata) { - $this->fileMetadataMapper->insertOrUpdate($metadata); - } - } - } - } - } - - public function clearMetadata(int $fileId): void { - $this->fileMetadataMapper->clear($fileId); - } - - /** - * @return array<int, FileMetadata> - */ - public function fetchMetadataFor(string $group, array $fileIds): array { - return $this->fileMetadataMapper->findForGroupForFiles($fileIds, $group); - } - - public function getCapabilities(): array { - $capabilities = []; - foreach ($this->providers as $supportedMimetype => $provider) { - foreach ($provider::groupsProvided() as $group) { - if (isset($capabilities[$group])) { - $capabilities[$group][] = $supportedMimetype; - } - $capabilities[$group] = [$supportedMimetype]; - } - } - return $capabilities; - } -} diff --git a/lib/private/Metadata/Provider/ExifProvider.php b/lib/private/Metadata/Provider/ExifProvider.php deleted file mode 100644 index b1598abbbc8..00000000000 --- a/lib/private/Metadata/Provider/ExifProvider.php +++ /dev/null @@ -1,142 +0,0 @@ -<?php - -declare(strict_types=1); -/** - * @copyright Copyright 2022 Carl Schwan <carl@carlschwan.eu> - * @copyright Copyright 2022 Louis Chmn <louis@chmn.me> - * @license AGPL-3.0-or-later - * - * This code is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License, version 3, - * as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License, version 3, - * along with this program. If not, see <http://www.gnu.org/licenses/> - * - */ - -namespace OC\Metadata\Provider; - -use OC\Metadata\FileMetadata; -use OC\Metadata\IMetadataProvider; -use OCP\Files\File; -use Psr\Log\LoggerInterface; - -class ExifProvider implements IMetadataProvider { - private LoggerInterface $logger; - - public function __construct( - LoggerInterface $logger - ) { - $this->logger = $logger; - } - - public static function groupsProvided(): array { - return ['size', 'gps']; - } - - public static function isAvailable(): bool { - return extension_loaded('exif'); - } - - /** @return array{'gps'?: FileMetadata, 'size'?: FileMetadata} */ - public function execute(File $file): array { - $exifData = []; - $fileDescriptor = $file->fopen('rb'); - - if ($fileDescriptor === false) { - return []; - } - - $data = null; - try { - // Needed to make reading exif data reliable. - // This is to trigger this condition: https://github.com/php/php-src/blob/d64aa6f646a7b5e58359dc79479860164239580a/main/streams/streams.c#L710 - // But I don't understand why 1 as a special meaning. - // Revert right after reading the exif data. - $oldBufferSize = stream_set_chunk_size($fileDescriptor, 1); - $data = @exif_read_data($fileDescriptor, 'ANY_TAG', true); - stream_set_chunk_size($fileDescriptor, $oldBufferSize); - } catch (\Exception $ex) { - $this->logger->info("Couldn't extract metadata for ".$file->getId(), ['exception' => $ex]); - } - - $size = new FileMetadata(); - $size->setGroupName('size'); - $size->setId($file->getId()); - $size->setArrayAsValue([]); - - if (!$data) { - $sizeResult = getimagesizefromstring($file->getContent()); - if ($sizeResult !== false) { - $size->setArrayAsValue([ - 'width' => $sizeResult[0], - 'height' => $sizeResult[1], - ]); - - $exifData['size'] = $size; - } - } elseif (array_key_exists('COMPUTED', $data)) { - if (array_key_exists('Width', $data['COMPUTED']) && array_key_exists('Height', $data['COMPUTED'])) { - $size->setArrayAsValue([ - 'width' => $data['COMPUTED']['Width'], - 'height' => $data['COMPUTED']['Height'], - ]); - - $exifData['size'] = $size; - } - } - - if ($data && array_key_exists('GPS', $data) - && array_key_exists('GPSLatitude', $data['GPS']) && array_key_exists('GPSLatitudeRef', $data['GPS']) - && array_key_exists('GPSLongitude', $data['GPS']) && array_key_exists('GPSLongitudeRef', $data['GPS']) - ) { - $gps = new FileMetadata(); - $gps->setGroupName('gps'); - $gps->setId($file->getId()); - $gps->setArrayAsValue([ - 'latitude' => $this->gpsDegreesToDecimal($data['GPS']['GPSLatitude'], $data['GPS']['GPSLatitudeRef']), - 'longitude' => $this->gpsDegreesToDecimal($data['GPS']['GPSLongitude'], $data['GPS']['GPSLongitudeRef']), - ]); - - $exifData['gps'] = $gps; - } - - return $exifData; - } - - public static function getMimetypesSupported(): string { - return '/image\/(png|jpeg|heif|webp|tiff)/'; - } - - /** - * @param array|string $coordinates - */ - private static function gpsDegreesToDecimal($coordinates, ?string $hemisphere): float { - if (is_string($coordinates)) { - $coordinates = array_map("trim", explode(",", $coordinates)); - } - - if (count($coordinates) !== 3) { - throw new \Exception('Invalid coordinate format: ' . json_encode($coordinates)); - } - - [$degrees, $minutes, $seconds] = array_map(function (string $rawDegree) { - $parts = explode('/', $rawDegree); - - if ($parts[1] === '0') { - return 0; - } - - return floatval($parts[0]) / floatval($parts[1] ?? 1); - }, $coordinates); - - $sign = ($hemisphere === 'W' || $hemisphere === 'S') ? -1 : 1; - return $sign * ($degrees + $minutes / 60 + $seconds / 3600); - } -} diff --git a/lib/private/Migration/BackgroundRepair.php b/lib/private/Migration/BackgroundRepair.php index 579ba494e58..cbc21e4fe12 100644 --- a/lib/private/Migration/BackgroundRepair.php +++ b/lib/private/Migration/BackgroundRepair.php @@ -1,38 +1,18 @@ <?php + /** - * @copyright Copyright (c) 2016, ownCloud, Inc. - * - * @author Christoph Wurst <christoph@winzerhof-wurst.at> - * @author Daniel Kesselberg <mail@danielkesselberg.de> - * @author Lukas Reschke <lukas@statuscode.ch> - * @author Robin Appelman <robin@icewind.nl> - * @author Roeland Jago Douma <roeland@famdouma.nl> - * @author Thomas Müller <thomas.mueller@tmit.eu> - * - * @license AGPL-3.0 - * - * This code is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License, version 3, - * as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License, version 3, - * along with this program. If not, see <http://www.gnu.org/licenses/> - * + * SPDX-FileCopyrightText: 2016-2024 Nextcloud GmbH and Nextcloud contributors + * SPDX-FileCopyrightText: 2016 ownCloud, Inc. + * SPDX-License-Identifier: AGPL-3.0-only */ namespace OC\Migration; -use OCP\AppFramework\Utility\ITimeFactory; -use OCP\BackgroundJob\IJobList; -use OCP\BackgroundJob\TimedJob; -use OCP\EventDispatcher\IEventDispatcher; use OC\NeedsUpdateException; use OC\Repair; use OC_App; +use OCP\AppFramework\Utility\ITimeFactory; +use OCP\BackgroundJob\IJobList; +use OCP\BackgroundJob\TimedJob; use Psr\Log\LoggerInterface; /** @@ -41,15 +21,13 @@ use Psr\Log\LoggerInterface; * @package OC\Migration */ class BackgroundRepair extends TimedJob { - private IJobList $jobList; - private LoggerInterface $logger; - private IEventDispatcher $dispatcher; - - public function __construct(IEventDispatcher $dispatcher, ITimeFactory $time, LoggerInterface $logger, IJobList $jobList) { + public function __construct( + private Repair $repair, + ITimeFactory $time, + private LoggerInterface $logger, + private IJobList $jobList, + ) { parent::__construct($time); - $this->dispatcher = $dispatcher; - $this->logger = $logger; - $this->jobList = $jobList; $this->setInterval(15 * 60); } @@ -58,7 +36,7 @@ class BackgroundRepair extends TimedJob { * @throws \Exception * @throws \OC\NeedsUpdateException */ - protected function run($argument) { + protected function run($argument): void { if (!isset($argument['app']) || !isset($argument['step'])) { // remove the job - we can never execute it $this->jobList->remove($this, $this->argument); @@ -75,9 +53,9 @@ class BackgroundRepair extends TimedJob { } $step = $argument['step']; - $repair = new Repair([], $this->dispatcher, \OC::$server->get(LoggerInterface::class)); + $this->repair->setRepairSteps([]); try { - $repair->addStep($step); + $this->repair->addStep($step); } catch (\Exception $ex) { $this->logger->error($ex->getMessage(), [ 'app' => 'migration', @@ -90,7 +68,7 @@ class BackgroundRepair extends TimedJob { } // execute the repair step - $repair->run(); + $this->repair->run(); // remove the job once executed successfully $this->jobList->remove($this, $this->argument); @@ -101,7 +79,7 @@ class BackgroundRepair extends TimedJob { * @param $app * @throws NeedsUpdateException */ - protected function loadApp($app) { + protected function loadApp($app): void { OC_App::loadApp($app); } } diff --git a/lib/private/Migration/ConsoleOutput.php b/lib/private/Migration/ConsoleOutput.php index 9e3396f2a75..7ccc4e7825a 100644 --- a/lib/private/Migration/ConsoleOutput.php +++ b/lib/private/Migration/ConsoleOutput.php @@ -1,23 +1,8 @@ <?php /** - * @copyright Copyright (c) 2017, ownCloud GmbH - * - * @author Morris Jobke <hey@morrisjobke.de> - * - * @license AGPL-3.0 - * - * This code is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License, version 3, - * as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License, version 3, - * along with this program. If not, see <http://www.gnu.org/licenses/> - * + * SPDX-FileCopyrightText: 2017-2024 Nextcloud GmbH and Nextcloud contributors + * SPDX-FileCopyrightText: 2015 ownCloud GmbH + * SPDX-License-Identifier: AGPL-3.0-only */ namespace OC\Migration; @@ -34,34 +19,35 @@ use Symfony\Component\Console\Output\OutputInterface; * @package OC\Migration */ class ConsoleOutput implements IOutput { - /** @var OutputInterface */ - private $output; + private ?ProgressBar $progressBar = null; - /** @var ProgressBar */ - private $progressBar; + public function __construct( + private OutputInterface $output, + ) { + } - public function __construct(OutputInterface $output) { - $this->output = $output; + public function debug(string $message): void { + $this->output->writeln($message, OutputInterface::VERBOSITY_VERBOSE); } /** * @param string $message */ - public function info($message) { + public function info($message): void { $this->output->writeln("<info>$message</info>"); } /** * @param string $message */ - public function warning($message) { + public function warning($message): void { $this->output->writeln("<comment>$message</comment>"); } /** * @param int $max */ - public function startProgress($max = 0) { + public function startProgress($max = 0): void { if (!is_null($this->progressBar)) { $this->progressBar->finish(); } @@ -73,7 +59,7 @@ class ConsoleOutput implements IOutput { * @param int $step * @param string $description */ - public function advance($step = 1, $description = '') { + public function advance($step = 1, $description = ''): void { if (is_null($this->progressBar)) { $this->progressBar = new ProgressBar($this->output); $this->progressBar->start(); @@ -84,7 +70,7 @@ class ConsoleOutput implements IOutput { } } - public function finishProgress() { + public function finishProgress(): void { if (is_null($this->progressBar)) { return; } diff --git a/lib/private/Migration/SimpleOutput.php b/lib/private/Migration/SimpleOutput.php index f97bcb767f8..31420d49932 100644 --- a/lib/private/Migration/SimpleOutput.php +++ b/lib/private/Migration/SimpleOutput.php @@ -1,23 +1,8 @@ <?php /** - * @copyright Copyright (c) 2017, ownCloud GmbH - * - * @author Joas Schilling <coding@schilljs.com> - * - * @license AGPL-3.0 - * - * This code is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License, version 3, - * as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License, version 3, - * along with this program. If not, see <http://www.gnu.org/licenses/> - * + * SPDX-FileCopyrightText: 2017-2024 Nextcloud GmbH and Nextcloud contributors + * SPDX-FileCopyrightText: 2015 ownCloud GmbH + * SPDX-License-Identifier: AGPL-3.0-only */ namespace OC\Migration; @@ -33,19 +18,21 @@ use Psr\Log\LoggerInterface; * @package OC\Migration */ class SimpleOutput implements IOutput { - private LoggerInterface $logger; - private $appName; + public function __construct( + private LoggerInterface $logger, + private $appName, + ) { + } - public function __construct(LoggerInterface $logger, $appName) { - $this->logger = $logger; - $this->appName = $appName; + public function debug(string $message): void { + $this->logger->debug($message, ['app' => $this->appName]); } /** * @param string $message * @since 9.1.0 */ - public function info($message) { + public function info($message): void { $this->logger->info($message, ['app' => $this->appName]); } @@ -53,7 +40,7 @@ class SimpleOutput implements IOutput { * @param string $message * @since 9.1.0 */ - public function warning($message) { + public function warning($message): void { $this->logger->warning($message, ['app' => $this->appName]); } @@ -61,7 +48,7 @@ class SimpleOutput implements IOutput { * @param int $max * @since 9.1.0 */ - public function startProgress($max = 0) { + public function startProgress($max = 0): void { } /** @@ -69,12 +56,12 @@ class SimpleOutput implements IOutput { * @param string $description * @since 9.1.0 */ - public function advance($step = 1, $description = '') { + public function advance($step = 1, $description = ''): void { } /** * @since 9.1.0 */ - public function finishProgress() { + public function finishProgress(): void { } } diff --git a/lib/private/NaturalSort.php b/lib/private/NaturalSort.php index 976db4e7803..120e05a8eb2 100644 --- a/lib/private/NaturalSort.php +++ b/lib/private/NaturalSort.php @@ -1,28 +1,9 @@ <?php + /** - * @copyright Copyright (c) 2016, ownCloud, Inc. - * - * @author Arthur Schiwon <blizzz@arthur-schiwon.de> - * @author AW-UC <git@a-wesemann.de> - * @author Christoph Wurst <christoph@winzerhof-wurst.at> - * @author Lukas Reschke <lukas@statuscode.ch> - * @author Robin McCorkell <robin@mccorkell.me.uk> - * @author Vincent Petry <vincent@nextcloud.com> - * - * @license AGPL-3.0 - * - * This code is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License, version 3, - * as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License, version 3, - * along with this program. If not, see <http://www.gnu.org/licenses/> - * + * SPDX-FileCopyrightText: 2016-2024 Nextcloud GmbH and Nextcloud contributors + * SPDX-FileCopyrightText: 2016 ownCloud, Inc. + * SPDX-License-Identifier: AGPL-3.0-only */ namespace OC; diff --git a/lib/private/NaturalSort_DefaultCollator.php b/lib/private/NaturalSort_DefaultCollator.php index 35bed8a37c7..d688a34133a 100644 --- a/lib/private/NaturalSort_DefaultCollator.php +++ b/lib/private/NaturalSort_DefaultCollator.php @@ -1,25 +1,9 @@ <?php + /** - * @copyright Copyright (c) 2016, ownCloud, Inc. - * - * @author AW-UC <git@a-wesemann.de> - * @author Joas Schilling <coding@schilljs.com> - * @author Morris Jobke <hey@morrisjobke.de> - * - * @license AGPL-3.0 - * - * This code is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License, version 3, - * as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License, version 3, - * along with this program. If not, see <http://www.gnu.org/licenses/> - * + * SPDX-FileCopyrightText: 2016-2024 Nextcloud GmbH and Nextcloud contributors + * SPDX-FileCopyrightText: 2016 ownCloud, Inc. + * SPDX-License-Identifier: AGPL-3.0-only */ namespace OC; diff --git a/lib/private/NavigationManager.php b/lib/private/NavigationManager.php index 56f55e80331..05a3a9c85fe 100644 --- a/lib/private/NavigationManager.php +++ b/lib/private/NavigationManager.php @@ -1,33 +1,9 @@ <?php + /** - * @copyright Copyright (c) 2016, ownCloud GmbH - * - * @author Arthur Schiwon <blizzz@arthur-schiwon.de> - * @author Bart Visscher <bartv@thisnet.nl> - * @author Christoph Wurst <christoph@winzerhof-wurst.at> - * @author Daniel Kesselberg <mail@danielkesselberg.de> - * @author Georg Ehrke <oc.list@georgehrke.com> - * @author Joas Schilling <coding@schilljs.com> - * @author John Molakvoæ <skjnldsv@protonmail.com> - * @author Julius Härtl <jus@bitgrid.net> - * @author Lukas Reschke <lukas@statuscode.ch> - * @author Morris Jobke <hey@morrisjobke.de> - * @author Thomas Müller <thomas.mueller@tmit.eu> - * - * @license AGPL-3.0 - * - * This code is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License, version 3, - * as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License, version 3, - * along with this program. If not, see <http://www.gnu.org/licenses/> - * + * SPDX-FileCopyrightText: 2016-2024 Nextcloud GmbH and Nextcloud contributors + * SPDX-FileCopyrightText: 2016 ownCloud GmbH + * SPDX-License-Identifier: AGPL-3.0-only */ namespace OC; @@ -65,19 +41,25 @@ class NavigationManager implements INavigationManager { private $groupManager; /** @var IConfig */ private $config; + /** The default app for the current user (cached for the `add` function) */ + private ?string $defaultApp; + /** User defined app order (cached for the `add` function) */ + private array $customAppOrder; public function __construct(IAppManager $appManager, - IURLGenerator $urlGenerator, - IFactory $l10nFac, - IUserSession $userSession, - IGroupManager $groupManager, - IConfig $config) { + IURLGenerator $urlGenerator, + IFactory $l10nFac, + IUserSession $userSession, + IGroupManager $groupManager, + IConfig $config) { $this->appManager = $appManager; $this->urlGenerator = $urlGenerator; $this->l10nFac = $l10nFac; $this->userSession = $userSession; $this->groupManager = $groupManager; $this->config = $config; + + $this->defaultApp = null; } /** @@ -88,8 +70,12 @@ class NavigationManager implements INavigationManager { $this->closureEntries[] = $entry; return; } + $this->init(); + + $id = $entry['id']; $entry['active'] = false; + $entry['unread'] = $this->unreadCounters[$id] ?? 0; if (!isset($entry['icon'])) { $entry['icon'] = ''; } @@ -100,8 +86,17 @@ class NavigationManager implements INavigationManager { $entry['type'] = 'link'; } - $id = $entry['id']; - $entry['unread'] = isset($this->unreadCounters[$id]) ? $this->unreadCounters[$id] : 0; + if ($entry['type'] === 'link') { + // app might not be set when using closures, in this case try to fallback to ID + if (!isset($entry['app']) && $this->appManager->isEnabledForUser($id)) { + $entry['app'] = $id; + } + + // This is the default app that will always be shown first + $entry['default'] = ($entry['app'] ?? false) === $this->defaultApp; + // Set order from user defined app order + $entry['order'] = $this->customAppOrder[$id]['order'] ?? $entry['order'] ?? 100; + } $this->entries[$id] = $entry; } @@ -123,26 +118,44 @@ class NavigationManager implements INavigationManager { }); } - return $this->proceedNavigation($result); + return $this->proceedNavigation($result, $type); } /** - * Sort navigation entries by order, name and set active flag + * Sort navigation entries default app is always sorted first, then by order, name and set active flag * * @param array $list * @return array */ - private function proceedNavigation(array $list): array { + private function proceedNavigation(array $list, string $type): array { uasort($list, function ($a, $b) { - if (isset($a['order']) && isset($b['order'])) { + if (($a['default'] ?? false) xor ($b['default'] ?? false)) { + // Always sort the default app first + return ($a['default'] ?? false) ? -1 : 1; + } elseif (isset($a['order']) && isset($b['order'])) { + // Sort by order return ($a['order'] < $b['order']) ? -1 : 1; } elseif (isset($a['order']) || isset($b['order'])) { + // Sort the one that has an order property first return isset($a['order']) ? -1 : 1; } else { + // Sort by name otherwise return ($a['name'] < $b['name']) ? -1 : 1; } }); + if ($type === 'all' || $type === 'link') { + // There might be the case that no default app was set, in this case the first app is the default app. + // Otherwise the default app is already the ordered first, so setting the default prop will make no difference. + foreach ($list as $index => &$navEntry) { + if ($navEntry['type'] === 'link') { + $navEntry['default'] = true; + break; + } + } + unset($navEntry); + } + $activeApp = $this->getActiveEntry(); if ($activeApp !== null) { foreach ($list as $index => &$navEntry) { @@ -171,8 +184,8 @@ class NavigationManager implements INavigationManager { /** * @inheritDoc */ - public function setActiveEntry($id) { - $this->activeEntry = $id; + public function setActiveEntry($appId) { + $this->activeEntry = $appId; } /** @@ -195,12 +208,26 @@ class NavigationManager implements INavigationManager { 'id' => 'help', 'order' => 99998, 'href' => $this->urlGenerator->linkToRoute('settings.Help.help'), - 'name' => $l->t('Help'), + 'name' => $l->t('Help & privacy'), 'icon' => $this->urlGenerator->imagePath('settings', 'help.svg'), ]); } + $this->defaultApp = $this->appManager->getDefaultAppForUser($this->userSession->getUser(), false); + if ($this->userSession->isLoggedIn()) { + // Profile + $this->add([ + 'type' => 'settings', + 'id' => 'profile', + 'order' => 1, + 'href' => $this->urlGenerator->linkToRoute( + 'core.ProfilePage.index', + ['targetUserId' => $this->userSession->getUser()->getUID()], + ), + 'name' => $l->t('View profile'), + ]); + // Accessibility settings if ($this->appManager->isEnabledForUser('theming', $this->userSession->getUser())) { $this->add([ @@ -212,6 +239,7 @@ class NavigationManager implements INavigationManager { 'icon' => $this->urlGenerator->imagePath('theming', 'accessibility-dark.svg'), ]); } + if ($this->isAdmin()) { // App management $this->add([ @@ -274,20 +302,19 @@ class NavigationManager implements INavigationManager { 'id' => 'core_users', 'order' => 6, 'href' => $this->urlGenerator->linkToRoute('settings.Users.usersList'), - 'name' => $l->t('Users'), + 'name' => $l->t('Accounts'), 'icon' => $this->urlGenerator->imagePath('settings', 'users.svg'), ]); } } - if ($this->appManager === 'null') { - return; - } - if ($this->userSession->isLoggedIn()) { - $apps = $this->appManager->getEnabledAppsForUser($this->userSession->getUser()); + $user = $this->userSession->getUser(); + $apps = $this->appManager->getEnabledAppsForUser($user); + $this->customAppOrder = json_decode($this->config->getUserValue($user->getUID(), 'core', 'apporder', '[]'), true, flags:JSON_THROW_ON_ERROR); } else { $apps = $this->appManager->getInstalledApps(); + $this->customAppOrder = []; } foreach ($apps as $app) { @@ -309,36 +336,48 @@ class NavigationManager implements INavigationManager { if (!isset($nav['route']) && $nav['type'] !== 'settings') { continue; } - $role = isset($nav['@attributes']['role']) ? $nav['@attributes']['role'] : 'all'; + $role = $nav['@attributes']['role'] ?? 'all'; if ($role === 'admin' && !$this->isAdmin()) { continue; } $l = $this->l10nFac->get($app); $id = $nav['id'] ?? $app . ($key === 0 ? '' : $key); - $order = isset($nav['order']) ? $nav['order'] : 100; + $order = $nav['order'] ?? 100; $type = $nav['type']; $route = !empty($nav['route']) ? $this->urlGenerator->linkToRoute($nav['route']) : ''; - $icon = isset($nav['icon']) ? $nav['icon'] : 'app.svg'; - foreach ([$icon, "$app.svg"] as $i) { + $icon = $nav['icon'] ?? null; + if ($icon !== null) { try { - $icon = $this->urlGenerator->imagePath($app, $i); - break; + $icon = $this->urlGenerator->imagePath($app, $icon); } catch (\RuntimeException $ex) { - // no icon? - ignore it then + // ignore } } if ($icon === null) { + $icon = $this->appManager->getAppIcon($app); + } + if ($icon === null) { $icon = $this->urlGenerator->imagePath('core', 'default-app-icon'); } - $this->add([ + $this->add(array_merge([ + // Navigation id 'id' => $id, + // Order where this entry should be shown 'order' => $order, + // Target of the navigation entry 'href' => $route, + // The icon used for the naviation entry 'icon' => $icon, + // Type of the navigation entry ('link' vs 'settings') 'type' => $type, + // Localized name of the navigation entry 'name' => $l->t($nav['name']), - ]); + ], $type === 'link' ? [ + // App that registered this navigation entry (not necessarly the same as the id) + 'app' => $app, + ] : [] + )); } } } diff --git a/lib/private/NeedsUpdateException.php b/lib/private/NeedsUpdateException.php index fcf63a6b373..da467d96299 100644 --- a/lib/private/NeedsUpdateException.php +++ b/lib/private/NeedsUpdateException.php @@ -1,23 +1,9 @@ <?php + /** - * @copyright Copyright (c) 2016, ownCloud, Inc. - * - * @author Robin Appelman <robin@icewind.nl> - * - * @license AGPL-3.0 - * - * This code is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License, version 3, - * as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License, version 3, - * along with this program. If not, see <http://www.gnu.org/licenses/> - * + * SPDX-FileCopyrightText: 2016-2024 Nextcloud GmbH and Nextcloud contributors + * SPDX-FileCopyrightText: 2016 ownCloud, Inc. + * SPDX-License-Identifier: AGPL-3.0-only */ namespace OC; diff --git a/lib/private/Net/HostnameClassifier.php b/lib/private/Net/HostnameClassifier.php index 626aa47083e..e0f1435d6f1 100644 --- a/lib/private/Net/HostnameClassifier.php +++ b/lib/private/Net/HostnameClassifier.php @@ -2,25 +2,9 @@ declare(strict_types=1); -/* - * @copyright 2022 Christoph Wurst <christoph@winzerhof-wurst.at> - * - * @author 2022 Christoph Wurst <christoph@winzerhof-wurst.at> - * - * @license GNU AGPL version 3 or any later version - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as - * published by the Free Software Foundation, either version 3 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. +/** + * SPDX-FileCopyrightText: 2022 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later */ namespace OC\Net; @@ -52,10 +36,6 @@ class HostnameClassifier { * Check host identifier for local hostname * * IP addresses are not considered local. Use the IpAddressClassifier for those. - * - * @param string $hostname - * - * @return bool */ public function isLocalHostname(string $hostname): bool { // Disallow local network top-level domains from RFC 6762 diff --git a/lib/private/Net/IpAddressClassifier.php b/lib/private/Net/IpAddressClassifier.php index d4698864ec8..5f41f4a086a 100644 --- a/lib/private/Net/IpAddressClassifier.php +++ b/lib/private/Net/IpAddressClassifier.php @@ -2,25 +2,9 @@ declare(strict_types=1); -/* - * @copyright 2022 Christoph Wurst <christoph@winzerhof-wurst.at> - * - * @author 2022 Christoph Wurst <christoph@winzerhof-wurst.at> - * - * @license GNU AGPL version 3 or any later version - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as - * published by the Free Software Foundation, either version 3 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. +/** + * SPDX-FileCopyrightText: 2022 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later */ namespace OC\Net; @@ -46,10 +30,6 @@ class IpAddressClassifier { * Check host identifier for local IPv4 and IPv6 address ranges * * Hostnames are not considered local. Use the HostnameClassifier for those. - * - * @param string $ip - * - * @return bool */ public function isLocalAddress(string $ip): bool { $parsedIp = Factory::parseAddressString( diff --git a/lib/private/NotSquareException.php b/lib/private/NotSquareException.php index 40957e7d924..7be7ad09521 100644 --- a/lib/private/NotSquareException.php +++ b/lib/private/NotSquareException.php @@ -1,23 +1,9 @@ <?php + /** - * @copyright Copyright (c) 2016, ownCloud, Inc. - * - * @author Christopher Schäpers <kondou@ts.unde.re> - * - * @license AGPL-3.0 - * - * This code is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License, version 3, - * as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License, version 3, - * along with this program. If not, see <http://www.gnu.org/licenses/> - * + * SPDX-FileCopyrightText: 2016-2024 Nextcloud GmbH and Nextcloud contributors + * SPDX-FileCopyrightText: 2016 ownCloud, Inc. + * SPDX-License-Identifier: AGPL-3.0-only */ namespace OC; diff --git a/lib/private/Notification/Action.php b/lib/private/Notification/Action.php index ff9cf9e38f5..e2a75bea030 100644 --- a/lib/private/Notification/Action.php +++ b/lib/private/Notification/Action.php @@ -1,106 +1,61 @@ <?php declare(strict_types=1); - /** - * @copyright Copyright (c) 2016, ownCloud, Inc. - * - * @author Joas Schilling <coding@schilljs.com> - * - * @license AGPL-3.0 - * - * This code is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License, version 3, - * as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License, version 3, - * along with this program. If not, see <http://www.gnu.org/licenses/> - * + * SPDX-FileCopyrightText: 2019-2024 Nextcloud GmbH and Nextcloud contributors + * SPDX-FileCopyrightText: 2016 ownCloud, Inc. + * SPDX-License-Identifier: AGPL-3.0-only */ namespace OC\Notification; use OCP\Notification\IAction; +use OCP\Notification\InvalidValueException; class Action implements IAction { - /** @var string */ - protected $label; - - /** @var string */ - protected $labelParsed; - - /** @var string */ - protected $link; - - /** @var string */ - protected $requestType; - - /** @var string */ - protected $icon; - - /** @var bool */ - protected $primary; - - public function __construct() { - $this->label = ''; - $this->labelParsed = ''; - $this->link = ''; - $this->requestType = ''; - $this->primary = false; - } + protected string $label = ''; + protected string $labelParsed = ''; + protected string $link = ''; + protected string $requestType = ''; + protected bool $primary = false; /** - * @param string $label - * @return $this - * @throws \InvalidArgumentException if the label is invalid - * @since 8.2.0 + * {@inheritDoc} */ public function setLabel(string $label): IAction { if ($label === '' || isset($label[32])) { - throw new \InvalidArgumentException('The given label is invalid'); + throw new InvalidValueException('label'); } $this->label = $label; return $this; } /** - * @return string - * @since 8.2.0 + * {@inheritDoc} */ public function getLabel(): string { return $this->label; } /** - * @param string $label - * @return $this - * @throws \InvalidArgumentException if the label is invalid - * @since 8.2.0 + * {@inheritDoc} */ public function setParsedLabel(string $label): IAction { if ($label === '') { - throw new \InvalidArgumentException('The given parsed label is invalid'); + throw new InvalidValueException('parsedLabel'); } $this->labelParsed = $label; return $this; } /** - * @return string - * @since 8.2.0 + * {@inheritDoc} */ public function getParsedLabel(): string { return $this->labelParsed; } /** - * @param $primary bool - * @return $this - * @since 9.0.0 + * {@inheritDoc} */ public function setPrimary(bool $primary): IAction { $this->primary = $primary; @@ -108,23 +63,18 @@ class Action implements IAction { } /** - * @return bool - * @since 9.0.0 + * {@inheritDoc} */ public function isPrimary(): bool { return $this->primary; } /** - * @param string $link - * @param string $requestType - * @return $this - * @throws \InvalidArgumentException if the link is invalid - * @since 8.2.0 + * {@inheritDoc} */ public function setLink(string $link, string $requestType): IAction { if ($link === '' || isset($link[256])) { - throw new \InvalidArgumentException('The given link is invalid'); + throw new InvalidValueException('link'); } if (!in_array($requestType, [ self::TYPE_GET, @@ -133,7 +83,7 @@ class Action implements IAction { self::TYPE_DELETE, self::TYPE_WEB, ], true)) { - throw new \InvalidArgumentException('The given request type is invalid'); + throw new InvalidValueException('requestType'); } $this->link = $link; $this->requestType = $requestType; @@ -141,30 +91,28 @@ class Action implements IAction { } /** - * @return string - * @since 8.2.0 + * {@inheritDoc} */ public function getLink(): string { return $this->link; } /** - * @return string - * @since 8.2.0 + * {@inheritDoc} */ public function getRequestType(): string { return $this->requestType; } /** - * @return bool + * {@inheritDoc} */ public function isValid(): bool { return $this->label !== '' && $this->link !== ''; } /** - * @return bool + * {@inheritDoc} */ public function isValidParsed(): bool { return $this->labelParsed !== '' && $this->link !== ''; diff --git a/lib/private/Notification/Manager.php b/lib/private/Notification/Manager.php index 3d77f643d93..7d042e6f8d8 100644 --- a/lib/private/Notification/Manager.php +++ b/lib/private/Notification/Manager.php @@ -1,28 +1,10 @@ <?php declare(strict_types=1); - /** - * @copyright Copyright (c) 2016, ownCloud, Inc. - * - * @author Joas Schilling <coding@schilljs.com> - * @author Morris Jobke <hey@morrisjobke.de> - * @author Roeland Jago Douma <roeland@famdouma.nl> - * - * @license AGPL-3.0 - * - * This code is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License, version 3, - * as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License, version 3, - * along with this program. If not, see <http://www.gnu.org/licenses/> - * + * SPDX-FileCopyrightText: 2016-2024 Nextcloud GmbH and Nextcloud contributors + * SPDX-FileCopyrightText: 2016 ownCloud, Inc. + * SPDX-License-Identifier: AGPL-3.0-only */ namespace OC\Notification; @@ -35,56 +17,46 @@ use OCP\Notification\IApp; use OCP\Notification\IDeferrableApp; use OCP\Notification\IDismissableNotifier; use OCP\Notification\IManager; +use OCP\Notification\IncompleteNotificationException; +use OCP\Notification\IncompleteParsedNotificationException; use OCP\Notification\INotification; use OCP\Notification\INotifier; +use OCP\Notification\UnknownNotificationException; use OCP\RichObjectStrings\IValidator; use OCP\Support\Subscription\IRegistry; use Psr\Container\ContainerExceptionInterface; use Psr\Log\LoggerInterface; class Manager implements IManager { - /** @var IValidator */ - protected $validator; - /** @var IUserManager */ - private $userManager; /** @var ICache */ - protected $cache; - /** @var IRegistry */ - protected $subscription; - /** @var LoggerInterface */ - protected $logger; - /** @var Coordinator */ - private $coordinator; + protected ICache $cache; /** @var IApp[] */ - protected $apps; + protected array $apps; /** @var string[] */ - protected $appClasses; + protected array $appClasses; /** @var INotifier[] */ - protected $notifiers; + protected array $notifiers; /** @var string[] */ - protected $notifierClasses; + protected array $notifierClasses; /** @var bool */ - protected $preparingPushNotification; + protected bool $preparingPushNotification; /** @var bool */ - protected $deferPushing; + protected bool $deferPushing; /** @var bool */ - private $parsedRegistrationContext; - - public function __construct(IValidator $validator, - IUserManager $userManager, - ICacheFactory $cacheFactory, - IRegistry $subscription, - LoggerInterface $logger, - Coordinator $coordinator) { - $this->validator = $validator; - $this->userManager = $userManager; + private bool $parsedRegistrationContext; + + public function __construct( + protected IValidator $validator, + private IUserManager $userManager, + ICacheFactory $cacheFactory, + protected IRegistry $subscription, + protected LoggerInterface $logger, + private Coordinator $coordinator, + ) { $this->cache = $cacheFactory->createDistributed('notifications'); - $this->subscription = $subscription; - $this->logger = $logger; - $this->coordinator = $coordinator; $this->apps = []; $this->notifiers = []; @@ -111,7 +83,7 @@ class Manager implements IManager { * @deprecated 17.0.0 use registerNotifierService instead. * @since 8.2.0 - Parameter $info was added in 9.0.0 */ - public function registerNotifier(\Closure $service, \Closure $info) { + public function registerNotifier(\Closure $service, \Closure $info): void { $infoData = $info(); $exception = new \InvalidArgumentException( 'Notifier ' . $infoData['name'] . ' (id: ' . $infoData['id'] . ') is not considered because it is using the old way to register.' @@ -313,13 +285,11 @@ class Manager implements IManager { } /** - * @param INotification $notification - * @throws \InvalidArgumentException When the notification is not valid - * @since 8.2.0 + * {@inheritDoc} */ public function notify(INotification $notification): void { if (!$notification->isValid()) { - throw new \InvalidArgumentException('The given notification is invalid'); + throw new IncompleteNotificationException('The given notification is invalid'); } $apps = $this->getApps(); @@ -327,7 +297,11 @@ class Manager implements IManager { foreach ($apps as $app) { try { $app->notify($notification); + } catch (IncompleteNotificationException) { } catch (\InvalidArgumentException $e) { + // todo 33.0.0 Log as warning + // todo 39.0.0 Log as error + $this->logger->debug(get_class($app) . '::notify() threw \InvalidArgumentException which is deprecated. Throw \OCP\Notification\IncompleteNotificationException when the notification is incomplete for your app and otherwise handle all \InvalidArgumentException yourself.'); } } } @@ -353,12 +327,7 @@ class Manager implements IManager { } /** - * @param INotification $notification - * @param string $languageCode The code of the language that should be used to prepare the notification - * @return INotification - * @throws \InvalidArgumentException When the notification was not prepared by a notifier - * @throws AlreadyProcessedException When the notification is not needed anymore and should be deleted - * @since 8.2.0 + * {@inheritDoc} */ public function prepare(INotification $notification, string $languageCode): INotification { $notifiers = $this->getNotifiers(); @@ -366,20 +335,44 @@ class Manager implements IManager { foreach ($notifiers as $notifier) { try { $notification = $notifier->prepare($notification, $languageCode); - } catch (\InvalidArgumentException $e) { - continue; } catch (AlreadyProcessedException $e) { $this->markProcessed($notification); - throw new \InvalidArgumentException('The given notification has been processed'); + throw $e; + } catch (UnknownNotificationException) { + continue; + } catch (\InvalidArgumentException $e) { + // todo 33.0.0 Log as warning + // todo 39.0.0 Log as error + $this->logger->debug(get_class($notifier) . '::prepare() threw \InvalidArgumentException which is deprecated. Throw \OCP\Notification\UnknownNotificationException when the notification is not known to your notifier and otherwise handle all \InvalidArgumentException yourself.'); + continue; } if (!$notification->isValidParsed()) { - throw new \InvalidArgumentException('The given notification has not been handled'); + $this->logger->info('Notification was claimed to be parsed, but was not fully parsed by ' . get_class($notifier) . ' [app: ' . $notification->getApp() . ', subject: ' . $notification->getSubject() . ']'); + throw new IncompleteParsedNotificationException(); } } if (!$notification->isValidParsed()) { - throw new \InvalidArgumentException('The given notification has not been handled'); + $this->logger->info('Notification was not parsed by any notifier [app: ' . $notification->getApp() . ', subject: ' . $notification->getSubject() . ']'); + throw new IncompleteParsedNotificationException(); + } + + $link = $notification->getLink(); + if ($link !== '' && !str_starts_with($link, 'http://') && !str_starts_with($link, 'https://')) { + $this->logger->warning('Link of notification is not an absolute URL and does not work in mobile and desktop clients [app: ' . $notification->getApp() . ', subject: ' . $notification->getSubject() . ']'); + } + + $icon = $notification->getIcon(); + if ($icon !== '' && !str_starts_with($icon, 'http://') && !str_starts_with($icon, 'https://')) { + $this->logger->warning('Icon of notification is not an absolute URL and does not work in mobile and desktop clients [app: ' . $notification->getApp() . ', subject: ' . $notification->getSubject() . ']'); + } + + foreach ($notification->getParsedActions() as $action) { + $link = $action->getLink(); + if ($link !== '' && !str_starts_with($link, 'http://') && !str_starts_with($link, 'https://')) { + $this->logger->warning('Link of action is not an absolute URL and does not work in mobile and desktop clients [app: ' . $notification->getApp() . ', subject: ' . $notification->getSubject() . ']'); + } } return $notification; @@ -411,6 +404,9 @@ class Manager implements IManager { return $count; } + /** + * {@inheritDoc} + */ public function dismissNotification(INotification $notification): void { $notifiers = $this->getNotifiers(); @@ -418,7 +414,12 @@ class Manager implements IManager { if ($notifier instanceof IDismissableNotifier) { try { $notifier->dismissNotification($notification); + } catch (UnknownNotificationException) { + continue; } catch (\InvalidArgumentException $e) { + // todo 33.0.0 Log as warning + // todo 39.0.0 Log as error + $this->logger->debug(get_class($notifier) . '::dismissNotification() threw \InvalidArgumentException which is deprecated. Throw \OCP\Notification\UnknownNotificationException when the notification is not known to your notifier and otherwise handle all \InvalidArgumentException yourself.'); continue; } } diff --git a/lib/private/Notification/Notification.php b/lib/private/Notification/Notification.php index 2291c4ae34f..19c836c2044 100644 --- a/lib/private/Notification/Notification.php +++ b/lib/private/Notification/Notification.php @@ -1,239 +1,139 @@ <?php declare(strict_types=1); - /** - * @copyright Copyright (c) 2016, ownCloud, Inc. - * - * @author Joas Schilling <coding@schilljs.com> - * @author Morris Jobke <hey@morrisjobke.de> - * @author Roeland Jago Douma <roeland@famdouma.nl> - * - * @license AGPL-3.0 - * - * This code is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License, version 3, - * as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License, version 3, - * along with this program. If not, see <http://www.gnu.org/licenses/> - * + * SPDX-FileCopyrightText: 2016-2024 Nextcloud GmbH and Nextcloud contributors + * SPDX-FileCopyrightText: 2016 ownCloud, Inc. + * SPDX-License-Identifier: AGPL-3.0-only */ namespace OC\Notification; use OCP\Notification\IAction; use OCP\Notification\INotification; +use OCP\Notification\InvalidValueException; use OCP\RichObjectStrings\InvalidObjectExeption; use OCP\RichObjectStrings\IValidator; class Notification implements INotification { - /** @var IValidator */ - protected $richValidator; - - /** @var string */ - protected $app; - - /** @var string */ - protected $user; - - /** @var \DateTime */ - protected $dateTime; - - /** @var string */ - protected $objectType; - - /** @var string */ - protected $objectId; - - /** @var string */ - protected $subject; - - /** @var array */ - protected $subjectParameters; - - /** @var string */ - protected $subjectParsed; - - /** @var string */ - protected $subjectRich; - - /** @var array */ - protected $subjectRichParameters; - - /** @var string */ - protected $message; - - /** @var array */ - protected $messageParameters; - - /** @var string */ - protected $messageParsed; - - /** @var string */ - protected $messageRich; - - /** @var array */ - protected $messageRichParameters; - - /** @var string */ - protected $link; - - /** @var string */ - protected $icon; - - /** @var array */ - protected $actions; - - /** @var array */ - protected $actionsParsed; - - /** @var bool */ - protected $hasPrimaryAction; - - /** @var bool */ - protected $hasPrimaryParsedAction; - - public function __construct(IValidator $richValidator) { - $this->richValidator = $richValidator; - $this->app = ''; - $this->user = ''; + protected string $app = ''; + protected string $user = ''; + protected \DateTime $dateTime; + protected string $objectType = ''; + protected string $objectId = ''; + protected string $subject = ''; + protected array $subjectParameters = []; + protected string $subjectParsed = ''; + protected string $subjectRich = ''; + protected array $subjectRichParameters = []; + protected string $message = ''; + protected array $messageParameters = []; + protected string $messageParsed = ''; + protected string $messageRich = ''; + protected array $messageRichParameters = []; + protected string $link = ''; + protected string $icon = ''; + protected array $actions = []; + protected array $actionsParsed = []; + protected bool $hasPrimaryAction = false; + protected bool $hasPrimaryParsedAction = false; + + public function __construct( + protected IValidator $richValidator, + ) { $this->dateTime = new \DateTime(); $this->dateTime->setTimestamp(0); - $this->objectType = ''; - $this->objectId = ''; - $this->subject = ''; - $this->subjectParameters = []; - $this->subjectParsed = ''; - $this->subjectRich = ''; - $this->subjectRichParameters = []; - $this->message = ''; - $this->messageParameters = []; - $this->messageParsed = ''; - $this->messageRich = ''; - $this->messageRichParameters = []; - $this->link = ''; - $this->icon = ''; - $this->actions = []; - $this->actionsParsed = []; - } - - /** - * @param string $app - * @return $this - * @throws \InvalidArgumentException if the app id is invalid - * @since 8.2.0 + } + + /** + * {@inheritDoc} */ public function setApp(string $app): INotification { if ($app === '' || isset($app[32])) { - throw new \InvalidArgumentException('The given app name is invalid'); + throw new InvalidValueException('app'); } $this->app = $app; return $this; } /** - * @return string - * @since 8.2.0 + * {@inheritDoc} */ public function getApp(): string { return $this->app; } /** - * @param string $user - * @return $this - * @throws \InvalidArgumentException if the user id is invalid - * @since 8.2.0 + * {@inheritDoc} */ public function setUser(string $user): INotification { if ($user === '' || isset($user[64])) { - throw new \InvalidArgumentException('The given user id is invalid'); + throw new InvalidValueException('user'); } $this->user = $user; return $this; } /** - * @return string - * @since 8.2.0 + * {@inheritDoc} */ public function getUser(): string { return $this->user; } /** - * @param \DateTime $dateTime - * @return $this - * @throws \InvalidArgumentException if the $dateTime is invalid - * @since 9.0.0 + * {@inheritDoc} */ public function setDateTime(\DateTime $dateTime): INotification { if ($dateTime->getTimestamp() === 0) { - throw new \InvalidArgumentException('The given date time is invalid'); + throw new InvalidValueException('dateTime'); } $this->dateTime = $dateTime; return $this; } /** - * @return \DateTime - * @since 9.0.0 + * {@inheritDoc} */ public function getDateTime(): \DateTime { return $this->dateTime; } /** - * @param string $type - * @param string $id - * @return $this - * @throws \InvalidArgumentException if the object type or id is invalid - * @since 8.2.0 - 9.0.0: Type of $id changed to string + * {@inheritDoc} */ public function setObject(string $type, string $id): INotification { if ($type === '' || isset($type[64])) { - throw new \InvalidArgumentException('The given object type is invalid'); + throw new InvalidValueException('objectType'); } $this->objectType = $type; if ($id === '' || isset($id[64])) { - throw new \InvalidArgumentException('The given object id is invalid'); + throw new InvalidValueException('objectId'); } $this->objectId = $id; return $this; } /** - * @return string - * @since 8.2.0 + * {@inheritDoc} */ public function getObjectType(): string { return $this->objectType; } /** - * @return string - * @since 8.2.0 - 9.0.0: Return type changed to string + * {@inheritDoc} */ public function getObjectId(): string { return $this->objectId; } /** - * @param string $subject - * @param array $parameters - * @return $this - * @throws \InvalidArgumentException if the subject or parameters are invalid - * @since 8.2.0 + * {@inheritDoc} */ public function setSubject(string $subject, array $parameters = []): INotification { if ($subject === '' || isset($subject[64])) { - throw new \InvalidArgumentException('The given subject is invalid'); + throw new InvalidValueException('subject'); } $this->subject = $subject; @@ -243,60 +143,54 @@ class Notification implements INotification { } /** - * @return string - * @since 8.2.0 + * {@inheritDoc} */ public function getSubject(): string { return $this->subject; } /** - * @return array - * @since 8.2.0 + * {@inheritDoc} */ public function getSubjectParameters(): array { return $this->subjectParameters; } /** - * @param string $subject - * @return $this - * @throws \InvalidArgumentException if the subject is invalid - * @since 8.2.0 + * {@inheritDoc} */ public function setParsedSubject(string $subject): INotification { if ($subject === '') { - throw new \InvalidArgumentException('The given parsed subject is invalid'); + throw new InvalidValueException('parsedSubject'); } $this->subjectParsed = $subject; return $this; } /** - * @return string - * @since 8.2.0 + * {@inheritDoc} */ public function getParsedSubject(): string { return $this->subjectParsed; } /** - * @param string $subject - * @param array $parameters - * @return $this - * @throws \InvalidArgumentException if the subject or parameters are invalid - * @since 11.0.0 + * {@inheritDoc} */ public function setRichSubject(string $subject, array $parameters = []): INotification { if ($subject === '') { - throw new \InvalidArgumentException('The given parsed subject is invalid'); + throw new InvalidValueException('richSubject'); } $this->subjectRich = $subject; $this->subjectRichParameters = $parameters; if ($this->subjectParsed === '') { - $this->subjectParsed = $this->richToParsed($subject, $parameters); + try { + $this->subjectParsed = $this->richToParsed($subject, $parameters); + } catch (\InvalidArgumentException $e) { + throw new InvalidValueException('richSubjectParameters', $e); + } } return $this; @@ -327,31 +221,25 @@ class Notification implements INotification { } /** - * @return string - * @since 11.0.0 + * {@inheritDoc} */ public function getRichSubject(): string { return $this->subjectRich; } /** - * @return array[] - * @since 11.0.0 + * {@inheritDoc} */ public function getRichSubjectParameters(): array { return $this->subjectRichParameters; } /** - * @param string $message - * @param array $parameters - * @return $this - * @throws \InvalidArgumentException if the message or parameters are invalid - * @since 8.2.0 + * {@inheritDoc} */ public function setMessage(string $message, array $parameters = []): INotification { if ($message === '' || isset($message[64])) { - throw new \InvalidArgumentException('The given message is invalid'); + throw new InvalidValueException('message'); } $this->message = $message; @@ -361,147 +249,127 @@ class Notification implements INotification { } /** - * @return string - * @since 8.2.0 + * {@inheritDoc} */ public function getMessage(): string { return $this->message; } /** - * @return array - * @since 8.2.0 + * {@inheritDoc} */ public function getMessageParameters(): array { return $this->messageParameters; } /** - * @param string $message - * @return $this - * @throws \InvalidArgumentException if the message is invalid - * @since 8.2.0 + * {@inheritDoc} */ public function setParsedMessage(string $message): INotification { if ($message === '') { - throw new \InvalidArgumentException('The given parsed message is invalid'); + throw new InvalidValueException('parsedMessage'); } $this->messageParsed = $message; return $this; } /** - * @return string - * @since 8.2.0 + * {@inheritDoc} */ public function getParsedMessage(): string { return $this->messageParsed; } /** - * @param string $message - * @param array $parameters - * @return $this - * @throws \InvalidArgumentException if the message or parameters are invalid - * @since 11.0.0 + * {@inheritDoc} */ public function setRichMessage(string $message, array $parameters = []): INotification { if ($message === '') { - throw new \InvalidArgumentException('The given parsed message is invalid'); + throw new InvalidValueException('richMessage'); } $this->messageRich = $message; $this->messageRichParameters = $parameters; if ($this->messageParsed === '') { - $this->messageParsed = $this->richToParsed($message, $parameters); + try { + $this->messageParsed = $this->richToParsed($message, $parameters); + } catch (\InvalidArgumentException $e) { + throw new InvalidValueException('richMessageParameters', $e); + } } return $this; } /** - * @return string - * @since 11.0.0 + * {@inheritDoc} */ public function getRichMessage(): string { return $this->messageRich; } /** - * @return array[] - * @since 11.0.0 + * {@inheritDoc} */ public function getRichMessageParameters(): array { return $this->messageRichParameters; } /** - * @param string $link - * @return $this - * @throws \InvalidArgumentException if the link is invalid - * @since 8.2.0 + * {@inheritDoc} */ public function setLink(string $link): INotification { if ($link === '' || isset($link[4000])) { - throw new \InvalidArgumentException('The given link is invalid'); + throw new InvalidValueException('link'); } $this->link = $link; return $this; } /** - * @return string - * @since 8.2.0 + * {@inheritDoc} */ public function getLink(): string { return $this->link; } /** - * @param string $icon - * @return $this - * @throws \InvalidArgumentException if the icon is invalid - * @since 11.0.0 + * {@inheritDoc} */ public function setIcon(string $icon): INotification { if ($icon === '' || isset($icon[4000])) { - throw new \InvalidArgumentException('The given icon is invalid'); + throw new InvalidValueException('icon'); } $this->icon = $icon; return $this; } /** - * @return string - * @since 11.0.0 + * {@inheritDoc} */ public function getIcon(): string { return $this->icon; } /** - * @return IAction - * @since 8.2.0 + * {@inheritDoc} */ public function createAction(): IAction { return new Action(); } /** - * @param IAction $action - * @return $this - * @throws \InvalidArgumentException if the action is invalid - * @since 8.2.0 + * {@inheritDoc} */ public function addAction(IAction $action): INotification { if (!$action->isValid()) { - throw new \InvalidArgumentException('The given action is invalid'); + throw new InvalidValueException('action'); } if ($action->isPrimary()) { if ($this->hasPrimaryAction) { - throw new \InvalidArgumentException('The notification already has a primary action'); + throw new InvalidValueException('primaryAction'); } $this->hasPrimaryAction = true; @@ -512,27 +380,23 @@ class Notification implements INotification { } /** - * @return IAction[] - * @since 8.2.0 + * {@inheritDoc} */ public function getActions(): array { return $this->actions; } /** - * @param IAction $action - * @return $this - * @throws \InvalidArgumentException if the action is invalid - * @since 8.2.0 + * {@inheritDoc} */ public function addParsedAction(IAction $action): INotification { if (!$action->isValidParsed()) { - throw new \InvalidArgumentException('The given parsed action is invalid'); + throw new InvalidValueException('action'); } if ($action->isPrimary()) { if ($this->hasPrimaryParsedAction) { - throw new \InvalidArgumentException('The notification already has a primary action'); + throw new InvalidValueException('primaryAction'); } $this->hasPrimaryParsedAction = true; @@ -547,16 +411,14 @@ class Notification implements INotification { } /** - * @return IAction[] - * @since 8.2.0 + * {@inheritDoc} */ public function getParsedActions(): array { return $this->actionsParsed; } /** - * @return bool - * @since 8.2.0 + * {@inheritDoc} */ public function isValid(): bool { return @@ -567,8 +429,7 @@ class Notification implements INotification { } /** - * @return bool - * @since 8.2.0 + * {@inheritDoc} */ public function isValidParsed(): bool { if ($this->getRichSubject() !== '' || !empty($this->getRichSubjectParameters())) { diff --git a/lib/private/OCM/Model/OCMProvider.php b/lib/private/OCM/Model/OCMProvider.php new file mode 100644 index 00000000000..17a356428f7 --- /dev/null +++ b/lib/private/OCM/Model/OCMProvider.php @@ -0,0 +1,217 @@ +<?php + +declare(strict_types=1); + +/** + * SPDX-FileCopyrightText: 2023 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later + */ + +namespace OC\OCM\Model; + +use OCP\EventDispatcher\IEventDispatcher; +use OCP\OCM\Events\ResourceTypeRegisterEvent; +use OCP\OCM\Exceptions\OCMArgumentException; +use OCP\OCM\Exceptions\OCMProviderException; +use OCP\OCM\IOCMProvider; +use OCP\OCM\IOCMResource; + +/** + * @since 28.0.0 + */ +class OCMProvider implements IOCMProvider { + private bool $enabled = false; + private string $apiVersion = ''; + private string $endPoint = ''; + /** @var IOCMResource[] */ + private array $resourceTypes = []; + + private bool $emittedEvent = false; + + public function __construct( + protected IEventDispatcher $dispatcher, + ) { + } + + /** + * @param bool $enabled + * + * @return $this + */ + public function setEnabled(bool $enabled): static { + $this->enabled = $enabled; + + return $this; + } + + /** + * @return bool + */ + public function isEnabled(): bool { + return $this->enabled; + } + + /** + * @param string $apiVersion + * + * @return $this + */ + public function setApiVersion(string $apiVersion): static { + $this->apiVersion = $apiVersion; + + return $this; + } + + /** + * @return string + */ + public function getApiVersion(): string { + return $this->apiVersion; + } + + /** + * @param string $endPoint + * + * @return $this + */ + public function setEndPoint(string $endPoint): static { + $this->endPoint = $endPoint; + + return $this; + } + + /** + * @return string + */ + public function getEndPoint(): string { + return $this->endPoint; + } + + /** + * create a new resource to later add it with {@see IOCMProvider::addResourceType()} + * @return IOCMResource + */ + public function createNewResourceType(): IOCMResource { + return new OCMResource(); + } + + /** + * @param IOCMResource $resource + * + * @return $this + */ + public function addResourceType(IOCMResource $resource): static { + $this->resourceTypes[] = $resource; + + return $this; + } + + /** + * @param IOCMResource[] $resourceTypes + * + * @return $this + */ + public function setResourceTypes(array $resourceTypes): static { + $this->resourceTypes = $resourceTypes; + + return $this; + } + + /** + * @return IOCMResource[] + */ + public function getResourceTypes(): array { + if (!$this->emittedEvent) { + $this->emittedEvent = true; + $event = new ResourceTypeRegisterEvent($this); + $this->dispatcher->dispatchTyped($event); + } + + return $this->resourceTypes; + } + + /** + * @param string $resourceName + * @param string $protocol + * + * @return string + * @throws OCMArgumentException + */ + public function extractProtocolEntry(string $resourceName, string $protocol): string { + foreach ($this->getResourceTypes() as $resource) { + if ($resource->getName() === $resourceName) { + $entry = $resource->getProtocols()[$protocol] ?? null; + if (is_null($entry)) { + throw new OCMArgumentException('protocol not found'); + } + + return (string)$entry; + } + } + + throw new OCMArgumentException('resource not found'); + } + + /** + * import data from an array + * + * @param array $data + * + * @return $this + * @throws OCMProviderException in case a descent provider cannot be generated from data + * @see self::jsonSerialize() + */ + public function import(array $data): static { + $this->setEnabled(is_bool($data['enabled'] ?? '') ? $data['enabled'] : false) + ->setApiVersion((string)($data['apiVersion'] ?? '')) + ->setEndPoint($data['endPoint'] ?? ''); + + $resources = []; + foreach (($data['resourceTypes'] ?? []) as $resourceData) { + $resource = new OCMResource(); + $resources[] = $resource->import($resourceData); + } + $this->setResourceTypes($resources); + + if (!$this->looksValid()) { + throw new OCMProviderException('remote provider does not look valid'); + } + + return $this; + } + + + /** + * @return bool + */ + private function looksValid(): bool { + return ($this->getApiVersion() !== '' && $this->getEndPoint() !== ''); + } + + + /** + * @return array{ + * enabled: bool, + * apiVersion: string, + * endPoint: string, + * resourceTypes: array{ + * name: string, + * shareTypes: string[], + * protocols: array<string, string> + * }[] + * } + */ + public function jsonSerialize(): array { + $resourceTypes = []; + foreach ($this->getResourceTypes() as $res) { + $resourceTypes[] = $res->jsonSerialize(); + } + + return [ + 'enabled' => $this->isEnabled(), + 'apiVersion' => $this->getApiVersion(), + 'endPoint' => $this->getEndPoint(), + 'resourceTypes' => $resourceTypes + ]; + } +} diff --git a/lib/private/OCM/Model/OCMResource.php b/lib/private/OCM/Model/OCMResource.php new file mode 100644 index 00000000000..c69763ca4ba --- /dev/null +++ b/lib/private/OCM/Model/OCMResource.php @@ -0,0 +1,106 @@ +<?php + +declare(strict_types=1); + +/** + * SPDX-FileCopyrightText: 2023 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later + */ + +namespace OC\OCM\Model; + +use OCP\OCM\IOCMResource; + +/** + * @since 28.0.0 + */ +class OCMResource implements IOCMResource { + private string $name = ''; + /** @var string[] */ + private array $shareTypes = []; + /** @var array<string, string> */ + private array $protocols = []; + + /** + * @param string $name + * + * @return $this + */ + public function setName(string $name): static { + $this->name = $name; + + return $this; + } + + /** + * @return string + */ + public function getName(): string { + return $this->name; + } + + /** + * @param string[] $shareTypes + * + * @return $this + */ + public function setShareTypes(array $shareTypes): static { + $this->shareTypes = $shareTypes; + + return $this; + } + + /** + * @return string[] + */ + public function getShareTypes(): array { + return $this->shareTypes; + } + + /** + * @param array<string, string> $protocols + * + * @return $this + */ + public function setProtocols(array $protocols): static { + $this->protocols = $protocols; + + return $this; + } + + /** + * @return array<string, string> + */ + public function getProtocols(): array { + return $this->protocols; + } + + /** + * import data from an array + * + * @param array $data + * + * @return $this + * @see self::jsonSerialize() + */ + public function import(array $data): static { + return $this->setName((string)($data['name'] ?? '')) + ->setShareTypes($data['shareTypes'] ?? []) + ->setProtocols($data['protocols'] ?? []); + } + + /** + * @return array{ + * name: string, + * shareTypes: string[], + * protocols: array<string, string> + * } + */ + public function jsonSerialize(): array { + return [ + 'name' => $this->getName(), + 'shareTypes' => $this->getShareTypes(), + 'protocols' => $this->getProtocols() + ]; + } +} diff --git a/lib/private/OCM/OCMDiscoveryService.php b/lib/private/OCM/OCMDiscoveryService.php new file mode 100644 index 00000000000..62313a9af80 --- /dev/null +++ b/lib/private/OCM/OCMDiscoveryService.php @@ -0,0 +1,120 @@ +<?php + +declare(strict_types=1); + +/** + * SPDX-FileCopyrightText: 2023 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later + */ + +namespace OC\OCM; + +use JsonException; +use OCP\AppFramework\Http; +use OCP\Http\Client\IClientService; +use OCP\ICache; +use OCP\ICacheFactory; +use OCP\IConfig; +use OCP\OCM\Exceptions\OCMProviderException; +use OCP\OCM\IOCMDiscoveryService; +use OCP\OCM\IOCMProvider; +use Psr\Log\LoggerInterface; + +/** + * @since 28.0.0 + */ +class OCMDiscoveryService implements IOCMDiscoveryService { + private ICache $cache; + private array $supportedAPIVersion = + [ + '1.0-proposal1', + '1.0', + '1.1' + ]; + + public function __construct( + ICacheFactory $cacheFactory, + private IClientService $clientService, + private IConfig $config, + private IOCMProvider $provider, + private LoggerInterface $logger, + ) { + $this->cache = $cacheFactory->createDistributed('ocm-discovery'); + } + + + /** + * @param string $remote + * @param bool $skipCache + * + * @return IOCMProvider + * @throws OCMProviderException + */ + public function discover(string $remote, bool $skipCache = false): IOCMProvider { + $remote = rtrim($remote, '/'); + + if (!$skipCache) { + try { + $this->provider->import(json_decode($this->cache->get($remote) ?? '', true, 8, JSON_THROW_ON_ERROR) ?? []); + if ($this->supportedAPIVersion($this->provider->getApiVersion())) { + return $this->provider; // if cache looks valid, we use it + } + } catch (JsonException|OCMProviderException $e) { + // we ignore cache on issues + } + } + + $client = $this->clientService->newClient(); + try { + $response = $client->get( + $remote . '/ocm-provider/', + [ + 'timeout' => 10, + 'verify' => !$this->config->getSystemValueBool('sharing.federation.allowSelfSignedCertificates'), + 'connect_timeout' => 10, + ] + ); + + if ($response->getStatusCode() === Http::STATUS_OK) { + $body = $response->getBody(); + // update provider with data returned by the request + $this->provider->import(json_decode($body, true, 8, JSON_THROW_ON_ERROR) ?? []); + $this->cache->set($remote, $body, 60 * 60 * 24); + } + } catch (JsonException|OCMProviderException $e) { + throw new OCMProviderException('data returned by remote seems invalid - ' . ($body ?? '')); + } catch (\Exception $e) { + $this->logger->warning('error while discovering ocm provider', [ + 'exception' => $e, + 'remote' => $remote + ]); + throw new OCMProviderException('error while requesting remote ocm provider'); + } + + if (!$this->supportedAPIVersion($this->provider->getApiVersion())) { + throw new OCMProviderException('API version not supported'); + } + + return $this->provider; + } + + /** + * Check the version from remote is supported. + * The minor version of the API will be ignored: + * 1.0.1 is identified as 1.0 + * + * @param string $version + * + * @return bool + */ + private function supportedAPIVersion(string $version): bool { + $dot1 = strpos($version, '.'); + $dot2 = strpos($version, '.', $dot1 + 1); + + if ($dot2 > 0) { + $version = substr($version, 0, $dot2); + } + + return (in_array($version, $this->supportedAPIVersion)); + } +} diff --git a/lib/private/OCS/CoreCapabilities.php b/lib/private/OCS/CoreCapabilities.php index 9cead57c6a3..3d988a8662e 100644 --- a/lib/private/OCS/CoreCapabilities.php +++ b/lib/private/OCS/CoreCapabilities.php @@ -1,24 +1,9 @@ <?php + /** - * @copyright Copyright (c) 2016, ownCloud, Inc. - * - * @author Roeland Jago Douma <roeland@famdouma.nl> - * @author Thomas Müller <thomas.mueller@tmit.eu> - * - * @license AGPL-3.0 - * - * This code is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License, version 3, - * as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License, version 3, - * along with this program. If not, see <http://www.gnu.org/licenses/> - * + * SPDX-FileCopyrightText: 2016-2024 Nextcloud GmbH and Nextcloud contributors + * SPDX-FileCopyrightText: 2016 ownCloud, Inc. + * SPDX-License-Identifier: AGPL-3.0-only */ namespace OC\OCS; @@ -32,26 +17,35 @@ use OCP\IURLGenerator; * @package OC\OCS */ class CoreCapabilities implements ICapability { - /** @var IConfig */ - private $config; - /** * @param IConfig $config */ - public function __construct(IConfig $config) { - $this->config = $config; + public function __construct( + private IConfig $config, + ) { } /** * Return this classes capabilities + * + * @return array{ + * core: array{ + * pollinterval: int, + * webdav-root: string, + * reference-api: boolean, + * reference-regex: string, + * mod-rewrite-working: boolean, + * }, + * } */ - public function getCapabilities() { + public function getCapabilities(): array { return [ 'core' => [ - 'pollinterval' => $this->config->getSystemValue('pollinterval', 60), - 'webdav-root' => $this->config->getSystemValue('webdav-root', 'remote.php/webdav'), + 'pollinterval' => $this->config->getSystemValueInt('pollinterval', 60), + 'webdav-root' => $this->config->getSystemValueString('webdav-root', 'remote.php/webdav'), 'reference-api' => true, 'reference-regex' => IURLGenerator::URL_REGEX_NO_MODIFIERS, + 'mod-rewrite-working' => $this->config->getSystemValueBool('htaccess.IgnoreFrontController') || getenv('front_controller_active') === 'true', ], ]; } diff --git a/lib/private/OCS/DiscoveryService.php b/lib/private/OCS/DiscoveryService.php index 8f98ff7d5ae..fc9c1bd4796 100644 --- a/lib/private/OCS/DiscoveryService.php +++ b/lib/private/OCS/DiscoveryService.php @@ -3,28 +3,8 @@ declare(strict_types=1); /** - * @copyright Copyright (c) 2017 Bjoern Schiessle <bjoern@schiessle.org> - * - * @author Bjoern Schiessle <bjoern@schiessle.org> - * @author Christoph Wurst <christoph@winzerhof-wurst.at> - * @author Morris Jobke <hey@morrisjobke.de> - * @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/>. - * + * SPDX-FileCopyrightText: 2017 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later */ namespace OC\OCS; @@ -37,17 +17,17 @@ use OCP\OCS\IDiscoveryService; class DiscoveryService implements IDiscoveryService { /** @var ICache */ - private $cache; + private ICache $cache; /** @var IClient */ - private $client; + private IClient $client; /** * @param ICacheFactory $cacheFactory * @param IClientService $clientService */ public function __construct(ICacheFactory $cacheFactory, - IClientService $clientService + IClientService $clientService ) { $this->cache = $cacheFactory->createDistributed('ocs-discovery'); $this->client = $clientService->newClient(); diff --git a/lib/private/OCS/Exception.php b/lib/private/OCS/Exception.php index ca3c3f70430..eca8ec26df0 100644 --- a/lib/private/OCS/Exception.php +++ b/lib/private/OCS/Exception.php @@ -1,37 +1,19 @@ <?php /** - * @copyright Copyright (c) 2016, ownCloud, Inc. - * - * @author Roeland Jago Douma <roeland@famdouma.nl> - * @author Thomas Müller <thomas.mueller@tmit.eu> - * - * @license AGPL-3.0 - * - * This code is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License, version 3, - * as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License, version 3, - * along with this program. If not, see <http://www.gnu.org/licenses/> - * + * SPDX-FileCopyrightText: 2016-2024 Nextcloud GmbH and Nextcloud contributors + * SPDX-FileCopyrightText: 2016 ownCloud, Inc. + * SPDX-License-Identifier: AGPL-3.0-only */ namespace OC\OCS; class Exception extends \Exception { - /** @var Result */ - private $result; - - public function __construct(Result $result) { + public function __construct( + private Result $result, + ) { parent::__construct(); - $this->result = $result; } - public function getResult() { + public function getResult(): Result { return $this->result; } } diff --git a/lib/private/OCS/Provider.php b/lib/private/OCS/Provider.php index 5e7a86a1341..23547da9433 100644 --- a/lib/private/OCS/Provider.php +++ b/lib/private/OCS/Provider.php @@ -1,49 +1,33 @@ <?php + /** - * @copyright Copyright (c) 2016, ownCloud, Inc. - * - * @author Bjoern Schiessle <bjoern@schiessle.org> - * @author Christoph Wurst <christoph@winzerhof-wurst.at> - * @author Joas Schilling <coding@schilljs.com> - * @author Lukas Reschke <lukas@statuscode.ch> - * - * @license AGPL-3.0 - * - * This code is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License, version 3, - * as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License, version 3, - * along with this program. If not, see <http://www.gnu.org/licenses/> - * + * SPDX-FileCopyrightText: 2016-2024 Nextcloud GmbH and Nextcloud contributors + * SPDX-FileCopyrightText: 2016 ownCloud, Inc. + * SPDX-License-Identifier: AGPL-3.0-only */ namespace OC\OCS; -class Provider extends \OCP\AppFramework\Controller { - /** @var \OCP\App\IAppManager */ - private $appManager; +use OCP\App\IAppManager; +use OCP\AppFramework\Controller; +use OCP\AppFramework\Http\JSONResponse; +use OCP\IRequest; +class Provider extends Controller { /** * @param string $appName - * @param \OCP\IRequest $request - * @param \OCP\App\IAppManager $appManager + * @param IRequest $request + * @param IAppManager $appManager */ public function __construct($appName, - \OCP\IRequest $request, - \OCP\App\IAppManager $appManager) { + \OCP\IRequest $request, + private \OCP\App\IAppManager $appManager) { parent::__construct($appName, $request); - $this->appManager = $appManager; } /** - * @return \OCP\AppFramework\Http\JSONResponse + * @return JSONResponse */ - public function buildProviderList() { + public function buildProviderList(): JSONResponse { $services = [ 'PRIVATE_DATA' => [ 'version' => 1, @@ -108,7 +92,7 @@ class Provider extends \OCP\AppFramework\Controller { ]; } - return new \OCP\AppFramework\Http\JSONResponse([ + return new JSONResponse([ 'version' => 2, 'services' => $services, ]); diff --git a/lib/private/OCS/Result.php b/lib/private/OCS/Result.php index d77e360e6d2..5460a8b275c 100644 --- a/lib/private/OCS/Result.php +++ b/lib/private/OCS/Result.php @@ -1,44 +1,20 @@ <?php + /** - * @copyright Copyright (c) 2016, ownCloud, Inc. - * - * @author Bart Visscher <bartv@thisnet.nl> - * @author Björn Schießle <bjoern@schiessle.org> - * @author Christopher Schäpers <kondou@ts.unde.re> - * @author Christoph Wurst <christoph@winzerhof-wurst.at> - * @author Lukas Reschke <lukas@statuscode.ch> - * @author Morris Jobke <hey@morrisjobke.de> - * @author Robin McCorkell <robin@mccorkell.me.uk> - * @author Roeland Jago Douma <roeland@famdouma.nl> - * @author Thomas Müller <thomas.mueller@tmit.eu> - * @author Tom Needham <tom@owncloud.com> - * - * @license AGPL-3.0 - * - * This code is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License, version 3, - * as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License, version 3, - * along with this program. If not, see <http://www.gnu.org/licenses/> - * + * SPDX-FileCopyrightText: 2016-2024 Nextcloud GmbH and Nextcloud contributors + * SPDX-FileCopyrightText: 2016 ownCloud, Inc. + * SPDX-License-Identifier: AGPL-3.0-only */ namespace OC\OCS; class Result { - /** @var array */ - protected $data; + protected array $data; /** @var null|string */ - protected $message; + protected ?string $message; /** @var int */ - protected $statusCode; + protected int $statusCode; /** @var integer */ protected $items; @@ -47,16 +23,17 @@ class Result { protected $perPage; /** @var array */ - private $headers = []; + private array $headers = []; /** * create the OCS_Result object - * @param mixed $data the data to return + * + * @param mixed|null $data the data to return * @param int $code - * @param null|string $message + * @param string|null $message * @param array $headers */ - public function __construct($data = null, $code = 100, $message = null, $headers = []) { + public function __construct(mixed $data = null, int $code = 100, ?string $message = null, array $headers = []) { if ($data === null) { $this->data = []; } elseif (!is_array($data)) { @@ -71,17 +48,19 @@ class Result { /** * optionally set the total number of items available + * * @param int $items */ - public function setTotalItems($items) { + public function setTotalItems(int $items): void { $this->items = $items; } /** - * optionally set the the number of items per page + * optionally set the number of items per page + * * @param int $items */ - public function setItemsPerPage($items) { + public function setItemsPerPage(int $items): void { $this->perPage = $items; } @@ -89,7 +68,7 @@ class Result { * get the status code * @return int */ - public function getStatusCode() { + public function getStatusCode(): int { return $this->statusCode; } @@ -97,7 +76,7 @@ class Result { * get the meta data for the result * @return array */ - public function getMeta() { + public function getMeta(): array { $meta = []; $meta['status'] = $this->succeeded() ? 'ok' : 'failure'; $meta['statuscode'] = $this->statusCode; @@ -115,7 +94,7 @@ class Result { * get the result data * @return array */ - public function getData() { + public function getData(): array { return $this->data; } @@ -123,17 +102,18 @@ class Result { * return bool Whether the method succeeded * @return bool */ - public function succeeded() { + public function succeeded(): bool { return ($this->statusCode == 100); } /** * Adds a new header to the response + * * @param string $name The name of the HTTP header * @param string $value The value, null will delete it * @return $this */ - public function addHeader($name, $value) { + public function addHeader(string $name, ?string $value): static { $name = trim($name); // always remove leading and trailing whitespace // to be able to reliably check for security // headers @@ -151,7 +131,7 @@ class Result { * Returns the set headers * @return array the headers */ - public function getHeaders() { + public function getHeaders(): array { return $this->headers; } } diff --git a/lib/private/PhoneNumberUtil.php b/lib/private/PhoneNumberUtil.php new file mode 100644 index 00000000000..e53c7a51a1b --- /dev/null +++ b/lib/private/PhoneNumberUtil.php @@ -0,0 +1,43 @@ +<?php + +declare(strict_types=1); +/** + * SPDX-FileCopyrightText: 2023 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later + */ + +namespace OC; + +use libphonenumber\NumberParseException; +use libphonenumber\PhoneNumberFormat; +use OCP\IPhoneNumberUtil; + +/** + * @since 28.0.0 + */ +class PhoneNumberUtil implements IPhoneNumberUtil { + /** + * {@inheritDoc} + */ + public function getCountryCodeForRegion(string $regionCode): ?int { + $phoneUtil = \libphonenumber\PhoneNumberUtil::getInstance(); + $countryCode = $phoneUtil->getCountryCodeForRegion($regionCode); + return $countryCode === 0 ? null : $countryCode; + } + + /** + * {@inheritDoc} + */ + public function convertToStandardFormat(string $input, ?string $defaultRegion = null): ?string { + $phoneUtil = \libphonenumber\PhoneNumberUtil::getInstance(); + try { + $phoneNumber = $phoneUtil->parse($input, $defaultRegion); + if ($phoneUtil->isValidNumber($phoneNumber)) { + return $phoneUtil->format($phoneNumber, PhoneNumberFormat::E164); + } + } catch (NumberParseException) { + } + + return null; + } +} diff --git a/lib/private/Preview/BMP.php b/lib/private/Preview/BMP.php index c429f31f0e2..f275aecf0cf 100644 --- a/lib/private/Preview/BMP.php +++ b/lib/private/Preview/BMP.php @@ -1,24 +1,9 @@ <?php + /** - * @copyright Copyright (c) 2016, ownCloud, Inc. - * - * @author Olivier Paroz <github@oparoz.com> - * @author Robin Appelman <robin@icewind.nl> - * - * @license AGPL-3.0 - * - * This code is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License, version 3, - * as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License, version 3, - * along with this program. If not, see <http://www.gnu.org/licenses/> - * + * SPDX-FileCopyrightText: 2016-2024 Nextcloud GmbH and Nextcloud contributors + * SPDX-FileCopyrightText: 2016 ownCloud, Inc. + * SPDX-License-Identifier: AGPL-3.0-only */ namespace OC\Preview; diff --git a/lib/private/Preview/BackgroundCleanupJob.php b/lib/private/Preview/BackgroundCleanupJob.php index 4eba96d1a82..49ff01486a3 100644 --- a/lib/private/Preview/BackgroundCleanupJob.php +++ b/lib/private/Preview/BackgroundCleanupJob.php @@ -3,25 +3,8 @@ declare(strict_types=1); /** - * @copyright Copyright (c) 2018, Roeland Jago Douma <roeland@famdouma.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/>. - * + * SPDX-FileCopyrightText: 2018 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later */ namespace OC\Preview; @@ -48,10 +31,10 @@ class BackgroundCleanupJob extends TimedJob { private $mimeTypeLoader; public function __construct(ITimeFactory $timeFactory, - IDBConnection $connection, - Root $previewFolder, - IMimeTypeLoader $mimeTypeLoader, - bool $isCLI) { + IDBConnection $connection, + Root $previewFolder, + IMimeTypeLoader $mimeTypeLoader, + bool $isCLI) { parent::__construct($timeFactory); // Run at most once an hour $this->setInterval(3600); diff --git a/lib/private/Preview/Bitmap.php b/lib/private/Preview/Bitmap.php index 84c7b4a8968..c9fad50841f 100644 --- a/lib/private/Preview/Bitmap.php +++ b/lib/private/Preview/Bitmap.php @@ -1,28 +1,9 @@ <?php + /** - * @copyright Copyright (c) 2016, ownCloud, Inc. - * - * @author Arthur Schiwon <blizzz@arthur-schiwon.de> - * @author Joas Schilling <coding@schilljs.com> - * @author Morris Jobke <hey@morrisjobke.de> - * @author Olivier Paroz <github@oparoz.com> - * @author Robin Appelman <robin@icewind.nl> - * @author Roeland Jago Douma <roeland@famdouma.nl> - * - * @license AGPL-3.0 - * - * This code is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License, version 3, - * as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License, version 3, - * along with this program. If not, see <http://www.gnu.org/licenses/> - * + * SPDX-FileCopyrightText: 2016-2024 Nextcloud GmbH and Nextcloud contributors + * SPDX-FileCopyrightText: 2016 ownCloud, Inc. + * SPDX-License-Identifier: AGPL-3.0-only */ namespace OC\Preview; @@ -38,6 +19,18 @@ use Psr\Log\LoggerInterface; */ abstract class Bitmap extends ProviderV2 { /** + * List of MIME types that this preview provider is allowed to process. + * + * These should correspond to the MIME types *identified* by Imagemagick + * for files to be processed by this provider. These do / will not + * necessarily need to match the MIME types stored in the database + * (which are identified by IMimeTypeDetector). + * + * @return string Regular expression + */ + abstract protected function getAllowedMimeTypes(): string; + + /** * {@inheritDoc} */ public function getThumbnail(File $file, int $maxX, int $maxY): ?IImage { @@ -86,10 +79,19 @@ abstract class Bitmap extends ProviderV2 { * @param int $maxY * * @return \Imagick + * + * @throws \Exception */ private function getResizedPreview($tmpPath, $maxX, $maxY) { $bp = new Imagick(); + // Validate mime type + $bp->pingImage($tmpPath . '[0]'); + $mimeType = $bp->getImageMimeType(); + if (!preg_match($this->getAllowedMimeTypes(), $mimeType)) { + throw new \Exception('File mime type does not match the preview provider: ' . $mimeType); + } + // Layer 0 contains either the bitmap or a flat representation of all vector layers $bp->readImage($tmpPath . '[0]'); diff --git a/lib/private/Preview/Bundled.php b/lib/private/Preview/Bundled.php index 2a408b90c4e..836dc4bd357 100644 --- a/lib/private/Preview/Bundled.php +++ b/lib/private/Preview/Bundled.php @@ -1,24 +1,7 @@ <?php /** - * @copyright Copyright (c) 2020 Julius Härtl <jus@bitgrid.net> - * - * @author Julius Härtl <jus@bitgrid.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/>. - * + * SPDX-FileCopyrightText: 2020 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later */ namespace OC\Preview; diff --git a/lib/private/Preview/EMF.php b/lib/private/Preview/EMF.php new file mode 100644 index 00000000000..a3c48e7d8aa --- /dev/null +++ b/lib/private/Preview/EMF.php @@ -0,0 +1,16 @@ +<?php + +declare(strict_types=1); + +/** + * SPDX-FileCopyrightText: 2023 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later + */ + +namespace OC\Preview; + +class EMF extends Office { + public function getMimeType(): string { + return '/image\/emf/'; + } +} diff --git a/lib/private/Preview/Font.php b/lib/private/Preview/Font.php index baf29e1defc..79e537f6ffb 100644 --- a/lib/private/Preview/Font.php +++ b/lib/private/Preview/Font.php @@ -1,24 +1,9 @@ <?php + /** - * @copyright Copyright (c) 2016, ownCloud, Inc. - * - * @author Olivier Paroz <github@oparoz.com> - * @author Robin Appelman <robin@icewind.nl> - * - * @license AGPL-3.0 - * - * This code is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License, version 3, - * as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License, version 3, - * along with this program. If not, see <http://www.gnu.org/licenses/> - * + * SPDX-FileCopyrightText: 2016-2024 Nextcloud GmbH and Nextcloud contributors + * SPDX-FileCopyrightText: 2016 ownCloud, Inc. + * SPDX-License-Identifier: AGPL-3.0-only */ namespace OC\Preview; @@ -30,4 +15,11 @@ class Font extends Bitmap { public function getMimeType(): string { return '/application\/(?:font-sfnt|x-font$)/'; } + + /** + * {@inheritDoc} + */ + protected function getAllowedMimeTypes(): string { + return '/(application|image)\/(?:font-sfnt|x-font|x-otf|x-ttf|x-pfb$)/'; + } } diff --git a/lib/private/Preview/GIF.php b/lib/private/Preview/GIF.php index 482f014e3dc..941ef68648a 100644 --- a/lib/private/Preview/GIF.php +++ b/lib/private/Preview/GIF.php @@ -1,24 +1,9 @@ <?php + /** - * @copyright Copyright (c) 2016, ownCloud, Inc. - * - * @author Olivier Paroz <github@oparoz.com> - * @author Robin Appelman <robin@icewind.nl> - * - * @license AGPL-3.0 - * - * This code is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License, version 3, - * as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License, version 3, - * along with this program. If not, see <http://www.gnu.org/licenses/> - * + * SPDX-FileCopyrightText: 2016-2024 Nextcloud GmbH and Nextcloud contributors + * SPDX-FileCopyrightText: 2016 ownCloud, Inc. + * SPDX-License-Identifier: AGPL-3.0-only */ namespace OC\Preview; diff --git a/lib/private/Preview/Generator.php b/lib/private/Preview/Generator.php index 4e4571f0857..4083b9f4f61 100644 --- a/lib/private/Preview/Generator.php +++ b/lib/private/Preview/Generator.php @@ -1,31 +1,7 @@ <?php /** - * @copyright Copyright (c) 2016, Roeland Jago Douma <roeland@famdouma.nl> - * - * @author Christoph Wurst <christoph@winzerhof-wurst.at> - * @author Elijah Martin-Merrill <elijah@nyp-itsours.com> - * @author J0WI <J0WI@users.noreply.github.com> - * @author John Molakvoæ <skjnldsv@protonmail.com> - * @author Morris Jobke <hey@morrisjobke.de> - * @author Robin Appelman <robin@icewind.nl> - * @author Roeland Jago Douma <roeland@famdouma.nl> - * @author Scott Dutton <scott@exussum.co.uk> - * - * @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/>. - * + * SPDX-FileCopyrightText: 2016 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later */ namespace OC\Preview; @@ -139,23 +115,6 @@ class Generator { $previewVersion = $file->getPreviewVersion() . '-'; } - // If imaginary is enabled, and we request a small thumbnail, - // let's not generate the max preview for performance reasons - if (count($specifications) === 1 - && ($specifications[0]['width'] <= 256 || $specifications[0]['height'] <= 256) - && preg_match(Imaginary::supportedMimeTypes(), $mimeType) - && $this->config->getSystemValueString('preview_imaginary_url', 'invalid') !== 'invalid') { - $crop = $specifications[0]['crop'] ?? false; - $preview = $this->getSmallImagePreview($previewFolder, $previewFiles, $file, $mimeType, $previewVersion, $crop); - - if ($preview->getSize() === 0) { - $preview->delete(); - throw new NotFoundException('Cached preview size 0, invalid!'); - } - - return $preview; - } - // Get the max preview and infer the max preview sizes from that $maxPreview = $this->getMaxPreview($previewFolder, $previewFiles, $file, $mimeType, $previewVersion); $maxPreviewImage = null; // only load the image when we need it @@ -232,32 +191,13 @@ class Generator { } /** - * Generate a small image straight away without generating a max preview first - * Preview generated is 256x256 - * - * @param ISimpleFile[] $previewFiles - * - * @throws NotFoundException - */ - private function getSmallImagePreview(ISimpleFolder $previewFolder, array $previewFiles, File $file, string $mimeType, string $prefix, bool $crop): ISimpleFile { - $width = 256; - $height = 256; - - try { - return $this->getCachedPreview($previewFiles, $width, $height, $crop, $mimeType, $prefix); - } catch (NotFoundException $e) { - return $this->generateProviderPreview($previewFolder, $file, $width, $height, $crop, false, $mimeType, $prefix); - } - } - - /** * Acquire a semaphore of the specified id and concurrency, blocking if necessary. * Return an identifier of the semaphore on success, which can be used to release it via * {@see Generator::unguardWithSemaphore()}. * * @param int $semId * @param int $concurrency - * @return false|resource the semaphore on success or false on failure + * @return false|\SysvSemaphore the semaphore on success or false on failure */ public static function guardWithSemaphore(int $semId, int $concurrency) { if (!extension_loaded('sysvsem')) { @@ -276,11 +216,11 @@ class Generator { /** * Releases the semaphore acquired from {@see Generator::guardWithSemaphore()}. * - * @param resource|bool $semId the semaphore identifier returned by guardWithSemaphore + * @param false|\SysvSemaphore $semId the semaphore identifier returned by guardWithSemaphore * @return bool */ - public static function unguardWithSemaphore($semId): bool { - if (!is_resource($semId) || !extension_loaded('sysvsem')) { + public static function unguardWithSemaphore(false|\SysvSemaphore $semId): bool { + if ($semId === false || !($semId instanceof \SysvSemaphore)) { return false; } return sem_release($semId); @@ -293,9 +233,15 @@ class Generator { */ public static function getHardwareConcurrency(): int { static $width; + if (!isset($width)) { - if (is_file("/proc/cpuinfo")) { - $width = substr_count(file_get_contents("/proc/cpuinfo"), "processor"); + if (function_exists('ini_get')) { + $openBasedir = ini_get('open_basedir'); + if (empty($openBasedir) || strpos($openBasedir, '/proc/cpuinfo') !== false) { + $width = is_readable('/proc/cpuinfo') ? substr_count(file_get_contents('/proc/cpuinfo'), 'processor') : 0; + } else { + $width = 0; + } } else { $width = 0; } @@ -319,7 +265,7 @@ class Generator { * @return int number of concurrent preview generations, or -1 if $type is invalid */ public function getNumConcurrentPreviews(string $type): int { - static $cached = array(); + static $cached = []; if (array_key_exists($type, $cached)) { return $cached[$type]; } @@ -651,6 +597,8 @@ class Generator { return 'png'; case 'image/jpeg': return 'jpg'; + case 'image/webp': + return 'webp'; case 'image/gif': return 'gif'; default: diff --git a/lib/private/Preview/GeneratorHelper.php b/lib/private/Preview/GeneratorHelper.php index 64714a9f899..5f43c94b624 100644 --- a/lib/private/Preview/GeneratorHelper.php +++ b/lib/private/Preview/GeneratorHelper.php @@ -1,27 +1,7 @@ <?php /** - * @copyright Copyright (c) 2016, Roeland Jago Douma <roeland@famdouma.nl> - * - * @author Christoph Wurst <christoph@winzerhof-wurst.at> - * @author Morris Jobke <hey@morrisjobke.de> - * @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/>. - * + * SPDX-FileCopyrightText: 2016 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later */ namespace OC\Preview; diff --git a/lib/private/Preview/HEIC.php b/lib/private/Preview/HEIC.php index 71df98f9ac6..d198f11fdef 100644 --- a/lib/private/Preview/HEIC.php +++ b/lib/private/Preview/HEIC.php @@ -3,29 +3,9 @@ declare(strict_types=1); /** - * @copyright Copyright (c) 2018, ownCloud GmbH - * @copyright Copyright (c) 2018, Sebastian Steinmetz (me@sebastiansteinmetz.ch) - * - * @author J0WI <J0WI@users.noreply.github.com> - * @author Robin Appelman <robin@icewind.nl> - * @author Roeland Jago Douma <roeland@famdouma.nl> - * @author Sebastian Steinmetz <462714+steiny2k@users.noreply.github.com> - * @author Sebastian Steinmetz <me@sebastiansteinmetz.ch> - * - * @license AGPL-3.0 - * - * This code is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License, version 3, - * as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License, version 3, - * along with this program. If not, see <http://www.gnu.org/licenses/> - * + * SPDX-FileCopyrightText: 2018 Nextcloud GmbH and Nextcloud contributors + * SPDX-FileCopyrightText: 2018 ownCloud GmbH + * SPDX-License-Identifier: AGPL-3.0-only */ namespace OC\Preview; @@ -44,7 +24,7 @@ class HEIC extends ProviderV2 { * {@inheritDoc} */ public function getMimeType(): string { - return '/image\/hei(f|c)/'; + return '/image\/(x-)?hei(f|c)/'; } /** @@ -108,10 +88,20 @@ class HEIC extends ProviderV2 { * @param int $maxY * * @return \Imagick + * + * @throws \Exception */ private function getResizedPreview($tmpPath, $maxX, $maxY) { $bp = new \Imagick(); + // Some HEIC files just contain (or at least are identified as) other formats + // like JPEG. We just need to check if the image is safe to process. + $bp->pingImage($tmpPath . '[0]'); + $mimeType = $bp->getImageMimeType(); + if (!preg_match('/^image\/(x-)?(png|jpeg|gif|bmp|tiff|webp|hei(f|c)|avif)$/', $mimeType)) { + throw new \Exception('File mime type does not match the preview provider: ' . $mimeType); + } + // Layer 0 contains either the bitmap or a flat representation of all vector layers $bp->readImage($tmpPath . '[0]'); diff --git a/lib/private/Preview/IMagickSupport.php b/lib/private/Preview/IMagickSupport.php index e22ae93ab94..8225b1e7644 100644 --- a/lib/private/Preview/IMagickSupport.php +++ b/lib/private/Preview/IMagickSupport.php @@ -1,5 +1,9 @@ <?php +/** + * SPDX-FileCopyrightText: 2023 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later + */ namespace OC\Preview; use OCP\ICache; diff --git a/lib/private/Preview/Illustrator.php b/lib/private/Preview/Illustrator.php index 3d926c304c5..bff556a3177 100644 --- a/lib/private/Preview/Illustrator.php +++ b/lib/private/Preview/Illustrator.php @@ -1,25 +1,9 @@ <?php + /** - * @copyright Copyright (c) 2016, ownCloud, Inc. - * - * @author Joas Schilling <coding@schilljs.com> - * @author Morris Jobke <hey@morrisjobke.de> - * @author Robin Appelman <robin@icewind.nl> - * - * @license AGPL-3.0 - * - * This code is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License, version 3, - * as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License, version 3, - * along with this program. If not, see <http://www.gnu.org/licenses/> - * + * SPDX-FileCopyrightText: 2016-2024 Nextcloud GmbH and Nextcloud contributors + * SPDX-FileCopyrightText: 2016 ownCloud, Inc. + * SPDX-License-Identifier: AGPL-3.0-only */ namespace OC\Preview; @@ -31,4 +15,11 @@ class Illustrator extends Bitmap { public function getMimeType(): string { return '/application\/illustrator/'; } + + /** + * {@inheritDoc} + */ + protected function getAllowedMimeTypes(): string { + return '/application\/(illustrator|pdf)/'; + } } diff --git a/lib/private/Preview/Image.php b/lib/private/Preview/Image.php index 95b66a922fd..69841f07929 100644 --- a/lib/private/Preview/Image.php +++ b/lib/private/Preview/Image.php @@ -1,31 +1,9 @@ <?php + /** - * @copyright Copyright (c) 2016, ownCloud, Inc. - * - * @author Georg Ehrke <oc.list@georgehrke.com> - * @author J0WI <J0WI@users.noreply.github.com> - * @author Joas Schilling <coding@schilljs.com> - * @author josh4trunks <joshruehlig@gmail.com> - * @author Olivier Paroz <github@oparoz.com> - * @author Robin Appelman <robin@icewind.nl> - * @author Roeland Jago Douma <roeland@famdouma.nl> - * @author Thomas Müller <thomas.mueller@tmit.eu> - * @author Thomas Tanghus <thomas@tanghus.net> - * - * @license AGPL-3.0 - * - * This code is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License, version 3, - * as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License, version 3, - * along with this program. If not, see <http://www.gnu.org/licenses/> - * + * SPDX-FileCopyrightText: 2016-2024 Nextcloud GmbH and Nextcloud contributors + * SPDX-FileCopyrightText: 2016 ownCloud, Inc. + * SPDX-License-Identifier: AGPL-3.0-only */ namespace OC\Preview; diff --git a/lib/private/Preview/Imaginary.php b/lib/private/Preview/Imaginary.php index da4864b1a22..23459be3b51 100644 --- a/lib/private/Preview/Imaginary.php +++ b/lib/private/Preview/Imaginary.php @@ -1,35 +1,18 @@ <?php /** - * @copyright Copyright (c) 2020, Nextcloud, GmbH. - * - * @author Vincent Petry <vincent@nextcloud.com> - * @author Carl Schwan <carl@carlschwan.eu> - * - * @license AGPL-3.0-or-later - * - * This code is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License, version 3, - * as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License, version 3, - * along with this program. If not, see <http://www.gnu.org/licenses/> - * + * SPDX-FileCopyrightText: 2020 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later */ namespace OC\Preview; +use OC\StreamImage; use OCP\Files\File; use OCP\Http\Client\IClientService; use OCP\IConfig; use OCP\IImage; -use OCP\Image; -use OC\StreamImage; +use OCP\Image; use Psr\Log\LoggerInterface; class Imaginary extends ProviderV2 { @@ -78,6 +61,9 @@ class Imaginary extends ProviderV2 { // Object store $stream = $file->fopen('r'); + if (!$stream || !is_resource($stream) || feof($stream)) { + return null; + } $httpClient = $this->service->newClient(); @@ -106,6 +92,15 @@ class Imaginary extends ProviderV2 { $mimeType = 'jpeg'; } + $preview_format = $this->config->getSystemValueString('preview_format', 'jpeg'); + + switch ($preview_format) { // Change the format to the correct one + case 'webp': + $mimeType = 'webp'; + break; + default: + } + $operations = []; if ($convert) { @@ -121,7 +116,16 @@ class Imaginary extends ProviderV2 { ]; } - $quality = $this->config->getAppValue('preview', 'jpeg_quality', '80'); + switch ($mimeType) { + case 'jpeg': + $quality = $this->config->getAppValue('preview', 'jpeg_quality', '80'); + break; + case 'webp': + $quality = $this->config->getAppValue('preview', 'webp_quality', '80'); + break; + default: + $quality = $this->config->getAppValue('preview', 'jpeg_quality', '80'); + } $operations[] = [ 'operation' => ($crop ? 'smartcrop' : 'fit'), @@ -147,7 +151,7 @@ class Imaginary extends ProviderV2 { 'timeout' => 120, 'connect_timeout' => 3, ]); - } catch (\Exception $e) { + } catch (\Throwable $e) { $this->logger->info('Imaginary preview generation failed: ' . $e->getMessage(), [ 'exception' => $e, ]); diff --git a/lib/private/Preview/JPEG.php b/lib/private/Preview/JPEG.php index 14ae95690c0..a1a394f81c9 100644 --- a/lib/private/Preview/JPEG.php +++ b/lib/private/Preview/JPEG.php @@ -1,24 +1,9 @@ <?php + /** - * @copyright Copyright (c) 2016, ownCloud, Inc. - * - * @author Olivier Paroz <github@oparoz.com> - * @author Robin Appelman <robin@icewind.nl> - * - * @license AGPL-3.0 - * - * This code is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License, version 3, - * as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License, version 3, - * along with this program. If not, see <http://www.gnu.org/licenses/> - * + * SPDX-FileCopyrightText: 2016-2024 Nextcloud GmbH and Nextcloud contributors + * SPDX-FileCopyrightText: 2016 ownCloud, Inc. + * SPDX-License-Identifier: AGPL-3.0-only */ namespace OC\Preview; diff --git a/lib/private/Preview/Krita.php b/lib/private/Preview/Krita.php index eb25db9928c..2e77c7befd2 100644 --- a/lib/private/Preview/Krita.php +++ b/lib/private/Preview/Krita.php @@ -1,24 +1,7 @@ <?php /** - * @copyright Copyright (c) 2020 Julius Härtl <jus@bitgrid.net> - * - * @author Julius Härtl <jus@bitgrid.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/>. - * + * SPDX-FileCopyrightText: 2020 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later */ namespace OC\Preview; diff --git a/lib/private/Preview/MP3.php b/lib/private/Preview/MP3.php index 4c5f932f477..59b791950b8 100644 --- a/lib/private/Preview/MP3.php +++ b/lib/private/Preview/MP3.php @@ -1,30 +1,9 @@ <?php + /** - * @copyright Copyright (c) 2016, ownCloud, Inc. - * - * @author Christoph Wurst <christoph@winzerhof-wurst.at> - * @author Georg Ehrke <oc.list@georgehrke.com> - * @author Joas Schilling <coding@schilljs.com> - * @author Lukas Reschke <lukas@statuscode.ch> - * @author Olivier Paroz <github@oparoz.com> - * @author Robin Appelman <robin@icewind.nl> - * @author Roeland Jago Douma <roeland@famdouma.nl> - * @author Thomas Tanghus <thomas@tanghus.net> - * - * @license AGPL-3.0 - * - * This code is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License, version 3, - * as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License, version 3, - * along with this program. If not, see <http://www.gnu.org/licenses/> - * + * SPDX-FileCopyrightText: 2016-2024 Nextcloud GmbH and Nextcloud contributors + * SPDX-FileCopyrightText: 2016 ownCloud, Inc. + * SPDX-License-Identifier: AGPL-3.0-only */ namespace OC\Preview; diff --git a/lib/private/Preview/MSOffice2003.php b/lib/private/Preview/MSOffice2003.php index 34cee499ff8..a52e618d484 100644 --- a/lib/private/Preview/MSOffice2003.php +++ b/lib/private/Preview/MSOffice2003.php @@ -1,25 +1,9 @@ <?php + /** - * @copyright Copyright (c) 2016, ownCloud, Inc. - * - * @author Joas Schilling <coding@schilljs.com> - * @author Robin Appelman <robin@icewind.nl> - * @author Roeland Jago Douma <roeland@famdouma.nl> - * - * @license AGPL-3.0 - * - * This code is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License, version 3, - * as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License, version 3, - * along with this program. If not, see <http://www.gnu.org/licenses/> - * + * SPDX-FileCopyrightText: 2016-2024 Nextcloud GmbH and Nextcloud contributors + * SPDX-FileCopyrightText: 2016 ownCloud, Inc. + * SPDX-License-Identifier: AGPL-3.0-only */ namespace OC\Preview; diff --git a/lib/private/Preview/MSOffice2007.php b/lib/private/Preview/MSOffice2007.php index 3d3049e1c12..317f2dcc7f1 100644 --- a/lib/private/Preview/MSOffice2007.php +++ b/lib/private/Preview/MSOffice2007.php @@ -1,25 +1,9 @@ <?php + /** - * @copyright Copyright (c) 2016, ownCloud, Inc. - * - * @author Joas Schilling <coding@schilljs.com> - * @author Robin Appelman <robin@icewind.nl> - * @author Roeland Jago Douma <roeland@famdouma.nl> - * - * @license AGPL-3.0 - * - * This code is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License, version 3, - * as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License, version 3, - * along with this program. If not, see <http://www.gnu.org/licenses/> - * + * SPDX-FileCopyrightText: 2016-2024 Nextcloud GmbH and Nextcloud contributors + * SPDX-FileCopyrightText: 2016 ownCloud, Inc. + * SPDX-License-Identifier: AGPL-3.0-only */ namespace OC\Preview; diff --git a/lib/private/Preview/MSOfficeDoc.php b/lib/private/Preview/MSOfficeDoc.php index e04503629e1..2e1044395f1 100644 --- a/lib/private/Preview/MSOfficeDoc.php +++ b/lib/private/Preview/MSOfficeDoc.php @@ -1,25 +1,9 @@ <?php + /** - * @copyright Copyright (c) 2016, ownCloud, Inc. - * - * @author Joas Schilling <coding@schilljs.com> - * @author Robin Appelman <robin@icewind.nl> - * @author Roeland Jago Douma <roeland@famdouma.nl> - * - * @license AGPL-3.0 - * - * This code is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License, version 3, - * as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License, version 3, - * along with this program. If not, see <http://www.gnu.org/licenses/> - * + * SPDX-FileCopyrightText: 2016-2024 Nextcloud GmbH and Nextcloud contributors + * SPDX-FileCopyrightText: 2016 ownCloud, Inc. + * SPDX-License-Identifier: AGPL-3.0-only */ namespace OC\Preview; diff --git a/lib/private/Preview/MarkDown.php b/lib/private/Preview/MarkDown.php index d3806ce6c0e..41a79455042 100644 --- a/lib/private/Preview/MarkDown.php +++ b/lib/private/Preview/MarkDown.php @@ -1,27 +1,9 @@ <?php + /** - * @copyright Copyright (c) 2016, ownCloud, Inc. - * - * @author Joas Schilling <coding@schilljs.com> - * @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> - * - * @license AGPL-3.0 - * - * This code is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License, version 3, - * as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License, version 3, - * along with this program. If not, see <http://www.gnu.org/licenses/> - * + * SPDX-FileCopyrightText: 2016-2024 Nextcloud GmbH and Nextcloud contributors + * SPDX-FileCopyrightText: 2016 ownCloud, Inc. + * SPDX-License-Identifier: AGPL-3.0-only */ namespace OC\Preview; diff --git a/lib/private/Preview/MimeIconProvider.php b/lib/private/Preview/MimeIconProvider.php index 1e44e8ca80a..167dd21b262 100644 --- a/lib/private/Preview/MimeIconProvider.php +++ b/lib/private/Preview/MimeIconProvider.php @@ -1,23 +1,7 @@ <?php /** - * @copyright Copyright (c) 2023 John Molakvoæ <skjnldsv@protonmail.com> - * - * @author John Molakvoæ <skjnldsv@protonmail.com> - * - * @license AGPL-3.0-or-later - * - * This code is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License, version 3, - * as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License, version 3, - * along with this program. If not, see <http://www.gnu.org/licenses/> - * + * SPDX-FileCopyrightText: 2023 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later */ namespace OC\Preview; @@ -47,7 +31,7 @@ class MimeIconProvider implements IMimeIconProvider { $aliases = $this->mimetypeDetector->getAllAliases(); // Remove comments - $aliases = array_filter($aliases, static function ($key) { + $aliases = array_filter($aliases, static function (string $key) { return !($key === '' || $key[0] === '_'); }, ARRAY_FILTER_USE_KEY); @@ -82,7 +66,7 @@ class MimeIconProvider implements IMimeIconProvider { } } - // Previously, we used to pass thi through Theming + // Previously, we used to pass this through Theming // But it was only used to colour icons containing // 0082c9. Since with vue we moved to inline svg icons, // we can just use the default core icons. diff --git a/lib/private/Preview/Movie.php b/lib/private/Preview/Movie.php index 13d868cd583..cfc05b8cce9 100644 --- a/lib/private/Preview/Movie.php +++ b/lib/private/Preview/Movie.php @@ -1,31 +1,9 @@ <?php + /** - * @copyright Copyright (c) 2016, ownCloud, Inc. - * - * @author Alexander A. Klimov <grandmaster@al2klimov.de> - * @author Daniel Schneider <daniel@schneidoa.de> - * @author Georg Ehrke <oc.list@georgehrke.com> - * @author Joas Schilling <coding@schilljs.com> - * @author Morris Jobke <hey@morrisjobke.de> - * @author Olivier Paroz <github@oparoz.com> - * @author Robin Appelman <robin@icewind.nl> - * @author Roeland Jago Douma <roeland@famdouma.nl> - * @author Thomas Müller <thomas.mueller@tmit.eu> - * - * @license AGPL-3.0 - * - * This code is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License, version 3, - * as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License, version 3, - * along with this program. If not, see <http://www.gnu.org/licenses/> - * + * SPDX-FileCopyrightText: 2016-2024 Nextcloud GmbH and Nextcloud contributors + * SPDX-FileCopyrightText: 2016 ownCloud, Inc. + * SPDX-License-Identifier: AGPL-3.0-only */ namespace OC\Preview; @@ -163,7 +141,7 @@ class Movie extends ProviderV2 { if ($second === 0) { $logger = \OC::$server->get(LoggerInterface::class); - $logger->error('Movie preview generation failed Output: {output}', ['app' => 'core', 'output' => $output]); + $logger->info('Movie preview generation failed Output: {output}', ['app' => 'core', 'output' => $output]); } unlink($tmpPath); diff --git a/lib/private/Preview/Office.php b/lib/private/Preview/Office.php index 3ba7c5a21a0..20fbef6eb23 100644 --- a/lib/private/Preview/Office.php +++ b/lib/private/Preview/Office.php @@ -1,37 +1,17 @@ <?php + /** - * @copyright Copyright (c) 2016, ownCloud, Inc. - * - * @author Arthur Schiwon <blizzz@arthur-schiwon.de> - * @author Joas Schilling <coding@schilljs.com> - * @author Morris Jobke <hey@morrisjobke.de> - * @author Olivier Paroz <github@oparoz.com> - * @author Robin Appelman <robin@icewind.nl> - * @author Robin McCorkell <robin@mccorkell.me.uk> - * @author Roeland Jago Douma <roeland@famdouma.nl> - * @author Tor Lillqvist <tml@collabora.com> - * - * @license AGPL-3.0 - * - * This code is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License, version 3, - * as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License, version 3, - * along with this program. If not, see <http://www.gnu.org/licenses/> - * + * SPDX-FileCopyrightText: 2016-2024 Nextcloud GmbH and Nextcloud contributors + * SPDX-FileCopyrightText: 2016 ownCloud, Inc. + * SPDX-License-Identifier: AGPL-3.0-only */ namespace OC\Preview; use OCP\Files\File; use OCP\Files\FileInfo; use OCP\IImage; -use Psr\Log\LoggerInterface; +use OCP\ITempManager; +use OCP\Server; abstract class Office extends ProviderV2 { /** @@ -49,51 +29,60 @@ abstract class Office extends ProviderV2 { return null; } - $absPath = $this->getLocalFile($file); - - $tmpDir = \OC::$server->getTempManager()->getTempBaseDir(); + $tempManager = Server::get(ITempManager::class); - $defaultParameters = ' -env:UserInstallation=file://' . escapeshellarg($tmpDir . '/owncloud-' . \OC_Util::getInstanceId() . '/') . ' --headless --nologo --nofirststartwizard --invisible --norestore --convert-to png --outdir '; - $clParameters = \OC::$server->getConfig()->getSystemValue('preview_office_cl_parameters', $defaultParameters); + // The file to generate the preview for. + $absPath = $this->getLocalFile($file); - $cmd = $this->options['officeBinary'] . $clParameters . escapeshellarg($tmpDir) . ' ' . escapeshellarg($absPath); + // The destination for the LibreOffice user profile. + // LibreOffice can rune once per user profile and therefore instance id and file id are included. + $profile = $tempManager->getTemporaryFolder( + 'nextcloud-office-profile-' . \OC_Util::getInstanceId() . '-' . $file->getId() + ); - exec($cmd, $output, $returnCode); + // The destination for the LibreOffice convert result. + $outdir = $tempManager->getTemporaryFolder( + 'nextcloud-office-preview-' . \OC_Util::getInstanceId() . '-' . $file->getId() + ); - if ($returnCode !== 0) { + if ($profile === false || $outdir === false) { $this->cleanTmpFiles(); return null; } - //create imagick object from png - $pngPreview = null; - try { - [$dirname, , , $filename] = array_values(pathinfo($absPath)); - $pngPreview = $tmpDir . '/' . $filename . '.png'; + $parameters = [ + $this->options['officeBinary'], + '-env:UserInstallation=file://' . escapeshellarg($profile), + '--headless', + '--nologo', + '--nofirststartwizard', + '--invisible', + '--norestore', + '--convert-to png', + '--outdir ' . escapeshellarg($outdir), + escapeshellarg($absPath), + ]; - $png = new \Imagick($pngPreview . '[0]'); - $png->setImageFormat('jpg'); - } catch (\Exception $e) { + $cmd = implode(' ', $parameters); + exec($cmd, $output, $returnCode); + + if ($returnCode !== 0) { $this->cleanTmpFiles(); - unlink($pngPreview); - \OC::$server->get(LoggerInterface::class)->error($e->getMessage(), [ - 'exception' => $e, - 'app' => 'core', - ]); return null; } + $preview = $outdir . pathinfo($absPath, PATHINFO_FILENAME) . '.png'; + $image = new \OCP\Image(); - $image->loadFromData((string) $png); + $image->loadFromFile($preview); $this->cleanTmpFiles(); - unlink($pngPreview); if ($image->valid()) { $image->scaleDownToFit($maxX, $maxY); - return $image; } + return null; } } diff --git a/lib/private/Preview/OpenDocument.php b/lib/private/Preview/OpenDocument.php index 5f27e325d31..f590eb6a59c 100644 --- a/lib/private/Preview/OpenDocument.php +++ b/lib/private/Preview/OpenDocument.php @@ -1,26 +1,9 @@ <?php + /** - * @copyright Copyright (c) 2016, ownCloud, Inc. - * - * @author Joas Schilling <coding@schilljs.com> - * @author Julius Härtl <jus@bitgrid.net> - * @author Robin Appelman <robin@icewind.nl> - * @author Roeland Jago Douma <roeland@famdouma.nl> - * - * @license AGPL-3.0 - * - * This code is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License, version 3, - * as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License, version 3, - * along with this program. If not, see <http://www.gnu.org/licenses/> - * + * SPDX-FileCopyrightText: 2016-2024 Nextcloud GmbH and Nextcloud contributors + * SPDX-FileCopyrightText: 2016 ownCloud, Inc. + * SPDX-License-Identifier: AGPL-3.0-only */ namespace OC\Preview; diff --git a/lib/private/Preview/PDF.php b/lib/private/Preview/PDF.php index 405fd1545f9..9de14685925 100644 --- a/lib/private/Preview/PDF.php +++ b/lib/private/Preview/PDF.php @@ -1,25 +1,9 @@ <?php + /** - * @copyright Copyright (c) 2016, ownCloud, Inc. - * - * @author Joas Schilling <coding@schilljs.com> - * @author Morris Jobke <hey@morrisjobke.de> - * @author Robin Appelman <robin@icewind.nl> - * - * @license AGPL-3.0 - * - * This code is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License, version 3, - * as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License, version 3, - * along with this program. If not, see <http://www.gnu.org/licenses/> - * + * SPDX-FileCopyrightText: 2016-2024 Nextcloud GmbH and Nextcloud contributors + * SPDX-FileCopyrightText: 2016 ownCloud, Inc. + * SPDX-License-Identifier: AGPL-3.0-only */ namespace OC\Preview; @@ -31,4 +15,11 @@ class PDF extends Bitmap { public function getMimeType(): string { return '/application\/pdf/'; } + + /** + * {@inheritDoc} + */ + protected function getAllowedMimeTypes(): string { + return '/application\/pdf/'; + } } diff --git a/lib/private/Preview/PNG.php b/lib/private/Preview/PNG.php index 5a3f2136575..49d0cd059ba 100644 --- a/lib/private/Preview/PNG.php +++ b/lib/private/Preview/PNG.php @@ -1,24 +1,9 @@ <?php + /** - * @copyright Copyright (c) 2016, ownCloud, Inc. - * - * @author Olivier Paroz <github@oparoz.com> - * @author Robin Appelman <robin@icewind.nl> - * - * @license AGPL-3.0 - * - * This code is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License, version 3, - * as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License, version 3, - * along with this program. If not, see <http://www.gnu.org/licenses/> - * + * SPDX-FileCopyrightText: 2016-2024 Nextcloud GmbH and Nextcloud contributors + * SPDX-FileCopyrightText: 2016 ownCloud, Inc. + * SPDX-License-Identifier: AGPL-3.0-only */ namespace OC\Preview; diff --git a/lib/private/Preview/Photoshop.php b/lib/private/Preview/Photoshop.php index 9d79abf8191..b7209120530 100644 --- a/lib/private/Preview/Photoshop.php +++ b/lib/private/Preview/Photoshop.php @@ -1,25 +1,9 @@ <?php + /** - * @copyright Copyright (c) 2016, ownCloud, Inc. - * - * @author Joas Schilling <coding@schilljs.com> - * @author Morris Jobke <hey@morrisjobke.de> - * @author Robin Appelman <robin@icewind.nl> - * - * @license AGPL-3.0 - * - * This code is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License, version 3, - * as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License, version 3, - * along with this program. If not, see <http://www.gnu.org/licenses/> - * + * SPDX-FileCopyrightText: 2016-2024 Nextcloud GmbH and Nextcloud contributors + * SPDX-FileCopyrightText: 2016 ownCloud, Inc. + * SPDX-License-Identifier: AGPL-3.0-only */ namespace OC\Preview; @@ -31,4 +15,11 @@ class Photoshop extends Bitmap { public function getMimeType(): string { return '/application\/x-photoshop/'; } + + /** + * {@inheritDoc} + */ + protected function getAllowedMimeTypes(): string { + return '/(application|image)\/(x-photoshop|x-psd)/'; + } } diff --git a/lib/private/Preview/Postscript.php b/lib/private/Preview/Postscript.php index 06ea7fc8037..04c667926aa 100644 --- a/lib/private/Preview/Postscript.php +++ b/lib/private/Preview/Postscript.php @@ -1,25 +1,9 @@ <?php + /** - * @copyright Copyright (c) 2016, ownCloud, Inc. - * - * @author Joas Schilling <coding@schilljs.com> - * @author Morris Jobke <hey@morrisjobke.de> - * @author Robin Appelman <robin@icewind.nl> - * - * @license AGPL-3.0 - * - * This code is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License, version 3, - * as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License, version 3, - * along with this program. If not, see <http://www.gnu.org/licenses/> - * + * SPDX-FileCopyrightText: 2016-2024 Nextcloud GmbH and Nextcloud contributors + * SPDX-FileCopyrightText: 2016 ownCloud, Inc. + * SPDX-License-Identifier: AGPL-3.0-only */ namespace OC\Preview; @@ -31,4 +15,11 @@ class Postscript extends Bitmap { public function getMimeType(): string { return '/application\/postscript/'; } + + /** + * {@inheritDoc} + */ + protected function getAllowedMimeTypes(): string { + return '/application\/postscript/'; + } } diff --git a/lib/private/Preview/Provider.php b/lib/private/Preview/Provider.php index e87aded4786..26f0ac09f08 100644 --- a/lib/private/Preview/Provider.php +++ b/lib/private/Preview/Provider.php @@ -1,28 +1,9 @@ <?php + /** - * @copyright Copyright (c) 2016, ownCloud, Inc. - * - * @author Georg Ehrke <oc.list@georgehrke.com> - * @author Joas Schilling <coding@schilljs.com> - * @author Jörn Friedrich Dreyer <jfd@butonic.de> - * @author Olivier Paroz <github@oparoz.com> - * @author Robin Appelman <robin@icewind.nl> - * @author Roeland Jago Douma <roeland@famdouma.nl> - * - * @license AGPL-3.0 - * - * This code is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License, version 3, - * as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License, version 3, - * along with this program. If not, see <http://www.gnu.org/licenses/> - * + * SPDX-FileCopyrightText: 2016-2024 Nextcloud GmbH and Nextcloud contributors + * SPDX-FileCopyrightText: 2016 ownCloud, Inc. + * SPDX-License-Identifier: AGPL-3.0-only */ namespace OC\Preview; diff --git a/lib/private/Preview/ProviderV1Adapter.php b/lib/private/Preview/ProviderV1Adapter.php index 5e50db9d8da..ba8826ef765 100644 --- a/lib/private/Preview/ProviderV1Adapter.php +++ b/lib/private/Preview/ProviderV1Adapter.php @@ -3,26 +3,8 @@ declare(strict_types=1); /** - * @copyright Copyright (c) 2019 Robin Appelman <robin@icewind.nl> - * - * @author Julius Härtl <jus@bitgrid.net> - * @author Robin Appelman <robin@icewind.nl> - * - * @license GNU AGPL version 3 or any later version - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as - * published by the Free Software Foundation, either version 3 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - * + * SPDX-FileCopyrightText: 2019 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later */ namespace OC\Preview; diff --git a/lib/private/Preview/ProviderV2.php b/lib/private/Preview/ProviderV2.php index 0cb7eb59e21..ca10aa67b36 100644 --- a/lib/private/Preview/ProviderV2.php +++ b/lib/private/Preview/ProviderV2.php @@ -3,25 +3,8 @@ declare(strict_types=1); /** - * @copyright Copyright (c) 2019 Robin Appelman <robin@icewind.nl> - * - * @author Robin Appelman <robin@icewind.nl> - * - * @license GNU AGPL version 3 or any later version - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as - * published by the Free Software Foundation, either version 3 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - * + * SPDX-FileCopyrightText: 2019 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later */ namespace OC\Preview; @@ -83,7 +66,7 @@ abstract class ProviderV2 implements IProviderV2 { * @param int $maxSize maximum size for temporary files * @return string|false */ - protected function getLocalFile(File $file, int $maxSize = null) { + protected function getLocalFile(File $file, ?int $maxSize = null) { if ($this->useTempFile($file)) { $absPath = \OC::$server->getTempManager()->getTemporaryFile(); diff --git a/lib/private/Preview/SGI.php b/lib/private/Preview/SGI.php index 9f392d2252f..06ea9c0c69a 100644 --- a/lib/private/Preview/SGI.php +++ b/lib/private/Preview/SGI.php @@ -1,24 +1,7 @@ <?php /** - * @copyright 2021 John Molakvoæ <skjnldsv@protonmail.com> - * - * @author John Molakvoæ <skjnldsv@protonmail.com> - * - * @license GNU AGPL version 3 or any later version - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as - * published by the Free Software Foundation, either version 3 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - * + * SPDX-FileCopyrightText: 2021 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later */ namespace OC\Preview; @@ -28,6 +11,13 @@ class SGI extends Bitmap { * {@inheritDoc} */ public function getMimeType(): string { - return '/image\/sgi/'; + return '/image\/(x-)?sgi/'; + } + + /** + * {@inheritDoc} + */ + protected function getAllowedMimeTypes(): string { + return '/image\/(x-)?sgi/'; } } diff --git a/lib/private/Preview/SVG.php b/lib/private/Preview/SVG.php index fd472083533..73dc0488bf1 100644 --- a/lib/private/Preview/SVG.php +++ b/lib/private/Preview/SVG.php @@ -1,29 +1,9 @@ <?php + /** - * @copyright Copyright (c) 2016, ownCloud, Inc. - * - * @author Arthur Schiwon <blizzz@arthur-schiwon.de> - * @author Georg Ehrke <oc.list@georgehrke.com> - * @author Joas Schilling <coding@schilljs.com> - * @author Morris Jobke <hey@morrisjobke.de> - * @author Olivier Paroz <github@oparoz.com> - * @author Robin Appelman <robin@icewind.nl> - * @author Roeland Jago Douma <roeland@famdouma.nl> - * - * @license AGPL-3.0 - * - * This code is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License, version 3, - * as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License, version 3, - * along with this program. If not, see <http://www.gnu.org/licenses/> - * + * SPDX-FileCopyrightText: 2016-2024 Nextcloud GmbH and Nextcloud contributors + * SPDX-FileCopyrightText: 2016 ownCloud, Inc. + * SPDX-License-Identifier: AGPL-3.0-only */ namespace OC\Preview; @@ -44,26 +24,35 @@ class SVG extends ProviderV2 { */ public function getThumbnail(File $file, int $maxX, int $maxY): ?IImage { try { - $svg = new \Imagick(); - $svg->setBackgroundColor(new \ImagickPixel('transparent')); - $content = stream_get_contents($file->fopen('r')); if (substr($content, 0, 5) !== '<?xml') { $content = '<?xml version="1.0" encoding="UTF-8" standalone="no"?>' . $content; } // Do not parse SVG files with references - if (stripos($content, 'xlink:href') !== false) { + if (preg_match('/["\s](xlink:)?href\s*=/i', $content)) { return null; } + $svg = new \Imagick(); + + $svg->pingImageBlob($content); + $mimeType = $svg->getImageMimeType(); + if (!preg_match($this->getMimeType(), $mimeType)) { + throw new \Exception('File mime type does not match the preview provider: ' . $mimeType); + } + + $svg->setBackgroundColor(new \ImagickPixel('transparent')); $svg->readImageBlob($content); $svg->setImageFormat('png32'); } catch (\Exception $e) { - \OC::$server->get(LoggerInterface::class)->error($e->getMessage(), [ - 'exception' => $e, - 'app' => 'core', - ]); + \OC::$server->get(LoggerInterface::class)->error( + 'File: ' . $file->getPath() . ' Imagick says:', + [ + 'exception' => $e, + 'app' => 'core', + ] + ); return null; } diff --git a/lib/private/Preview/StarOffice.php b/lib/private/Preview/StarOffice.php index 58f210d2ff6..9ea540dc912 100644 --- a/lib/private/Preview/StarOffice.php +++ b/lib/private/Preview/StarOffice.php @@ -1,25 +1,9 @@ <?php + /** - * @copyright Copyright (c) 2016, ownCloud, Inc. - * - * @author Joas Schilling <coding@schilljs.com> - * @author Robin Appelman <robin@icewind.nl> - * @author Roeland Jago Douma <roeland@famdouma.nl> - * - * @license AGPL-3.0 - * - * This code is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License, version 3, - * as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License, version 3, - * along with this program. If not, see <http://www.gnu.org/licenses/> - * + * SPDX-FileCopyrightText: 2016-2024 Nextcloud GmbH and Nextcloud contributors + * SPDX-FileCopyrightText: 2016 ownCloud, Inc. + * SPDX-License-Identifier: AGPL-3.0-only */ namespace OC\Preview; diff --git a/lib/private/Preview/Storage/Root.php b/lib/private/Preview/Storage/Root.php index c4191228ec7..41378653962 100644 --- a/lib/private/Preview/Storage/Root.php +++ b/lib/private/Preview/Storage/Root.php @@ -3,26 +3,8 @@ declare(strict_types=1); /** - * @copyright Copyright (c) 2020, Roeland Jago Douma <roeland@famdouma.nl> - * - * @author Morris Jobke <hey@morrisjobke.de> - * @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/>. - * + * SPDX-FileCopyrightText: 2020 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later */ namespace OC\Preview\Storage; diff --git a/lib/private/Preview/TGA.php b/lib/private/Preview/TGA.php index cb591be2231..62e5aadc2af 100644 --- a/lib/private/Preview/TGA.php +++ b/lib/private/Preview/TGA.php @@ -1,24 +1,7 @@ <?php /** - * @copyright 2021 John Molakvoæ <skjnldsv@protonmail.com> - * - * @author John Molakvoæ <skjnldsv@protonmail.com> - * - * @license GNU AGPL version 3 or any later version - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as - * published by the Free Software Foundation, either version 3 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - * + * SPDX-FileCopyrightText: 2021 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later */ namespace OC\Preview; @@ -28,6 +11,13 @@ class TGA extends Bitmap { * {@inheritDoc} */ public function getMimeType(): string { - return '/image\/t(ar)?ga/'; + return '/image\/(x-)?t(ar)?ga/'; + } + + /** + * {@inheritDoc} + */ + protected function getAllowedMimeTypes(): string { + return '/image\/(x-)?t(ar)?ga/'; } } diff --git a/lib/private/Preview/TIFF.php b/lib/private/Preview/TIFF.php index 1a5d957d3ee..cd81e611d0b 100644 --- a/lib/private/Preview/TIFF.php +++ b/lib/private/Preview/TIFF.php @@ -1,25 +1,9 @@ <?php + /** - * @copyright Copyright (c) 2016, ownCloud, Inc. - * - * @author Joas Schilling <coding@schilljs.com> - * @author Morris Jobke <hey@morrisjobke.de> - * @author Robin Appelman <robin@icewind.nl> - * - * @license AGPL-3.0 - * - * This code is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License, version 3, - * as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License, version 3, - * along with this program. If not, see <http://www.gnu.org/licenses/> - * + * SPDX-FileCopyrightText: 2016-2024 Nextcloud GmbH and Nextcloud contributors + * SPDX-FileCopyrightText: 2016 ownCloud, Inc. + * SPDX-License-Identifier: AGPL-3.0-only */ namespace OC\Preview; @@ -31,4 +15,11 @@ class TIFF extends Bitmap { public function getMimeType(): string { return '/image\/tiff/'; } + + /** + * {@inheritDoc} + */ + protected function getAllowedMimeTypes(): string { + return '/image\/tiff/'; + } } diff --git a/lib/private/Preview/TXT.php b/lib/private/Preview/TXT.php index f5a68f92be4..68597f8dbd0 100644 --- a/lib/private/Preview/TXT.php +++ b/lib/private/Preview/TXT.php @@ -1,31 +1,9 @@ <?php + /** - * @copyright Copyright (c) 2016, ownCloud, Inc. - * - * @author Christoph Wurst <christoph@winzerhof-wurst.at> - * @author Georg Ehrke <oc.list@georgehrke.com> - * @author Jan-Christoph Borchardt <hey@jancborchardt.net> - * @author Joas Schilling <coding@schilljs.com> - * @author Morris Jobke <hey@morrisjobke.de> - * @author Nmz <nemesiz@nmz.lt> - * @author Robin Appelman <robin@icewind.nl> - * @author Roeland Jago Douma <roeland@famdouma.nl> - * @author Thomas Müller <thomas.mueller@tmit.eu> - * - * @license AGPL-3.0 - * - * This code is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License, version 3, - * as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License, version 3, - * along with this program. If not, see <http://www.gnu.org/licenses/> - * + * SPDX-FileCopyrightText: 2016-2024 Nextcloud GmbH and Nextcloud contributors + * SPDX-FileCopyrightText: 2016 ownCloud, Inc. + * SPDX-License-Identifier: AGPL-3.0-only */ namespace OC\Preview; diff --git a/lib/private/Preview/Watcher.php b/lib/private/Preview/Watcher.php index 7f4593f9fe3..abddd7b5acb 100644 --- a/lib/private/Preview/Watcher.php +++ b/lib/private/Preview/Watcher.php @@ -3,25 +3,8 @@ declare(strict_types=1); /** - * @copyright Copyright (c) 2016, Roeland Jago Douma <roeland@famdouma.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/>. - * + * SPDX-FileCopyrightText: 2016 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later */ namespace OC\Preview; @@ -61,6 +44,9 @@ class Watcher { } try { + if (is_null($node->getId())) { + return; + } $folder = $this->appData->getFolder((string)$node->getId()); $folder->delete(); } catch (NotFoundException $e) { diff --git a/lib/private/Preview/WatcherConnector.php b/lib/private/Preview/WatcherConnector.php index ffbdf825211..ae2a136ca78 100644 --- a/lib/private/Preview/WatcherConnector.php +++ b/lib/private/Preview/WatcherConnector.php @@ -3,25 +3,8 @@ declare(strict_types=1); /** - * @copyright Copyright (c) 2016, Roeland Jago Douma <roeland@famdouma.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/>. - * + * SPDX-FileCopyrightText: 2016 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later */ namespace OC\Preview; @@ -43,7 +26,7 @@ class WatcherConnector { * @param SystemConfig $config */ public function __construct(IRootFolder $root, - SystemConfig $config) { + SystemConfig $config) { $this->root = $root; $this->config = $config; } diff --git a/lib/private/Preview/WebP.php b/lib/private/Preview/WebP.php index c8f8d11c393..25b922e9190 100644 --- a/lib/private/Preview/WebP.php +++ b/lib/private/Preview/WebP.php @@ -3,25 +3,8 @@ declare(strict_types=1); /** - * @copyright Copyright (c) 2018 Roeland Jago Douma <roeland@famdouma.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/>. - * + * SPDX-FileCopyrightText: 2018 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later */ namespace OC\Preview; diff --git a/lib/private/Preview/XBitmap.php b/lib/private/Preview/XBitmap.php index e0adb48b881..c8337cc252d 100644 --- a/lib/private/Preview/XBitmap.php +++ b/lib/private/Preview/XBitmap.php @@ -1,24 +1,9 @@ <?php + /** - * @copyright Copyright (c) 2016, ownCloud, Inc. - * - * @author Olivier Paroz <github@oparoz.com> - * @author Robin Appelman <robin@icewind.nl> - * - * @license AGPL-3.0 - * - * This code is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License, version 3, - * as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License, version 3, - * along with this program. If not, see <http://www.gnu.org/licenses/> - * + * SPDX-FileCopyrightText: 2016-2024 Nextcloud GmbH and Nextcloud contributors + * SPDX-FileCopyrightText: 2016 ownCloud, Inc. + * SPDX-License-Identifier: AGPL-3.0-only */ namespace OC\Preview; diff --git a/lib/private/PreviewManager.php b/lib/private/PreviewManager.php index 3af6848686e..21dd8f8a8ab 100644 --- a/lib/private/PreviewManager.php +++ b/lib/private/PreviewManager.php @@ -1,32 +1,9 @@ <?php + /** - * @copyright Copyright (c) 2016, ownCloud, Inc. - * - * @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 Morris Jobke <hey@morrisjobke.de> - * @author Olivier Paroz <github@oparoz.com> - * @author Robin Appelman <robin@icewind.nl> - * @author Roeland Jago Douma <roeland@famdouma.nl> - * @author Sebastian Steinmetz <462714+steiny2k@users.noreply.github.com> - * @author Thomas Müller <thomas.mueller@tmit.eu> - * - * @license AGPL-3.0 - * - * This code is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License, version 3, - * as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License, version 3, - * along with this program. If not, see <http://www.gnu.org/licenses/> - * + * SPDX-FileCopyrightText: 2016-2024 Nextcloud GmbH and Nextcloud contributors + * SPDX-FileCopyrightText: 2016 ownCloud, Inc. + * SPDX-License-Identifier: AGPL-3.0-only */ namespace OC; @@ -366,7 +343,7 @@ class PreviewManager implements IPreview { $this->registerCoreProvider(Preview\OpenDocument::class, '/application\/vnd.oasis.opendocument.*/'); $this->registerCoreProvider(Preview\Imaginary::class, Preview\Imaginary::supportedMimeTypes()); - // SVG, Office and Bitmap require imagick + // SVG and Bitmap require imagick if ($this->imagickSupport->hasExtension()) { $imagickProviders = [ 'SVG' => ['mimetype' => '/image\/svg\+xml/', 'class' => Preview\SVG::class], @@ -376,9 +353,9 @@ class PreviewManager implements IPreview { 'PSD' => ['mimetype' => '/application\/x-photoshop/', 'class' => Preview\Photoshop::class], 'EPS' => ['mimetype' => '/application\/postscript/', 'class' => Preview\Postscript::class], 'TTF' => ['mimetype' => '/application\/(?:font-sfnt|x-font$)/', 'class' => Preview\Font::class], - 'HEIC' => ['mimetype' => '/image\/hei(f|c)/', 'class' => Preview\HEIC::class], - 'TGA' => ['mimetype' => '/image\/t(ar)?ga/', 'class' => Preview\TGA::class], - 'SGI' => ['mimetype' => '/image\/sgi/', 'class' => Preview\SGI::class], + 'HEIC' => ['mimetype' => '/image\/(x-)?hei(f|c)/', 'class' => Preview\HEIC::class], + 'TGA' => ['mimetype' => '/image\/(x-)?t(ar)?ga/', 'class' => Preview\TGA::class], + 'SGI' => ['mimetype' => '/image\/(x-)?sgi/', 'class' => Preview\SGI::class], ]; foreach ($imagickProviders as $queryFormat => $provider) { @@ -391,27 +368,10 @@ class PreviewManager implements IPreview { $this->registerCoreProvider($class, $provider['mimetype']); } } - - if ($this->imagickSupport->supportsFormat('PDF')) { - // Office requires openoffice or libreoffice - $officeBinary = $this->config->getSystemValue('preview_libreoffice_path', null); - if (!is_string($officeBinary)) { - $officeBinary = $this->binaryFinder->findBinaryPath('libreoffice'); - } - if (!is_string($officeBinary)) { - $officeBinary = $this->binaryFinder->findBinaryPath('openoffice'); - } - - if (is_string($officeBinary)) { - $this->registerCoreProvider(Preview\MSOfficeDoc::class, '/application\/msword/', ["officeBinary" => $officeBinary]); - $this->registerCoreProvider(Preview\MSOffice2003::class, '/application\/vnd.ms-.*/', ["officeBinary" => $officeBinary]); - $this->registerCoreProvider(Preview\MSOffice2007::class, '/application\/vnd.openxmlformats-officedocument.*/', ["officeBinary" => $officeBinary]); - $this->registerCoreProvider(Preview\OpenDocument::class, '/application\/vnd.oasis.opendocument.*/', ["officeBinary" => $officeBinary]); - $this->registerCoreProvider(Preview\StarOffice::class, '/application\/vnd.sun.xml.*/', ["officeBinary" => $officeBinary]); - } - } } + $this->registerCoreProvidersOffice(); + // Video requires avconv or ffmpeg if (in_array(Preview\Movie::class, $this->getEnabledDefaultProvider())) { $movieBinary = $this->config->getSystemValue('preview_ffmpeg_path', null); @@ -429,6 +389,43 @@ class PreviewManager implements IPreview { } } + private function registerCoreProvidersOffice(): void { + $officeProviders = [ + ['mimetype' => '/application\/msword/', 'class' => Preview\MSOfficeDoc::class], + ['mimetype' => '/application\/vnd.ms-.*/', 'class' => Preview\MSOffice2003::class], + ['mimetype' => '/application\/vnd.openxmlformats-officedocument.*/', 'class' => Preview\MSOffice2007::class], + ['mimetype' => '/application\/vnd.oasis.opendocument.*/', 'class' => Preview\OpenDocument::class], + ['mimetype' => '/application\/vnd.sun.xml.*/', 'class' => Preview\StarOffice::class], + ['mimetype' => '/image\/emf/', 'class' => Preview\EMF::class], + ]; + + $findBinary = true; + $officeBinary = false; + + foreach ($officeProviders as $provider) { + $class = $provider['class']; + if (!in_array(trim($class, '\\'), $this->getEnabledDefaultProvider())) { + continue; + } + + if ($findBinary) { + // Office requires openoffice or libreoffice + $officeBinary = $this->config->getSystemValue('preview_libreoffice_path', false); + if ($officeBinary === false) { + $officeBinary = $this->binaryFinder->findBinaryPath('libreoffice'); + } + if ($officeBinary === false) { + $officeBinary = $this->binaryFinder->findBinaryPath('openoffice'); + } + $findBinary = false; + } + + if ($officeBinary) { + $this->registerCoreProvider($class, $provider['mimetype'], ['officeBinary' => $officeBinary]); + } + } + } + private function registerBootstrapProviders(): void { $context = $this->bootstrapCoordinator->getRegistrationContext(); diff --git a/lib/private/PreviewNotAvailableException.php b/lib/private/PreviewNotAvailableException.php index f4564546ea8..b1fec912769 100644 --- a/lib/private/PreviewNotAvailableException.php +++ b/lib/private/PreviewNotAvailableException.php @@ -1,24 +1,7 @@ <?php /** - * @copyright Copyright (c) 2016 Morris Jobke <hey@morrisjobke.de> - * - * @author Morris Jobke <hey@morrisjobke.de> - * - * @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/>. - * + * SPDX-FileCopyrightText: 2016 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later */ namespace OC; diff --git a/lib/private/Profile/Actions/EmailAction.php b/lib/private/Profile/Actions/EmailAction.php index 8ab4939b515..c01f368b476 100644 --- a/lib/private/Profile/Actions/EmailAction.php +++ b/lib/private/Profile/Actions/EmailAction.php @@ -3,25 +3,8 @@ declare(strict_types=1); /** - * @copyright 2021 Christopher Ng <chrng8@gmail.com> - * - * @author Christopher Ng <chrng8@gmail.com> - * - * @license GNU AGPL version 3 or any later version - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as - * published by the Free Software Foundation, either version 3 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - * + * SPDX-FileCopyrightText: 2021 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later */ namespace OC\Profile\Actions; @@ -33,26 +16,13 @@ use OCP\L10N\IFactory; use OCP\Profile\ILinkAction; class EmailAction implements ILinkAction { - /** @var string */ - private $value; - - /** @var IAccountManager */ - private $accountManager; - - /** @var IFactory */ - private $l10nFactory; - - /** @var IUrlGenerator */ - private $urlGenerator; + private string $value = ''; public function __construct( - IAccountManager $accountManager, - IFactory $l10nFactory, - IURLGenerator $urlGenerator + private IAccountManager $accountManager, + private IFactory $l10nFactory, + private IURLGenerator $urlGenerator, ) { - $this->accountManager = $accountManager; - $this->l10nFactory = $l10nFactory; - $this->urlGenerator = $urlGenerator; } public function preload(IUser $targetUser): void { diff --git a/lib/private/Profile/Actions/FediverseAction.php b/lib/private/Profile/Actions/FediverseAction.php index ed3fcd80b52..1076027629d 100644 --- a/lib/private/Profile/Actions/FediverseAction.php +++ b/lib/private/Profile/Actions/FediverseAction.php @@ -3,50 +3,27 @@ declare(strict_types=1); /** - * @copyright 2021 Christopher Ng <chrng8@gmail.com> - * - * @author Christopher Ng <chrng8@gmail.com> - * - * @license GNU AGPL version 3 or any later version - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as - * published by the Free Software Foundation, either version 3 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - * + * SPDX-FileCopyrightText: 2021 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later */ namespace OC\Profile\Actions; -use function Safe\substr; use OCP\Accounts\IAccountManager; use OCP\IURLGenerator; use OCP\IUser; use OCP\L10N\IFactory; use OCP\Profile\ILinkAction; +use function substr; class FediverseAction implements ILinkAction { - private ?string $value = null; - private IAccountManager $accountManager; - private IFactory $l10nFactory; - private IURLGenerator $urlGenerator; + private string $value = ''; public function __construct( - IAccountManager $accountManager, - IFactory $l10nFactory, - IURLGenerator $urlGenerator + private IAccountManager $accountManager, + private IFactory $l10nFactory, + private IURLGenerator $urlGenerator, ) { - $this->accountManager = $accountManager; - $this->l10nFactory = $l10nFactory; - $this->urlGenerator = $urlGenerator; } public function preload(IUser $targetUser): void { diff --git a/lib/private/Profile/Actions/PhoneAction.php b/lib/private/Profile/Actions/PhoneAction.php index 6081a04ad7e..598cdc49051 100644 --- a/lib/private/Profile/Actions/PhoneAction.php +++ b/lib/private/Profile/Actions/PhoneAction.php @@ -3,25 +3,8 @@ declare(strict_types=1); /** - * @copyright 2021 Christopher Ng <chrng8@gmail.com> - * - * @author Christopher Ng <chrng8@gmail.com> - * - * @license GNU AGPL version 3 or any later version - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as - * published by the Free Software Foundation, either version 3 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - * + * SPDX-FileCopyrightText: 2021 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later */ namespace OC\Profile\Actions; @@ -33,26 +16,13 @@ use OCP\L10N\IFactory; use OCP\Profile\ILinkAction; class PhoneAction implements ILinkAction { - /** @var string */ - private $value; - - /** @var IAccountManager */ - private $accountManager; - - /** @var IFactory */ - private $l10nFactory; - - /** @var IUrlGenerator */ - private $urlGenerator; + private string $value = ''; public function __construct( - IAccountManager $accountManager, - IFactory $l10nFactory, - IURLGenerator $urlGenerator + private IAccountManager $accountManager, + private IFactory $l10nFactory, + private IURLGenerator $urlGenerator, ) { - $this->accountManager = $accountManager; - $this->l10nFactory = $l10nFactory; - $this->urlGenerator = $urlGenerator; } public function preload(IUser $targetUser): void { diff --git a/lib/private/Profile/Actions/TwitterAction.php b/lib/private/Profile/Actions/TwitterAction.php index 041da42e539..78e7137f64b 100644 --- a/lib/private/Profile/Actions/TwitterAction.php +++ b/lib/private/Profile/Actions/TwitterAction.php @@ -3,57 +3,27 @@ declare(strict_types=1); /** - * @copyright 2021 Christopher Ng <chrng8@gmail.com> - * - * @author Christopher Ng <chrng8@gmail.com> - * - * @license GNU AGPL version 3 or any later version - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as - * published by the Free Software Foundation, either version 3 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - * + * SPDX-FileCopyrightText: 2021 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later */ namespace OC\Profile\Actions; -use function Safe\substr; use OCP\Accounts\IAccountManager; use OCP\IURLGenerator; use OCP\IUser; use OCP\L10N\IFactory; use OCP\Profile\ILinkAction; +use function substr; class TwitterAction implements ILinkAction { - /** @var string */ - private $value; - - /** @var IAccountManager */ - private $accountManager; - - /** @var IFactory */ - private $l10nFactory; - - /** @var IUrlGenerator */ - private $urlGenerator; + private string $value = ''; public function __construct( - IAccountManager $accountManager, - IFactory $l10nFactory, - IURLGenerator $urlGenerator + private IAccountManager $accountManager, + private IFactory $l10nFactory, + private IURLGenerator $urlGenerator, ) { - $this->accountManager = $accountManager; - $this->l10nFactory = $l10nFactory; - $this->urlGenerator = $urlGenerator; } public function preload(IUser $targetUser): void { diff --git a/lib/private/Profile/Actions/WebsiteAction.php b/lib/private/Profile/Actions/WebsiteAction.php index 6b052be57bd..4f5dcb568e7 100644 --- a/lib/private/Profile/Actions/WebsiteAction.php +++ b/lib/private/Profile/Actions/WebsiteAction.php @@ -3,25 +3,8 @@ declare(strict_types=1); /** - * @copyright 2021 Christopher Ng <chrng8@gmail.com> - * - * @author Christopher Ng <chrng8@gmail.com> - * - * @license GNU AGPL version 3 or any later version - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as - * published by the Free Software Foundation, either version 3 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - * + * SPDX-FileCopyrightText: 2021 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later */ namespace OC\Profile\Actions; @@ -33,26 +16,13 @@ use OCP\L10N\IFactory; use OCP\Profile\ILinkAction; class WebsiteAction implements ILinkAction { - /** @var string */ - private $value; - - /** @var IAccountManager */ - private $accountManager; - - /** @var IFactory */ - private $l10nFactory; - - /** @var IUrlGenerator */ - private $urlGenerator; + private string $value = ''; public function __construct( - IAccountManager $accountManager, - IFactory $l10nFactory, - IURLGenerator $urlGenerator + private IAccountManager $accountManager, + private IFactory $l10nFactory, + private IURLGenerator $urlGenerator, ) { - $this->accountManager = $accountManager; - $this->l10nFactory = $l10nFactory; - $this->urlGenerator = $urlGenerator; } public function preload(IUser $targetUser): void { diff --git a/lib/private/Profile/ProfileManager.php b/lib/private/Profile/ProfileManager.php index f20ae74768e..e575740f970 100644 --- a/lib/private/Profile/ProfileManager.php +++ b/lib/private/Profile/ProfileManager.php @@ -3,85 +3,42 @@ declare(strict_types=1); /** - * @copyright 2021 Christopher Ng <chrng8@gmail.com> - * - * @author Christopher Ng <chrng8@gmail.com> - * - * @license GNU AGPL version 3 or any later version - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as - * published by the Free Software Foundation, either version 3 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - * + * SPDX-FileCopyrightText: 2021 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later */ namespace OC\Profile; -use function Safe\array_flip; -use function Safe\usort; use OC\AppFramework\Bootstrap\Coordinator; use OC\Core\Db\ProfileConfig; use OC\Core\Db\ProfileConfigMapper; use OC\KnownUser\KnownUserService; use OC\Profile\Actions\EmailAction; +use OC\Profile\Actions\FediverseAction; use OC\Profile\Actions\PhoneAction; use OC\Profile\Actions\TwitterAction; -use OC\Profile\Actions\FediverseAction; use OC\Profile\Actions\WebsiteAction; use OCP\Accounts\IAccountManager; use OCP\Accounts\PropertyDoesNotExistException; use OCP\App\IAppManager; use OCP\AppFramework\Db\DoesNotExistException; +use OCP\Cache\CappedMemoryCache; use OCP\IConfig; use OCP\IUser; use OCP\L10N\IFactory; use OCP\Profile\ILinkAction; -use OCP\Cache\CappedMemoryCache; +use OCP\Profile\IProfileManager; use Psr\Container\ContainerInterface; use Psr\Log\LoggerInterface; +use function array_flip; +use function usort; -class ProfileManager { - /** @var IAccountManager */ - private $accountManager; - - /** @var IAppManager */ - private $appManager; - - /** @var IConfig */ - private $config; - - /** @var ProfileConfigMapper */ - private $configMapper; - - /** @var ContainerInterface */ - private $container; - - /** @var KnownUserService */ - private $knownUserService; - - /** @var IFactory */ - private $l10nFactory; - - /** @var LoggerInterface */ - private $logger; - - /** @var Coordinator */ - private $coordinator; - +class ProfileManager implements IProfileManager { /** @var ILinkAction[] */ - private $actions = []; + private array $actions = []; /** @var null|ILinkAction[] */ - private $sortedActions = null; + private ?array $sortedActions = null; /** @var CappedMemoryCache<ProfileConfig> */ private CappedMemoryCache $configCache; @@ -112,32 +69,23 @@ class ProfileManager { ]; public function __construct( - IAccountManager $accountManager, - IAppManager $appManager, - IConfig $config, - ProfileConfigMapper $configMapper, - ContainerInterface $container, - KnownUserService $knownUserService, - IFactory $l10nFactory, - LoggerInterface $logger, - Coordinator $coordinator + private IAccountManager $accountManager, + private IAppManager $appManager, + private IConfig $config, + private ProfileConfigMapper $configMapper, + private ContainerInterface $container, + private KnownUserService $knownUserService, + private IFactory $l10nFactory, + private LoggerInterface $logger, + private Coordinator $coordinator, ) { - $this->accountManager = $accountManager; - $this->appManager = $appManager; - $this->config = $config; - $this->configMapper = $configMapper; - $this->container = $container; - $this->knownUserService = $knownUserService; - $this->l10nFactory = $l10nFactory; - $this->logger = $logger; - $this->coordinator = $coordinator; $this->configCache = new CappedMemoryCache(); } /** * If no user is passed as an argument return whether profile is enabled globally in `config.php` */ - public function isProfileEnabled(?IUser $user = null): ?bool { + public function isProfileEnabled(?IUser $user = null): bool { $profileEnabledGlobally = $this->config->getSystemValueBool('profile.enabled', true); if (empty($user) || !$profileEnabledGlobally) { @@ -145,7 +93,7 @@ class ProfileManager { } $account = $this->accountManager->getAccount($user); - return filter_var( + return (bool) filter_var( $account->getProperty(IAccountManager::PROPERTY_PROFILE_ENABLED)->getValue(), FILTER_VALIDATE_BOOLEAN, FILTER_NULL_ON_FAILURE, @@ -229,57 +177,54 @@ class ProfileManager { * Return whether the profile parameter of the target user * is visible to the visiting user */ - private function isParameterVisible(string $paramId, IUser $targetUser, ?IUser $visitingUser): bool { + public function isProfileFieldVisible(string $profileField, IUser $targetUser, ?IUser $visitingUser): bool { try { $account = $this->accountManager->getAccount($targetUser); - $scope = $account->getProperty($paramId)->getScope(); + $scope = $account->getProperty($profileField)->getScope(); } catch (PropertyDoesNotExistException $e) { // Allow the exception as not all profile parameters are account properties } - $visibility = $this->getProfileConfig($targetUser, $visitingUser)[$paramId]['visibility']; + $visibility = $this->getProfileConfig($targetUser, $visitingUser)[$profileField]['visibility']; // Handle profile visibility and account property scope - switch ($visibility) { - case ProfileConfig::VISIBILITY_HIDE: - return false; - case ProfileConfig::VISIBILITY_SHOW_USERS_ONLY: - if (!empty($scope)) { - switch ($scope) { - case IAccountManager::SCOPE_PRIVATE: - return $visitingUser !== null && $this->knownUserService->isKnownToUser($targetUser->getUID(), $visitingUser->getUID()); - case IAccountManager::SCOPE_LOCAL: - case IAccountManager::SCOPE_FEDERATED: - case IAccountManager::SCOPE_PUBLISHED: - return $visitingUser !== null; - default: - return false; - } - } + + if ($visibility === self::VISIBILITY_SHOW_USERS_ONLY) { + if (empty($scope)) { return $visitingUser !== null; - case ProfileConfig::VISIBILITY_SHOW: - if (!empty($scope)) { - switch ($scope) { - case IAccountManager::SCOPE_PRIVATE: - return $visitingUser !== null && $this->knownUserService->isKnownToUser($targetUser->getUID(), $visitingUser->getUID()); - case IAccountManager::SCOPE_LOCAL: - case IAccountManager::SCOPE_FEDERATED: - case IAccountManager::SCOPE_PUBLISHED: - return true; - default: - return false; - } - } + } + + return match ($scope) { + IAccountManager::SCOPE_PRIVATE => $visitingUser !== null && $this->knownUserService->isKnownToUser($targetUser->getUID(), $visitingUser->getUID()), + IAccountManager::SCOPE_LOCAL, + IAccountManager::SCOPE_FEDERATED, + IAccountManager::SCOPE_PUBLISHED => $visitingUser !== null, + default => false, + }; + } + + if ($visibility === self::VISIBILITY_SHOW) { + if (empty($scope)) { return true; - default: - return false; + } + + return match ($scope) { + IAccountManager::SCOPE_PRIVATE => $visitingUser !== null && $this->knownUserService->isKnownToUser($targetUser->getUID(), $visitingUser->getUID()), + IAccountManager::SCOPE_LOCAL, + IAccountManager::SCOPE_FEDERATED, + IAccountManager::SCOPE_PUBLISHED => true, + default => false, + }; } + + return false; } /** * Return the profile parameters of the target user that are visible to the visiting user * in an associative array + * @return array{userId: string, address?: string|null, biography?: string|null, displayname?: string|null, headline?: string|null, isUserAvatarVisible?: bool, organisation?: string|null, role?: string|null, actions: list<array{id: string, icon: string, title: string, target: ?string}>} */ - public function getProfileParams(IUser $targetUser, ?IUser $visitingUser): array { + public function getProfileFields(IUser $targetUser, ?IUser $visitingUser): array { $account = $this->accountManager->getAccount($targetUser); // Initialize associative array of profile parameters @@ -297,14 +242,14 @@ class ProfileManager { case IAccountManager::PROPERTY_ORGANISATION: case IAccountManager::PROPERTY_ROLE: $profileParameters[$property] = - $this->isParameterVisible($property, $targetUser, $visitingUser) + $this->isProfileFieldVisible($property, $targetUser, $visitingUser) // Explicitly set to null when value is empty string ? ($account->getProperty($property)->getValue() ?: null) : null; break; case IAccountManager::PROPERTY_AVATAR: // Add avatar visibility - $profileParameters['isUserAvatarVisible'] = $this->isParameterVisible($property, $targetUser, $visitingUser); + $profileParameters['isUserAvatarVisible'] = $this->isProfileFieldVisible($property, $targetUser, $visitingUser); break; } } @@ -324,7 +269,7 @@ class ProfileManager { array_filter( $this->getActions($targetUser, $visitingUser), function (ILinkAction $action) use ($targetUser, $visitingUser) { - return $this->isParameterVisible($action->getId(), $targetUser, $visitingUser); + return $this->isProfileFieldVisible($action->getId(), $targetUser, $visitingUser); } ), ) @@ -356,12 +301,12 @@ class ProfileManager { // Construct the default config for actions $actionsConfig = []; foreach ($this->getActions($targetUser, $visitingUser) as $action) { - $actionsConfig[$action->getId()] = ['visibility' => ProfileConfig::DEFAULT_VISIBILITY]; + $actionsConfig[$action->getId()] = ['visibility' => self::DEFAULT_VISIBILITY]; } // Construct the default config for account properties $propertiesConfig = []; - foreach (ProfileConfig::DEFAULT_PROPERTY_VISIBILITY as $property => $visibility) { + foreach (self::DEFAULT_PROPERTY_VISIBILITY as $property => $visibility) { $propertiesConfig[$property] = ['visibility' => $visibility]; } diff --git a/lib/private/Profile/TProfileHelper.php b/lib/private/Profile/TProfileHelper.php index 5b57e1c9d5c..ad24d82445c 100644 --- a/lib/private/Profile/TProfileHelper.php +++ b/lib/private/Profile/TProfileHelper.php @@ -3,25 +3,8 @@ declare(strict_types=1); /** - * @copyright 2022 Christopher Ng <chrng8@gmail.com> - * - * @author Christopher Ng <chrng8@gmail.com> - * - * @license GNU AGPL version 3 or any later version - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as - * published by the Free Software Foundation, either version 3 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - * + * SPDX-FileCopyrightText: 2022 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later */ namespace OC\Profile; diff --git a/lib/private/Profiler/FileProfilerStorage.php b/lib/private/Profiler/FileProfilerStorage.php index d7f3df752a6..8cb6f2e6f79 100644 --- a/lib/private/Profiler/FileProfilerStorage.php +++ b/lib/private/Profiler/FileProfilerStorage.php @@ -2,26 +2,8 @@ declare(strict_types = 1); /** - * @copyright 2022 Carl Schwan <carl@carlschwan.eu> - * - * @author Carl Schwan <carl@carlschwan.eu> - * @author Alexandre Salomé <alexandre.salome@gmail.com> - * - * @license AGPL-3.0-or-later AND MIT - * - * 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/>. - * + * SPDX-FileCopyrightText: 2022 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later */ namespace OC\Profiler; @@ -45,12 +27,12 @@ class FileProfilerStorage { public function __construct(string $folder) { $this->folder = $folder; - if (!is_dir($this->folder) && false === @mkdir($this->folder, 0777, true) && !is_dir($this->folder)) { + if (!is_dir($this->folder) && @mkdir($this->folder, 0777, true) === false && !is_dir($this->folder)) { throw new \RuntimeException(sprintf('Unable to create the storage directory (%s).', $this->folder)); } } - public function find(?string $url, ?int $limit, ?string $method, int $start = null, int $end = null, string $statusCode = null): array { + public function find(?string $url, ?int $limit, ?string $method, ?int $start = null, ?int $end = null, ?string $statusCode = null): array { $file = $this->getIndexFilename(); if (!file_exists($file)) { @@ -130,7 +112,7 @@ class FileProfilerStorage { if (!$profileIndexed) { // Create directory $dir = \dirname($file); - if (!is_dir($dir) && false === @mkdir($dir, 0777, true) && !is_dir($dir)) { + if (!is_dir($dir) && @mkdir($dir, 0777, true) === false && !is_dir($dir)) { throw new \RuntimeException(sprintf('Unable to create the storage directory (%s).', $dir)); } } @@ -162,7 +144,7 @@ class FileProfilerStorage { stream_context_set_option($context, 'zlib', 'level', 3); } - if (false === file_put_contents($file, serialize($data), 0, $context)) { + if (file_put_contents($file, serialize($data), 0, $context) === false) { return false; } @@ -221,7 +203,7 @@ class FileProfilerStorage { $line = ''; $position = ftell($file); - if (0 === $position) { + if ($position === 0) { return null; } @@ -230,7 +212,7 @@ class FileProfilerStorage { $position -= $chunkSize; fseek($file, $position); - if (0 === $chunkSize) { + if ($chunkSize === 0) { // bof reached break; } @@ -246,15 +228,15 @@ class FileProfilerStorage { $line = substr($buffer, $upTo + 1).$line; fseek($file, max(0, $position), \SEEK_SET); - if ('' !== $line) { + if ($line !== '') { break; } } - return '' === $line ? null : $line; + return $line === '' ? null : $line; } - protected function createProfileFromData(string $token, array $data, IProfile $parent = null): IProfile { + protected function createProfileFromData(string $token, array $data, ?IProfile $parent = null): IProfile { $profile = new Profile($token); $profile->setMethod($data['method']); $profile->setUrl($data['url']); diff --git a/lib/private/Profiler/Profile.php b/lib/private/Profiler/Profile.php index 648c49c0330..c611d79e259 100644 --- a/lib/private/Profiler/Profile.php +++ b/lib/private/Profiler/Profile.php @@ -2,25 +2,8 @@ declare(strict_types = 1); /** - * @copyright 2022 Carl Schwan <carl@carlschwan.eu> - * - * @author Carl Schwan <carl@carlschwan.eu> - * - * @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/>. - * + * SPDX-FileCopyrightText: 2022 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later */ namespace OC\Profiler; diff --git a/lib/private/Profiler/Profiler.php b/lib/private/Profiler/Profiler.php index 40050b7bf43..84a4e3eff34 100644 --- a/lib/private/Profiler/Profiler.php +++ b/lib/private/Profiler/Profiler.php @@ -3,35 +3,18 @@ declare(strict_types = 1); /** - * @copyright 2021 Carl Schwan <carl@carlschwan.eu> - * - * @author Carl Schwan <carl@carlschwan.eu> - * - * @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/>. - * + * SPDX-FileCopyrightText: 2021 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later */ namespace OC\Profiler; use OC\AppFramework\Http\Request; +use OC\SystemConfig; use OCP\AppFramework\Http\Response; use OCP\DataCollector\IDataCollector; -use OCP\Profiler\IProfiler; use OCP\Profiler\IProfile; -use OC\SystemConfig; +use OCP\Profiler\IProfiler; class Profiler implements IProfiler { /** @var array<string, IDataCollector> */ @@ -44,7 +27,7 @@ class Profiler implements IProfiler { public function __construct(SystemConfig $config) { $this->enabled = $config->getValue('profiler', false); if ($this->enabled) { - $this->storage = new FileProfilerStorage($config->getValue('datadirectory', \OC::$SERVERROOT . '/data') . '/profiler'); + $this->storage = new FileProfilerStorage($config->getValue('datadirectory', \OC::$SERVERROOT . '/data') . '/__profiler'); } } @@ -95,7 +78,7 @@ class Profiler implements IProfiler { * @return array[] */ public function find(?string $url, ?int $limit, ?string $method, ?int $start, ?int $end, - string $statusCode = null): array { + ?string $statusCode = null): array { if ($this->storage) { return $this->storage->find($url, $limit, $method, $start, $end, $statusCode); } else { diff --git a/lib/private/Profiler/RoutingDataCollector.php b/lib/private/Profiler/RoutingDataCollector.php index e6659230879..c8952c76a38 100644 --- a/lib/private/Profiler/RoutingDataCollector.php +++ b/lib/private/Profiler/RoutingDataCollector.php @@ -3,25 +3,8 @@ declare(strict_types=1); /** - * @copyright 2022 Carl Schwan <carl@carlschwan.eu> - * - * @author Carl Schwan <carl@carlschwan.eu> - * - * @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/>. - * + * SPDX-FileCopyrightText: 2022 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later */ namespace OC\Profiler; @@ -41,7 +24,7 @@ class RoutingDataCollector extends AbstractDataCollector { $this->actionName = $actionName; } - public function collect(Request $request, Response $response, \Throwable $exception = null): void { + public function collect(Request $request, Response $response, ?\Throwable $exception = null): void { $this->data = [ 'appName' => $this->appName, 'controllerName' => $this->controllerName, diff --git a/lib/private/RedisFactory.php b/lib/private/RedisFactory.php index 4903a8713df..abfec608a39 100644 --- a/lib/private/RedisFactory.php +++ b/lib/private/RedisFactory.php @@ -1,28 +1,9 @@ <?php + /** - * @copyright Copyright (c) 2016, ownCloud, Inc. - * - * @author Alejandro Varela <epma01@gmail.com> - * @author Christoph Wurst <christoph@winzerhof-wurst.at> - * @author Jörn Friedrich Dreyer <jfd@butonic.de> - * @author Morris Jobke <hey@morrisjobke.de> - * @author Robin Appelman <robin@icewind.nl> - * @author Robin McCorkell <robin@mccorkell.me.uk> - * - * @license AGPL-3.0 - * - * This code is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License, version 3, - * as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License, version 3, - * along with this program. If not, see <http://www.gnu.org/licenses/> - * + * SPDX-FileCopyrightText: 2016-2024 Nextcloud GmbH and Nextcloud contributors + * SPDX-FileCopyrightText: 2016 ownCloud, Inc. + * SPDX-License-Identifier: AGPL-3.0-only */ namespace OC; diff --git a/lib/private/Remote/Api/ApiBase.php b/lib/private/Remote/Api/ApiBase.php index af114bd9498..dff3edb51b9 100644 --- a/lib/private/Remote/Api/ApiBase.php +++ b/lib/private/Remote/Api/ApiBase.php @@ -1,24 +1,7 @@ <?php /** - * @copyright Copyright (c) 2017 Robin Appelman <robin@icewind.nl> - * - * @author Robin Appelman <robin@icewind.nl> - * - * @license GNU AGPL version 3 or any later version - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as - * published by the Free Software Foundation, either version 3 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - * + * SPDX-FileCopyrightText: 2017 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later */ namespace OC\Remote\Api; diff --git a/lib/private/Remote/Api/ApiCollection.php b/lib/private/Remote/Api/ApiCollection.php index 261f7e6c9b0..65039f4b5aa 100644 --- a/lib/private/Remote/Api/ApiCollection.php +++ b/lib/private/Remote/Api/ApiCollection.php @@ -1,24 +1,7 @@ <?php /** - * @copyright Copyright (c) 2017 Robin Appelman <robin@icewind.nl> - * - * @author Robin Appelman <robin@icewind.nl> - * - * @license GNU AGPL version 3 or any later version - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as - * published by the Free Software Foundation, either version 3 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - * + * SPDX-FileCopyrightText: 2017 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later */ namespace OC\Remote\Api; diff --git a/lib/private/Remote/Api/ApiFactory.php b/lib/private/Remote/Api/ApiFactory.php index 27eaa163496..7daddd16011 100644 --- a/lib/private/Remote/Api/ApiFactory.php +++ b/lib/private/Remote/Api/ApiFactory.php @@ -1,24 +1,7 @@ <?php /** - * @copyright Copyright (c) 2017 Robin Appelman <robin@icewind.nl> - * - * @author Robin Appelman <robin@icewind.nl> - * - * @license GNU AGPL version 3 or any later version - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as - * published by the Free Software Foundation, either version 3 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - * + * SPDX-FileCopyrightText: 2017 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later */ namespace OC\Remote\Api; diff --git a/lib/private/Remote/Api/NotFoundException.php b/lib/private/Remote/Api/NotFoundException.php index 8d226b00120..5251313f5f0 100644 --- a/lib/private/Remote/Api/NotFoundException.php +++ b/lib/private/Remote/Api/NotFoundException.php @@ -1,24 +1,7 @@ <?php /** - * @copyright Copyright (c) 2017 Robin Appelman <robin@icewind.nl> - * - * @author Robin Appelman <robin@icewind.nl> - * - * @license GNU AGPL version 3 or any later version - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as - * published by the Free Software Foundation, either version 3 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - * + * SPDX-FileCopyrightText: 2017 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later */ namespace OC\Remote\Api; diff --git a/lib/private/Remote/Api/OCS.php b/lib/private/Remote/Api/OCS.php index e6ee65378c4..de07eb8bc56 100644 --- a/lib/private/Remote/Api/OCS.php +++ b/lib/private/Remote/Api/OCS.php @@ -1,26 +1,7 @@ <?php /** - * @copyright Copyright (c) 2017 Robin Appelman <robin@icewind.nl> - * - * @author Christoph Wurst <christoph@winzerhof-wurst.at> - * @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/>. - * + * SPDX-FileCopyrightText: 2017 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later */ namespace OC\Remote\Api; diff --git a/lib/private/Remote/Credentials.php b/lib/private/Remote/Credentials.php index 56cf745c34c..fb0f03ae148 100644 --- a/lib/private/Remote/Credentials.php +++ b/lib/private/Remote/Credentials.php @@ -1,24 +1,7 @@ <?php /** - * @copyright Copyright (c) 2017 Robin Appelman <robin@icewind.nl> - * - * @author Robin Appelman <robin@icewind.nl> - * - * @license GNU AGPL version 3 or any later version - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as - * published by the Free Software Foundation, either version 3 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - * + * SPDX-FileCopyrightText: 2017 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later */ namespace OC\Remote; diff --git a/lib/private/Remote/Instance.php b/lib/private/Remote/Instance.php index 591bc3d6ae0..4ae734521cf 100644 --- a/lib/private/Remote/Instance.php +++ b/lib/private/Remote/Instance.php @@ -1,24 +1,7 @@ <?php /** - * @copyright Copyright (c) 2017 Robin Appelman <robin@icewind.nl> - * - * @author Robin Appelman <robin@icewind.nl> - * - * @license GNU AGPL version 3 or any later version - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as - * published by the Free Software Foundation, either version 3 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - * + * SPDX-FileCopyrightText: 2017 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later */ namespace OC\Remote; diff --git a/lib/private/Remote/InstanceFactory.php b/lib/private/Remote/InstanceFactory.php index 6941d06cb2e..f3047b851b2 100644 --- a/lib/private/Remote/InstanceFactory.php +++ b/lib/private/Remote/InstanceFactory.php @@ -1,24 +1,7 @@ <?php /** - * @copyright Copyright (c) 2017 Robin Appelman <robin@icewind.nl> - * - * @author Robin Appelman <robin@icewind.nl> - * - * @license GNU AGPL version 3 or any later version - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as - * published by the Free Software Foundation, either version 3 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - * + * SPDX-FileCopyrightText: 2017 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later */ namespace OC\Remote; diff --git a/lib/private/Remote/User.php b/lib/private/Remote/User.php index 5590fcfba38..5c8e9d3ca4e 100644 --- a/lib/private/Remote/User.php +++ b/lib/private/Remote/User.php @@ -1,25 +1,7 @@ <?php /** - * @copyright Copyright (c) 2017 Robin Appelman <robin@icewind.nl> - * - * @author Christoph Wurst <christoph@winzerhof-wurst.at> - * @author Robin Appelman <robin@icewind.nl> - * - * @license GNU AGPL version 3 or any later version - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as - * published by the Free Software Foundation, either version 3 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - * + * SPDX-FileCopyrightText: 2017 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later */ namespace OC\Remote; @@ -92,7 +74,7 @@ class User implements IUser { * @return string */ public function getTwitter() { - return isset($this->data['twitter']) ? $this->data['twitter'] : ''; + return $this->data['twitter'] ?? ''; } /** diff --git a/lib/private/Repair.php b/lib/private/Repair.php index 05624a2423a..fa279325453 100644 --- a/lib/private/Repair.php +++ b/lib/private/Repair.php @@ -1,52 +1,21 @@ <?php + /** - * @copyright Copyright (c) 2016, ownCloud, Inc. - * - * @author Arthur Schiwon <blizzz@arthur-schiwon.de> - * @author Christoph Wurst <christoph@winzerhof-wurst.at> - * @author Daniel Kesselberg <mail@danielkesselberg.de> - * @author Georg Ehrke <oc.list@georgehrke.com> - * @author Joas Schilling <coding@schilljs.com> - * @author John Molakvoæ <skjnldsv@protonmail.com> - * @author Julius Härtl <jus@bitgrid.net> - * @author Lukas Reschke <lukas@statuscode.ch> - * @author Michael Weimann <mail@michael-weimann.eu> - * @author Morris Jobke <hey@morrisjobke.de> - * @author Robin Appelman <robin@icewind.nl> - * @author Roeland Jago Douma <roeland@famdouma.nl> - * @author Thomas Müller <thomas.mueller@tmit.eu> - * @author Vincent Petry <vincent@nextcloud.com> - * - * @license AGPL-3.0 - * - * This code is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License, version 3, - * as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License, version 3, - * along with this program. If not, see <http://www.gnu.org/licenses/> - * + * SPDX-FileCopyrightText: 2016-2024 Nextcloud GmbH and Nextcloud contributors + * SPDX-FileCopyrightText: 2016 ownCloud, Inc. + * SPDX-License-Identifier: AGPL-3.0-only */ namespace OC; -use OC\Repair\AddRemoveOldTasksBackgroundJob; -use OC\Repair\CleanUpAbandonedApps; -use OCP\AppFramework\QueryException; -use OCP\AppFramework\Utility\ITimeFactory; -use OCP\Collaboration\Resources\IManager; -use OCP\EventDispatcher\IEventDispatcher; -use OCP\Migration\IOutput; -use OCP\Migration\IRepairStep; use OC\DB\Connection; use OC\DB\ConnectionAdapter; +use OC\Repair\AddAppConfigLazyMigration; use OC\Repair\AddBruteForceCleanupJob; use OC\Repair\AddCleanupUpdaterBackupsJob; +use OC\Repair\AddMetadataGenerationJob; +use OC\Repair\AddRemoveOldTasksBackgroundJob; use OC\Repair\CleanTags; +use OC\Repair\CleanUpAbandonedApps; use OC\Repair\ClearFrontendCaches; use OC\Repair\ClearGeneratedAvatarCache; use OC\Repair\Collation; @@ -84,30 +53,31 @@ use OC\Repair\RemoveLinkShares; use OC\Repair\RepairDavShares; use OC\Repair\RepairInvalidShares; use OC\Repair\RepairMimeTypes; -use OC\Repair\SqliteAutoincrement; use OC\Template\JSCombiner; +use OCP\AppFramework\QueryException; +use OCP\AppFramework\Utility\ITimeFactory; +use OCP\Collaboration\Resources\IManager; +use OCP\EventDispatcher\IEventDispatcher; +use OCP\Migration\IOutput; +use OCP\Migration\IRepairStep; use Psr\Log\LoggerInterface; use Throwable; class Repair implements IOutput { /** @var IRepairStep[] */ - private array $repairSteps; - - private IEventDispatcher $dispatcher; + private array $repairSteps = []; private string $currentStep; - private LoggerInterface $logger; + public function __construct( + private IEventDispatcher $dispatcher, + private LoggerInterface $logger + ) { + } - /** - * Creates a new repair step runner - * - * @param IRepairStep[] $repairSteps array of RepairStep instances - */ - public function __construct(array $repairSteps, IEventDispatcher $dispatcher, LoggerInterface $logger) { + /** @param IRepairStep[] $repairSteps */ + public function setRepairSteps(array $repairSteps): void { $this->repairSteps = $repairSteps; - $this->dispatcher = $dispatcher; - $this->logger = $logger; } /** @@ -212,6 +182,8 @@ class Repair implements IOutput { \OCP\Server::get(CleanUpAbandonedApps::class), \OCP\Server::get(AddMissingSecretJob::class), \OCP\Server::get(AddRemoveOldTasksBackgroundJob::class), + \OCP\Server::get(AddMetadataGenerationJob::class), + \OCP\Server::get(AddAppConfigLazyMigration::class), ]; } @@ -235,14 +207,11 @@ class Repair implements IOutput { * @return IRepairStep[] */ public static function getBeforeUpgradeRepairSteps() { - /** @var Connection $connection */ - $connection = \OC::$server->get(Connection::class); /** @var ConnectionAdapter $connectionAdapter */ $connectionAdapter = \OC::$server->get(ConnectionAdapter::class); $config = \OC::$server->getConfig(); $steps = [ new Collation(\OC::$server->getConfig(), \OC::$server->get(LoggerInterface::class), $connectionAdapter, true), - new SqliteAutoincrement($connection), new SaveAccountsTableData($connectionAdapter, $config), new DropAccountTermsTable($connectionAdapter), ]; @@ -250,6 +219,9 @@ class Repair implements IOutput { return $steps; } + public function debug(string $message): void { + } + /** * @param string $message */ diff --git a/lib/private/Repair/AddAppConfigLazyMigration.php b/lib/private/Repair/AddAppConfigLazyMigration.php new file mode 100644 index 00000000000..7ae83e87669 --- /dev/null +++ b/lib/private/Repair/AddAppConfigLazyMigration.php @@ -0,0 +1,45 @@ +<?php + +declare(strict_types=1); +/** + * SPDX-FileCopyrightText: 2024 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later + */ +namespace OC\Repair; + +use OCP\IAppConfig; +use OCP\Migration\IOutput; +use OCP\Migration\IRepairStep; +use Psr\Log\LoggerInterface; + +class AddAppConfigLazyMigration implements IRepairStep { + /** + * Just add config values that needs to be migrated to lazy loading + */ + private static array $lazyAppConfig = [ + 'core' => [ + 'oc.integritycheck.checker', + ], + ]; + + public function __construct( + private IAppConfig $appConfig, + private LoggerInterface $logger, + ) { + } + + public function getName() { + return 'migrate lazy config values'; + } + + public function run(IOutput $output) { + $c = 0; + foreach (self::$lazyAppConfig as $appId => $configKeys) { + foreach ($configKeys as $configKey) { + $c += (int)$this->appConfig->updateLazy($appId, $configKey, true); + } + } + + $this->logger->notice('core/BackgroundJobs/AppConfigLazyMigration: ' . $c . ' config values updated'); + } +} diff --git a/lib/private/Repair/AddBruteForceCleanupJob.php b/lib/private/Repair/AddBruteForceCleanupJob.php index 5584e5b81c7..dd08e36a597 100644 --- a/lib/private/Repair/AddBruteForceCleanupJob.php +++ b/lib/private/Repair/AddBruteForceCleanupJob.php @@ -3,25 +3,8 @@ declare(strict_types=1); /** - * @copyright Copyright (c) 2020, Roeland Jago Douma <roeland@famdouma.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/>. - * + * SPDX-FileCopyrightText: 2020 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later */ namespace OC\Repair; diff --git a/lib/private/Repair/AddCleanupUpdaterBackupsJob.php b/lib/private/Repair/AddCleanupUpdaterBackupsJob.php index 3642931694a..8bd938b7e3a 100644 --- a/lib/private/Repair/AddCleanupUpdaterBackupsJob.php +++ b/lib/private/Repair/AddCleanupUpdaterBackupsJob.php @@ -1,24 +1,7 @@ <?php /** - * @copyright Copyright (c) 2018 Morris Jobke <hey@morrisjobke.de> - * - * @author Morris Jobke <hey@morrisjobke.de> - * - * @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/>. - * + * SPDX-FileCopyrightText: 2018 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later */ namespace OC\Repair; diff --git a/lib/private/Repair/AddMetadataGenerationJob.php b/lib/private/Repair/AddMetadataGenerationJob.php new file mode 100644 index 00000000000..4535fb0c9e0 --- /dev/null +++ b/lib/private/Repair/AddMetadataGenerationJob.php @@ -0,0 +1,26 @@ +<?php +/** + * SPDX-FileCopyrightText: 2023 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later + */ +namespace OC\Repair; + +use OC\Core\BackgroundJobs\GenerateMetadataJob; +use OCP\BackgroundJob\IJobList; +use OCP\Migration\IOutput; +use OCP\Migration\IRepairStep; + +class AddMetadataGenerationJob implements IRepairStep { + public function __construct( + private IJobList $jobList, + ) { + } + + public function getName() { + return 'Queue a job to generate metadata'; + } + + public function run(IOutput $output) { + $this->jobList->add(GenerateMetadataJob::class); + } +} diff --git a/lib/private/Repair/AddRemoveOldTasksBackgroundJob.php b/lib/private/Repair/AddRemoveOldTasksBackgroundJob.php index 94ae39f2183..4ad320a0311 100644 --- a/lib/private/Repair/AddRemoveOldTasksBackgroundJob.php +++ b/lib/private/Repair/AddRemoveOldTasksBackgroundJob.php @@ -3,29 +3,14 @@ declare(strict_types=1); /** - * @copyright 2023 Marcel Klehr <mklehr@gmx.net> - * - * @author Marcel Klehr <mklehr@gmx.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/>. - * + * SPDX-FileCopyrightText: 2023 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later */ namespace OC\Repair; -use OC\TextProcessing\RemoveOldTasksBackgroundJob; +use OC\TaskProcessing\RemoveOldTasksBackgroundJob; +use OC\TextProcessing\RemoveOldTasksBackgroundJob as RemoveOldTextProcessingTasksBackgroundJob; +use OC\TextToImage\RemoveOldTasksBackgroundJob as RemoveOldTextToImageTasksBackgroundJob; use OCP\BackgroundJob\IJobList; use OCP\Migration\IOutput; use OCP\Migration\IRepairStep; @@ -38,10 +23,12 @@ class AddRemoveOldTasksBackgroundJob implements IRepairStep { } public function getName(): string { - return 'Add language model tasks cleanup job'; + return 'Add AI tasks cleanup jobs'; } public function run(IOutput $output) { + $this->jobList->add(RemoveOldTextProcessingTasksBackgroundJob::class); + $this->jobList->add(RemoveOldTextToImageTasksBackgroundJob::class); $this->jobList->add(RemoveOldTasksBackgroundJob::class); } } diff --git a/lib/private/Repair/CleanTags.php b/lib/private/Repair/CleanTags.php index 531fcc1112f..f2fc8156f29 100644 --- a/lib/private/Repair/CleanTags.php +++ b/lib/private/Repair/CleanTags.php @@ -1,28 +1,9 @@ <?php + /** - * @copyright Copyright (c) 2016, ownCloud, Inc. - * - * @author Christoph Wurst <christoph@winzerhof-wurst.at> - * @author Joas Schilling <coding@schilljs.com> - * @author Morris Jobke <hey@morrisjobke.de> - * @author Robin Appelman <robin@icewind.nl> - * @author Roeland Jago Douma <roeland@famdouma.nl> - * @author Thomas Müller <thomas.mueller@tmit.eu> - * - * @license AGPL-3.0 - * - * This code is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License, version 3, - * as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License, version 3, - * along with this program. If not, see <http://www.gnu.org/licenses/> - * + * SPDX-FileCopyrightText: 2016-2024 Nextcloud GmbH and Nextcloud contributors + * SPDX-FileCopyrightText: 2016 ownCloud, Inc. + * SPDX-License-Identifier: AGPL-3.0-only */ namespace OC\Repair; diff --git a/lib/private/Repair/CleanUpAbandonedApps.php b/lib/private/Repair/CleanUpAbandonedApps.php index ed8fa8d7d28..718f625be86 100644 --- a/lib/private/Repair/CleanUpAbandonedApps.php +++ b/lib/private/Repair/CleanUpAbandonedApps.php @@ -1,29 +1,10 @@ <?php declare(strict_types=1); - /** - * @copyright Copyright (c) 2022 Arthur Schiwon <blizzz@arthur-schiwon.de> - * - * @author Arthur Schiwon <blizzz@arthur-schiwon.de> - * - * @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 <https://www.gnu.org/licenses/>. - * + * SPDX-FileCopyrightText: 2022 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later */ - namespace OC\Repair; use OCP\IConfig; diff --git a/lib/private/Repair/ClearFrontendCaches.php b/lib/private/Repair/ClearFrontendCaches.php index bf94e5bfbff..77a3df5598a 100644 --- a/lib/private/Repair/ClearFrontendCaches.php +++ b/lib/private/Repair/ClearFrontendCaches.php @@ -1,26 +1,7 @@ <?php /** - * @copyright Copyright (c) 2018 Julius Härtl <jus@bitgrid.net> - * - * @author Christoph Wurst <christoph@winzerhof-wurst.at> - * @author Julius Härtl <jus@bitgrid.net> - * @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/>. - * + * SPDX-FileCopyrightText: 2018 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later */ namespace OC\Repair; @@ -37,7 +18,7 @@ class ClearFrontendCaches implements IRepairStep { protected $jsCombiner; public function __construct(ICacheFactory $cacheFactory, - JSCombiner $JSCombiner) { + JSCombiner $JSCombiner) { $this->cacheFactory = $cacheFactory; $this->jsCombiner = $JSCombiner; } diff --git a/lib/private/Repair/ClearGeneratedAvatarCache.php b/lib/private/Repair/ClearGeneratedAvatarCache.php index fb3b42847dc..2dea4bd2d61 100644 --- a/lib/private/Repair/ClearGeneratedAvatarCache.php +++ b/lib/private/Repair/ClearGeneratedAvatarCache.php @@ -1,32 +1,13 @@ <?php /** - * @copyright Copyright (c) 2018 John Molakvoæ <skjnldsv@protonmail.com> - * - * @author Christoph Wurst <christoph@winzerhof-wurst.at> - * @author John Molakvoæ <skjnldsv@protonmail.com> - * @author Michael Weimann <mail@michael-weimann.eu> - * - * @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/>. - * + * SPDX-FileCopyrightText: 2018 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later */ namespace OC\Repair; use OC\Avatar\AvatarManager; -use OCP\IConfig; use OCP\BackgroundJob\IJobList; +use OCP\IConfig; use OCP\Migration\IOutput; use OCP\Migration\IRepairStep; diff --git a/lib/private/Repair/ClearGeneratedAvatarCacheJob.php b/lib/private/Repair/ClearGeneratedAvatarCacheJob.php index e8513e7a933..38cf03b731a 100644 --- a/lib/private/Repair/ClearGeneratedAvatarCacheJob.php +++ b/lib/private/Repair/ClearGeneratedAvatarCacheJob.php @@ -1,28 +1,13 @@ <?php /** - * @copyright 2022 Carl Schwan <carl@carlschwan.eu> - * - * @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/>. - * + * SPDX-FileCopyrightText: 2022 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later */ namespace OC\Repair; -use OCP\BackgroundJob\QueuedJob; -use OCP\AppFramework\Utility\ITimeFactory; use OC\Avatar\AvatarManager; +use OCP\AppFramework\Utility\ITimeFactory; +use OCP\BackgroundJob\QueuedJob; class ClearGeneratedAvatarCacheJob extends QueuedJob { protected AvatarManager $avatarManager; diff --git a/lib/private/Repair/Collation.php b/lib/private/Repair/Collation.php index 6f7dde68865..0affb3b1ca9 100644 --- a/lib/private/Repair/Collation.php +++ b/lib/private/Repair/Collation.php @@ -1,28 +1,9 @@ <?php + /** - * @copyright Copyright (c) 2016, ownCloud, Inc. - * - * @author Christoph Wurst <christoph@winzerhof-wurst.at> - * @author Joas Schilling <coding@schilljs.com> - * @author Morris Jobke <hey@morrisjobke.de> - * @author Robin Appelman <robin@icewind.nl> - * @author Robin Müller <coder-hugo@users.noreply.github.com> - * @author Thomas Müller <thomas.mueller@tmit.eu> - * - * @license AGPL-3.0 - * - * This code is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License, version 3, - * as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License, version 3, - * along with this program. If not, see <http://www.gnu.org/licenses/> - * + * SPDX-FileCopyrightText: 2016-2024 Nextcloud GmbH and Nextcloud contributors + * SPDX-FileCopyrightText: 2016 ownCloud, Inc. + * SPDX-License-Identifier: AGPL-3.0-only */ namespace OC\Repair; diff --git a/lib/private/Repair/Events/RepairAdvanceEvent.php b/lib/private/Repair/Events/RepairAdvanceEvent.php index 174d4ec48cd..c4be72ce530 100644 --- a/lib/private/Repair/Events/RepairAdvanceEvent.php +++ b/lib/private/Repair/Events/RepairAdvanceEvent.php @@ -3,24 +3,8 @@ declare(strict_types=1); /** - * @copyright Copyright (c) 2022 Côme Chilliet <come.chilliet@nextcloud.com> - * - * @author Côme Chilliet <come.chilliet@nextcloud.com> - * - * @license AGPL-3.0 - * - * This code is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License, version 3, - * as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License, version 3, - * along with this program. If not, see <http://www.gnu.org/licenses/> - * + * SPDX-FileCopyrightText: 2022 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-only */ namespace OC\Repair\Events; diff --git a/lib/private/Repair/Events/RepairErrorEvent.php b/lib/private/Repair/Events/RepairErrorEvent.php index 7f8a87cfda3..8cd5d41b1b4 100644 --- a/lib/private/Repair/Events/RepairErrorEvent.php +++ b/lib/private/Repair/Events/RepairErrorEvent.php @@ -3,24 +3,8 @@ declare(strict_types=1); /** - * @copyright Copyright (c) 2022 Côme Chilliet <come.chilliet@nextcloud.com> - * - * @author Côme Chilliet <come.chilliet@nextcloud.com> - * - * @license AGPL-3.0 - * - * This code is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License, version 3, - * as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License, version 3, - * along with this program. If not, see <http://www.gnu.org/licenses/> - * + * SPDX-FileCopyrightText: 2022 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-only */ namespace OC\Repair\Events; diff --git a/lib/private/Repair/Events/RepairFinishEvent.php b/lib/private/Repair/Events/RepairFinishEvent.php index abdc2acd1ce..767a7506e6f 100644 --- a/lib/private/Repair/Events/RepairFinishEvent.php +++ b/lib/private/Repair/Events/RepairFinishEvent.php @@ -3,24 +3,8 @@ declare(strict_types=1); /** - * @copyright Copyright (c) 2022 Côme Chilliet <come.chilliet@nextcloud.com> - * - * @author Côme Chilliet <come.chilliet@nextcloud.com> - * - * @license AGPL-3.0 - * - * This code is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License, version 3, - * as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License, version 3, - * along with this program. If not, see <http://www.gnu.org/licenses/> - * + * SPDX-FileCopyrightText: 2022 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-only */ namespace OC\Repair\Events; diff --git a/lib/private/Repair/Events/RepairInfoEvent.php b/lib/private/Repair/Events/RepairInfoEvent.php index 5c2a53fce4f..c48b295a9a9 100644 --- a/lib/private/Repair/Events/RepairInfoEvent.php +++ b/lib/private/Repair/Events/RepairInfoEvent.php @@ -3,24 +3,8 @@ declare(strict_types=1); /** - * @copyright Copyright (c) 2022 Côme Chilliet <come.chilliet@nextcloud.com> - * - * @author Côme Chilliet <come.chilliet@nextcloud.com> - * - * @license AGPL-3.0 - * - * This code is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License, version 3, - * as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License, version 3, - * along with this program. If not, see <http://www.gnu.org/licenses/> - * + * SPDX-FileCopyrightText: 2022 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-only */ namespace OC\Repair\Events; diff --git a/lib/private/Repair/Events/RepairStartEvent.php b/lib/private/Repair/Events/RepairStartEvent.php index 7bb270595c5..e154df5e6e1 100644 --- a/lib/private/Repair/Events/RepairStartEvent.php +++ b/lib/private/Repair/Events/RepairStartEvent.php @@ -3,24 +3,8 @@ declare(strict_types=1); /** - * @copyright Copyright (c) 2022 Côme Chilliet <come.chilliet@nextcloud.com> - * - * @author Côme Chilliet <come.chilliet@nextcloud.com> - * - * @license AGPL-3.0 - * - * This code is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License, version 3, - * as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License, version 3, - * along with this program. If not, see <http://www.gnu.org/licenses/> - * + * SPDX-FileCopyrightText: 2022 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-only */ namespace OC\Repair\Events; diff --git a/lib/private/Repair/Events/RepairStepEvent.php b/lib/private/Repair/Events/RepairStepEvent.php index 82af670b950..7140d13687d 100644 --- a/lib/private/Repair/Events/RepairStepEvent.php +++ b/lib/private/Repair/Events/RepairStepEvent.php @@ -3,24 +3,8 @@ declare(strict_types=1); /** - * @copyright Copyright (c) 2022 Côme Chilliet <come.chilliet@nextcloud.com> - * - * @author Côme Chilliet <come.chilliet@nextcloud.com> - * - * @license AGPL-3.0 - * - * This code is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License, version 3, - * as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License, version 3, - * along with this program. If not, see <http://www.gnu.org/licenses/> - * + * SPDX-FileCopyrightText: 2022 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-only */ namespace OC\Repair\Events; diff --git a/lib/private/Repair/Events/RepairWarningEvent.php b/lib/private/Repair/Events/RepairWarningEvent.php index 2aaa44d4719..403eec87158 100644 --- a/lib/private/Repair/Events/RepairWarningEvent.php +++ b/lib/private/Repair/Events/RepairWarningEvent.php @@ -3,24 +3,8 @@ declare(strict_types=1); /** - * @copyright Copyright (c) 2022 Côme Chilliet <come.chilliet@nextcloud.com> - * - * @author Côme Chilliet <come.chilliet@nextcloud.com> - * - * @license AGPL-3.0 - * - * This code is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License, version 3, - * as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License, version 3, - * along with this program. If not, see <http://www.gnu.org/licenses/> - * + * SPDX-FileCopyrightText: 2022 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-only */ namespace OC\Repair\Events; diff --git a/lib/private/Repair/MoveUpdaterStepFile.php b/lib/private/Repair/MoveUpdaterStepFile.php index fc3b9dce1f3..c9b51b308c4 100644 --- a/lib/private/Repair/MoveUpdaterStepFile.php +++ b/lib/private/Repair/MoveUpdaterStepFile.php @@ -1,25 +1,7 @@ <?php /** - * @copyright Copyright (c) 2016 Morris Jobke <hey@morrisjobke.de> - * - * @author Christoph Wurst <christoph@winzerhof-wurst.at> - * @author Morris Jobke <hey@morrisjobke.de> - * - * @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/>. - * + * SPDX-FileCopyrightText: 2016 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later */ namespace OC\Repair; diff --git a/lib/private/Repair/NC11/FixMountStorages.php b/lib/private/Repair/NC11/FixMountStorages.php index 4cf721e9b13..b1663102d2f 100644 --- a/lib/private/Repair/NC11/FixMountStorages.php +++ b/lib/private/Repair/NC11/FixMountStorages.php @@ -1,25 +1,7 @@ <?php /** - * @copyright 2016 Joas Schilling <coding@schilljs.com> - * - * @author Joas Schilling <coding@schilljs.com> - * @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/>. - * + * SPDX-FileCopyrightText: 2016 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later */ namespace OC\Repair\NC11; diff --git a/lib/private/Repair/NC13/AddLogRotateJob.php b/lib/private/Repair/NC13/AddLogRotateJob.php index e0156ff873c..8fe68a42819 100644 --- a/lib/private/Repair/NC13/AddLogRotateJob.php +++ b/lib/private/Repair/NC13/AddLogRotateJob.php @@ -1,24 +1,7 @@ <?php /** - * @copyright 2017, Roeland Jago Douma <roeland@famdouma.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/>. - * + * SPDX-FileCopyrightText: 2017 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later */ namespace OC\Repair\NC13; diff --git a/lib/private/Repair/NC14/AddPreviewBackgroundCleanupJob.php b/lib/private/Repair/NC14/AddPreviewBackgroundCleanupJob.php index e12f2771903..417bc5e6adc 100644 --- a/lib/private/Repair/NC14/AddPreviewBackgroundCleanupJob.php +++ b/lib/private/Repair/NC14/AddPreviewBackgroundCleanupJob.php @@ -3,25 +3,8 @@ declare(strict_types=1); /** - * @copyright 2018, Roeland Jago Douma <roeland@famdouma.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/>. - * + * SPDX-FileCopyrightText: 2018 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later */ namespace OC\Repair\NC14; diff --git a/lib/private/Repair/NC16/AddClenupLoginFlowV2BackgroundJob.php b/lib/private/Repair/NC16/AddClenupLoginFlowV2BackgroundJob.php index 217141cf0b9..ab5f93415fc 100644 --- a/lib/private/Repair/NC16/AddClenupLoginFlowV2BackgroundJob.php +++ b/lib/private/Repair/NC16/AddClenupLoginFlowV2BackgroundJob.php @@ -3,25 +3,8 @@ declare(strict_types=1); /** - * @copyright Copyright (c) 2019, Roeland Jago Douma <roeland@famdouma.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/>. - * + * SPDX-FileCopyrightText: 2019 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later */ namespace OC\Repair\NC16; diff --git a/lib/private/Repair/NC16/CleanupCardDAVPhotoCache.php b/lib/private/Repair/NC16/CleanupCardDAVPhotoCache.php index 394e47dfcda..a9cbbb4cbbf 100644 --- a/lib/private/Repair/NC16/CleanupCardDAVPhotoCache.php +++ b/lib/private/Repair/NC16/CleanupCardDAVPhotoCache.php @@ -3,26 +3,8 @@ declare(strict_types=1); /** - * @copyright Copyright (c) 2019, Daniel Kesselberg (mail@danielkesselberg.de) - * - * @author Daniel Kesselberg <mail@danielkesselberg.de> - * @author Morris Jobke <hey@morrisjobke.de> - * - * @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/>. - * + * SPDX-FileCopyrightText: 2019 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later */ namespace OC\Repair\NC16; diff --git a/lib/private/Repair/NC16/ClearCollectionsAccessCache.php b/lib/private/Repair/NC16/ClearCollectionsAccessCache.php index 076e27a6ceb..1627ed40b98 100644 --- a/lib/private/Repair/NC16/ClearCollectionsAccessCache.php +++ b/lib/private/Repair/NC16/ClearCollectionsAccessCache.php @@ -3,25 +3,8 @@ declare(strict_types=1); /** - * @copyright Copyright (c) 2019 Joas Schilling <coding@schilljs.com> - * - * @author Joas Schilling <coding@schilljs.com> - * - * @license GNU AGPL version 3 or any later version - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as - * published by the Free Software Foundation, either version 3 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - * + * SPDX-FileCopyrightText: 2019 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later */ namespace OC\Repair\NC16; diff --git a/lib/private/Repair/NC18/ResetGeneratedAvatarFlag.php b/lib/private/Repair/NC18/ResetGeneratedAvatarFlag.php index d5ae1d7ab63..b0dfec295e7 100644 --- a/lib/private/Repair/NC18/ResetGeneratedAvatarFlag.php +++ b/lib/private/Repair/NC18/ResetGeneratedAvatarFlag.php @@ -3,25 +3,8 @@ declare(strict_types=1); /** - * @copyright Copyright (c) 2019, Joas Schilling <coding@schilljs.com> - * - * @author Joas Schilling <coding@schilljs.com> - * - * @license GNU AGPL version 3 or any later version - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as - * published by the Free Software Foundation, either version 3 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - * + * SPDX-FileCopyrightText: 2019 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later */ namespace OC\Repair\NC18; @@ -37,7 +20,7 @@ class ResetGeneratedAvatarFlag implements IRepairStep { private $connection; public function __construct(IConfig $config, - IDBConnection $connection) { + IDBConnection $connection) { $this->config = $config; $this->connection = $connection; } diff --git a/lib/private/Repair/NC20/EncryptionLegacyCipher.php b/lib/private/Repair/NC20/EncryptionLegacyCipher.php index a7d008e87be..89473ffd5e8 100644 --- a/lib/private/Repair/NC20/EncryptionLegacyCipher.php +++ b/lib/private/Repair/NC20/EncryptionLegacyCipher.php @@ -3,26 +3,8 @@ declare(strict_types=1); /** - * @copyright Copyright (c) 2020, Roeland Jago Douma <roeland@famdouma.nl> - * - * @author Julius Härtl <jus@bitgrid.net> - * @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/>. - * + * SPDX-FileCopyrightText: 2020 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later */ namespace OC\Repair\NC20; @@ -38,7 +20,7 @@ class EncryptionLegacyCipher implements IRepairStep { private $manager; public function __construct(IConfig $config, - IManager $manager) { + IManager $manager) { $this->config = $config; $this->manager = $manager; } diff --git a/lib/private/Repair/NC20/EncryptionMigration.php b/lib/private/Repair/NC20/EncryptionMigration.php index 239a62c2718..6de143747b3 100644 --- a/lib/private/Repair/NC20/EncryptionMigration.php +++ b/lib/private/Repair/NC20/EncryptionMigration.php @@ -3,26 +3,8 @@ declare(strict_types=1); /** - * @copyright Copyright (c) 2020, Roeland Jago Douma <roeland@famdouma.nl> - * - * @author Julius Härtl <jus@bitgrid.net> - * @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/>. - * + * SPDX-FileCopyrightText: 2020 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later */ namespace OC\Repair\NC20; @@ -38,7 +20,7 @@ class EncryptionMigration implements IRepairStep { private $manager; public function __construct(IConfig $config, - IManager $manager) { + IManager $manager) { $this->config = $config; $this->manager = $manager; } diff --git a/lib/private/Repair/NC20/ShippedDashboardEnable.php b/lib/private/Repair/NC20/ShippedDashboardEnable.php index a9713d49a6f..955011a8c84 100644 --- a/lib/private/Repair/NC20/ShippedDashboardEnable.php +++ b/lib/private/Repair/NC20/ShippedDashboardEnable.php @@ -3,25 +3,8 @@ declare(strict_types=1); /** - * @copyright Copyright (c) 2020 Julius Härtl <jus@bitgrid.net> - * - * @author Julius Härtl <jus@bitgrid.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/>. - * + * SPDX-FileCopyrightText: 2020 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later */ namespace OC\Repair\NC20; diff --git a/lib/private/Repair/NC21/AddCheckForUserCertificatesJob.php b/lib/private/Repair/NC21/AddCheckForUserCertificatesJob.php index ee413ce12ca..4f80b3809e8 100644 --- a/lib/private/Repair/NC21/AddCheckForUserCertificatesJob.php +++ b/lib/private/Repair/NC21/AddCheckForUserCertificatesJob.php @@ -1,24 +1,7 @@ <?php /** - * @copyright Copyright (c) 2020 Morris Jobke <hey@morrisjobke.de> - * - * @author Morris Jobke <hey@morrisjobke.de> - * - * @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/>. - * + * SPDX-FileCopyrightText: 2020 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later */ namespace OC\Repair\NC21; diff --git a/lib/private/Repair/NC21/ValidatePhoneNumber.php b/lib/private/Repair/NC21/ValidatePhoneNumber.php index b3534dbeae8..3a6ace37bd2 100644 --- a/lib/private/Repair/NC21/ValidatePhoneNumber.php +++ b/lib/private/Repair/NC21/ValidatePhoneNumber.php @@ -3,26 +3,8 @@ declare(strict_types=1); /** - * @copyright Copyright (c) 2020 Joas Schilling <coding@schilljs.com> - * - * @author Arthur Schiwon <blizzz@arthur-schiwon.de> - * @author Joas Schilling <coding@schilljs.com> - * - * @license GNU AGPL version 3 or any later version - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as - * published by the Free Software Foundation, either version 3 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - * + * SPDX-FileCopyrightText: 2020 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later */ namespace OC\Repair\NC21; @@ -42,8 +24,8 @@ class ValidatePhoneNumber implements IRepairStep { private $accountManager; public function __construct(IUserManager $userManager, - IAccountManager $accountManager, - IConfig $config) { + IAccountManager $accountManager, + IConfig $config) { $this->config = $config; $this->userManager = $userManager; $this->accountManager = $accountManager; diff --git a/lib/private/Repair/NC22/LookupServerSendCheck.php b/lib/private/Repair/NC22/LookupServerSendCheck.php index eb320593d98..540dc2a730d 100644 --- a/lib/private/Repair/NC22/LookupServerSendCheck.php +++ b/lib/private/Repair/NC22/LookupServerSendCheck.php @@ -3,26 +3,8 @@ declare(strict_types=1); /** - * @copyright Copyright (c) 2021 Roeland Jago Douma <roeland@famdouma.nl> - * - * @author Morris Jobke <hey@morrisjobke.de> - * @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/>. - * + * SPDX-FileCopyrightText: 2021 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later */ namespace OC\Repair\NC22; diff --git a/lib/private/Repair/NC24/AddTokenCleanupJob.php b/lib/private/Repair/NC24/AddTokenCleanupJob.php index 97539c5692f..f1dac2d4e12 100644 --- a/lib/private/Repair/NC24/AddTokenCleanupJob.php +++ b/lib/private/Repair/NC24/AddTokenCleanupJob.php @@ -3,25 +3,8 @@ declare(strict_types=1); /** - * @copyright 2022 Thomas Citharel <nextcloud@tcit.fr> - * - * @author Thomas Citharel <nextcloud@tcit.fr> - * - * @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/>. - * + * SPDX-FileCopyrightText: 2022 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later */ namespace OC\Repair\NC24; diff --git a/lib/private/Repair/NC25/AddMissingSecretJob.php b/lib/private/Repair/NC25/AddMissingSecretJob.php index d3a438b89c1..b407ef2a2a9 100644 --- a/lib/private/Repair/NC25/AddMissingSecretJob.php +++ b/lib/private/Repair/NC25/AddMissingSecretJob.php @@ -3,22 +3,8 @@ declare(strict_types=1); /** - * @copyright 2022 Carl Schwan <carl@carlschwan.eu> - * @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/>. - * + * SPDX-FileCopyrightText: 2022 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later */ namespace OC\Repair\NC25; diff --git a/lib/private/Repair/OldGroupMembershipShares.php b/lib/private/Repair/OldGroupMembershipShares.php index be507debbd9..54f2078395e 100644 --- a/lib/private/Repair/OldGroupMembershipShares.php +++ b/lib/private/Repair/OldGroupMembershipShares.php @@ -1,24 +1,9 @@ <?php + /** - * @copyright Copyright (c) 2016, ownCloud, Inc. - * - * @author Joas Schilling <coding@schilljs.com> - * @author Thomas Müller <thomas.mueller@tmit.eu> - * - * @license AGPL-3.0 - * - * This code is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License, version 3, - * as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License, version 3, - * along with this program. If not, see <http://www.gnu.org/licenses/> - * + * SPDX-FileCopyrightText: 2016-2024 Nextcloud GmbH and Nextcloud contributors + * SPDX-FileCopyrightText: 2016 ownCloud, Inc. + * SPDX-License-Identifier: AGPL-3.0-only */ namespace OC\Repair; diff --git a/lib/private/Repair/Owncloud/CleanPreviews.php b/lib/private/Repair/Owncloud/CleanPreviews.php index 853a94c8adc..86e173cf402 100644 --- a/lib/private/Repair/Owncloud/CleanPreviews.php +++ b/lib/private/Repair/Owncloud/CleanPreviews.php @@ -1,24 +1,7 @@ <?php /** - * @copyright 2016 Roeland Jago Douma <roeland@famdouma.nl> - * - * @author Julius Härtl <jus@bitgrid.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/>. - * + * SPDX-FileCopyrightText: 2016 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later */ namespace OC\Repair\Owncloud; @@ -47,8 +30,8 @@ class CleanPreviews implements IRepairStep { * @param IConfig $config */ public function __construct(IJobList $jobList, - IUserManager $userManager, - IConfig $config) { + IUserManager $userManager, + IConfig $config) { $this->jobList = $jobList; $this->userManager = $userManager; $this->config = $config; diff --git a/lib/private/Repair/Owncloud/CleanPreviewsBackgroundJob.php b/lib/private/Repair/Owncloud/CleanPreviewsBackgroundJob.php index 7f4bbc35c17..6c606453bb9 100644 --- a/lib/private/Repair/Owncloud/CleanPreviewsBackgroundJob.php +++ b/lib/private/Repair/Owncloud/CleanPreviewsBackgroundJob.php @@ -1,30 +1,16 @@ <?php + +declare(strict_types=1); + /** - * @copyright 2016 Roeland Jago Douma <roeland@famdouma.nl> - * - * @author Julius Härtl <jus@bitgrid.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/>. - * + * SPDX-FileCopyrightText: 2016 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later */ namespace OC\Repair\Owncloud; -use OC\BackgroundJob\QueuedJob; use OCP\AppFramework\Utility\ITimeFactory; use OCP\BackgroundJob\IJobList; +use OCP\BackgroundJob\QueuedJob; use OCP\Files\Folder; use OCP\Files\IRootFolder; use OCP\Files\NotFoundException; @@ -33,33 +19,14 @@ use OCP\IUserManager; use Psr\Log\LoggerInterface; class CleanPreviewsBackgroundJob extends QueuedJob { - /** @var IRootFolder */ - private $rootFolder; - - private LoggerInterface $logger; - - /** @var IJobList */ - private $jobList; - - /** @var ITimeFactory */ - private $timeFactory; - - /** @var IUserManager */ - private $userManager; - - /** - * CleanPreviewsBackgroundJob constructor. - */ - public function __construct(IRootFolder $rootFolder, - LoggerInterface $logger, - IJobList $jobList, - ITimeFactory $timeFactory, - IUserManager $userManager) { - $this->rootFolder = $rootFolder; - $this->logger = $logger; - $this->jobList = $jobList; - $this->timeFactory = $timeFactory; - $this->userManager = $userManager; + public function __construct( + private IRootFolder $rootFolder, + private LoggerInterface $logger, + private IJobList $jobList, + ITimeFactory $timeFactory, + private IUserManager $userManager, + ) { + parent::__construct($timeFactory); } public function run($arguments) { @@ -80,10 +47,9 @@ class CleanPreviewsBackgroundJob extends QueuedJob { } /** - * @param $uid - * @return bool + * @param string $uid */ - private function cleanupPreviews($uid) { + private function cleanupPreviews($uid): bool { try { $userFolder = $this->rootFolder->getUserFolder($uid); } catch (NotFoundException $e) { @@ -101,7 +67,7 @@ class CleanPreviewsBackgroundJob extends QueuedJob { $thumbnails = $thumbnailFolder->getDirectoryListing(); - $start = $this->timeFactory->getTime(); + $start = $this->time->getTime(); foreach ($thumbnails as $thumbnail) { try { $thumbnail->delete(); @@ -109,7 +75,7 @@ class CleanPreviewsBackgroundJob extends QueuedJob { // Ignore } - if (($this->timeFactory->getTime() - $start) > 15) { + if (($this->time->getTime() - $start) > 15) { return false; } } diff --git a/lib/private/Repair/Owncloud/DropAccountTermsTable.php b/lib/private/Repair/Owncloud/DropAccountTermsTable.php index d5c01d64131..18f169c9b49 100644 --- a/lib/private/Repair/Owncloud/DropAccountTermsTable.php +++ b/lib/private/Repair/Owncloud/DropAccountTermsTable.php @@ -1,24 +1,7 @@ <?php /** - * @copyright Copyright (c) 2017 Joas Schilling <coding@schilljs.com> - * - * @author Joas Schilling <coding@schilljs.com> - * - * @license GNU AGPL version 3 or any later version - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as - * published by the Free Software Foundation, either version 3 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - * + * SPDX-FileCopyrightText: 2017 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later */ namespace OC\Repair\Owncloud; diff --git a/lib/private/Repair/Owncloud/MigrateOauthTables.php b/lib/private/Repair/Owncloud/MigrateOauthTables.php index 5bf0816d8de..e8728cd2f66 100644 --- a/lib/private/Repair/Owncloud/MigrateOauthTables.php +++ b/lib/private/Repair/Owncloud/MigrateOauthTables.php @@ -1,30 +1,15 @@ <?php /** - * @copyright 2021 Louis Chemineau <louis@chmn.me> - * - * @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/>. - * + * SPDX-FileCopyrightText: 2021 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later */ namespace OC\Repair\Owncloud; -use OCP\Migration\IOutput; -use OCP\Migration\IRepairStep; use OC\DB\Connection; use OC\DB\SchemaWrapper; use OCP\DB\QueryBuilder\IQueryBuilder; +use OCP\Migration\IOutput; +use OCP\Migration\IRepairStep; class MigrateOauthTables implements IRepairStep { /** @var Connection */ diff --git a/lib/private/Repair/Owncloud/MoveAvatars.php b/lib/private/Repair/Owncloud/MoveAvatars.php index 44ba9b7643b..7fdabae7a66 100644 --- a/lib/private/Repair/Owncloud/MoveAvatars.php +++ b/lib/private/Repair/Owncloud/MoveAvatars.php @@ -1,24 +1,7 @@ <?php /** - * @copyright 2016 Roeland Jago Douma <roeland@famdouma.nl> - * - * @author Julius Härtl <jus@bitgrid.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/>. - * + * SPDX-FileCopyrightText: 2016 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later */ namespace OC\Repair\Owncloud; @@ -41,7 +24,7 @@ class MoveAvatars implements IRepairStep { * @param IConfig $config */ public function __construct(IJobList $jobList, - IConfig $config) { + IConfig $config) { $this->jobList = $jobList; $this->config = $config; } diff --git a/lib/private/Repair/Owncloud/MoveAvatarsBackgroundJob.php b/lib/private/Repair/Owncloud/MoveAvatarsBackgroundJob.php index 83c78c2cba4..e145fb71863 100644 --- a/lib/private/Repair/Owncloud/MoveAvatarsBackgroundJob.php +++ b/lib/private/Repair/Owncloud/MoveAvatarsBackgroundJob.php @@ -1,32 +1,18 @@ <?php + +declare(strict_types=1); + /** - * @copyright 2016 Roeland Jago Douma <roeland@famdouma.nl> - * - * @author Christoph Wurst <christoph@winzerhof-wurst.at> - * @author Julius Härtl <jus@bitgrid.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/>. - * + * SPDX-FileCopyrightText: 2016 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later */ namespace OC\Repair\Owncloud; -use OC\BackgroundJob\QueuedJob; +use OCP\AppFramework\Utility\ITimeFactory; +use OCP\BackgroundJob\QueuedJob; use OCP\Files\IRootFolder; use OCP\Files\NotFoundException; -use OCP\Files\Storage; +use OCP\Files\Storage\IStorage; use OCP\IAvatarManager; use OCP\IUser; use OCP\IUserManager; @@ -34,22 +20,16 @@ use Psr\Log\LoggerInterface; use function is_resource; class MoveAvatarsBackgroundJob extends QueuedJob { - /** @var IUserManager */ - private $userManager; - - /** @var LoggerInterface */ - private $logger; - - /** @var IAvatarManager */ - private $avatarManager; - - /** @var Storage */ - private $owncloudAvatarStorage; + private ?IStorage $owncloudAvatarStorage = null; - public function __construct(IUserManager $userManager, LoggerInterface $logger, IAvatarManager $avatarManager, IRootFolder $rootFolder) { - $this->userManager = $userManager; - $this->logger = $logger; - $this->avatarManager = $avatarManager; + public function __construct( + private IUserManager $userManager, + private LoggerInterface $logger, + private IAvatarManager $avatarManager, + private IRootFolder $rootFolder, + ITimeFactory $time, + ) { + parent::__construct($time); try { $this->owncloudAvatarStorage = $rootFolder->get('avatars')->getStorage(); } catch (\Exception $e) { @@ -69,7 +49,7 @@ class MoveAvatarsBackgroundJob extends QueuedJob { } $counter = 0; - $this->userManager->callForSeenUsers(function (IUser $user) use ($counter) { + $this->userManager->callForSeenUsers(function (IUser $user) use (&$counter) { $uid = $user->getUID(); $path = 'avatars/' . $this->buildOwnCloudAvatarPath($uid); diff --git a/lib/private/Repair/Owncloud/SaveAccountsTableData.php b/lib/private/Repair/Owncloud/SaveAccountsTableData.php index 94fd1554293..1b6da7c858f 100644 --- a/lib/private/Repair/Owncloud/SaveAccountsTableData.php +++ b/lib/private/Repair/Owncloud/SaveAccountsTableData.php @@ -1,26 +1,7 @@ <?php /** - * @copyright Copyright (c) 2017 Joas Schilling <coding@schilljs.com> - * - * @author Christoph Wurst <christoph@winzerhof-wurst.at> - * @author Joas Schilling <coding@schilljs.com> - * @author Julius Härtl <jus@bitgrid.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/>. - * + * SPDX-FileCopyrightText: 2017 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later */ namespace OC\Repair\Owncloud; diff --git a/lib/private/Repair/Owncloud/UpdateLanguageCodes.php b/lib/private/Repair/Owncloud/UpdateLanguageCodes.php index e08a0b55a9a..e27ab06b2f3 100644 --- a/lib/private/Repair/Owncloud/UpdateLanguageCodes.php +++ b/lib/private/Repair/Owncloud/UpdateLanguageCodes.php @@ -1,24 +1,7 @@ <?php /** - * @copyright Copyright (c) 2016 Morris Jobke <hey@morrisjobke.de> - * - * @author Julius Härtl <jus@bitgrid.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/>. - * + * SPDX-FileCopyrightText: 2016 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later */ namespace OC\Repair\Owncloud; @@ -40,7 +23,7 @@ class UpdateLanguageCodes implements IRepairStep { * @param IConfig $config */ public function __construct(IDBConnection $connection, - IConfig $config) { + IConfig $config) { $this->connection = $connection; $this->config = $config; } diff --git a/lib/private/Repair/RemoveLinkShares.php b/lib/private/Repair/RemoveLinkShares.php index b45a1d83a56..f128b6f731b 100644 --- a/lib/private/Repair/RemoveLinkShares.php +++ b/lib/private/Repair/RemoveLinkShares.php @@ -3,29 +3,8 @@ declare(strict_types=1); /** - * @copyright Copyright (c) 2019, Roeland Jago Douma <roeland@famdouma.nl> - * - * @author Christoph Wurst <christoph@winzerhof-wurst.at> - * @author Daniel Kesselberg <mail@danielkesselberg.de> - * @author Joas Schilling <coding@schilljs.com> - * @author John Molakvoæ <skjnldsv@protonmail.com> - * @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/>. - * + * SPDX-FileCopyrightText: 2019 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later */ namespace OC\Repair; @@ -54,10 +33,10 @@ class RemoveLinkShares implements IRepairStep { private $timeFactory; public function __construct(IDBConnection $connection, - IConfig $config, - IGroupManager $groupManager, - IManager $notificationManager, - ITimeFactory $timeFactory) { + IConfig $config, + IGroupManager $groupManager, + IManager $notificationManager, + ITimeFactory $timeFactory) { $this->connection = $connection; $this->config = $config; $this->groupManager = $groupManager; diff --git a/lib/private/Repair/RepairDavShares.php b/lib/private/Repair/RepairDavShares.php index 467adc2b0d9..792fdd4033e 100644 --- a/lib/private/Repair/RepairDavShares.php +++ b/lib/private/Repair/RepairDavShares.php @@ -3,25 +3,8 @@ declare(strict_types=1); /** - * @copyright Copyright (c) 2020 Arthur Schiwon <blizzz@arthur-schiwon.de> - * - * @author Arthur Schiwon <blizzz@arthur-schiwon.de> - * - * @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/>. - * + * SPDX-FileCopyrightText: 2020 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later */ namespace OC\Repair; diff --git a/lib/private/Repair/RepairInvalidShares.php b/lib/private/Repair/RepairInvalidShares.php index 9255034f6fe..f28ae1c45fb 100644 --- a/lib/private/Repair/RepairInvalidShares.php +++ b/lib/private/Repair/RepairInvalidShares.php @@ -1,27 +1,9 @@ <?php + /** - * @copyright Copyright (c) 2016, ownCloud, Inc. - * - * @author Christoph Wurst <christoph@winzerhof-wurst.at> - * @author Joas Schilling <coding@schilljs.com> - * @author Lukas Reschke <lukas@statuscode.ch> - * @author Thomas Müller <thomas.mueller@tmit.eu> - * @author Vincent Petry <vincent@nextcloud.com> - * - * @license AGPL-3.0 - * - * This code is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License, version 3, - * as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License, version 3, - * along with this program. If not, see <http://www.gnu.org/licenses/> - * + * SPDX-FileCopyrightText: 2016-2024 Nextcloud GmbH and Nextcloud contributors + * SPDX-FileCopyrightText: 2016 ownCloud, Inc. + * SPDX-License-Identifier: AGPL-3.0-only */ namespace OC\Repair; diff --git a/lib/private/Repair/RepairMimeTypes.php b/lib/private/Repair/RepairMimeTypes.php index ee5a84ad65c..103ce9c13fc 100644 --- a/lib/private/Repair/RepairMimeTypes.php +++ b/lib/private/Repair/RepairMimeTypes.php @@ -1,35 +1,9 @@ <?php + /** - * @copyright Copyright (c) 2016, ownCloud, Inc. - * - * @author Arthur Schiwon <blizzz@arthur-schiwon.de> - * @author Christoph Wurst <christoph@winzerhof-wurst.at> - * @author Joas Schilling <coding@schilljs.com> - * @author Julius Härtl <jus@bitgrid.net> - * @author Morris Jobke <hey@morrisjobke.de> - * @author nik gaffney <nik@fo.am> - * @author Olivier Paroz <github@oparoz.com> - * @author Rello <Rello@users.noreply.github.com> - * @author Roeland Jago Douma <roeland@famdouma.nl> - * @author Stefan Weil <sw@weilnetz.de> - * @author Thomas Ebert <thomas.ebert@usability.de> - * @author Thomas Müller <thomas.mueller@tmit.eu> - * @author Vincent Petry <vincent@nextcloud.com> - * - * @license AGPL-3.0 - * - * This code is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License, version 3, - * as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License, version 3, - * along with this program. If not, see <http://www.gnu.org/licenses/> - * + * SPDX-FileCopyrightText: 2016-2024 Nextcloud GmbH and Nextcloud contributors + * SPDX-FileCopyrightText: 2016 ownCloud, Inc. + * SPDX-License-Identifier: AGPL-3.0-only */ namespace OC\Repair; @@ -49,7 +23,7 @@ class RepairMimeTypes implements IRepairStep { protected $folderMimeTypeId; public function __construct(IConfig $config, - IDBConnection $connection) { + IDBConnection $connection) { $this->config = $config; $this->connection = $connection; } @@ -229,6 +203,38 @@ class RepairMimeTypes implements IRepairStep { return $this->updateMimetypes($updatedMimetypes); } + private function introduceEnhancedMetafileFormatType() { + $updatedMimetypes = [ + 'emf' => 'image/emf', + ]; + + return $this->updateMimetypes($updatedMimetypes); + } + + private function introduceEmlAndMsgFormatType() { + $updatedMimetypes = [ + 'eml' => 'message/rfc822', + 'msg' => 'application/vnd.ms-outlook', + ]; + + return $this->updateMimetypes($updatedMimetypes); + } + + private function introduceAacAudioType() { + $updatedMimetypes = [ + 'aac' => 'audio/aac', + ]; + + return $this->updateMimetypes($updatedMimetypes); + } + + private function introduceReStructuredTextFormatType() { + $updatedMimetypes = [ + 'rst' => 'text/x-rst', + ]; + + return $this->updateMimetypes($updatedMimetypes); + } /** * Fix mime types @@ -286,5 +292,21 @@ class RepairMimeTypes implements IRepairStep { if (version_compare($ocVersionFromBeforeUpdate, '26.0.0.1', '<') && $this->introduceAsciidocType()) { $out->info('Fixed AsciiDoc mime types'); } + + if (version_compare($ocVersionFromBeforeUpdate, '28.0.0.5', '<') && $this->introduceEnhancedMetafileFormatType()) { + $out->info('Fixed Enhanced Metafile Format mime types'); + } + + if (version_compare($ocVersionFromBeforeUpdate, '29.0.0.2', '<') && $this->introduceEmlAndMsgFormatType()) { + $out->info('Fixed eml and msg mime type'); + } + + if (version_compare($ocVersionFromBeforeUpdate, '29.0.0.6', '<') && $this->introduceAacAudioType()) { + $out->info('Fixed aac mime type'); + } + + if (version_compare($ocVersionFromBeforeUpdate, '29.0.0.10', '<') && $this->introduceReStructuredTextFormatType()) { + $out->info('Fixed ReStructured Text mime type'); + } } } diff --git a/lib/private/Repair/SqliteAutoincrement.php b/lib/private/Repair/SqliteAutoincrement.php deleted file mode 100644 index 4a8b2a45d3f..00000000000 --- a/lib/private/Repair/SqliteAutoincrement.php +++ /dev/null @@ -1,100 +0,0 @@ -<?php -/** - * @copyright Copyright (c) 2016, ownCloud, Inc. - * - * @author Roeland Jago Douma <roeland@famdouma.nl> - * @author Thomas Müller <thomas.mueller@tmit.eu> - * @author Vincent Petry <vincent@nextcloud.com> - * - * @license AGPL-3.0 - * - * This code is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License, version 3, - * as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License, version 3, - * along with this program. If not, see <http://www.gnu.org/licenses/> - * - */ -namespace OC\Repair; - -use Doctrine\DBAL\Platforms\SqlitePlatform; -use Doctrine\DBAL\Schema\ColumnDiff; -use Doctrine\DBAL\Schema\SchemaDiff; -use Doctrine\DBAL\Schema\SchemaException; -use Doctrine\DBAL\Schema\TableDiff; -use OCP\Migration\IOutput; -use OCP\Migration\IRepairStep; - -/** - * Fixes Sqlite autoincrement by forcing the SQLite table schemas to be - * altered in order to retrigger SQL schema generation through OCSqlitePlatform. - */ -class SqliteAutoincrement implements IRepairStep { - /** - * @var \OC\DB\Connection - */ - protected $connection; - - /** - * @param \OC\DB\Connection $connection - */ - public function __construct($connection) { - $this->connection = $connection; - } - - public function getName() { - return 'Repair SQLite autoincrement'; - } - - /** - * Fix mime types - */ - public function run(IOutput $out) { - if (!$this->connection->getDatabasePlatform() instanceof SqlitePlatform) { - return; - } - - $sourceSchema = $this->connection->getSchemaManager()->createSchema(); - - $schemaDiff = new SchemaDiff(); - - foreach ($sourceSchema->getTables() as $tableSchema) { - $primaryKey = $tableSchema->getPrimaryKey(); - if (!$primaryKey) { - continue; - } - - $columnNames = $primaryKey->getColumns(); - - // add a column diff for every primary key column, - // but do not actually change anything, this will - // force the generation of SQL statements to alter - // those tables, which will then trigger the - // specific SQL code from OCSqlitePlatform - try { - $tableDiff = new TableDiff($tableSchema->getName()); - $tableDiff->fromTable = $tableSchema; - foreach ($columnNames as $columnName) { - $columnSchema = $tableSchema->getColumn($columnName); - $columnDiff = new ColumnDiff($columnSchema->getName(), $columnSchema); - $tableDiff->changedColumns[$columnSchema->getName()] = $columnDiff; - $schemaDiff->changedTables[] = $tableDiff; - } - } catch (SchemaException $e) { - // ignore - } - } - - $this->connection->beginTransaction(); - foreach ($schemaDiff->toSql($this->connection->getDatabasePlatform()) as $sql) { - $this->connection->query($sql); - } - $this->connection->commit(); - } -} diff --git a/lib/private/RepairException.php b/lib/private/RepairException.php index 6cce3b0e0d6..bd6d4600bfb 100644 --- a/lib/private/RepairException.php +++ b/lib/private/RepairException.php @@ -1,23 +1,9 @@ <?php + /** - * @copyright Copyright (c) 2016, ownCloud, Inc. - * - * @author Vincent Petry <vincent@nextcloud.com> - * - * @license AGPL-3.0 - * - * This code is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License, version 3, - * as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License, version 3, - * along with this program. If not, see <http://www.gnu.org/licenses/> - * + * SPDX-FileCopyrightText: 2016-2024 Nextcloud GmbH and Nextcloud contributors + * SPDX-FileCopyrightText: 2016 ownCloud, Inc. + * SPDX-License-Identifier: AGPL-3.0-only */ namespace OC; diff --git a/lib/private/RichObjectStrings/Validator.php b/lib/private/RichObjectStrings/Validator.php index 4585cbfc814..41c2456ba27 100644 --- a/lib/private/RichObjectStrings/Validator.php +++ b/lib/private/RichObjectStrings/Validator.php @@ -1,26 +1,7 @@ <?php /** - * @copyright Copyright (c) 2016 Joas Schilling <coding@schilljs.com> - * - * @author Joas Schilling <coding@schilljs.com> - * @author Lukas Reschke <lukas@statuscode.ch> - * @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/>. - * + * SPDX-FileCopyrightText: 2016 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later */ namespace OC\RichObjectStrings; @@ -95,7 +76,7 @@ class Validator implements IValidator { $missingKeys = array_diff($requiredParameters, array_keys($parameter)); if (!empty($missingKeys)) { - throw new InvalidObjectExeption('Object is invalid'); + throw new InvalidObjectExeption('Object is invalid, missing keys:'.json_encode($missingKeys)); } } diff --git a/lib/private/Route/CachingRouter.php b/lib/private/Route/CachingRouter.php index 69fb3c986c9..7dd26827d3c 100644 --- a/lib/private/Route/CachingRouter.php +++ b/lib/private/Route/CachingRouter.php @@ -1,29 +1,13 @@ <?php + /** - * @copyright Copyright (c) 2016, ownCloud, Inc. - * - * @author Christoph Wurst <christoph@winzerhof-wurst.at> - * @author Lukas Reschke <lukas@statuscode.ch> - * @author Morris Jobke <hey@morrisjobke.de> - * @author Robin Appelman <robin@icewind.nl> - * - * @license AGPL-3.0 - * - * This code is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License, version 3, - * as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License, version 3, - * along with this program. If not, see <http://www.gnu.org/licenses/> - * + * SPDX-FileCopyrightText: 2016-2024 Nextcloud GmbH and Nextcloud contributors + * SPDX-FileCopyrightText: 2016 ownCloud, Inc. + * SPDX-License-Identifier: AGPL-3.0-only */ namespace OC\Route; +use OCP\App\IAppManager; use OCP\Diagnostics\IEventLogger; use OCP\ICache; use OCP\ICacheFactory; @@ -41,10 +25,11 @@ class CachingRouter extends Router { IRequest $request, IConfig $config, IEventLogger $eventLogger, - ContainerInterface $container + ContainerInterface $container, + IAppManager $appManager, ) { $this->cache = $cacheFactory->createLocal('route'); - parent::__construct($logger, $request, $config, $eventLogger, $container); + parent::__construct($logger, $request, $config, $eventLogger, $container, $appManager); } /** diff --git a/lib/private/Route/Route.php b/lib/private/Route/Route.php index ad440a001af..2fef3b10806 100644 --- a/lib/private/Route/Route.php +++ b/lib/private/Route/Route.php @@ -1,30 +1,9 @@ <?php + /** - * @copyright Copyright (c) 2016, ownCloud, Inc. - * - * @author Bart Visscher <bartv@thisnet.nl> - * @author Christoph Wurst <christoph@winzerhof-wurst.at> - * @author David Prévot <taffit@debian.org> - * @author Morris Jobke <hey@morrisjobke.de> - * @author Robin Appelman <robin@icewind.nl> - * @author Robin McCorkell <robin@mccorkell.me.uk> - * @author Roeland Jago Douma <roeland@famdouma.nl> - * @author Thomas Tanghus <thomas@tanghus.net> - * - * @license AGPL-3.0 - * - * This code is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License, version 3, - * as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License, version 3, - * along with this program. If not, see <http://www.gnu.org/licenses/> - * + * SPDX-FileCopyrightText: 2016-2024 Nextcloud GmbH and Nextcloud contributors + * SPDX-FileCopyrightText: 2016 ownCloud, Inc. + * SPDX-License-Identifier: AGPL-3.0-only */ namespace OC\Route; diff --git a/lib/private/Route/Router.php b/lib/private/Route/Router.php index fe97623176d..b04b6a4d21c 100644 --- a/lib/private/Route/Router.php +++ b/lib/private/Route/Router.php @@ -1,39 +1,18 @@ <?php + /** - * @copyright Copyright (c) 2016, ownCloud, Inc. - * - * @author Bart Visscher <bartv@thisnet.nl> - * @author Bernhard Posselt <dev@bernhard-posselt.com> - * @author Christoph Wurst <christoph@winzerhof-wurst.at> - * @author Joas Schilling <coding@schilljs.com> - * @author Jörn Friedrich Dreyer <jfd@butonic.de> - * @author Julius Härtl <jus@bitgrid.net> - * @author Lukas Reschke <lukas@statuscode.ch> - * @author Morris Jobke <hey@morrisjobke.de> - * @author Robin Appelman <robin@icewind.nl> - * @author Robin McCorkell <robin@mccorkell.me.uk> - * @author Roeland Jago Douma <roeland@famdouma.nl> - * @author Thomas Müller <thomas.mueller@tmit.eu> - * - * @license AGPL-3.0 - * - * This code is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License, version 3, - * as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License, version 3, - * along with this program. If not, see <http://www.gnu.org/licenses/> - * + * SPDX-FileCopyrightText: 2016-2024 Nextcloud GmbH and Nextcloud contributors + * SPDX-FileCopyrightText: 2016 ownCloud, Inc. + * SPDX-License-Identifier: AGPL-3.0-only */ namespace OC\Route; +use DirectoryIterator; use OC\AppFramework\Routing\RouteParser; +use OCP\App\AppPathNotFoundException; +use OCP\App\IAppManager; use OCP\AppFramework\App; +use OCP\AppFramework\Http\Attribute\Route as RouteAttribute; use OCP\Diagnostics\IEventLogger; use OCP\IConfig; use OCP\IRequest; @@ -41,6 +20,9 @@ use OCP\Route\IRouter; use OCP\Util; use Psr\Container\ContainerInterface; use Psr\Log\LoggerInterface; +use ReflectionAttribute; +use ReflectionClass; +use ReflectionException; use Symfony\Component\Routing\Exception\ResourceNotFoundException; use Symfony\Component\Routing\Exception\RouteNotFoundException; use Symfony\Component\Routing\Generator\UrlGenerator; @@ -65,22 +47,17 @@ class Router implements IRouter { protected $loaded = false; /** @var array */ protected $loadedApps = []; - protected LoggerInterface $logger; /** @var RequestContext */ protected $context; - private IEventLogger $eventLogger; - private IConfig $config; - private ContainerInterface $container; public function __construct( - LoggerInterface $logger, + protected LoggerInterface $logger, IRequest $request, - IConfig $config, - IEventLogger $eventLogger, - ContainerInterface $container + private IConfig $config, + private IEventLogger $eventLogger, + private ContainerInterface $container, + private IAppManager $appManager, ) { - $this->logger = $logger; - $this->config = $config; $baseUrl = \OC::$WEBROOT; if (!($config->getSystemValue('htaccess.IgnoreFrontController', false) === true || getenv('front_controller_active') === 'true')) { $baseUrl .= '/index.php'; @@ -95,8 +72,6 @@ class Router implements IRouter { $this->context = new RequestContext($baseUrl, $method, $host, $schema); // TODO cache $this->root = $this->getCollection('root'); - $this->eventLogger = $eventLogger; - $this->container = $container; } /** @@ -108,12 +83,14 @@ class Router implements IRouter { if ($this->routingFiles === null) { $this->routingFiles = []; foreach (\OC_APP::getEnabledApps() as $app) { - $appPath = \OC_App::getAppPath($app); - if ($appPath !== false) { + try { + $appPath = $this->appManager->getAppPath($app); $file = $appPath . '/appinfo/routes.php'; if (file_exists($file)) { $this->routingFiles[$app] = $file; } + } catch (AppPathNotFoundException) { + /* ignore */ } } } @@ -150,6 +127,22 @@ class Router implements IRouter { } } $this->eventLogger->start('route:load:' . $requestedApp, 'Loading Routes for ' . $requestedApp); + + if ($requestedApp !== null && in_array($requestedApp, \OC_App::getEnabledApps())) { + $routes = $this->getAttributeRoutes($requestedApp); + if (count($routes) > 0) { + $this->useCollection($requestedApp); + $this->setupRoutes($routes, $requestedApp); + $collection = $this->getCollection($requestedApp); + $this->root->addCollection($collection); + + // Also add the OCS collection + $collection = $this->getCollection($requestedApp . '.ocs'); + $collection->addPrefix('/ocsapp'); + $this->root->addCollection($collection); + } + } + foreach ($routingFiles as $app => $file) { if (!isset($this->loadedApps[$app])) { if (!\OC_App::isAppLoaded($app)) { @@ -173,6 +166,7 @@ class Router implements IRouter { if (!isset($this->loadedApps['core'])) { $this->loadedApps['core'] = true; $this->useCollection('root'); + $this->setupRoutes($this->getAttributeRoutes('core'), 'core'); require_once __DIR__ . '/../../../core/routes.php'; // Also add the OCS collection @@ -230,9 +224,9 @@ class Router implements IRouter { * @return \OC\Route\Route */ public function create($name, - $pattern, - array $defaults = [], - array $requirements = []) { + $pattern, + array $defaults = [], + array $requirements = []) { $route = new Route($pattern, $defaults, $requirements); $this->collection->add($name, $route); return $route; @@ -247,23 +241,23 @@ class Router implements IRouter { */ public function findMatchingRoute(string $url): array { $this->eventLogger->start('route:match', 'Match route'); - if (substr($url, 0, 6) === '/apps/') { + if (str_starts_with($url, '/apps/')) { // empty string / 'apps' / $app / rest of the route [, , $app,] = explode('/', $url, 4); $app = \OC_App::cleanAppId($app); \OC::$REQUESTEDAPP = $app; $this->loadRoutes($app); - } elseif (substr($url, 0, 13) === '/ocsapp/apps/') { + } elseif (str_starts_with($url, '/ocsapp/apps/')) { // empty string / 'ocsapp' / 'apps' / $app / rest of the route [, , , $app,] = explode('/', $url, 5); $app = \OC_App::cleanAppId($app); \OC::$REQUESTEDAPP = $app; $this->loadRoutes($app); - } elseif (substr($url, 0, 10) === '/settings/') { + } elseif (str_starts_with($url, '/settings/')) { $this->loadRoutes('settings'); - } elseif (substr($url, 0, 6) === '/core/') { + } elseif (str_starts_with($url, '/core/')) { \OC::$REQUESTEDAPP = $url; if (!$this->config->getSystemValueBool('maintenance') && !Util::needUpgrade()) { \OC_App::loadApps(); @@ -277,7 +271,7 @@ class Router implements IRouter { try { $parameters = $matcher->match($url); } catch (ResourceNotFoundException $e) { - if (substr($url, -1) !== '/') { + if (!str_ends_with($url, '/')) { // We allow links to apps/files? for backwards compatibility reasons // However, since Symfony does not allow empty route names, the route // we need to match is '/', so we need to append the '/' here. @@ -338,7 +332,7 @@ class Router implements IRouter { * */ public function getGenerator() { - if (null !== $this->generator) { + if ($this->generator !== null) { return $this->generator; } @@ -354,12 +348,19 @@ class Router implements IRouter { * @return string */ public function generate($name, - $parameters = [], - $absolute = false) { + $parameters = [], + $absolute = false) { $referenceType = UrlGenerator::ABSOLUTE_URL; if ($absolute === false) { $referenceType = UrlGenerator::ABSOLUTE_PATH; } + /* + * The route name has to be lowercase, for symfony to match it correctly. + * This is required because smyfony allows mixed casing for controller names in the routes. + * To avoid breaking all the existing route names, registering and matching will only use the lowercase names. + * This is also safe on the PHP side because class and method names collide regardless of the casing. + */ + $name = strtolower($name); $name = $this->fixLegacyRootName($name); if (str_contains($name, '.')) { [$appName, $other] = explode('.', $name, 3); @@ -385,34 +386,79 @@ class Router implements IRouter { } protected function fixLegacyRootName(string $routeName): string { - if ($routeName === 'files.viewcontroller.showFile') { - return 'files.View.showFile'; + if ($routeName === 'files.viewcontroller.showfile') { + return 'files.view.showfile'; } - if ($routeName === 'files_sharing.sharecontroller.showShare') { - return 'files_sharing.Share.showShare'; + if ($routeName === 'files_sharing.sharecontroller.showshare') { + return 'files_sharing.share.showshare'; } - if ($routeName === 'files_sharing.sharecontroller.showAuthenticate') { - return 'files_sharing.Share.showAuthenticate'; + if ($routeName === 'files_sharing.sharecontroller.showauthenticate') { + return 'files_sharing.share.showauthenticate'; } if ($routeName === 'files_sharing.sharecontroller.authenticate') { - return 'files_sharing.Share.authenticate'; + return 'files_sharing.share.authenticate'; } - if ($routeName === 'files_sharing.sharecontroller.downloadShare') { - return 'files_sharing.Share.downloadShare'; + if ($routeName === 'files_sharing.sharecontroller.downloadshare') { + return 'files_sharing.share.downloadshare'; } - if ($routeName === 'files_sharing.publicpreview.directLink') { - return 'files_sharing.PublicPreview.directLink'; + if ($routeName === 'files_sharing.publicpreview.directlink') { + return 'files_sharing.publicpreview.directlink'; } - if ($routeName === 'cloud_federation_api.requesthandlercontroller.addShare') { - return 'cloud_federation_api.RequestHandler.addShare'; + if ($routeName === 'cloud_federation_api.requesthandlercontroller.addshare') { + return 'cloud_federation_api.requesthandler.addshare'; } - if ($routeName === 'cloud_federation_api.requesthandlercontroller.receiveNotification') { - return 'cloud_federation_api.RequestHandler.receiveNotification'; + if ($routeName === 'cloud_federation_api.requesthandlercontroller.receivenotification') { + return 'cloud_federation_api.requesthandler.receivenotification'; } return $routeName; } /** + * @throws ReflectionException + */ + private function getAttributeRoutes(string $app): array { + $routes = []; + + if ($app === 'core') { + $appControllerPath = __DIR__ . '/../../../core/Controller'; + $appNameSpace = 'OC\\Core'; + } else { + $appControllerPath = \OC_App::getAppPath($app) . '/lib/Controller'; + $appNameSpace = App::buildAppNamespace($app); + } + + if (!file_exists($appControllerPath)) { + return []; + } + + $dir = new DirectoryIterator($appControllerPath); + foreach ($dir as $file) { + if (!str_ends_with($file->getPathname(), 'Controller.php')) { + continue; + } + + $class = new ReflectionClass($appNameSpace . '\\Controller\\' . basename($file->getPathname(), '.php')); + + foreach ($class->getMethods() as $method) { + foreach ($method->getAttributes(RouteAttribute::class, ReflectionAttribute::IS_INSTANCEOF) as $attribute) { + $route = $attribute->newInstance(); + + $serializedRoute = $route->toArray(); + // Remove 'Controller' suffix + $serializedRoute['name'] = substr($class->getShortName(), 0, -10) . '#' . $method->getName(); + + $key = $route->getType(); + + $routes[$key] ??= []; + $routes[$key][] = $serializedRoute; + } + } + } + + return $routes; + } + + /** * To isolate the variable scope used inside the $file it is required in it's own method * * @param string $file the route file location to include diff --git a/lib/private/Search.php b/lib/private/Search.php index b1e39843e49..d51a305b331 100644 --- a/lib/private/Search.php +++ b/lib/private/Search.php @@ -1,35 +1,16 @@ <?php + /** - * @copyright Copyright (c) 2016, ownCloud, Inc. - * - * @author Andrew Brown <andrew@casabrown.com> - * @author Bart Visscher <bartv@thisnet.nl> - * @author Christoph Wurst <christoph@winzerhof-wurst.at> - * @author Jörn Friedrich Dreyer <jfd@butonic.de> - * @author Morris Jobke <hey@morrisjobke.de> - * @author Robin Appelman <robin@icewind.nl> - * @author Roeland Jago Douma <roeland@famdouma.nl> - * - * @license AGPL-3.0 - * - * This code is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License, version 3, - * as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License, version 3, - * along with this program. If not, see <http://www.gnu.org/licenses/> - * + * SPDX-FileCopyrightText: 2016-2024 Nextcloud GmbH and Nextcloud contributors + * SPDX-FileCopyrightText: 2016 ownCloud, Inc. + * SPDX-License-Identifier: AGPL-3.0-only */ namespace OC; use OCP\ISearch; use OCP\Search\PagedProvider; use OCP\Search\Provider; +use Psr\Log\LoggerInterface; /** * Provide an interface to all search providers @@ -65,7 +46,7 @@ class Search implements ISearch { $results = array_merge($results, $providerResults); } } else { - \OC::$server->getLogger()->warning('Ignoring Unknown search provider', ['provider' => $provider]); + \OCP\Server::get(LoggerInterface::class)->warning('Ignoring Unknown search provider', ['provider' => $provider]); } } return $results; diff --git a/lib/private/Search/Filter/BooleanFilter.php b/lib/private/Search/Filter/BooleanFilter.php new file mode 100644 index 00000000000..92572a60073 --- /dev/null +++ b/lib/private/Search/Filter/BooleanFilter.php @@ -0,0 +1,29 @@ +<?php + +declare(strict_types=1); + +/** + * SPDX-FileCopyrightText: 2023 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later + */ + +namespace OC\Search\Filter; + +use InvalidArgumentException; +use OCP\Search\IFilter; + +class BooleanFilter implements IFilter { + private bool $value; + + public function __construct(string $value) { + $this->value = match ($value) { + 'true', 'yes', 'y', '1' => true, + 'false', 'no', 'n', '0', '' => false, + default => throw new InvalidArgumentException('Invalid boolean value '. $value), + }; + } + + public function get(): bool { + return $this->value; + } +} diff --git a/lib/private/Search/Filter/DateTimeFilter.php b/lib/private/Search/Filter/DateTimeFilter.php new file mode 100644 index 00000000000..367af80b505 --- /dev/null +++ b/lib/private/Search/Filter/DateTimeFilter.php @@ -0,0 +1,29 @@ +<?php + +declare(strict_types=1); + +/** + * SPDX-FileCopyrightText: 2023 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later + */ + +namespace OC\Search\Filter; + +use DateTimeImmutable; +use OCP\Search\IFilter; + +class DateTimeFilter implements IFilter { + private DateTimeImmutable $value; + + public function __construct(string $value) { + if (filter_var($value, FILTER_VALIDATE_INT)) { + $value = '@'.$value; + } + + $this->value = new DateTimeImmutable($value); + } + + public function get(): DateTimeImmutable { + return $this->value; + } +} diff --git a/lib/private/Search/Filter/FloatFilter.php b/lib/private/Search/Filter/FloatFilter.php new file mode 100644 index 00000000000..97d767e62cb --- /dev/null +++ b/lib/private/Search/Filter/FloatFilter.php @@ -0,0 +1,28 @@ +<?php + +declare(strict_types=1); + +/** + * SPDX-FileCopyrightText: 2023 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later + */ + +namespace OC\Search\Filter; + +use InvalidArgumentException; +use OCP\Search\IFilter; + +class FloatFilter implements IFilter { + private float $value; + + public function __construct(string $value) { + $this->value = filter_var($value, FILTER_VALIDATE_FLOAT); + if ($this->value === false) { + throw new InvalidArgumentException('Invalid float value '. $value); + } + } + + public function get(): float { + return $this->value; + } +} diff --git a/lib/private/Search/Filter/GroupFilter.php b/lib/private/Search/Filter/GroupFilter.php new file mode 100644 index 00000000000..e7694a6daa2 --- /dev/null +++ b/lib/private/Search/Filter/GroupFilter.php @@ -0,0 +1,34 @@ +<?php + +declare(strict_types=1); + +/** + * SPDX-FileCopyrightText: 2023 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later + */ + +namespace OC\Search\Filter; + +use InvalidArgumentException; +use OCP\IGroup; +use OCP\IGroupManager; +use OCP\Search\IFilter; + +class GroupFilter implements IFilter { + private IGroup $group; + + public function __construct( + string $value, + IGroupManager $groupManager, + ) { + $group = $groupManager->get($value); + if ($group === null) { + throw new InvalidArgumentException('Group '.$value.' not found'); + } + $this->group = $group; + } + + public function get(): IGroup { + return $this->group; + } +} diff --git a/lib/private/Search/Filter/IntegerFilter.php b/lib/private/Search/Filter/IntegerFilter.php new file mode 100644 index 00000000000..825c7ddd3eb --- /dev/null +++ b/lib/private/Search/Filter/IntegerFilter.php @@ -0,0 +1,28 @@ +<?php + +declare(strict_types=1); + +/** + * SPDX-FileCopyrightText: 2023 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later + */ + +namespace OC\Search\Filter; + +use InvalidArgumentException; +use OCP\Search\IFilter; + +class IntegerFilter implements IFilter { + private int $value; + + public function __construct(string $value) { + $this->value = filter_var($value, FILTER_VALIDATE_INT); + if ($this->value === false) { + throw new InvalidArgumentException('Invalid integer value '. $value); + } + } + + public function get(): int { + return $this->value; + } +} diff --git a/lib/private/Search/Filter/StringFilter.php b/lib/private/Search/Filter/StringFilter.php new file mode 100644 index 00000000000..6944a7803f3 --- /dev/null +++ b/lib/private/Search/Filter/StringFilter.php @@ -0,0 +1,27 @@ +<?php + +declare(strict_types=1); + +/** + * SPDX-FileCopyrightText: 2023 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later + */ + +namespace OC\Search\Filter; + +use InvalidArgumentException; +use OCP\Search\IFilter; + +class StringFilter implements IFilter { + public function __construct( + private string $value, + ) { + if ($value === '') { + throw new InvalidArgumentException('String filter can’t be empty'); + } + } + + public function get(): string { + return $this->value; + } +} diff --git a/lib/private/Search/Filter/StringsFilter.php b/lib/private/Search/Filter/StringsFilter.php new file mode 100644 index 00000000000..8b8fabb5347 --- /dev/null +++ b/lib/private/Search/Filter/StringsFilter.php @@ -0,0 +1,34 @@ +<?php + +declare(strict_types=1); + +/** + * SPDX-FileCopyrightText: 2023 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later + */ + +namespace OC\Search\Filter; + +use InvalidArgumentException; +use OCP\Search\IFilter; + +class StringsFilter implements IFilter { + /** + * @var string[] + */ + private array $values; + + public function __construct(string ...$values) { + $this->values = array_unique(array_filter($values)); + if (empty($this->values)) { + throw new InvalidArgumentException('Strings filter can’t be empty'); + } + } + + /** + * @return string[] + */ + public function get(): array { + return $this->values; + } +} diff --git a/lib/private/Search/Filter/UserFilter.php b/lib/private/Search/Filter/UserFilter.php new file mode 100644 index 00000000000..fbbc793e633 --- /dev/null +++ b/lib/private/Search/Filter/UserFilter.php @@ -0,0 +1,34 @@ +<?php + +declare(strict_types=1); + +/** + * SPDX-FileCopyrightText: 2023 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later + */ + +namespace OC\Search\Filter; + +use InvalidArgumentException; +use OCP\IUser; +use OCP\IUserManager; +use OCP\Search\IFilter; + +class UserFilter implements IFilter { + private IUser $user; + + public function __construct( + string $value, + IUserManager $userManager, + ) { + $user = $userManager->get($value); + if ($user === null) { + throw new InvalidArgumentException('User '.$value.' not found'); + } + $this->user = $user; + } + + public function get(): IUser { + return $this->user; + } +} diff --git a/lib/private/Search/FilterCollection.php b/lib/private/Search/FilterCollection.php new file mode 100644 index 00000000000..173c967245a --- /dev/null +++ b/lib/private/Search/FilterCollection.php @@ -0,0 +1,43 @@ +<?php + +declare(strict_types=1); + +/** + * SPDX-FileCopyrightText: 2023 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later + */ +namespace OC\Search; + +use Generator; +use OCP\Search\IFilter; +use OCP\Search\IFilterCollection; + +/** + * Interface for search filters + * + * @since 28.0.0 + */ +class FilterCollection implements IFilterCollection { + /** + * @var IFilter[] + */ + private array $filters; + + public function __construct(IFilter ...$filters) { + $this->filters = $filters; + } + + public function has(string $name): bool { + return isset($this->filters[$name]); + } + + public function get(string $name): ?IFilter { + return $this->filters[$name] ?? null; + } + + public function getIterator(): Generator { + foreach ($this->filters as $k => $v) { + yield $k => $v; + } + } +} diff --git a/lib/private/Search/FilterFactory.php b/lib/private/Search/FilterFactory.php new file mode 100644 index 00000000000..1317dd759af --- /dev/null +++ b/lib/private/Search/FilterFactory.php @@ -0,0 +1,43 @@ +<?php + +declare(strict_types=1); + +/** + * SPDX-FileCopyrightText: 2023 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later + */ +namespace OC\Search; + +use OCP\IGroupManager; +use OCP\IUserManager; +use OCP\Search\FilterDefinition; +use OCP\Search\IFilter; +use RuntimeException; + +final class FilterFactory { + private const PERSON_TYPE_SEPARATOR = '/'; + + public static function get(string $type, string|array $filter): IFilter { + return match ($type) { + FilterDefinition::TYPE_BOOL => new Filter\BooleanFilter($filter), + FilterDefinition::TYPE_DATETIME => new Filter\DateTimeFilter($filter), + FilterDefinition::TYPE_FLOAT => new Filter\FloatFilter($filter), + FilterDefinition::TYPE_INT => new Filter\IntegerFilter($filter), + FilterDefinition::TYPE_NC_GROUP => new Filter\GroupFilter($filter, \OC::$server->get(IGroupManager::class)), + FilterDefinition::TYPE_NC_USER => new Filter\UserFilter($filter, \OC::$server->get(IUserManager::class)), + FilterDefinition::TYPE_PERSON => self::getPerson($filter), + FilterDefinition::TYPE_STRING => new Filter\StringFilter($filter), + FilterDefinition::TYPE_STRINGS => new Filter\StringsFilter(... (array) $filter), + default => throw new RuntimeException('Invalid filter type '. $type), + }; + } + + private static function getPerson(string $person): IFilter { + $parts = explode(self::PERSON_TYPE_SEPARATOR, $person, 2); + + return match (count($parts)) { + 1 => self::get(FilterDefinition::TYPE_NC_USER, $person), + 2 => self::get(... $parts), + }; + } +} diff --git a/lib/private/Search/Provider/File.php b/lib/private/Search/Provider/File.php index 5ec0132cc03..ca4b1b8677f 100644 --- a/lib/private/Search/Provider/File.php +++ b/lib/private/Search/Provider/File.php @@ -1,29 +1,9 @@ <?php + /** - * @copyright Copyright (c) 2016, ownCloud, Inc. - * - * @author Andrew Brown <andrew@casabrown.com> - * @author Bart Visscher <bartv@thisnet.nl> - * @author Christoph Wurst <christoph@winzerhof-wurst.at> - * @author Jakob Sack <mail@jakobsack.de> - * @author John Molakvoæ <skjnldsv@protonmail.com> - * @author Jörn Friedrich Dreyer <jfd@butonic.de> - * @author Robin Appelman <robin@icewind.nl> - * - * @license AGPL-3.0 - * - * This code is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License, version 3, - * as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License, version 3, - * along with this program. If not, see <http://www.gnu.org/licenses/> - * + * SPDX-FileCopyrightText: 2016-2024 Nextcloud GmbH and Nextcloud contributors + * SPDX-FileCopyrightText: 2016 ownCloud, Inc. + * SPDX-License-Identifier: AGPL-3.0-only */ namespace OC\Search\Provider; @@ -51,7 +31,7 @@ class File extends PagedProvider { * @return \OCP\Search\Result[] * @deprecated 20.0.0 */ - public function search($query, int $limit = null, int $offset = null) { + public function search($query, ?int $limit = null, ?int $offset = null) { /** @var IRootFolder $rootFolder */ $rootFolder = \OCP\Server::get(IRootFolder::class); /** @var IUserSession $userSession */ diff --git a/lib/private/Search/Result/Audio.php b/lib/private/Search/Result/Audio.php index 41afe691e8c..42b8ac75da0 100644 --- a/lib/private/Search/Result/Audio.php +++ b/lib/private/Search/Result/Audio.php @@ -1,25 +1,9 @@ <?php + /** - * @copyright Copyright (c) 2016, ownCloud, Inc. - * - * @author Andrew Brown <andrew@casabrown.com> - * @author Christoph Wurst <christoph@winzerhof-wurst.at> - * @author Jörn Friedrich Dreyer <jfd@butonic.de> - * - * @license AGPL-3.0 - * - * This code is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License, version 3, - * as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License, version 3, - * along with this program. If not, see <http://www.gnu.org/licenses/> - * + * SPDX-FileCopyrightText: 2016-2024 Nextcloud GmbH and Nextcloud contributors + * SPDX-FileCopyrightText: 2016 ownCloud, Inc. + * SPDX-License-Identifier: AGPL-3.0-only */ namespace OC\Search\Result; diff --git a/lib/private/Search/Result/File.php b/lib/private/Search/Result/File.php index 59111b0cee2..f1346481b86 100644 --- a/lib/private/Search/Result/File.php +++ b/lib/private/Search/Result/File.php @@ -1,28 +1,9 @@ <?php + /** - * @copyright Copyright (c) 2016, ownCloud, Inc. - * - * @author Andrew Brown <andrew@casabrown.com> - * @author Christoph Wurst <christoph@winzerhof-wurst.at> - * @author John Molakvoæ <skjnldsv@protonmail.com> - * @author Jörn Friedrich Dreyer <jfd@butonic.de> - * @author Lukas Reschke <lukas@statuscode.ch> - * @author Robin Appelman <robin@icewind.nl> - * - * @license AGPL-3.0 - * - * This code is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License, version 3, - * as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License, version 3, - * along with this program. If not, see <http://www.gnu.org/licenses/> - * + * SPDX-FileCopyrightText: 2016-2024 Nextcloud GmbH and Nextcloud contributors + * SPDX-FileCopyrightText: 2016 ownCloud, Inc. + * SPDX-License-Identifier: AGPL-3.0-only */ namespace OC\Search\Result; diff --git a/lib/private/Search/Result/Folder.php b/lib/private/Search/Result/Folder.php index 36d0e91c042..37de0a031a7 100644 --- a/lib/private/Search/Result/Folder.php +++ b/lib/private/Search/Result/Folder.php @@ -1,25 +1,9 @@ <?php + /** - * @copyright Copyright (c) 2016, ownCloud, Inc. - * - * @author Andrew Brown <andrew@casabrown.com> - * @author Christoph Wurst <christoph@winzerhof-wurst.at> - * @author Jörn Friedrich Dreyer <jfd@butonic.de> - * - * @license AGPL-3.0 - * - * This code is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License, version 3, - * as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License, version 3, - * along with this program. If not, see <http://www.gnu.org/licenses/> - * + * SPDX-FileCopyrightText: 2016-2024 Nextcloud GmbH and Nextcloud contributors + * SPDX-FileCopyrightText: 2016 ownCloud, Inc. + * SPDX-License-Identifier: AGPL-3.0-only */ namespace OC\Search\Result; diff --git a/lib/private/Search/Result/Image.php b/lib/private/Search/Result/Image.php index 27f5702e39a..e1333424017 100644 --- a/lib/private/Search/Result/Image.php +++ b/lib/private/Search/Result/Image.php @@ -1,25 +1,9 @@ <?php + /** - * @copyright Copyright (c) 2016, ownCloud, Inc. - * - * @author Andrew Brown <andrew@casabrown.com> - * @author Christoph Wurst <christoph@winzerhof-wurst.at> - * @author Jörn Friedrich Dreyer <jfd@butonic.de> - * - * @license AGPL-3.0 - * - * This code is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License, version 3, - * as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License, version 3, - * along with this program. If not, see <http://www.gnu.org/licenses/> - * + * SPDX-FileCopyrightText: 2016-2024 Nextcloud GmbH and Nextcloud contributors + * SPDX-FileCopyrightText: 2016 ownCloud, Inc. + * SPDX-License-Identifier: AGPL-3.0-only */ namespace OC\Search\Result; diff --git a/lib/private/Search/SearchComposer.php b/lib/private/Search/SearchComposer.php index 4ec73ec54e9..8108c6bcde4 100644 --- a/lib/private/Search/SearchComposer.php +++ b/lib/private/Search/SearchComposer.php @@ -3,39 +3,26 @@ declare(strict_types=1); /** - * @copyright 2020 Christoph Wurst <christoph@winzerhof-wurst.at> - * - * @author Christoph Wurst <christoph@winzerhof-wurst.at> - * @author Joas Schilling <coding@schilljs.com> - * @author John Molakvoæ <skjnldsv@protonmail.com> - * - * @license GNU AGPL version 3 or any later version - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as - * published by the Free Software Foundation, either version 3 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - * + * SPDX-FileCopyrightText: 2020 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later */ namespace OC\Search; use InvalidArgumentException; -use OCP\AppFramework\QueryException; -use OCP\IServerContainer; +use OC\AppFramework\Bootstrap\Coordinator; +use OCP\IURLGenerator; use OCP\IUser; +use OCP\Search\FilterDefinition; +use OCP\Search\IFilter; +use OCP\Search\IFilteringProvider; +use OCP\Search\IInAppSearch; use OCP\Search\IProvider; use OCP\Search\ISearchQuery; use OCP\Search\SearchResult; -use OC\AppFramework\Bootstrap\Coordinator; +use Psr\Container\ContainerExceptionInterface; +use Psr\Container\ContainerInterface; use Psr\Log\LoggerInterface; +use RuntimeException; use function array_map; /** @@ -58,31 +45,40 @@ use function array_map; * @see IProvider::search() for the arguments of the individual search requests */ class SearchComposer { - /** @var IProvider[] */ - private $providers = []; - - /** @var Coordinator */ - private $bootstrapCoordinator; + /** + * @var array<string, array{appId: string, provider: IProvider}> + */ + private array $providers = []; - /** @var IServerContainer */ - private $container; + private array $commonFilters; + private array $customFilters = []; - private LoggerInterface $logger; + private array $handlers = []; - public function __construct(Coordinator $bootstrapCoordinator, - IServerContainer $container, - LoggerInterface $logger) { - $this->container = $container; - $this->logger = $logger; - $this->bootstrapCoordinator = $bootstrapCoordinator; + public function __construct( + private Coordinator $bootstrapCoordinator, + private ContainerInterface $container, + private IURLGenerator $urlGenerator, + private LoggerInterface $logger + ) { + $this->commonFilters = [ + IFilter::BUILTIN_TERM => new FilterDefinition(IFilter::BUILTIN_TERM, FilterDefinition::TYPE_STRING), + IFilter::BUILTIN_SINCE => new FilterDefinition(IFilter::BUILTIN_SINCE, FilterDefinition::TYPE_DATETIME), + IFilter::BUILTIN_UNTIL => new FilterDefinition(IFilter::BUILTIN_UNTIL, FilterDefinition::TYPE_DATETIME), + IFilter::BUILTIN_TITLE_ONLY => new FilterDefinition(IFilter::BUILTIN_TITLE_ONLY, FilterDefinition::TYPE_BOOL, false), + IFilter::BUILTIN_PERSON => new FilterDefinition(IFilter::BUILTIN_PERSON, FilterDefinition::TYPE_PERSON), + IFilter::BUILTIN_PLACES => new FilterDefinition(IFilter::BUILTIN_PLACES, FilterDefinition::TYPE_STRINGS, false), + IFilter::BUILTIN_PROVIDER => new FilterDefinition(IFilter::BUILTIN_PROVIDER, FilterDefinition::TYPE_STRING, false), + ]; } /** * Load all providers dynamically that were registered through `registerProvider` * + * If $targetProviderId is provided, only this provider is loaded * If a provider can't be loaded we log it but the operation continues nevertheless */ - private function loadLazyProviders(): void { + private function loadLazyProviders(?string $targetProviderId = null): void { $context = $this->bootstrapCoordinator->getRegistrationContext(); if ($context === null) { // Too early, nothing registered yet @@ -93,9 +89,20 @@ class SearchComposer { foreach ($registrations as $registration) { try { /** @var IProvider $provider */ - $provider = $this->container->query($registration->getService()); - $this->providers[$provider->getId()] = $provider; - } catch (QueryException $e) { + $provider = $this->container->get($registration->getService()); + $providerId = $provider->getId(); + if ($targetProviderId !== null && $targetProviderId !== $providerId) { + continue; + } + $this->providers[$providerId] = [ + 'appId' => $registration->getAppId(), + 'provider' => $provider, + ]; + $this->handlers[$providerId] = [$providerId]; + if ($targetProviderId !== null) { + break; + } + } catch (ContainerExceptionInterface $e) { // Log an continue. We can be fault tolerant here. $this->logger->error('Could not load search provider dynamically: ' . $e->getMessage(), [ 'exception' => $e, @@ -103,6 +110,43 @@ class SearchComposer { ]); } } + + $this->loadFilters(); + } + + private function loadFilters(): void { + foreach ($this->providers as $providerId => $providerData) { + $appId = $providerData['appId']; + $provider = $providerData['provider']; + if (!$provider instanceof IFilteringProvider) { + continue; + } + + foreach ($provider->getCustomFilters() as $filter) { + $this->registerCustomFilter($filter, $providerId); + } + foreach ($provider->getAlternateIds() as $alternateId) { + $this->handlers[$alternateId][] = $providerId; + } + foreach ($provider->getSupportedFilters() as $filterName) { + if ($this->getFilterDefinition($filterName, $providerId) === null) { + throw new InvalidArgumentException('Invalid filter '. $filterName); + } + } + } + } + + private function registerCustomFilter(FilterDefinition $filter, string $providerId): void { + $name = $filter->name(); + if (isset($this->commonFilters[$name])) { + throw new InvalidArgumentException('Filter name is already used'); + } + + if (isset($this->customFilters[$providerId])) { + $this->customFilters[$providerId][$name] = $filter; + } else { + $this->customFilters[$providerId] = [$name => $filter]; + } } /** @@ -117,26 +161,146 @@ class SearchComposer { public function getProviders(string $route, array $routeParameters): array { $this->loadLazyProviders(); - $providers = array_values( - array_map(function (IProvider $provider) use ($route, $routeParameters) { + $providers = array_map( + function (array $providerData) use ($route, $routeParameters) { + $appId = $providerData['appId']; + $provider = $providerData['provider']; + $order = $provider->getOrder($route, $routeParameters); + if ($order === null) { + return; + } + $triggers = [$provider->getId()]; + if ($provider instanceof IFilteringProvider) { + $triggers += $provider->getAlternateIds(); + $filters = $provider->getSupportedFilters(); + } else { + $filters = [IFilter::BUILTIN_TERM]; + } + return [ 'id' => $provider->getId(), + 'appId' => $appId, 'name' => $provider->getName(), - 'order' => $provider->getOrder($route, $routeParameters), + 'icon' => $this->fetchIcon($appId, $provider->getId()), + 'order' => $order, + 'triggers' => $triggers, + 'filters' => $this->getFiltersType($filters, $provider->getId()), + 'inAppSearch' => $provider instanceof IInAppSearch, ]; - }, $this->providers) + }, + $this->providers, ); + $providers = array_filter($providers); + // Sort providers by order and strip associative keys usort($providers, function ($provider1, $provider2) { return $provider1['order'] <=> $provider2['order']; }); - /** - * Return an array with the IDs, but strip the associative keys - */ return $providers; } + private function fetchIcon(string $appId, string $providerId): string { + $icons = [ + [$providerId, $providerId.'.svg'], + [$providerId, 'app.svg'], + [$appId, $providerId.'.svg'], + [$appId, $appId.'.svg'], + [$appId, 'app.svg'], + ['core', 'places/default-app-icon.svg'], + ]; + if ($appId === 'settings' && $providerId === 'users') { + // Conflict: + // the file /apps/settings/users.svg is already used in black version by top right user menu + // Override icon name here + $icons = [['settings', 'users-white.svg']]; + } + foreach ($icons as $i => $icon) { + try { + return $this->urlGenerator->imagePath(... $icon); + } catch (RuntimeException $e) { + // Ignore error + } + } + + return ''; + } + + /** + * @param $filters string[] + * @return array<string, string> + */ + private function getFiltersType(array $filters, string $providerId): array { + $filterList = []; + foreach ($filters as $filter) { + $filterList[$filter] = $this->getFilterDefinition($filter, $providerId)->type(); + } + + return $filterList; + } + + private function getFilterDefinition(string $name, string $providerId): ?FilterDefinition { + if (isset($this->commonFilters[$name])) { + return $this->commonFilters[$name]; + } + if (isset($this->customFilters[$providerId][$name])) { + return $this->customFilters[$providerId][$name]; + } + + return null; + } + + /** + * @param array<string, string> $parameters + */ + public function buildFilterList(string $providerId, array $parameters): FilterCollection { + $this->loadLazyProviders($providerId); + + $list = []; + foreach ($parameters as $name => $value) { + $filter = $this->buildFilter($name, $value, $providerId); + if ($filter === null) { + continue; + } + $list[$name] = $filter; + } + + return new FilterCollection(... $list); + } + + private function buildFilter(string $name, string $value, string $providerId): ?IFilter { + $filterDefinition = $this->getFilterDefinition($name, $providerId); + if ($filterDefinition === null) { + $this->logger->debug('Unable to find {name} definition', [ + 'name' => $name, + 'value' => $value, + ]); + + return null; + } + + if (!$this->filterSupportedByProvider($filterDefinition, $providerId)) { + // FIXME Use dedicated exception and handle it + throw new UnsupportedFilter($name, $providerId); + } + + return FilterFactory::get($filterDefinition->type(), $value); + } + + private function filterSupportedByProvider(FilterDefinition $filterDefinition, string $providerId): bool { + // Non exclusive filters can be ommited by apps + if (!$filterDefinition->exclusive()) { + return true; + } + + $provider = $this->providers[$providerId]['provider']; + $supportedFilters = $provider instanceof IFilteringProvider + ? $provider->getSupportedFilters() + : [IFilter::BUILTIN_TERM]; + + return in_array($filterDefinition->name(), $supportedFilters, true); + } + /** * Query an individual search provider for results * @@ -147,15 +311,18 @@ class SearchComposer { * @return SearchResult * @throws InvalidArgumentException when the $providerId does not correspond to a registered provider */ - public function search(IUser $user, - string $providerId, - ISearchQuery $query): SearchResult { - $this->loadLazyProviders(); + public function search( + IUser $user, + string $providerId, + ISearchQuery $query, + ): SearchResult { + $this->loadLazyProviders($providerId); - $provider = $this->providers[$providerId] ?? null; + $provider = $this->providers[$providerId]['provider'] ?? null; if ($provider === null) { throw new InvalidArgumentException("Provider $providerId is unknown"); } + return $provider->search($user, $query); } } diff --git a/lib/private/Search/SearchQuery.php b/lib/private/Search/SearchQuery.php index c89446d5970..a78443922ae 100644 --- a/lib/private/Search/SearchQuery.php +++ b/lib/private/Search/SearchQuery.php @@ -3,113 +3,62 @@ declare(strict_types=1); /** - * @copyright 2020 Christoph Wurst <christoph@winzerhof-wurst.at> - * - * @author Christoph Wurst <christoph@winzerhof-wurst.at> - * @author Joas Schilling <coding@schilljs.com> - * @author John Molakvoæ <skjnldsv@protonmail.com> - * - * @license GNU AGPL version 3 or any later version - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as - * published by the Free Software Foundation, either version 3 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - * + * SPDX-FileCopyrightText: 2020 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later */ namespace OC\Search; +use OCP\Search\IFilter; +use OCP\Search\IFilterCollection; use OCP\Search\ISearchQuery; class SearchQuery implements ISearchQuery { public const LIMIT_DEFAULT = 5; - /** @var string */ - private $term; - - /** @var int */ - private $sortOrder; - - /** @var int */ - private $limit; - - /** @var int|string|null */ - private $cursor; - - /** @var string */ - private $route; - - /** @var array */ - private $routeParameters; - /** - * @param string $term - * @param int $sortOrder - * @param int $limit - * @param int|string|null $cursor - * @param string $route - * @param array $routeParameters + * @param string[] $params Request query + * @param string[] $routeParameters */ - public function __construct(string $term, - int $sortOrder = ISearchQuery::SORT_DATE_DESC, - int $limit = self::LIMIT_DEFAULT, - $cursor = null, - string $route = '', - array $routeParameters = []) { - $this->term = $term; - $this->sortOrder = $sortOrder; - $this->limit = $limit; - $this->cursor = $cursor; - $this->route = $route; - $this->routeParameters = $routeParameters; + public function __construct( + private IFilterCollection $filters, + private int $sortOrder = ISearchQuery::SORT_DATE_DESC, + private int $limit = self::LIMIT_DEFAULT, + private int|string|null $cursor = null, + private string $route = '', + private array $routeParameters = [], + ) { } - /** - * @inheritDoc - */ public function getTerm(): string { - return $this->term; + return $this->getFilter('term')?->get() ?? ''; + } + + public function getFilter(string $name): ?IFilter { + return $this->filters->has($name) + ? $this->filters->get($name) + : null; + } + + public function getFilters(): IFilterCollection { + return $this->filters; } - /** - * @inheritDoc - */ public function getSortOrder(): int { return $this->sortOrder; } - /** - * @inheritDoc - */ public function getLimit(): int { return $this->limit; } - /** - * @inheritDoc - */ - public function getCursor() { + public function getCursor(): int|string|null { return $this->cursor; } - /** - * @inheritDoc - */ public function getRoute(): string { return $this->route; } - /** - * @inheritDoc - */ public function getRouteParameters(): array { return $this->routeParameters; } diff --git a/lib/private/Search/UnsupportedFilter.php b/lib/private/Search/UnsupportedFilter.php new file mode 100644 index 00000000000..bf251a91d31 --- /dev/null +++ b/lib/private/Search/UnsupportedFilter.php @@ -0,0 +1,17 @@ +<?php + +declare(strict_types=1); + +/** + * SPDX-FileCopyrightText: 2023 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later + */ +namespace OC\Search; + +use Exception; + +final class UnsupportedFilter extends Exception { + public function __construct(string $filerName, $providerId) { + parent::__construct('Provider '.$providerId.' doesn’t support filter '.$filerName.'.'); + } +} diff --git a/lib/private/Security/Bruteforce/Backend/DatabaseBackend.php b/lib/private/Security/Bruteforce/Backend/DatabaseBackend.php index 04f2a7b6397..0e272d94d0d 100644 --- a/lib/private/Security/Bruteforce/Backend/DatabaseBackend.php +++ b/lib/private/Security/Bruteforce/Backend/DatabaseBackend.php @@ -3,25 +3,8 @@ declare(strict_types=1); /** - * @copyright Copyright (c) 2023 Joas Schilling <coding@schilljs.com> - * - * @author Joas Schilling <coding@schilljs.com> - * - * @license GNU AGPL version 3 or any later version - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as - * published by the Free Software Foundation, either version 3 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - * + * SPDX-FileCopyrightText: 2023 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later */ namespace OC\Security\Bruteforce\Backend; diff --git a/lib/private/Security/Bruteforce/Backend/IBackend.php b/lib/private/Security/Bruteforce/Backend/IBackend.php index 4b40262e645..7118123cbb5 100644 --- a/lib/private/Security/Bruteforce/Backend/IBackend.php +++ b/lib/private/Security/Bruteforce/Backend/IBackend.php @@ -3,25 +3,8 @@ declare(strict_types=1); /** - * @copyright Copyright (c) 2023 Joas Schilling <coding@schilljs.com> - * - * @author Joas Schilling <coding@schilljs.com> - * - * @license GNU AGPL version 3 or any later version - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as - * published by the Free Software Foundation, either version 3 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - * + * SPDX-FileCopyrightText: 2023 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later */ namespace OC\Security\Bruteforce\Backend; diff --git a/lib/private/Security/Bruteforce/Backend/MemoryCacheBackend.php b/lib/private/Security/Bruteforce/Backend/MemoryCacheBackend.php index 432e99700fe..32571c72fae 100644 --- a/lib/private/Security/Bruteforce/Backend/MemoryCacheBackend.php +++ b/lib/private/Security/Bruteforce/Backend/MemoryCacheBackend.php @@ -3,25 +3,8 @@ declare(strict_types=1); /** - * @copyright Copyright (c) 2023 Joas Schilling <coding@schilljs.com> - * - * @author Joas Schilling <coding@schilljs.com> - * - * @license GNU AGPL version 3 or any later version - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as - * published by the Free Software Foundation, either version 3 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - * + * SPDX-FileCopyrightText: 2023 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later */ namespace OC\Security\Bruteforce\Backend; diff --git a/lib/private/Security/Bruteforce/Capabilities.php b/lib/private/Security/Bruteforce/Capabilities.php index b50eea0b7af..581b4480a27 100644 --- a/lib/private/Security/Bruteforce/Capabilities.php +++ b/lib/private/Security/Bruteforce/Capabilities.php @@ -3,34 +3,13 @@ declare(strict_types=1); /** - * @copyright Copyright (c) 2023 Joas Schilling <coding@schilljs.com> - * @copyright Copyright (c) 2017 Roeland Jago Douma <roeland@famdouma.nl> - * - * @author J0WI <J0WI@users.noreply.github.com> - * @author Joas Schilling <coding@schilljs.com> - * @author Julius Härtl <jus@bitgrid.net> - * @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/>. - * + * SPDX-FileCopyrightText: 2017 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later */ namespace OC\Security\Bruteforce; -use OCP\Capabilities\IPublicCapability; use OCP\Capabilities\IInitialStateExcludedCapability; +use OCP\Capabilities\IPublicCapability; use OCP\IRequest; use OCP\Security\Bruteforce\IThrottler; diff --git a/lib/private/Security/Bruteforce/CleanupJob.php b/lib/private/Security/Bruteforce/CleanupJob.php index 45cfe572acb..e7d981caa08 100644 --- a/lib/private/Security/Bruteforce/CleanupJob.php +++ b/lib/private/Security/Bruteforce/CleanupJob.php @@ -3,25 +3,8 @@ declare(strict_types=1); /** - * @copyright Copyright (c) 2020, Roeland Jago Douma <roeland@famdouma.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/>. - * + * SPDX-FileCopyrightText: 2020 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later */ namespace OC\Security\Bruteforce; @@ -32,19 +15,18 @@ use OCP\DB\QueryBuilder\IQueryBuilder; use OCP\IDBConnection; class CleanupJob extends TimedJob { - /** @var IDBConnection */ - private $connection; - - public function __construct(ITimeFactory $time, IDBConnection $connection) { + public function __construct( + ITimeFactory $time, + private IDBConnection $connection, + ) { parent::__construct($time); - $this->connection = $connection; // Run once a day $this->setInterval(3600 * 24); $this->setTimeSensitivity(IJob::TIME_INSENSITIVE); } - protected function run($argument) { + protected function run($argument): void { // Delete all entries more than 48 hours old $time = $this->time->getTime() - (48 * 3600); diff --git a/lib/private/Security/Bruteforce/Throttler.php b/lib/private/Security/Bruteforce/Throttler.php index 2803373e8ba..7e310035ce4 100644 --- a/lib/private/Security/Bruteforce/Throttler.php +++ b/lib/private/Security/Bruteforce/Throttler.php @@ -3,33 +3,8 @@ declare(strict_types=1); /** - * @copyright Copyright (c) 2023 Joas Schilling <coding@schilljs.com> - * @copyright Copyright (c) 2016 Lukas Reschke <lukas@statuscode.ch> - * - * @author Bjoern Schiessle <bjoern@schiessle.org> - * @author Christoph Wurst <christoph@winzerhof-wurst.at> - * @author Joas Schilling <coding@schilljs.com> - * @author Johannes Riedel <joeried@users.noreply.github.com> - * @author Lukas Reschke <lukas@statuscode.ch> - * @author Morris Jobke <hey@morrisjobke.de> - * @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/>. - * + * SPDX-FileCopyrightText: 2016 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later */ namespace OC\Security\Bruteforce; @@ -72,8 +47,8 @@ class Throttler implements IThrottler { * {@inheritDoc} */ public function registerAttempt(string $action, - string $ip, - array $metadata = []): void { + string $ip, + array $metadata = []): void { // No need to log if the bruteforce protection is disabled if (!$this->config->getSystemValueBool('auth.bruteforce.protection.enabled', true)) { return; @@ -106,9 +81,6 @@ class Throttler implements IThrottler { /** * Check if the IP is whitelisted - * - * @param string $ip - * @return bool */ public function isBypassListed(string $ip): bool { if (isset($this->ipIsWhitelisted[$ip])) { diff --git a/lib/private/Security/CSP/ContentSecurityPolicy.php b/lib/private/Security/CSP/ContentSecurityPolicy.php index e2d115cf34e..890251db040 100644 --- a/lib/private/Security/CSP/ContentSecurityPolicy.php +++ b/lib/private/Security/CSP/ContentSecurityPolicy.php @@ -1,28 +1,10 @@ <?php declare(strict_types=1); - /** - * @copyright Copyright (c) 2016, ownCloud, Inc. - * - * @author Lukas Reschke <lukas@statuscode.ch> - * @author Roeland Jago Douma <roeland@famdouma.nl> - * @author Thomas Citharel <nextcloud@tcit.fr> - * - * @license AGPL-3.0 - * - * This code is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License, version 3, - * as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License, version 3, - * along with this program. If not, see <http://www.gnu.org/licenses/> - * + * SPDX-FileCopyrightText: 2016-2024 Nextcloud GmbH and Nextcloud contributors + * SPDX-FileCopyrightText: 2016 ownCloud, Inc. + * SPDX-License-Identifier: AGPL-3.0-only */ namespace OC\Security\CSP; @@ -34,33 +16,22 @@ namespace OC\Security\CSP; * @package OC\Security\CSP */ class ContentSecurityPolicy extends \OCP\AppFramework\Http\ContentSecurityPolicy { - /** - * @return boolean - */ public function isInlineScriptAllowed(): bool { return $this->inlineScriptAllowed; } - /** - * @param boolean $inlineScriptAllowed - */ - public function setInlineScriptAllowed(bool $inlineScriptAllowed) { + public function setInlineScriptAllowed(bool $inlineScriptAllowed): void { $this->inlineScriptAllowed = $inlineScriptAllowed; } - /** - * @return boolean - */ public function isEvalScriptAllowed(): bool { return $this->evalScriptAllowed; } /** - * @param boolean $evalScriptAllowed - * * @deprecated 17.0.0 Unsafe eval should not be used anymore. */ - public function setEvalScriptAllowed(bool $evalScriptAllowed) { + public function setEvalScriptAllowed(bool $evalScriptAllowed): void { $this->evalScriptAllowed = $evalScriptAllowed; } @@ -72,134 +43,79 @@ class ContentSecurityPolicy extends \OCP\AppFramework\Http\ContentSecurityPolicy $this->evalWasmAllowed = $evalWasmAllowed; } - /** - * @return array - */ public function getAllowedScriptDomains(): array { return $this->allowedScriptDomains; } - /** - * @param array $allowedScriptDomains - */ - public function setAllowedScriptDomains(array $allowedScriptDomains) { + public function setAllowedScriptDomains(array $allowedScriptDomains): void { $this->allowedScriptDomains = $allowedScriptDomains; } - /** - * @return boolean - */ public function isInlineStyleAllowed(): bool { return $this->inlineStyleAllowed; } - /** - * @param boolean $inlineStyleAllowed - */ - public function setInlineStyleAllowed(bool $inlineStyleAllowed) { + public function setInlineStyleAllowed(bool $inlineStyleAllowed): void { $this->inlineStyleAllowed = $inlineStyleAllowed; } - /** - * @return array - */ public function getAllowedStyleDomains(): array { return $this->allowedStyleDomains; } - /** - * @param array $allowedStyleDomains - */ - public function setAllowedStyleDomains(array $allowedStyleDomains) { + public function setAllowedStyleDomains(array $allowedStyleDomains): void { $this->allowedStyleDomains = $allowedStyleDomains; } - /** - * @return array - */ public function getAllowedImageDomains(): array { return $this->allowedImageDomains; } - /** - * @param array $allowedImageDomains - */ - public function setAllowedImageDomains(array $allowedImageDomains) { + public function setAllowedImageDomains(array $allowedImageDomains): void { $this->allowedImageDomains = $allowedImageDomains; } - /** - * @return array - */ public function getAllowedConnectDomains(): array { return $this->allowedConnectDomains; } - /** - * @param array $allowedConnectDomains - */ - public function setAllowedConnectDomains(array $allowedConnectDomains) { + public function setAllowedConnectDomains(array $allowedConnectDomains): void { $this->allowedConnectDomains = $allowedConnectDomains; } - /** - * @return array - */ public function getAllowedMediaDomains(): array { return $this->allowedMediaDomains; } - /** - * @param array $allowedMediaDomains - */ - public function setAllowedMediaDomains(array $allowedMediaDomains) { + public function setAllowedMediaDomains(array $allowedMediaDomains): void { $this->allowedMediaDomains = $allowedMediaDomains; } - /** - * @return array - */ public function getAllowedObjectDomains(): array { return $this->allowedObjectDomains; } - /** - * @param array $allowedObjectDomains - */ - public function setAllowedObjectDomains(array $allowedObjectDomains) { + public function setAllowedObjectDomains(array $allowedObjectDomains): void { $this->allowedObjectDomains = $allowedObjectDomains; } - /** - * @return array - */ public function getAllowedFrameDomains(): array { return $this->allowedFrameDomains; } - /** - * @param array $allowedFrameDomains - */ - public function setAllowedFrameDomains(array $allowedFrameDomains) { + public function setAllowedFrameDomains(array $allowedFrameDomains): void { $this->allowedFrameDomains = $allowedFrameDomains; } - /** - * @return array - */ public function getAllowedFontDomains(): array { return $this->allowedFontDomains; } - /** - * @param array $allowedFontDomains - */ - public function setAllowedFontDomains($allowedFontDomains) { + public function setAllowedFontDomains($allowedFontDomains): void { $this->allowedFontDomains = $allowedFontDomains; } /** - * @return array * @deprecated 15.0.0 use FrameDomains and WorkerSrcDomains */ public function getAllowedChildSrcDomains(): array { @@ -210,13 +126,10 @@ class ContentSecurityPolicy extends \OCP\AppFramework\Http\ContentSecurityPolicy * @param array $allowedChildSrcDomains * @deprecated 15.0.0 use FrameDomains and WorkerSrcDomains */ - public function setAllowedChildSrcDomains($allowedChildSrcDomains) { + public function setAllowedChildSrcDomains($allowedChildSrcDomains): void { $this->allowedChildSrcDomains = $allowedChildSrcDomains; } - /** - * @return array - */ public function getAllowedFrameAncestors(): array { return $this->allowedFrameAncestors; } @@ -224,7 +137,7 @@ class ContentSecurityPolicy extends \OCP\AppFramework\Http\ContentSecurityPolicy /** * @param array $allowedFrameAncestors */ - public function setAllowedFrameAncestors($allowedFrameAncestors) { + public function setAllowedFrameAncestors($allowedFrameAncestors): void { $this->allowedFrameAncestors = $allowedFrameAncestors; } @@ -232,7 +145,7 @@ class ContentSecurityPolicy extends \OCP\AppFramework\Http\ContentSecurityPolicy return $this->allowedWorkerSrcDomains; } - public function setAllowedWorkerSrcDomains(array $allowedWorkerSrcDomains) { + public function setAllowedWorkerSrcDomains(array $allowedWorkerSrcDomains): void { $this->allowedWorkerSrcDomains = $allowedWorkerSrcDomains; } @@ -249,21 +162,23 @@ class ContentSecurityPolicy extends \OCP\AppFramework\Http\ContentSecurityPolicy return $this->reportTo; } - public function setReportTo(array $reportTo) { + public function setReportTo(array $reportTo): void { $this->reportTo = $reportTo; } - /** - * @return boolean - */ public function isStrictDynamicAllowed(): bool { return $this->strictDynamicAllowed; } - /** - * @param boolean $strictDynamicAllowed - */ - public function setStrictDynamicAllowed(bool $strictDynamicAllowed) { + public function setStrictDynamicAllowed(bool $strictDynamicAllowed): void { $this->strictDynamicAllowed = $strictDynamicAllowed; } + + public function isStrictDynamicAllowedOnScripts(): bool { + return $this->strictDynamicAllowedOnScripts; + } + + public function setStrictDynamicAllowedOnScripts(bool $strictDynamicAllowedOnScripts): void { + $this->strictDynamicAllowedOnScripts = $strictDynamicAllowedOnScripts; + } } diff --git a/lib/private/Security/CSP/ContentSecurityPolicyManager.php b/lib/private/Security/CSP/ContentSecurityPolicyManager.php index 4930dcb759c..77ecceb03c3 100644 --- a/lib/private/Security/CSP/ContentSecurityPolicyManager.php +++ b/lib/private/Security/CSP/ContentSecurityPolicyManager.php @@ -3,27 +3,9 @@ declare(strict_types=1); /** - * @copyright Copyright (c) 2016, ownCloud, Inc. - * - * @author Christoph Wurst <christoph@winzerhof-wurst.at> - * @author Lukas Reschke <lukas@statuscode.ch> - * @author Morris Jobke <hey@morrisjobke.de> - * @author Roeland Jago Douma <roeland@famdouma.nl> - * - * @license AGPL-3.0 - * - * This code is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License, version 3, - * as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License, version 3, - * along with this program. If not, see <http://www.gnu.org/licenses/> - * + * SPDX-FileCopyrightText: 2016-2024 Nextcloud GmbH and Nextcloud contributors + * SPDX-FileCopyrightText: 2016 ownCloud, Inc. + * SPDX-License-Identifier: AGPL-3.0-only */ namespace OC\Security\CSP; @@ -35,25 +17,21 @@ use OCP\Security\IContentSecurityPolicyManager; class ContentSecurityPolicyManager implements IContentSecurityPolicyManager { /** @var ContentSecurityPolicy[] */ - private $policies = []; + private array $policies = []; - /** @var IEventDispatcher */ - private $dispatcher; - - public function __construct(IEventDispatcher $dispatcher) { - $this->dispatcher = $dispatcher; + public function __construct( + private IEventDispatcher $dispatcher, + ) { } /** {@inheritdoc} */ - public function addDefaultPolicy(EmptyContentSecurityPolicy $policy) { + public function addDefaultPolicy(EmptyContentSecurityPolicy $policy): void { $this->policies[] = $policy; } /** * Get the configured default policy. This is not in the public namespace * as it is only supposed to be used by core itself. - * - * @return ContentSecurityPolicy */ public function getDefaultPolicy(): ContentSecurityPolicy { $event = new AddContentSecurityPolicyEvent($this); @@ -68,13 +46,11 @@ class ContentSecurityPolicyManager implements IContentSecurityPolicyManager { /** * Merges the first given policy with the second one - * - * @param ContentSecurityPolicy $defaultPolicy - * @param EmptyContentSecurityPolicy $originalPolicy - * @return ContentSecurityPolicy */ - public function mergePolicies(ContentSecurityPolicy $defaultPolicy, - EmptyContentSecurityPolicy $originalPolicy): ContentSecurityPolicy { + public function mergePolicies( + ContentSecurityPolicy $defaultPolicy, + EmptyContentSecurityPolicy $originalPolicy, + ): ContentSecurityPolicy { foreach ((object)(array)$originalPolicy as $name => $value) { $setter = 'set'.ucfirst($name); if (\is_array($value)) { diff --git a/lib/private/Security/CSP/ContentSecurityPolicyNonceManager.php b/lib/private/Security/CSP/ContentSecurityPolicyNonceManager.php index 1167b3358d2..0f637e5afd6 100644 --- a/lib/private/Security/CSP/ContentSecurityPolicyNonceManager.php +++ b/lib/private/Security/CSP/ContentSecurityPolicyNonceManager.php @@ -3,30 +3,8 @@ declare(strict_types=1); /** - * @copyright Copyright (c) 2016 Lukas Reschke <lukas@statuscode.ch> - * - * @author Christoph Wurst <christoph@winzerhof-wurst.at> - * @author Joas Schilling <coding@schilljs.com> - * @author Lukas Reschke <lukas@statuscode.ch> - * @author Pavel Krasikov <klonishe@gmail.com> - * @author Roeland Jago Douma <roeland@famdouma.nl> - * @author Sam Bull <aa6bs0@sambull.org> - * - * @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/>. - * + * SPDX-FileCopyrightText: 2016 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later */ namespace OC\Security\CSP; @@ -38,27 +16,16 @@ use OCP\IRequest; * @package OC\Security\CSP */ class ContentSecurityPolicyNonceManager { - /** @var CsrfTokenManager */ - private $csrfTokenManager; - /** @var IRequest */ - private $request; - /** @var string */ - private $nonce = ''; + private string $nonce = ''; - /** - * @param CsrfTokenManager $csrfTokenManager - * @param IRequest $request - */ - public function __construct(CsrfTokenManager $csrfTokenManager, - IRequest $request) { - $this->csrfTokenManager = $csrfTokenManager; - $this->request = $request; + public function __construct( + private CsrfTokenManager $csrfTokenManager, + private IRequest $request, + ) { } /** - * Returns the current CSP nounce - * - * @return string + * Returns the current CSP nonce */ public function getNonce(): string { if ($this->nonce === '') { @@ -74,20 +41,16 @@ class ContentSecurityPolicyNonceManager { /** * Check if the browser supports CSP v3 - * - * @return bool */ public function browserSupportsCspV3(): bool { - $browserWhitelist = [ - Request::USER_AGENT_CHROME, - Request::USER_AGENT_FIREFOX, - Request::USER_AGENT_SAFARI, + $browserBlocklist = [ + Request::USER_AGENT_IE, ]; - if ($this->request->isUserAgent($browserWhitelist)) { - return true; + if ($this->request->isUserAgent($browserBlocklist)) { + return false; } - return false; + return true; } } diff --git a/lib/private/Security/CSRF/CsrfToken.php b/lib/private/Security/CSRF/CsrfToken.php index a76e169e5b9..6aad0cd5944 100644 --- a/lib/private/Security/CSRF/CsrfToken.php +++ b/lib/private/Security/CSRF/CsrfToken.php @@ -1,29 +1,10 @@ <?php declare(strict_types=1); - /** - * @copyright Copyright (c) 2016, ownCloud, Inc. - * - * @author Christoph Wurst <christoph@winzerhof-wurst.at> - * @author Leon Klingele <git@leonklingele.de> - * @author Lukas Reschke <lukas@statuscode.ch> - * @author Roeland Jago Douma <roeland@famdouma.nl> - * - * @license AGPL-3.0 - * - * This code is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License, version 3, - * as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License, version 3, - * along with this program. If not, see <http://www.gnu.org/licenses/> - * + * SPDX-FileCopyrightText: 2016-2024 Nextcloud GmbH and Nextcloud contributors + * SPDX-FileCopyrightText: 2016 ownCloud, Inc. + * SPDX-License-Identifier: AGPL-3.0-only */ namespace OC\Security\CSRF; @@ -36,23 +17,19 @@ namespace OC\Security\CSRF; * @package OC\Security\CSRF */ class CsrfToken { - /** @var string */ - private $value; - /** @var string */ - private $encryptedValue = ''; + private string $encryptedValue = ''; /** * @param string $value Value of the token. Can be encrypted or not encrypted. */ - public function __construct(string $value) { - $this->value = $value; + public function __construct( + private string $value, + ) { } /** * Encrypted value of the token. This is used to mitigate BREACH alike * vulnerabilities. For display measures do use this functionality. - * - * @return string */ public function getEncryptedValue(): string { if ($this->encryptedValue === '') { @@ -66,8 +43,6 @@ class CsrfToken { /** * The unencrypted value of the token. Used for decrypting an already * encrypted token. - * - * @return string */ public function getDecryptedValue(): string { $token = explode(':', $this->value); diff --git a/lib/private/Security/CSRF/CsrfTokenGenerator.php b/lib/private/Security/CSRF/CsrfTokenGenerator.php index 0576fda9e06..9e3f15c63d2 100644 --- a/lib/private/Security/CSRF/CsrfTokenGenerator.php +++ b/lib/private/Security/CSRF/CsrfTokenGenerator.php @@ -1,27 +1,10 @@ <?php declare(strict_types=1); - /** - * @copyright Copyright (c) 2016, ownCloud, Inc. - * - * @author Lukas Reschke <lukas@statuscode.ch> - * @author Roeland Jago Douma <roeland@famdouma.nl> - * - * @license AGPL-3.0 - * - * This code is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License, version 3, - * as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License, version 3, - * along with this program. If not, see <http://www.gnu.org/licenses/> - * + * SPDX-FileCopyrightText: 2016-2024 Nextcloud GmbH and Nextcloud contributors + * SPDX-FileCopyrightText: 2016 ownCloud, Inc. + * SPDX-License-Identifier: AGPL-3.0-only */ namespace OC\Security\CSRF; @@ -34,21 +17,15 @@ use OCP\Security\ISecureRandom; * @package OC\Security\CSRF */ class CsrfTokenGenerator { - /** @var ISecureRandom */ - private $random; - - /** - * @param ISecureRandom $random - */ - public function __construct(ISecureRandom $random) { - $this->random = $random; + public function __construct( + private ISecureRandom $random, + ) { } /** * Generate a new CSRF token. * * @param int $length Length of the token in characters. - * @return string */ public function generateToken(int $length = 32): string { return $this->random->generate($length); diff --git a/lib/private/Security/CSRF/CsrfTokenManager.php b/lib/private/Security/CSRF/CsrfTokenManager.php index 2c6dd45866d..00e1be5bedf 100644 --- a/lib/private/Security/CSRF/CsrfTokenManager.php +++ b/lib/private/Security/CSRF/CsrfTokenManager.php @@ -1,28 +1,10 @@ <?php declare(strict_types=1); - /** - * @copyright Copyright (c) 2016, ownCloud, Inc. - * - * @author Christoph Wurst <christoph@winzerhof-wurst.at> - * @author Lukas Reschke <lukas@statuscode.ch> - * @author Roeland Jago Douma <roeland@famdouma.nl> - * - * @license AGPL-3.0 - * - * This code is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License, version 3, - * as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License, version 3, - * along with this program. If not, see <http://www.gnu.org/licenses/> - * + * SPDX-FileCopyrightText: 2016-2024 Nextcloud GmbH and Nextcloud contributors + * SPDX-FileCopyrightText: 2016 ownCloud, Inc. + * SPDX-License-Identifier: AGPL-3.0-only */ namespace OC\Security\CSRF; @@ -34,27 +16,18 @@ use OC\Security\CSRF\TokenStorage\SessionStorage; * @package OC\Security\CSRF */ class CsrfTokenManager { - /** @var CsrfTokenGenerator */ - private $tokenGenerator; - /** @var SessionStorage */ - private $sessionStorage; - /** @var CsrfToken|null */ - private $csrfToken = null; + private SessionStorage $sessionStorage; + private ?CsrfToken $csrfToken = null; - /** - * @param CsrfTokenGenerator $tokenGenerator - * @param SessionStorage $storageInterface - */ - public function __construct(CsrfTokenGenerator $tokenGenerator, - SessionStorage $storageInterface) { - $this->tokenGenerator = $tokenGenerator; + public function __construct( + private CsrfTokenGenerator $tokenGenerator, + SessionStorage $storageInterface, + ) { $this->sessionStorage = $storageInterface; } /** * Returns the current CSRF token, if none set it will create a new one. - * - * @return CsrfToken */ public function getToken(): CsrfToken { if (!\is_null($this->csrfToken)) { @@ -74,8 +47,6 @@ class CsrfTokenManager { /** * Invalidates any current token and sets a new one. - * - * @return CsrfToken */ public function refreshToken(): CsrfToken { $value = $this->tokenGenerator->generateToken(); @@ -87,16 +58,13 @@ class CsrfTokenManager { /** * Remove the current token from the storage. */ - public function removeToken() { + public function removeToken(): void { $this->csrfToken = null; $this->sessionStorage->removeToken(); } /** * Verifies whether the provided token is valid. - * - * @param CsrfToken $token - * @return bool */ public function isTokenValid(CsrfToken $token): bool { if (!$this->sessionStorage->hasToken()) { diff --git a/lib/private/Security/CSRF/TokenStorage/SessionStorage.php b/lib/private/Security/CSRF/TokenStorage/SessionStorage.php index ab05d5b1493..1f0f8bcaa0a 100644 --- a/lib/private/Security/CSRF/TokenStorage/SessionStorage.php +++ b/lib/private/Security/CSRF/TokenStorage/SessionStorage.php @@ -1,29 +1,10 @@ <?php declare(strict_types=1); - /** - * @copyright Copyright (c) 2016, ownCloud, Inc. - * - * @author Christoph Wurst <christoph@winzerhof-wurst.at> - * @author Joas Schilling <coding@schilljs.com> - * @author Lukas Reschke <lukas@statuscode.ch> - * @author Roeland Jago Douma <roeland@famdouma.nl> - * - * @license AGPL-3.0 - * - * This code is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License, version 3, - * as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License, version 3, - * along with this program. If not, see <http://www.gnu.org/licenses/> - * + * SPDX-FileCopyrightText: 2016-2024 Nextcloud GmbH and Nextcloud contributors + * SPDX-FileCopyrightText: 2016 ownCloud, Inc. + * SPDX-License-Identifier: AGPL-3.0-only */ namespace OC\Security\CSRF\TokenStorage; @@ -35,27 +16,18 @@ use OCP\ISession; * @package OC\Security\CSRF\TokenStorage */ class SessionStorage { - /** @var ISession */ - private $session; - - /** - * @param ISession $session - */ - public function __construct(ISession $session) { - $this->session = $session; + public function __construct( + private ISession $session, + ) { } - /** - * @param ISession $session - */ - public function setSession(ISession $session) { + public function setSession(ISession $session): void { $this->session = $session; } /** * Returns the current token or throws an exception if none is found. * - * @return string * @throws \Exception */ public function getToken(): string { @@ -69,23 +41,20 @@ class SessionStorage { /** * Set the valid current token to $value. - * - * @param string $value */ - public function setToken(string $value) { + public function setToken(string $value): void { $this->session->set('requesttoken', $value); } /** * Removes the current token. */ - public function removeToken() { + public function removeToken(): void { $this->session->remove('requesttoken'); } + /** * Whether the storage has a storage. - * - * @return bool */ public function hasToken(): bool { return $this->session->exists('requesttoken'); diff --git a/lib/private/Security/Certificate.php b/lib/private/Security/Certificate.php index fb5b9aa8a93..b9d84caeca3 100644 --- a/lib/private/Security/Certificate.php +++ b/lib/private/Security/Certificate.php @@ -1,54 +1,33 @@ <?php declare(strict_types=1); - /** - * @copyright Copyright (c) 2016, ownCloud, Inc. - * - * @author Christoph Wurst <christoph@winzerhof-wurst.at> - * @author J0WI <J0WI@users.noreply.github.com> - * @author Lukas Reschke <lukas@statuscode.ch> - * @author Robin Appelman <robin@icewind.nl> - * - * @license AGPL-3.0 - * - * This code is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License, version 3, - * as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License, version 3, - * along with this program. If not, see <http://www.gnu.org/licenses/> - * + * SPDX-FileCopyrightText: 2016-2024 Nextcloud GmbH and Nextcloud contributors + * SPDX-FileCopyrightText: 2016 ownCloud, Inc. + * SPDX-License-Identifier: AGPL-3.0-only */ namespace OC\Security; use OCP\ICertificate; class Certificate implements ICertificate { - protected $name; + protected string $name; - protected $commonName; + protected ?string $commonName; - protected $organization; + protected ?string $organization; - protected $serial; - protected $issueDate; + protected \DateTime $issueDate; - protected $expireDate; + protected \DateTime $expireDate; - protected $issuerName; + protected ?string $issuerName; - protected $issuerOrganization; + protected ?string $issuerOrganization; /** * @param string $data base64 encoded certificate - * @param string $name * @throws \Exception If the certificate could not get parsed */ public function __construct(string $data, string $name) { @@ -66,67 +45,43 @@ class Certificate implements ICertificate { throw new \Exception('Certificate could not get parsed.'); } - $this->commonName = isset($info['subject']['CN']) ? $info['subject']['CN'] : null; - $this->organization = isset($info['subject']['O']) ? $info['subject']['O'] : null; + $this->commonName = $info['subject']['CN'] ?? null; + $this->organization = $info['subject']['O'] ?? null; $this->issueDate = new \DateTime('@' . $info['validFrom_time_t'], $gmt); $this->expireDate = new \DateTime('@' . $info['validTo_time_t'], $gmt); - $this->issuerName = isset($info['issuer']['CN']) ? $info['issuer']['CN'] : null; - $this->issuerOrganization = isset($info['issuer']['O']) ? $info['issuer']['O'] : null; + $this->issuerName = $info['issuer']['CN'] ?? null; + $this->issuerOrganization = $info['issuer']['O'] ?? null; } - /** - * @return string - */ public function getName(): string { return $this->name; } - /** - * @return string|null - */ public function getCommonName(): ?string { return $this->commonName; } - /** - * @return string|null - */ public function getOrganization(): ?string { return $this->organization; } - /** - * @return \DateTime - */ public function getIssueDate(): \DateTime { return $this->issueDate; } - /** - * @return \DateTime - */ public function getExpireDate(): \DateTime { return $this->expireDate; } - /** - * @return bool - */ public function isExpired(): bool { $now = new \DateTime(); return $this->issueDate > $now or $now > $this->expireDate; } - /** - * @return string|null - */ public function getIssuerName(): ?string { return $this->issuerName; } - /** - * @return string|null - */ public function getIssuerOrganization(): ?string { return $this->issuerOrganization; } diff --git a/lib/private/Security/CertificateManager.php b/lib/private/Security/CertificateManager.php index 3a87b7f1a00..4e3ec96b87b 100644 --- a/lib/private/Security/CertificateManager.php +++ b/lib/private/Security/CertificateManager.php @@ -1,34 +1,10 @@ <?php declare(strict_types=1); - /** - * @copyright Copyright (c) 2016, ownCloud, Inc. - * - * @author Bjoern Schiessle <bjoern@schiessle.org> - * @author Björn Schießle <bjoern@schiessle.org> - * @author Christoph Wurst <christoph@winzerhof-wurst.at> - * @author J0WI <J0WI@users.noreply.github.com> - * @author Joas Schilling <coding@schilljs.com> - * @author Lukas Reschke <lukas@statuscode.ch> - * @author Morris Jobke <hey@morrisjobke.de> - * @author Robin Appelman <robin@icewind.nl> - * @author Roeland Jago Douma <roeland@famdouma.nl> - * - * @license AGPL-3.0 - * - * This code is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License, version 3, - * as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License, version 3, - * along with this program. If not, see <http://www.gnu.org/licenses/> - * + * SPDX-FileCopyrightText: 2016-2024 Nextcloud GmbH and Nextcloud contributors + * SPDX-FileCopyrightText: 2016 ownCloud, Inc. + * SPDX-License-Identifier: AGPL-3.0-only */ namespace OC\Security; @@ -44,21 +20,14 @@ use Psr\Log\LoggerInterface; * Manage trusted certificates for users */ class CertificateManager implements ICertificateManager { - protected View $view; - protected IConfig $config; - protected LoggerInterface $logger; - protected ISecureRandom $random; - private ?string $bundlePath = null; - public function __construct(View $view, - IConfig $config, - LoggerInterface $logger, - ISecureRandom $random) { - $this->view = $view; - $this->config = $config; - $this->logger = $logger; - $this->random = $random; + public function __construct( + protected View $view, + protected IConfig $config, + protected LoggerInterface $logger, + protected ISecureRandom $random, + ) { } /** @@ -178,7 +147,6 @@ class CertificateManager implements ICertificateManager { * * @param string $certificate the certificate data * @param string $name the filename for the certificate - * @return \OCP\ICertificate * @throws \Exception If the certificate could not get added */ public function addCertificate(string $certificate, string $name): ICertificate { @@ -205,9 +173,6 @@ class CertificateManager implements ICertificateManager { /** * Remove the certificate and re-generate the certificate bundle - * - * @param string $name - * @return bool */ public function removeCertificate(string $name): bool { if (!Filesystem::isValidPath($name)) { @@ -225,8 +190,6 @@ class CertificateManager implements ICertificateManager { /** * Get the path to the certificate bundle - * - * @return string */ public function getCertificateBundle(): string { return $this->getPathToCertificates() . 'rootcerts.crt'; @@ -267,8 +230,6 @@ class CertificateManager implements ICertificateManager { /** * Check if we need to re-bundle the certificates because one of the sources has updated - * - * @return bool */ private function needsRebundling(): bool { $targetBundle = $this->getCertificateBundle(); @@ -282,8 +243,6 @@ class CertificateManager implements ICertificateManager { /** * get mtime of ca-bundle shipped by Nextcloud - * - * @return int */ protected function getFilemtimeOfCaBundle(): int { return filemtime(\OC::$SERVERROOT . '/resources/config/ca-bundle.crt'); diff --git a/lib/private/Security/CredentialsManager.php b/lib/private/Security/CredentialsManager.php index 0bddaeda1b0..fdf2c46ecf8 100644 --- a/lib/private/Security/CredentialsManager.php +++ b/lib/private/Security/CredentialsManager.php @@ -1,30 +1,10 @@ <?php declare(strict_types=1); - /** - * @copyright Copyright (c) 2016, ownCloud, Inc. - * - * @author Arthur Schiwon <blizzz@arthur-schiwon.de> - * @author J0WI <J0WI@users.noreply.github.com> - * @author Joas Schilling <coding@schilljs.com> - * @author Robin McCorkell <robin@mccorkell.me.uk> - * @author Roeland Jago Douma <roeland@famdouma.nl> - * - * @license AGPL-3.0 - * - * This code is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License, version 3, - * as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License, version 3, - * along with this program. If not, see <http://www.gnu.org/licenses/> - * + * SPDX-FileCopyrightText: 2016-2024 Nextcloud GmbH and Nextcloud contributors + * SPDX-FileCopyrightText: 2016 ownCloud, Inc. + * SPDX-License-Identifier: AGPL-3.0-only */ namespace OC\Security; @@ -40,26 +20,16 @@ use OCP\Security\ICrypto; class CredentialsManager implements ICredentialsManager { public const DB_TABLE = 'storages_credentials'; - /** @var ICrypto */ - protected $crypto; - - /** @var IDBConnection */ - protected $dbConnection; - - /** - * @param ICrypto $crypto - * @param IDBConnection $dbConnection - */ - public function __construct(ICrypto $crypto, IDBConnection $dbConnection) { - $this->crypto = $crypto; - $this->dbConnection = $dbConnection; + public function __construct( + protected ICrypto $crypto, + protected IDBConnection $dbConnection, + ) { } /** * Store a set of credentials * * @param string $userId empty string for system-wide credentials - * @param string $identifier * @param mixed $credentials */ public function store(string $userId, string $identifier, $credentials): void { @@ -77,10 +47,8 @@ class CredentialsManager implements ICredentialsManager { * Retrieve a set of credentials * * @param string $userId empty string for system-wide credentials - * @param string $identifier - * @return mixed */ - public function retrieve(string $userId, string $identifier) { + public function retrieve(string $userId, string $identifier): mixed { $qb = $this->dbConnection->getQueryBuilder(); $qb->select('credentials') ->from(self::DB_TABLE) @@ -108,7 +76,6 @@ class CredentialsManager implements ICredentialsManager { * Delete a set of credentials * * @param string $userId empty string for system-wide credentials - * @param string $identifier * @return int rows removed */ public function delete(string $userId, string $identifier): int { @@ -128,7 +95,6 @@ class CredentialsManager implements ICredentialsManager { /** * Erase all credentials stored for a user * - * @param string $userId * @return int rows removed */ public function erase(string $userId): int { diff --git a/lib/private/Security/Crypto.php b/lib/private/Security/Crypto.php index 2a7905376ef..b03f8a4ddce 100644 --- a/lib/private/Security/Crypto.php +++ b/lib/private/Security/Crypto.php @@ -1,38 +1,16 @@ <?php declare(strict_types=1); - /** - * @copyright Copyright (c) 2016, ownCloud, Inc. - * - * @author Andreas Fischer <bantu@owncloud.com> - * @author Christoph Wurst <christoph@winzerhof-wurst.at> - * @author Lukas Reschke <lukas@statuscode.ch> - * @author lynn-stephenson <lynn.stephenson@protonmail.com> - * @author Morris Jobke <hey@morrisjobke.de> - * @author Roeland Jago Douma <roeland@famdouma.nl> - * - * @license AGPL-3.0 - * - * This code is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License, version 3, - * as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License, version 3, - * along with this program. If not, see <http://www.gnu.org/licenses/> - * + * SPDX-FileCopyrightText: 2016-2024 Nextcloud GmbH and Nextcloud contributors + * SPDX-FileCopyrightText: 2016 ownCloud, Inc. + * SPDX-License-Identifier: AGPL-3.0-only */ namespace OC\Security; use Exception; use OCP\IConfig; use OCP\Security\ICrypto; -use OCP\Security\ISecureRandom; use phpseclib\Crypt\AES; use phpseclib\Crypt\Hash; @@ -47,20 +25,13 @@ use phpseclib\Crypt\Hash; * @package OC\Security */ class Crypto implements ICrypto { - /** @var AES $cipher */ - private $cipher; - /** @var int */ - private $ivLength = 16; - /** @var IConfig */ - private $config; + private AES $cipher; + private int $ivLength = 16; - /** - * @param IConfig $config - * @param ISecureRandom $random - */ - public function __construct(IConfig $config) { + public function __construct( + private IConfig $config, + ) { $this->cipher = new AES(); - $this->config = $config; } /** @@ -84,7 +55,6 @@ class Crypto implements ICrypto { /** * Encrypts a value and adds an HMAC (Encrypt-Then-MAC) * - * @param string $plaintext * @param string $password Password to encrypt, if not specified the secret from config.php will be taken * @return string Authenticated ciphertext * @throws Exception if it was not possible to gather sufficient entropy @@ -115,9 +85,7 @@ class Crypto implements ICrypto { /** * Decrypts a value and verifies the HMAC (Encrypt-Then-Mac) - * @param string $authenticatedCiphertext * @param string $password Password to encrypt, if not specified the secret from config.php will be taken - * @return string plaintext * @throws Exception If the HMAC does not match * @throws Exception If the decryption failed */ diff --git a/lib/private/Security/FeaturePolicy/FeaturePolicy.php b/lib/private/Security/FeaturePolicy/FeaturePolicy.php index a0ab2065ece..9b513b80813 100644 --- a/lib/private/Security/FeaturePolicy/FeaturePolicy.php +++ b/lib/private/Security/FeaturePolicy/FeaturePolicy.php @@ -3,25 +3,8 @@ declare(strict_types=1); /** - * @copyright Copyright (c) 2019, Roeland Jago Douma <roeland@famdouma.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/>. - * + * SPDX-FileCopyrightText: 2019 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later */ namespace OC\Security\FeaturePolicy; diff --git a/lib/private/Security/FeaturePolicy/FeaturePolicyManager.php b/lib/private/Security/FeaturePolicy/FeaturePolicyManager.php index 3aa93ac3da4..e50aaac0faf 100644 --- a/lib/private/Security/FeaturePolicy/FeaturePolicyManager.php +++ b/lib/private/Security/FeaturePolicy/FeaturePolicyManager.php @@ -3,26 +3,8 @@ declare(strict_types=1); /** - * @copyright Copyright (c) 2019, Roeland Jago Douma <roeland@famdouma.nl> - * - * @author Morris Jobke <hey@morrisjobke.de> - * @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/>. - * + * SPDX-FileCopyrightText: 2019 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later */ namespace OC\Security\FeaturePolicy; @@ -32,13 +14,11 @@ use OCP\Security\FeaturePolicy\AddFeaturePolicyEvent; class FeaturePolicyManager { /** @var EmptyFeaturePolicy[] */ - private $policies = []; + private array $policies = []; - /** @var IEventDispatcher */ - private $dispatcher; - - public function __construct(IEventDispatcher $dispatcher) { - $this->dispatcher = $dispatcher; + public function __construct( + private IEventDispatcher $dispatcher, + ) { } public function addDefaultPolicy(EmptyFeaturePolicy $policy): void { @@ -60,8 +40,10 @@ class FeaturePolicyManager { * Merges the first given policy with the second one * */ - public function mergePolicies(FeaturePolicy $defaultPolicy, - EmptyFeaturePolicy $originalPolicy): FeaturePolicy { + public function mergePolicies( + FeaturePolicy $defaultPolicy, + EmptyFeaturePolicy $originalPolicy, + ): FeaturePolicy { foreach ((object)(array)$originalPolicy as $name => $value) { $setter = 'set' . ucfirst($name); if (\is_array($value)) { diff --git a/lib/private/Security/Hasher.php b/lib/private/Security/Hasher.php index 85f69263925..22e850157fc 100644 --- a/lib/private/Security/Hasher.php +++ b/lib/private/Security/Hasher.php @@ -1,30 +1,10 @@ <?php declare(strict_types=1); - /** - * @copyright Copyright (c) 2016, ownCloud, Inc. - * - * @author Arthur Schiwon <blizzz@arthur-schiwon.de> - * @author Christoph Wurst <christoph@winzerhof-wurst.at> - * @author Lukas Reschke <lukas@statuscode.ch> - * @author MichaIng <micha@dietpi.com> - * @author Roeland Jago Douma <roeland@famdouma.nl> - * - * @license AGPL-3.0 - * - * This code is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License, version 3, - * as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License, version 3, - * along with this program. If not, see <http://www.gnu.org/licenses/> - * + * SPDX-FileCopyrightText: 2016-2024 Nextcloud GmbH and Nextcloud contributors + * SPDX-FileCopyrightText: 2016 ownCloud, Inc. + * SPDX-License-Identifier: AGPL-3.0-only */ namespace OC\Security; @@ -42,28 +22,23 @@ use OCP\Security\IHasher; * * Usage: * // Hashing a message - * $hash = \OC::$server->getHasher()->hash('MessageToHash'); + * $hash = \OC::$server->get(\OCP\Security\IHasher::class)->hash('MessageToHash'); * // Verifying a message - $newHash will contain the newly calculated hash * $newHash = null; - * var_dump(\OC::$server->getHasher()->verify('a', '86f7e437faa5a7fce15d1ddcb9eaeaea377667b8', $newHash)); + * var_dump(\OC::$server->get(\OCP\Security\IHasher::class)->verify('a', '86f7e437faa5a7fce15d1ddcb9eaeaea377667b8', $newHash)); * var_dump($newHash); * * @package OC\Security */ class Hasher implements IHasher { - /** @var IConfig */ - private $config; - /** @var array Options passed to password_hash and password_needs_rehash */ - private $options = []; - /** @var string Salt used for legacy passwords */ - private $legacySalt = null; - - /** - * @param IConfig $config - */ - public function __construct(IConfig $config) { - $this->config = $config; - + /** Options passed to password_hash and password_needs_rehash */ + private array $options = []; + /** Salt used for legacy passwords */ + private ?string $legacySalt = null; + + public function __construct( + private IConfig $config, + ) { if (\defined('PASSWORD_ARGON2ID') || \defined('PASSWORD_ARGON2I')) { // password_hash fails, when the minimum values are undershot. // In this case, apply minimum. @@ -106,7 +81,7 @@ class Hasher implements IHasher { * @param string $prefixedHash * @return null|array Null if the hash is not prefixed, otherwise array('version' => 1, 'hash' => 'foo') */ - protected function splitHash(string $prefixedHash) { + protected function splitHash(string $prefixedHash): ?array { $explodedString = explode('|', $prefixedHash, 2); if (\count($explodedString) === 2) { if ((int)$explodedString[0] > 0) { @@ -198,7 +173,7 @@ class Hasher implements IHasher { return password_needs_rehash($hash, $algorithm, $this->options); } - private function getPrefferedAlgorithm() { + private function getPrefferedAlgorithm(): string { $default = PASSWORD_BCRYPT; if (\defined('PASSWORD_ARGON2I')) { $default = PASSWORD_ARGON2I; diff --git a/lib/private/Security/IdentityProof/Key.php b/lib/private/Security/IdentityProof/Key.php index bde828a3859..0bfcd6bf9ed 100644 --- a/lib/private/Security/IdentityProof/Key.php +++ b/lib/private/Security/IdentityProof/Key.php @@ -3,26 +3,8 @@ declare(strict_types=1); /** - * @copyright Copyright (c) 2016 Lukas Reschke <lukas@statuscode.ch> - * - * @author Lukas Reschke <lukas@statuscode.ch> - * @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/>. - * + * SPDX-FileCopyrightText: 2016 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later */ namespace OC\Security\IdentityProof; diff --git a/lib/private/Security/IdentityProof/Manager.php b/lib/private/Security/IdentityProof/Manager.php index 49b9bb10c3e..0ce760ccc63 100644 --- a/lib/private/Security/IdentityProof/Manager.php +++ b/lib/private/Security/IdentityProof/Manager.php @@ -3,29 +3,8 @@ declare(strict_types=1); /** - * @copyright Copyright (c) 2016 Lukas Reschke <lukas@statuscode.ch> - * - * @author Bjoern Schiessle <bjoern@schiessle.org> - * @author Christoph Wurst <christoph@winzerhof-wurst.at> - * @author Joas Schilling <coding@schilljs.com> - * @author Lukas Reschke <lukas@statuscode.ch> - * @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/>. - * + * SPDX-FileCopyrightText: 2016 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later */ namespace OC\Security\IdentityProof; diff --git a/lib/private/Security/IdentityProof/Signer.php b/lib/private/Security/IdentityProof/Signer.php index 1458390c327..6083cbb5c9b 100644 --- a/lib/private/Security/IdentityProof/Signer.php +++ b/lib/private/Security/IdentityProof/Signer.php @@ -3,27 +3,8 @@ declare(strict_types=1); /** - * @copyright Copyright (c) 2016 Lukas Reschke <lukas@statuscode.ch> - * - * @author Christoph Wurst <christoph@winzerhof-wurst.at> - * @author Lukas Reschke <lukas@statuscode.ch> - * @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/>. - * + * SPDX-FileCopyrightText: 2016 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later */ namespace OC\Security\IdentityProof; @@ -74,12 +55,12 @@ class Signer { $user = $this->userManager->get($userId); if ($user !== null) { $key = $this->keyManager->getKey($user); - return (bool)openssl_verify( + return openssl_verify( json_encode($data['message']), base64_decode($data['signature']), $key->getPublic(), OPENSSL_ALGO_SHA512 - ); + ) === 1; } } diff --git a/lib/private/Security/Normalizer/IpAddress.php b/lib/private/Security/Normalizer/IpAddress.php index 98d85ce07a1..dc7c784fa4c 100644 --- a/lib/private/Security/Normalizer/IpAddress.php +++ b/lib/private/Security/Normalizer/IpAddress.php @@ -3,30 +3,8 @@ declare(strict_types=1); /** - * @copyright Copyright (c) 2017 Lukas Reschke <lukas@statuscode.ch> - * - * @author Christoph Wurst <christoph@winzerhof-wurst.at> - * @author Konrad Bucheli <kb@open.ch> - * @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> - * - * @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/>. - * + * SPDX-FileCopyrightText: 2017 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later */ namespace OC\Security\Normalizer; @@ -37,43 +15,18 @@ namespace OC\Security\Normalizer; * @package OC\Security\Normalizer */ class IpAddress { - /** @var string */ - private $ip; - - /** - * @param string $ip IP to normalized - */ - public function __construct(string $ip) { - $this->ip = $ip; - } - /** - * Return the given subnet for an IPv4 address and mask bits - * - * @param string $ip - * @param int $maskBits - * @return string + * @param string $ip IP to normalize */ - private function getIPv4Subnet(string $ip, int $maskBits = 32): string { - $binary = \inet_pton($ip); - for ($i = 32; $i > $maskBits; $i -= 8) { - $j = \intdiv($i, 8) - 1; - $k = \min(8, $i - $maskBits); - $mask = (0xff - ((2 ** $k) - 1)); - $int = \unpack('C', $binary[$j]); - $binary[$j] = \pack('C', $int[1] & $mask); - } - return \inet_ntop($binary).'/'.$maskBits; + public function __construct( + private string $ip, + ) { } /** - * Return the given subnet for an IPv6 address and mask bits - * - * @param string $ip - * @param int $maskBits - * @return string + * Return the given subnet for an IPv6 address (64 first bits) */ - private function getIPv6Subnet(string $ip, int $maskBits = 48): string { + private function getIPv6Subnet(string $ip): string { if ($ip[0] === '[' && $ip[-1] === ']') { // If IP is with brackets, for example [::1] $ip = substr($ip, 1, strlen($ip) - 2); } @@ -81,15 +34,11 @@ class IpAddress { if ($pos !== false) { $ip = substr($ip, 0, $pos - 1); } + $binary = \inet_pton($ip); - for ($i = 128; $i > $maskBits; $i -= 8) { - $j = \intdiv($i, 8) - 1; - $k = \min(8, $i - $maskBits); - $mask = (0xff - ((2 ** $k) - 1)); - $int = \unpack('C', $binary[$j]); - $binary[$j] = \pack('C', $int[1] & $mask); - } - return \inet_ntop($binary).'/'.$maskBits; + $mask = inet_pton('FFFF:FFFF:FFFF:FFFF::'); + + return inet_ntop($binary & $mask).'/64'; } /** @@ -103,58 +52,34 @@ class IpAddress { if (!$binary) { return null; } - for ($i = 0; $i <= 9; $i++) { - if (unpack('C', $binary[$i])[1] !== 0) { - return null; - } - } - - for ($i = 10; $i <= 11; $i++) { - if (unpack('C', $binary[$i])[1] !== 255) { - return null; - } - } - $binary4 = ''; - for ($i = 12; $i < 16; $i++) { - $binary4 .= $binary[$i]; + $mask = inet_pton('::FFFF:FFFF'); + if (($binary & ~$mask) !== inet_pton('::FFFF:0.0.0.0')) { + return null; } - return inet_ntop($binary4); + return inet_ntop(substr($binary, -4)); } /** * Gets either the /32 (IPv4) or the /64 (IPv6) subnet of an IP address - * - * @return string */ public function getSubnet(): string { - if (\preg_match('/^[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}$/', $this->ip)) { - return $this->getIPv4Subnet( - $this->ip, - 32 - ); + if (filter_var($this->ip, FILTER_VALIDATE_IP, FILTER_FLAG_IPV4)) { + return $this->ip.'/32'; } $ipv4 = $this->getEmbeddedIpv4($this->ip); if ($ipv4 !== null) { - return $this->getIPv4Subnet( - $ipv4, - 32 - ); + return $ipv4.'/32'; } - return $this->getIPv6Subnet( - $this->ip, - 64 - ); + return $this->getIPv6Subnet($this->ip); } /** * Returns the specified IP address - * - * @return string */ public function __toString(): string { return $this->ip; diff --git a/lib/private/Security/RateLimiting/Backend/DatabaseBackend.php b/lib/private/Security/RateLimiting/Backend/DatabaseBackend.php index 41f50a90b5c..34fddff539b 100644 --- a/lib/private/Security/RateLimiting/Backend/DatabaseBackend.php +++ b/lib/private/Security/RateLimiting/Backend/DatabaseBackend.php @@ -3,27 +3,8 @@ declare(strict_types=1); /** - * @copyright Copyright (c) 2023 Joas Schilling <coding@schilljs.com> - * @copyright Copyright (c) 2021 Lukas Reschke <lukas@statuscode.ch> - * - * @author Joas Schilling <coding@schilljs.com> - * @author Lukas Reschke <lukas@statuscode.ch> - * - * @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/>. - * + * SPDX-FileCopyrightText: 2021 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later */ namespace OC\Security\RateLimiting\Backend; diff --git a/lib/private/Security/RateLimiting/Backend/IBackend.php b/lib/private/Security/RateLimiting/Backend/IBackend.php index 24715391a96..43eff5dcf02 100644 --- a/lib/private/Security/RateLimiting/Backend/IBackend.php +++ b/lib/private/Security/RateLimiting/Backend/IBackend.php @@ -3,26 +3,8 @@ declare(strict_types=1); /** - * @copyright Copyright (c) 2017 Lukas Reschke <lukas@statuscode.ch> - * - * @author Lukas Reschke <lukas@statuscode.ch> - * @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/>. - * + * SPDX-FileCopyrightText: 2017 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later */ namespace OC\Security\RateLimiting\Backend; diff --git a/lib/private/Security/RateLimiting/Backend/MemoryCacheBackend.php b/lib/private/Security/RateLimiting/Backend/MemoryCacheBackend.php index b59178c7d7b..84eb9fbd084 100644 --- a/lib/private/Security/RateLimiting/Backend/MemoryCacheBackend.php +++ b/lib/private/Security/RateLimiting/Backend/MemoryCacheBackend.php @@ -3,30 +3,8 @@ declare(strict_types=1); /** - * @copyright Copyright (c) 2023 Joas Schilling <coding@schilljs.com> - * @copyright Copyright (c) 2017 Lukas Reschke <lukas@statuscode.ch> - * - * @author Christoph Wurst <christoph@winzerhof-wurst.at> - * @author Joas Schilling <coding@schilljs.com> - * @author Lukas Reschke <lukas@statuscode.ch> - * @author Morris Jobke <hey@morrisjobke.de> - * @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/>. - * + * SPDX-FileCopyrightText: 2017 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later */ namespace OC\Security\RateLimiting\Backend; diff --git a/lib/private/Security/RateLimiting/Exception/RateLimitExceededException.php b/lib/private/Security/RateLimiting/Exception/RateLimitExceededException.php index 08091e997ca..19defc2d896 100644 --- a/lib/private/Security/RateLimiting/Exception/RateLimitExceededException.php +++ b/lib/private/Security/RateLimiting/Exception/RateLimitExceededException.php @@ -3,32 +3,16 @@ declare(strict_types=1); /** - * @copyright Copyright (c) 2017 Lukas Reschke <lukas@statuscode.ch> - * - * @author Lukas Reschke <lukas@statuscode.ch> - * - * @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/>. - * + * SPDX-FileCopyrightText: 2017 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later */ namespace OC\Security\RateLimiting\Exception; use OC\AppFramework\Middleware\Security\Exceptions\SecurityException; use OCP\AppFramework\Http; +use OCP\Security\RateLimiting\IRateLimitExceededException; -class RateLimitExceededException extends SecurityException { +class RateLimitExceededException extends SecurityException implements IRateLimitExceededException { public function __construct() { parent::__construct('Rate limit exceeded', Http::STATUS_TOO_MANY_REQUESTS); } diff --git a/lib/private/Security/RateLimiting/Limiter.php b/lib/private/Security/RateLimiting/Limiter.php index c8c0e2ce101..b7ac26d9132 100644 --- a/lib/private/Security/RateLimiting/Limiter.php +++ b/lib/private/Security/RateLimiting/Limiter.php @@ -3,26 +3,8 @@ declare(strict_types=1); /** - * @copyright Copyright (c) 2017 Lukas Reschke <lukas@statuscode.ch> - * - * @author Lukas Reschke <lukas@statuscode.ch> - * @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/>. - * + * SPDX-FileCopyrightText: 2017 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later */ namespace OC\Security\RateLimiting; @@ -30,8 +12,9 @@ use OC\Security\Normalizer\IpAddress; use OC\Security\RateLimiting\Backend\IBackend; use OC\Security\RateLimiting\Exception\RateLimitExceededException; use OCP\IUser; +use OCP\Security\RateLimiting\ILimiter; -class Limiter { +class Limiter implements ILimiter { public function __construct( private IBackend $backend, ) { diff --git a/lib/private/Security/RemoteHostValidator.php b/lib/private/Security/RemoteHostValidator.php index 38129fbd81b..30bd59db2c1 100644 --- a/lib/private/Security/RemoteHostValidator.php +++ b/lib/private/Security/RemoteHostValidator.php @@ -2,25 +2,9 @@ declare(strict_types=1); -/* - * @copyright 2022 Christoph Wurst <christoph@winzerhof-wurst.at> - * - * @author 2022 Christoph Wurst <christoph@winzerhof-wurst.at> - * - * @license GNU AGPL version 3 or any later version - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as - * published by the Free Software Foundation, either version 3 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. +/** + * SPDX-FileCopyrightText: 2022 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later */ namespace OC\Security; @@ -38,19 +22,12 @@ use function urldecode; * @internal */ final class RemoteHostValidator implements IRemoteHostValidator { - private IConfig $config; - private HostnameClassifier $hostnameClassifier; - private IpAddressClassifier $ipAddressClassifier; - private LoggerInterface $logger; - - public function __construct(IConfig $config, - HostnameClassifier $hostnameClassifier, - IpAddressClassifier $ipAddressClassifier, - LoggerInterface $logger) { - $this->config = $config; - $this->hostnameClassifier = $hostnameClassifier; - $this->ipAddressClassifier = $ipAddressClassifier; - $this->logger = $logger; + public function __construct( + private IConfig $config, + private HostnameClassifier $hostnameClassifier, + private IpAddressClassifier $ipAddressClassifier, + private LoggerInterface $logger, + ) { } public function isValid(string $host): bool { @@ -59,6 +36,10 @@ final class RemoteHostValidator implements IRemoteHostValidator { } $host = idn_to_utf8(strtolower(urldecode($host))); + if ($host === false) { + return false; + } + // Remove brackets from IPv6 addresses if (str_starts_with($host, '[') && str_ends_with($host, ']')) { $host = substr($host, 1, -1); diff --git a/lib/private/Security/SecureRandom.php b/lib/private/Security/SecureRandom.php index cbd1dc8db6d..f6257779486 100644 --- a/lib/private/Security/SecureRandom.php +++ b/lib/private/Security/SecureRandom.php @@ -1,29 +1,10 @@ <?php declare(strict_types=1); - /** - * @copyright Copyright (c) 2016, ownCloud, Inc. - * - * @author Christoph Wurst <christoph@winzerhof-wurst.at> - * @author Lukas Reschke <lukas@statuscode.ch> - * @author Morris Jobke <hey@morrisjobke.de> - * @author Roeland Jago Douma <roeland@famdouma.nl> - * - * @license AGPL-3.0 - * - * This code is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License, version 3, - * as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License, version 3, - * along with this program. If not, see <http://www.gnu.org/licenses/> - * + * SPDX-FileCopyrightText: 2016-2024 Nextcloud GmbH and Nextcloud contributors + * SPDX-FileCopyrightText: 2016 ownCloud, Inc. + * SPDX-License-Identifier: AGPL-3.0-only */ namespace OC\Security; @@ -44,11 +25,12 @@ class SecureRandom implements ISecureRandom { * @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 { + public function generate( + int $length, + string $characters = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/', + ): string { if ($length <= 0) { throw new \LengthException('Invalid length specified: ' . $length . ' must be bigger than 0'); } diff --git a/lib/private/Security/TrustedDomainHelper.php b/lib/private/Security/TrustedDomainHelper.php index ca6a5cba073..a65779780e8 100644 --- a/lib/private/Security/TrustedDomainHelper.php +++ b/lib/private/Security/TrustedDomainHelper.php @@ -1,31 +1,10 @@ <?php declare(strict_types=1); - /** - * @copyright Copyright (c) 2016, ownCloud, Inc. - * - * @author Christoph Wurst <christoph@winzerhof-wurst.at> - * @author J0WI <J0WI@users.noreply.github.com> - * @author Johannes Ernst <jernst@indiecomputing.com> - * @author Julius Härtl <jus@bitgrid.net> - * @author Lukas Reschke <lukas@statuscode.ch> - * @author Roeland Jago Douma <roeland@famdouma.nl> - * - * @license AGPL-3.0 - * - * This code is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License, version 3, - * as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License, version 3, - * along with this program. If not, see <http://www.gnu.org/licenses/> - * + * SPDX-FileCopyrightText: 2016-2024 Nextcloud GmbH and Nextcloud contributors + * SPDX-FileCopyrightText: 2016 ownCloud, Inc. + * SPDX-License-Identifier: AGPL-3.0-only */ namespace OC\Security; @@ -34,19 +13,13 @@ use OCP\IConfig; use OCP\Security\ITrustedDomainHelper; class TrustedDomainHelper implements ITrustedDomainHelper { - /** @var IConfig */ - private $config; - - /** - * @param IConfig $config - */ - public function __construct(IConfig $config) { - $this->config = $config; + public function __construct( + private IConfig $config, + ) { } /** * Strips a potential port from a domain (in format domain:port) - * @param string $host * @return string $host without appended port */ private function getDomainWithoutPort(string $host): string { diff --git a/lib/private/Security/VerificationToken/CleanUpJob.php b/lib/private/Security/VerificationToken/CleanUpJob.php index 1f4af046451..ba8f4352f80 100644 --- a/lib/private/Security/VerificationToken/CleanUpJob.php +++ b/lib/private/Security/VerificationToken/CleanUpJob.php @@ -1,36 +1,17 @@ <?php declare(strict_types=1); - /** - * @copyright Copyright (c) 2021 Arthur Schiwon <blizzz@arthur-schiwon.de> - * - * @author Arthur Schiwon <blizzz@arthur-schiwon.de> - * - * @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 <https://www.gnu.org/licenses/>. - * + * SPDX-FileCopyrightText: 2021 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later */ - namespace OC\Security\VerificationToken; use OCP\AppFramework\Utility\ITimeFactory; -use OCP\IConfig; -use OCP\IUserManager; use OCP\BackgroundJob\IJobList; use OCP\BackgroundJob\Job; +use OCP\IConfig; +use OCP\IUserManager; use OCP\Security\VerificationToken\InvalidTokenException; use OCP\Security\VerificationToken\IVerificationToken; diff --git a/lib/private/Security/VerificationToken/VerificationToken.php b/lib/private/Security/VerificationToken/VerificationToken.php index 5f606d0e049..f99e6745952 100644 --- a/lib/private/Security/VerificationToken/VerificationToken.php +++ b/lib/private/Security/VerificationToken/VerificationToken.php @@ -1,29 +1,10 @@ <?php declare(strict_types=1); - /** - * @copyright Copyright (c) 2021 Arthur Schiwon <blizzz@arthur-schiwon.de> - * - * @author Arthur Schiwon <blizzz@arthur-schiwon.de> - * - * @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 <https://www.gnu.org/licenses/>. - * + * SPDX-FileCopyrightText: 2021 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later */ - namespace OC\Security\VerificationToken; use OCP\AppFramework\Utility\ITimeFactory; diff --git a/lib/private/Server.php b/lib/private/Server.php index e8ade23d8fe..bcdf482f02d 100644 --- a/lib/private/Server.php +++ b/lib/private/Server.php @@ -1,54 +1,8 @@ <?php /** - * @copyright Copyright (c) 2016, ownCloud, Inc. - * @copyright Copyright (c) 2016, Lukas Reschke <lukas@statuscode.ch> - * - * @author Arne Hamann <kontakt+github@arne.email> - * @author Arthur Schiwon <blizzz@arthur-schiwon.de> - * @author Bart Visscher <bartv@thisnet.nl> - * @author Bernhard Posselt <dev@bernhard-posselt.com> - * @author Bernhard Reiter <ockham@raz.or.at> - * @author Bjoern Schiessle <bjoern@schiessle.org> - * @author Björn Schießle <bjoern@schiessle.org> - * @author Christopher Schäpers <kondou@ts.unde.re> - * @author Christoph Wurst <christoph@winzerhof-wurst.at> - * @author Damjan Georgievski <gdamjan@gmail.com> - * @author Daniel Kesselberg <mail@danielkesselberg.de> - * @author Georg Ehrke <oc.list@georgehrke.com> - * @author Joas Schilling <coding@schilljs.com> - * @author John Molakvoæ <skjnldsv@protonmail.com> - * @author Jörn Friedrich Dreyer <jfd@butonic.de> - * @author Julius Haertl <jus@bitgrid.net> - * @author Julius Härtl <jus@bitgrid.net> - * @author Lionel Elie Mamane <lionel@mamane.lu> - * @author Lukas Reschke <lukas@statuscode.ch> - * @author Maxence Lange <maxence@artificial-owl.com> - * @author Michael Weimann <mail@michael-weimann.eu> - * @author Morris Jobke <hey@morrisjobke.de> - * @author Piotr Mrówczyński <mrow4a@yahoo.com> - * @author Robin Appelman <robin@icewind.nl> - * @author Robin McCorkell <robin@mccorkell.me.uk> - * @author Roeland Jago Douma <roeland@famdouma.nl> - * @author root <root@localhost.localdomain> - * @author Thomas Müller <thomas.mueller@tmit.eu> - * @author Thomas Tanghus <thomas@tanghus.net> - * @author Tobia De Koninck <tobia@ledfan.be> - * @author Vincent Petry <vincent@nextcloud.com> - * - * @license AGPL-3.0 - * - * This code is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License, version 3, - * as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License, version 3, - * along with this program. If not, see <http://www.gnu.org/licenses/> - * + * SPDX-FileCopyrightText: 2016 Nextcloud GmbH and Nextcloud contributors + * SPDX-FileCopyrightText: 2016 ownCloud, Inc. + * SPDX-License-Identifier: AGPL-3.0-only */ namespace OC; @@ -68,6 +22,7 @@ use OC\Authentication\Listeners\UserLoggedInListener; use OC\Authentication\LoginCredentials\Store; use OC\Authentication\Token\IProvider; use OC\Avatar\AvatarManager; +use OC\Blurhash\Listener\GenerateBlurhashMetadata; use OC\Collaboration\Collaborators\GroupPlugin; use OC\Collaboration\Collaborators\MailPlugin; use OC\Collaboration\Collaborators\RemoteGroupPlugin; @@ -85,6 +40,7 @@ use OC\Diagnostics\QueryLogger; use OC\Federation\CloudFederationFactory; use OC\Federation\CloudFederationProviderManager; use OC\Federation\CloudIdManager; +use OC\Files\Cache\FileAccess; use OC\Files\Config\MountProviderCollection; use OC\Files\Config\UserMountCache; use OC\Files\Config\UserMountCacheListener; @@ -102,6 +58,7 @@ use OC\Files\Storage\StorageFactory; use OC\Files\Template\TemplateManager; use OC\Files\Type\Loader; use OC\Files\View; +use OC\FilesMetadata\FilesMetadataManager; use OC\FullTextSearch\FullTextSearchManager; use OC\Http\Client\ClientService; use OC\Http\Client\NegativeDnsCache; @@ -109,8 +66,8 @@ use OC\IntegrityCheck\Checker; use OC\IntegrityCheck\Helpers\AppLocator; use OC\IntegrityCheck\Helpers\EnvironmentHelper; use OC\IntegrityCheck\Helpers\FileAccessHelper; -use OC\LDAP\NullLDAPProviderFactory; use OC\KnownUser\KnownUserService; +use OC\LDAP\NullLDAPProviderFactory; use OC\Lock\DBLockingProvider; use OC\Lock\MemcacheLockingProvider; use OC\Lock\NoopLockingProvider; @@ -120,14 +77,15 @@ use OC\Log\PsrLoggerAdapter; use OC\Mail\Mailer; use OC\Memcache\ArrayCache; use OC\Memcache\Factory; -use OC\Metadata\Capabilities as MetadataCapabilities; -use OC\Metadata\IMetadataManager; -use OC\Metadata\MetadataManager; use OC\Notification\Manager; +use OC\OCM\Model\OCMProvider; +use OC\OCM\OCMDiscoveryService; use OC\OCS\DiscoveryService; use OC\Preview\GeneratorHelper; use OC\Preview\IMagickSupport; use OC\Preview\MimeIconProvider; +use OC\Profile\ProfileManager; +use OC\Profiler\Profiler; use OC\Remote\Api\ApiFactory; use OC\Remote\InstanceFactory; use OC\RichObjectStrings\Validator; @@ -142,27 +100,38 @@ use OC\Security\CSP\ContentSecurityPolicyNonceManager; use OC\Security\CSRF\CsrfTokenManager; use OC\Security\CSRF\TokenStorage\SessionStorage; use OC\Security\Hasher; +use OC\Security\RateLimiting\Limiter; use OC\Security\SecureRandom; use OC\Security\TrustedDomainHelper; use OC\Security\VerificationToken\VerificationToken; use OC\Session\CryptoWrapper; +use OC\Settings\DeclarativeManager; +use OC\SetupCheck\SetupCheckManager; use OC\Share20\ProviderFactory; use OC\Share20\ShareHelper; use OC\SpeechToText\SpeechToTextManager; use OC\SystemTag\ManagerFactory as SystemTagManagerFactory; use OC\Tagging\TagMapper; use OC\Talk\Broker; +use OC\Teams\TeamManager; use OC\Template\JSCombiner; use OC\Translation\TranslationManager; +use OC\User\AvailabilityCoordinator; use OC\User\DisplayNameCache; use OC\User\Listeners\BeforeUserDeletedListener; use OC\User\Listeners\UserChangedListener; use OC\User\Session; +use OCA\Files_External\Service\BackendService; +use OCA\Files_External\Service\GlobalStoragesService; +use OCA\Files_External\Service\UserGlobalStoragesService; +use OCA\Files_External\Service\UserStoragesService; use OCA\Theming\ImageManager; +use OCA\Theming\Service\BackgroundService; use OCA\Theming\ThemingDefaults; use OCA\Theming\Util; use OCP\Accounts\IAccountManager; use OCP\App\IAppManager; +use OCP\AppFramework\Utility\ITimeFactory; use OCP\Authentication\LoginCredentials\IStore; use OCP\Authentication\Token\IProvider as OCPIProvider; use OCP\BackgroundJob\IJobList; @@ -181,6 +150,7 @@ use OCP\EventDispatcher\IEventDispatcher; use OCP\Federation\ICloudFederationFactory; use OCP\Federation\ICloudFederationProviderManager; use OCP\Federation\ICloudIdManager; +use OCP\Files\Cache\IFileAccess; use OCP\Files\Config\IMountProviderCollection; use OCP\Files\Config\IUserMountCache; use OCP\Files\IMimeTypeDetector; @@ -190,16 +160,17 @@ use OCP\Files\Lock\ILockManager; use OCP\Files\Mount\IMountManager; use OCP\Files\Storage\IStorageFactory; use OCP\Files\Template\ITemplateManager; +use OCP\FilesMetadata\IFilesMetadataManager; use OCP\FullTextSearch\IFullTextSearchManager; use OCP\GlobalScale\IConfig; use OCP\Group\ISubAdmin; use OCP\Http\Client\IClientService; use OCP\IAppConfig; use OCP\IAvatarManager; +use OCP\IBinaryFinder; use OCP\ICache; use OCP\ICacheFactory; use OCP\ICertificateManager; -use OCP\IBinaryFinder; use OCP\IDateTimeFormatter; use OCP\IDateTimeZone; use OCP\IDBConnection; @@ -209,6 +180,7 @@ use OCP\IInitialStateService; use OCP\IL10N; use OCP\ILogger; use OCP\INavigationManager; +use OCP\IPhoneNumberUtil; use OCP\IPreview; use OCP\IRequest; use OCP\IRequestId; @@ -227,6 +199,11 @@ use OCP\Lock\ILockingProvider; use OCP\Lockdown\ILockdownManager; use OCP\Log\ILogFactory; use OCP\Mail\IMailer; +use OCP\OCM\IOCMDiscoveryService; +use OCP\OCM\IOCMProvider; +use OCP\Preview\IMimeIconProvider; +use OCP\Profile\IProfileManager; +use OCP\Profiler\IProfiler; use OCP\Remote\Api\IApiFactory; use OCP\Remote\IInstanceFactory; use OCP\RichObjectStrings\IValidator; @@ -238,12 +215,17 @@ use OCP\Security\ICrypto; use OCP\Security\IHasher; use OCP\Security\ISecureRandom; use OCP\Security\ITrustedDomainHelper; +use OCP\Security\RateLimiting\ILimiter; use OCP\Security\VerificationToken\IVerificationToken; +use OCP\Settings\IDeclarativeManager; +use OCP\SetupCheck\ISetupCheckManager; +use OCP\Share\IProviderFactory; use OCP\Share\IShareHelper; use OCP\SpeechToText\ISpeechToTextManager; use OCP\SystemTag\ISystemTagManager; use OCP\SystemTag\ISystemTagObjectMapper; use OCP\Talk\IBroker; +use OCP\Teams\ITeamManager; use OCP\Translation\ITranslationManager; use OCP\User\Events\BeforeUserDeletedEvent; use OCP\User\Events\BeforeUserLoggedInEvent; @@ -254,16 +236,10 @@ use OCP\User\Events\UserChangedEvent; use OCP\User\Events\UserLoggedInEvent; use OCP\User\Events\UserLoggedInWithCookieEvent; use OCP\User\Events\UserLoggedOutEvent; +use OCP\User\IAvailabilityCoordinator; use Psr\Container\ContainerExceptionInterface; use Psr\Container\ContainerInterface; use Psr\Log\LoggerInterface; -use OCA\Files_External\Service\UserStoragesService; -use OCA\Files_External\Service\UserGlobalStoragesService; -use OCA\Files_External\Service\GlobalStoragesService; -use OCA\Files_External\Service\BackendService; -use OCP\Profiler\IProfiler; -use OC\Profiler\Profiler; -use OCP\Preview\IMimeIconProvider; /** * Class Server @@ -430,17 +406,21 @@ class Server extends ServerContainer implements IServerContainer { $this->registerService(ISystemTagObjectMapper::class, function (ContainerInterface $c) { return $c->get('SystemTagManagerFactory')->getObjectMapper(); }); + $this->registerAlias(IFileAccess::class, FileAccess::class); $this->registerService('RootFolder', function (ContainerInterface $c) { $manager = \OC\Files\Filesystem::getMountManager(); $view = new View(); + /** @var IUserSession $userSession */ + $userSession = $c->get(IUserSession::class); $root = new Root( $manager, $view, - null, + $userSession->getUser(), $c->get(IUserMountCache::class), $this->get(LoggerInterface::class), $this->get(IUserManager::class), $this->get(IEventDispatcher::class), + $this->get(ICacheFactory::class), ); $previewConnector = new \OC\Preview\WatcherConnector( @@ -455,7 +435,8 @@ class Server extends ServerContainer implements IServerContainer { return new HookConnector( $c->get(IRootFolder::class), new View(), - $c->get(IEventDispatcher::class) + $c->get(IEventDispatcher::class), + $c->get(LoggerInterface::class) ); }); @@ -523,7 +504,7 @@ class Server extends ServerContainer implements IServerContainer { $provider, $c->get(\OCP\IConfig::class), $c->get(ISecureRandom::class), - $c->getLockdownManager(), + $c->get('LockdownManager'), $c->get(LoggerInterface::class), $c->get(IEventDispatcher::class) ); @@ -630,7 +611,8 @@ class Server extends ServerContainer implements IServerContainer { $c->getRequest(), $c->get(IUserSession::class), $c->get(ICacheFactory::class), - \OC::$SERVERROOT + \OC::$SERVERROOT, + $c->get(IAppManager::class), ); }); /** @deprecated 19.0.0 */ @@ -701,7 +683,7 @@ class Server extends ServerContainer implements IServerContainer { $this->registerService('RedisFactory', function (Server $c) { $systemConfig = $c->get(SystemConfig::class); - return new RedisFactory($systemConfig, $c->getEventLogger()); + return new RedisFactory($systemConfig, $c->get(IEventLogger::class)); }); $this->registerService(\OCP\Activity\IManager::class, function (Server $c) { @@ -785,8 +767,8 @@ class Server extends ServerContainer implements IServerContainer { $this->registerDeprecatedAlias('Search', ISearch::class); $this->registerService(\OC\Security\RateLimiting\Backend\IBackend::class, function ($c) { - $cacheFactory = $c->get(ICacheFactory::class); - if ($cacheFactory->isAvailable()) { + $config = $c->get(\OCP\IConfig::class); + if (ltrim($config->getSystemValueString('memcache.distributed', ''), '\\') === \OC\Memcache\Redis::class) { $backend = new \OC\Security\RateLimiting\Backend\MemoryCacheBackend( $c->get(AllConfig::class), $this->get(ICacheFactory::class), @@ -829,8 +811,7 @@ class Server extends ServerContainer implements IServerContainer { if (!$factory->isValidType($type)) { throw new \OC\DatabaseException('Invalid database type'); } - $connectionParams = $factory->createConnectionParams(); - $connection = $factory->getConnection($type, $connectionParams); + $connection = $factory->getConnection($type, []); return $connection; }); /** @deprecated 19.0.0 */ @@ -870,11 +851,10 @@ class Server extends ServerContainer implements IServerContainer { return new \OC\App\AppManager( $c->get(IUserSession::class), $c->get(\OCP\IConfig::class), - $c->get(\OC\AppConfig::class), $c->get(IGroupManager::class), $c->get(ICacheFactory::class), $c->get(IEventDispatcher::class), - $c->get(LoggerInterface::class) + $c->get(LoggerInterface::class), ); }); /** @deprecated 19.0.0 */ @@ -962,15 +942,16 @@ class Server extends ServerContainer implements IServerContainer { return $backend; }); - $this->registerService('IntegrityCodeChecker', function (ContainerInterface $c) { + $this->registerDeprecatedAlias('IntegrityCodeChecker', Checker::class); + $this->registerService(Checker::class, function (ContainerInterface $c) { // IConfig and IAppManager requires a working database. This code // might however be called when ownCloud is not yet setup. if (\OC::$server->get(SystemConfig::class)->getValue('installed', false)) { $config = $c->get(\OCP\IConfig::class); + $appConfig = $c->get(\OCP\IAppConfig::class); $appManager = $c->get(IAppManager::class); } else { - $config = null; - $appManager = null; + $config = $appConfig = $appManager = null; } return new Checker( @@ -978,6 +959,7 @@ class Server extends ServerContainer implements IServerContainer { new FileAccessHelper(), new AppLocator(), $config, + $appConfig, $c->get(ICacheFactory::class), $appManager, $c->get(IMimeTypeDetector::class) @@ -1066,7 +1048,8 @@ class Server extends ServerContainer implements IServerContainer { $memcacheFactory = $c->get(ICacheFactory::class); $memcache = $memcacheFactory->createLocking('lock'); if (!($memcache instanceof \OC\Memcache\NullCache)) { - return new MemcacheLockingProvider($memcache, $ttl); + $timeFactory = $c->get(ITimeFactory::class); + return new MemcacheLockingProvider($memcache, $timeFactory, $ttl); } return new DBLockingProvider( $c->get(IDBConnection::class), @@ -1122,9 +1105,6 @@ class Server extends ServerContainer implements IServerContainer { $manager->registerCapability(function () use ($c) { return $c->get(\OC\Security\Bruteforce\Capabilities::class); }); - $manager->registerCapability(function () use ($c) { - return $c->get(MetadataCapabilities::class); - }); return $manager; }); /** @deprecated 19.0.0 */ @@ -1142,7 +1122,7 @@ class Server extends ServerContainer implements IServerContainer { $userDisplayName = $manager->getDisplayName($id); if ($userDisplayName === null) { $l = $c->get(IFactory::class)->get('core'); - return $l->t('Unknown user'); + return $l->t('Unknown account'); } return $userDisplayName; }); @@ -1161,14 +1141,21 @@ class Server extends ServerContainer implements IServerContainer { $classExists = false; } - if ($classExists && $c->get(\OCP\IConfig::class)->getSystemValueBool('installed', false) && $c->get(IAppManager::class)->isInstalled('theming') && $c->getTrustedDomainHelper()->isTrustedDomain($c->getRequest()->getInsecureServerHost())) { + if ($classExists && $c->get(\OCP\IConfig::class)->getSystemValueBool('installed', false) && $c->get(IAppManager::class)->isInstalled('theming') && $c->get(TrustedDomainHelper::class)->isTrustedDomain($c->getRequest()->getInsecureServerHost())) { + $backgroundService = new BackgroundService( + $c->get(IRootFolder::class), + $c->getAppDataDir('theming'), + $c->get(\OCP\IConfig::class), + $c->get(ISession::class)->get('user_id'), + ); $imageManager = new ImageManager( $c->get(\OCP\IConfig::class), $c->getAppDataDir('theming'), $c->get(IURLGenerator::class), - $this->get(ICacheFactory::class), - $this->get(ILogger::class), - $this->get(ITempManager::class) + $c->get(ICacheFactory::class), + $c->get(LoggerInterface::class), + $c->get(ITempManager::class), + $backgroundService, ); return new ThemingDefaults( $c->get(\OCP\IConfig::class), @@ -1179,7 +1166,8 @@ class Server extends ServerContainer implements IServerContainer { new Util($c->get(\OCP\IConfig::class), $this->get(IAppManager::class), $c->getAppDataDir('theming'), $imageManager), $imageManager, $c->get(IAppManager::class), - $c->get(INavigationManager::class) + $c->get(INavigationManager::class), + $backgroundService, ); } return new \OC_Defaults(); @@ -1229,34 +1217,14 @@ class Server extends ServerContainer implements IServerContainer { /** @deprecated 19.0.0 */ $this->registerDeprecatedAlias('ContentSecurityPolicyManager', ContentSecurityPolicyManager::class); - $this->registerService(\OCP\Share\IManager::class, function (IServerContainer $c) { + $this->registerService(IProviderFactory::class, function (ContainerInterface $c) { $config = $c->get(\OCP\IConfig::class); $factoryClass = $config->getSystemValue('sharing.managerFactory', ProviderFactory::class); /** @var \OCP\Share\IProviderFactory $factory */ - $factory = new $factoryClass($this); - - $manager = new \OC\Share20\Manager( - $c->get(LoggerInterface::class), - $c->get(\OCP\IConfig::class), - $c->get(ISecureRandom::class), - $c->get(IHasher::class), - $c->get(IMountManager::class), - $c->get(IGroupManager::class), - $c->getL10N('lib'), - $c->get(IFactory::class), - $factory, - $c->get(IUserManager::class), - $c->get(IRootFolder::class), - $c->get(IMailer::class), - $c->get(IURLGenerator::class), - $c->get('ThemingDefaults'), - $c->get(IEventDispatcher::class), - $c->get(IUserSession::class), - $c->get(KnownUserService::class) - ); - - return $manager; + return new $factoryClass($this); }); + + $this->registerAlias(\OCP\Share\IManager::class, \OC\Share20\Manager::class); /** @deprecated 19.0.0 */ $this->registerDeprecatedAlias('ShareManager', \OCP\Share\IManager::class); @@ -1282,6 +1250,7 @@ class Server extends ServerContainer implements IServerContainer { $this->registerAlias(\OCP\Collaboration\Resources\IManager::class, \OC\Collaboration\Resources\Manager::class); $this->registerAlias(IReferenceManager::class, ReferenceManager::class); + $this->registerAlias(ITeamManager::class, TeamManager::class); $this->registerDeprecatedAlias('SettingsManager', \OC\Settings\Manager::class); $this->registerAlias(\OCP\Settings\IManager::class, \OC\Settings\Manager::class); @@ -1304,6 +1273,7 @@ class Server extends ServerContainer implements IServerContainer { $c->get(IClientService::class) ); }); + $this->registerAlias(IOCMDiscoveryService::class, OCMDiscoveryService::class); $this->registerService(ICloudIdManager::class, function (ContainerInterface $c) { return new CloudIdManager( @@ -1319,9 +1289,11 @@ class Server extends ServerContainer implements IServerContainer { $this->registerService(ICloudFederationProviderManager::class, function (ContainerInterface $c) { return new CloudFederationProviderManager( + $c->get(\OCP\IConfig::class), $c->get(IAppManager::class), $c->get(IClientService::class), $c->get(ICloudIdManager::class), + $c->get(IOCMDiscoveryService::class), $c->get(LoggerInterface::class) ); }); @@ -1341,7 +1313,7 @@ class Server extends ServerContainer implements IServerContainer { $this->registerService(Defaults::class, function (Server $c) { return new Defaults( - $c->getThemingDefaults() + $c->get('ThemingDefaults') ); }); /** @deprecated 19.0.0 */ @@ -1385,6 +1357,7 @@ class Server extends ServerContainer implements IServerContainer { $this->registerAlias(\OCP\Dashboard\IManager::class, \OC\Dashboard\Manager::class); $this->registerAlias(IFullTextSearchManager::class, FullTextSearchManager::class); + $this->registerAlias(IFilesMetadataManager::class, FilesMetadataManager::class); $this->registerAlias(ISubAdmin::class, SubAdmin::class); @@ -1396,8 +1369,6 @@ class Server extends ServerContainer implements IServerContainer { $this->registerAlias(IBroker::class, Broker::class); - $this->registerAlias(IMetadataManager::class, MetadataManager::class); - $this->registerAlias(\OCP\Files\AppData\IAppDataFactory::class, \OC\Files\AppData\Factory::class); $this->registerAlias(IBinaryFinder::class, BinaryFinder::class); @@ -1412,6 +1383,24 @@ class Server extends ServerContainer implements IServerContainer { $this->registerAlias(\OCP\TextProcessing\IManager::class, \OC\TextProcessing\Manager::class); + $this->registerAlias(\OCP\TextToImage\IManager::class, \OC\TextToImage\Manager::class); + + $this->registerAlias(ILimiter::class, Limiter::class); + + $this->registerAlias(IPhoneNumberUtil::class, PhoneNumberUtil::class); + + $this->registerAlias(IOCMProvider::class, OCMProvider::class); + + $this->registerAlias(ISetupCheckManager::class, SetupCheckManager::class); + + $this->registerAlias(IProfileManager::class, ProfileManager::class); + + $this->registerAlias(IAvailabilityCoordinator::class, AvailabilityCoordinator::class); + + $this->registerAlias(IDeclarativeManager::class, DeclarativeManager::class); + + $this->registerAlias(\OCP\TaskProcessing\IManager::class, \OC\TaskProcessing\Manager::class); + $this->connectDispatcher(); } @@ -1452,6 +1441,9 @@ class Server extends ServerContainer implements IServerContainer { $eventDispatcher->addServiceListener(PostLoginEvent::class, UserLoggedInListener::class); $eventDispatcher->addServiceListener(UserChangedEvent::class, UserChangedListener::class); $eventDispatcher->addServiceListener(BeforeUserDeletedEvent::class, BeforeUserDeletedListener::class); + + FilesMetadataManager::loadListeners($eventDispatcher); + GenerateBlurhashMetadata::loadListeners($eventDispatcher); } /** @@ -1628,6 +1620,7 @@ class Server extends ServerContainer implements IServerContainer { /** * @param \OCP\ISession $session + * @return void */ public function setSession(\OCP\ISession $session) { $this->get(SessionStorage::class)->setSession($session); @@ -1691,7 +1684,7 @@ class Server extends ServerContainer implements IServerContainer { * @param string $app appid * @param string $lang * @return IL10N - * @deprecated 20.0.0 + * @deprecated 20.0.0 use DI of {@see IL10N} or {@see IFactory} instead, or {@see \OCP\Util::getL10N()} as a last resort */ public function getL10N($app, $lang = null) { return $this->get(IFactory::class)->get($app, $lang); diff --git a/lib/private/ServerContainer.php b/lib/private/ServerContainer.php index daa480a75d1..9f887b2d48a 100644 --- a/lib/private/ServerContainer.php +++ b/lib/private/ServerContainer.php @@ -1,28 +1,10 @@ <?php declare(strict_types=1); - /** - * @copyright Copyright (c) 2016, ownCloud, Inc. - * - * @author Christoph Wurst <christoph@winzerhof-wurst.at> - * @author Joas Schilling <coding@schilljs.com> - * @author Roeland Jago Douma <roeland@famdouma.nl> - * - * @license AGPL-3.0 - * - * This code is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License, version 3, - * as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License, version 3, - * along with this program. If not, see <http://www.gnu.org/licenses/> - * + * SPDX-FileCopyrightText: 2016-2024 Nextcloud GmbH and Nextcloud contributors + * SPDX-FileCopyrightText: 2016 ownCloud, Inc. + * SPDX-License-Identifier: AGPL-3.0-only */ namespace OC; diff --git a/lib/private/ServerNotAvailableException.php b/lib/private/ServerNotAvailableException.php index 89a9300e50e..d24f16f30a0 100644 --- a/lib/private/ServerNotAvailableException.php +++ b/lib/private/ServerNotAvailableException.php @@ -1,23 +1,9 @@ <?php + /** - * @copyright Copyright (c) 2016, ownCloud, Inc. - * - * @author Morris Jobke <hey@morrisjobke.de> - * - * @license AGPL-3.0 - * - * This code is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License, version 3, - * as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License, version 3, - * along with this program. If not, see <http://www.gnu.org/licenses/> - * + * SPDX-FileCopyrightText: 2016-2024 Nextcloud GmbH and Nextcloud contributors + * SPDX-FileCopyrightText: 2016 ownCloud, Inc. + * SPDX-License-Identifier: AGPL-3.0-only */ namespace OC; diff --git a/lib/private/ServiceUnavailableException.php b/lib/private/ServiceUnavailableException.php index 8c52dd9b74c..f0fe3b48dbe 100644 --- a/lib/private/ServiceUnavailableException.php +++ b/lib/private/ServiceUnavailableException.php @@ -1,24 +1,9 @@ <?php + /** - * @copyright Copyright (c) 2016, ownCloud, Inc. - * - * @author Morris Jobke <hey@morrisjobke.de> - * @author Robin Appelman <robin@icewind.nl> - * - * @license AGPL-3.0 - * - * This code is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License, version 3, - * as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License, version 3, - * along with this program. If not, see <http://www.gnu.org/licenses/> - * + * SPDX-FileCopyrightText: 2016-2024 Nextcloud GmbH and Nextcloud contributors + * SPDX-FileCopyrightText: 2016 ownCloud, Inc. + * SPDX-License-Identifier: AGPL-3.0-only */ namespace OC; diff --git a/lib/private/Session/CryptoSessionData.php b/lib/private/Session/CryptoSessionData.php index 1eb6987fc18..323253af534 100644 --- a/lib/private/Session/CryptoSessionData.php +++ b/lib/private/Session/CryptoSessionData.php @@ -1,42 +1,24 @@ <?php declare(strict_types=1); - /** - * @copyright Copyright (c) 2016, ownCloud, Inc. - * - * @author Christoph Wurst <christoph@winzerhof-wurst.at> - * @author Joas Schilling <coding@schilljs.com> - * @author Lukas Reschke <lukas@statuscode.ch> - * @author Morris Jobke <hey@morrisjobke.de> - * @author Roeland Jago Douma <roeland@famdouma.nl> - * @author Victor Dubiniuk <dubiniuk@owncloud.com> - * - * @license AGPL-3.0 - * - * This code is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License, version 3, - * as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License, version 3, - * along with this program. If not, see <http://www.gnu.org/licenses/> - * + * SPDX-FileCopyrightText: 2016-2024 Nextcloud GmbH and Nextcloud contributors + * SPDX-FileCopyrightText: 2016 ownCloud, Inc. + * SPDX-License-Identifier: AGPL-3.0-only */ namespace OC\Session; use OCP\ISession; use OCP\Security\ICrypto; use OCP\Session\Exceptions\SessionNotAvailableException; +use function json_decode; +use function OCP\Log\logger; /** * Class CryptoSessionData * * @package OC\Session + * @template-implements \ArrayAccess<string,mixed> */ class CryptoSessionData implements \ArrayAccess, ISession { /** @var ISession */ @@ -57,8 +39,8 @@ class CryptoSessionData implements \ArrayAccess, ISession { * @param string $passphrase */ public function __construct(ISession $session, - ICrypto $crypto, - string $passphrase) { + ICrypto $crypto, + string $passphrase) { $this->crypto = $crypto; $this->session = $session; $this->passphrase = $passphrase; @@ -79,14 +61,24 @@ class CryptoSessionData implements \ArrayAccess, ISession { protected function initializeSession() { $encryptedSessionData = $this->session->get(self::encryptedSessionName) ?: ''; - try { - $this->sessionValues = json_decode( - $this->crypto->decrypt($encryptedSessionData, $this->passphrase), - true - ); - } catch (\Exception $e) { + if ($encryptedSessionData === '') { + // Nothing to decrypt $this->sessionValues = []; - $this->regenerateId(true, false); + } else { + try { + $this->sessionValues = json_decode( + $this->crypto->decrypt($encryptedSessionData, $this->passphrase), + true, + 512, + JSON_THROW_ON_ERROR, + ); + } catch (\Exception $e) { + logger('core')->critical('Could not decrypt or decode encrypted session data', [ + 'exception' => $e, + ]); + $this->sessionValues = []; + $this->regenerateId(true, false); + } } } diff --git a/lib/private/Session/CryptoWrapper.php b/lib/private/Session/CryptoWrapper.php index e98aac3b8bf..aceb387ea74 100644 --- a/lib/private/Session/CryptoWrapper.php +++ b/lib/private/Session/CryptoWrapper.php @@ -1,27 +1,9 @@ <?php + /** - * @copyright Copyright (c) 2016, ownCloud, Inc. - * - * @author Christoph Wurst <christoph@winzerhof-wurst.at> - * @author Joas Schilling <coding@schilljs.com> - * @author Lukas Reschke <lukas@statuscode.ch> - * @author Phil Davis <phil.davis@inf.org> - * @author Roeland Jago Douma <roeland@famdouma.nl> - * - * @license AGPL-3.0 - * - * This code is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License, version 3, - * as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License, version 3, - * along with this program. If not, see <http://www.gnu.org/licenses/> - * + * SPDX-FileCopyrightText: 2016-2024 Nextcloud GmbH and Nextcloud contributors + * SPDX-FileCopyrightText: 2016 ownCloud, Inc. + * SPDX-License-Identifier: AGPL-3.0-only */ namespace OC\Session; @@ -68,9 +50,9 @@ class CryptoWrapper { * @param IRequest $request */ public function __construct(IConfig $config, - ICrypto $crypto, - ISecureRandom $random, - IRequest $request) { + ICrypto $crypto, + ISecureRandom $random, + IRequest $request) { $this->crypto = $crypto; $this->config = $config; $this->random = $random; diff --git a/lib/private/Session/Internal.php b/lib/private/Session/Internal.php index e8e2a4f2d8e..4384b0ab5c0 100644 --- a/lib/private/Session/Internal.php +++ b/lib/private/Session/Internal.php @@ -3,38 +3,14 @@ declare(strict_types=1); /** - * @copyright Copyright (c) 2016, ownCloud, Inc. - * - * @author Arthur Schiwon <blizzz@arthur-schiwon.de> - * @author cetra3 <peter@parashift.com.au> - * @author Christoph Wurst <christoph@winzerhof-wurst.at> - * @author Lukas Reschke <lukas@statuscode.ch> - * @author MartB <mart.b@outlook.de> - * @author Morris Jobke <hey@morrisjobke.de> - * @author Robin Appelman <robin@icewind.nl> - * @author Roeland Jago Douma <roeland@famdouma.nl> - * @author Thomas Müller <thomas.mueller@tmit.eu> - * @author Victor Dubiniuk <dubiniuk@owncloud.com> - * - * @license AGPL-3.0 - * - * This code is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License, version 3, - * as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License, version 3, - * along with this program. If not, see <http://www.gnu.org/licenses/> - * + * SPDX-FileCopyrightText: 2016-2024 Nextcloud GmbH and Nextcloud contributors + * SPDX-FileCopyrightText: 2016 ownCloud, Inc. + * SPDX-License-Identifier: AGPL-3.0-only */ namespace OC\Session; -use OC\Authentication\Exceptions\InvalidTokenException; use OC\Authentication\Token\IProvider; +use OCP\Authentication\Exceptions\InvalidTokenException; use OCP\Session\Exceptions\SessionNotAvailableException; /** @@ -121,7 +97,7 @@ class Internal extends Session { * Wrapper around session_regenerate_id * * @param bool $deleteOldSession Whether to delete the old associated session file or not. - * @param bool $updateToken Wheater to update the associated auth token + * @param bool $updateToken Whether to update the associated auth token * @return void */ public function regenerateId(bool $deleteOldSession = true, bool $updateToken = false) { diff --git a/lib/private/Session/Memory.php b/lib/private/Session/Memory.php index fe71ec77692..eb10ebe05b4 100644 --- a/lib/private/Session/Memory.php +++ b/lib/private/Session/Memory.php @@ -1,32 +1,10 @@ <?php declare(strict_types=1); - /** - * @copyright Copyright (c) 2016, ownCloud, Inc. - * - * @author Christoph Wurst <christoph@winzerhof-wurst.at> - * @author Jörn Friedrich Dreyer <jfd@butonic.de> - * @author Lukas Reschke <lukas@statuscode.ch> - * @author Morris Jobke <hey@morrisjobke.de> - * @author Robin Appelman <robin@icewind.nl> - * @author Roeland Jago Douma <roeland@famdouma.nl> - * @author Thomas Müller <thomas.mueller@tmit.eu> - * - * @license AGPL-3.0 - * - * This code is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License, version 3, - * as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License, version 3, - * along with this program. If not, see <http://www.gnu.org/licenses/> - * + * SPDX-FileCopyrightText: 2016-2024 Nextcloud GmbH and Nextcloud contributors + * SPDX-FileCopyrightText: 2016 ownCloud, Inc. + * SPDX-License-Identifier: AGPL-3.0-only */ namespace OC\Session; diff --git a/lib/private/Session/Session.php b/lib/private/Session/Session.php index b434461a335..3006eceecba 100644 --- a/lib/private/Session/Session.php +++ b/lib/private/Session/Session.php @@ -1,34 +1,18 @@ <?php declare(strict_types=1); - /** - * @copyright Copyright (c) 2016, ownCloud, Inc. - * - * @author Morris Jobke <hey@morrisjobke.de> - * @author Robin Appelman <robin@icewind.nl> - * @author Roeland Jago Douma <roeland@famdouma.nl> - * @author Thomas Müller <thomas.mueller@tmit.eu> - * - * @license AGPL-3.0 - * - * This code is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License, version 3, - * as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License, version 3, - * along with this program. If not, see <http://www.gnu.org/licenses/> - * + * SPDX-FileCopyrightText: 2016-2024 Nextcloud GmbH and Nextcloud contributors + * SPDX-FileCopyrightText: 2016 ownCloud, Inc. + * SPDX-License-Identifier: AGPL-3.0-only */ namespace OC\Session; use OCP\ISession; +/** + * @template-implements \ArrayAccess<string,mixed> + */ abstract class Session implements \ArrayAccess, ISession { /** * @var bool diff --git a/lib/private/Settings/AuthorizedGroup.php b/lib/private/Settings/AuthorizedGroup.php index 21b897b6787..0171e020171 100644 --- a/lib/private/Settings/AuthorizedGroup.php +++ b/lib/private/Settings/AuthorizedGroup.php @@ -1,27 +1,9 @@ <?php /** - * @copyright Copyright (c) 2021 Carl Schwan <carl@carlschwan.eu> - * - * @author Carl Schwan <carl@carlschwan.eu> - * - * @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 <https://www.gnu.org/licenses/>. - * + * SPDX-FileCopyrightText: 2021 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later */ - namespace OC\Settings; use OCP\AppFramework\Db\Entity; diff --git a/lib/private/Settings/AuthorizedGroupMapper.php b/lib/private/Settings/AuthorizedGroupMapper.php index c7c39cc6758..b622459426b 100644 --- a/lib/private/Settings/AuthorizedGroupMapper.php +++ b/lib/private/Settings/AuthorizedGroupMapper.php @@ -1,26 +1,9 @@ <?php + /** - * @copyright Copyright (c) 2021 Carl Schwan <carl@carlschwan.eu> - * - * @author Carl Schwan <carl@carlschwan.eu> - * - * @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 <https://www.gnu.org/licenses/>. - * + * SPDX-FileCopyrightText: 2021 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later */ - namespace OC\Settings; use OCP\AppFramework\Db\Entity; diff --git a/lib/private/Settings/DeclarativeManager.php b/lib/private/Settings/DeclarativeManager.php new file mode 100644 index 00000000000..5c2b868f417 --- /dev/null +++ b/lib/private/Settings/DeclarativeManager.php @@ -0,0 +1,386 @@ +<?php + +declare(strict_types=1); +/** + * SPDX-FileCopyrightText: 2023 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later + */ +namespace OC\Settings; + +use Exception; +use OC\AppFramework\Bootstrap\Coordinator; +use OC\AppFramework\Middleware\Security\Exceptions\NotAdminException; +use OCP\EventDispatcher\IEventDispatcher; +use OCP\IAppConfig; +use OCP\IConfig; +use OCP\IGroupManager; +use OCP\IUser; +use OCP\Server; +use OCP\Settings\DeclarativeSettingsTypes; +use OCP\Settings\Events\DeclarativeSettingsGetValueEvent; +use OCP\Settings\Events\DeclarativeSettingsRegisterFormEvent; +use OCP\Settings\Events\DeclarativeSettingsSetValueEvent; +use OCP\Settings\IDeclarativeManager; +use OCP\Settings\IDeclarativeSettingsForm; +use Psr\Log\LoggerInterface; + +/** + * @psalm-import-type DeclarativeSettingsValueTypes from IDeclarativeSettingsForm + * @psalm-import-type DeclarativeSettingsStorageType from IDeclarativeSettingsForm + * @psalm-import-type DeclarativeSettingsSectionType from IDeclarativeSettingsForm + * @psalm-import-type DeclarativeSettingsFormSchemaWithValues from IDeclarativeSettingsForm + * @psalm-import-type DeclarativeSettingsFormSchemaWithoutValues from IDeclarativeSettingsForm + */ +class DeclarativeManager implements IDeclarativeManager { + public function __construct( + private IEventDispatcher $eventDispatcher, + private IGroupManager $groupManager, + private Coordinator $coordinator, + private IConfig $config, + private IAppConfig $appConfig, + private LoggerInterface $logger, + ) { + } + + /** + * @var array<string, list<DeclarativeSettingsFormSchemaWithoutValues>> + */ + private array $appSchemas = []; + + /** + * @inheritdoc + */ + public function registerSchema(string $app, array $schema): void { + $this->appSchemas[$app] ??= []; + + if (!$this->validateSchema($app, $schema)) { + throw new Exception('Invalid schema. Please check the logs for more details.'); + } + + foreach ($this->appSchemas[$app] as $otherSchema) { + if ($otherSchema['id'] === $schema['id']) { + throw new Exception('Duplicate form IDs detected: ' . $schema['id']); + } + } + + $fieldIDs = array_map(fn ($field) => $field['id'], $schema['fields']); + $otherFieldIDs = array_merge(...array_map(fn ($schema) => array_map(fn ($field) => $field['id'], $schema['fields']), $this->appSchemas[$app])); + $intersectionFieldIDs = array_intersect($fieldIDs, $otherFieldIDs); + if (count($intersectionFieldIDs) > 0) { + throw new Exception('Non unique field IDs detected: ' . join(', ', $intersectionFieldIDs)); + } + + $this->appSchemas[$app][] = $schema; + } + + /** + * @inheritdoc + */ + public function loadSchemas(): void { + $declarativeSettings = $this->coordinator->getRegistrationContext()->getDeclarativeSettings(); + foreach ($declarativeSettings as $declarativeSetting) { + /** @var IDeclarativeSettingsForm $declarativeSettingObject */ + $declarativeSettingObject = Server::get($declarativeSetting->getService()); + $this->registerSchema($declarativeSetting->getAppId(), $declarativeSettingObject->getSchema()); + } + + $this->eventDispatcher->dispatchTyped(new DeclarativeSettingsRegisterFormEvent($this)); + } + + /** + * @inheritdoc + */ + public function getFormIDs(IUser $user, string $type, string $section): array { + $isAdmin = $this->groupManager->isAdmin($user->getUID()); + /** @var array<string, list<string>> $formIds */ + $formIds = []; + + foreach ($this->appSchemas as $app => $schemas) { + $ids = []; + usort($schemas, [$this, 'sortSchemasByPriorityCallback']); + foreach ($schemas as $schema) { + if ($schema['section_type'] === DeclarativeSettingsTypes::SECTION_TYPE_ADMIN && !$isAdmin) { + continue; + } + if ($schema['section_type'] === $type && $schema['section_id'] === $section) { + $ids[] = $schema['id']; + } + } + + if (!empty($ids)) { + $formIds[$app] = array_merge($formIds[$app] ?? [], $ids); + } + } + + return $formIds; + } + + /** + * @inheritdoc + * @throws Exception + */ + public function getFormsWithValues(IUser $user, ?string $type, ?string $section): array { + $isAdmin = $this->groupManager->isAdmin($user->getUID()); + $forms = []; + + foreach ($this->appSchemas as $app => $schemas) { + foreach ($schemas as $schema) { + if ($type !== null && $schema['section_type'] !== $type) { + continue; + } + if ($section !== null && $schema['section_id'] !== $section) { + continue; + } + // If listing all fields skip the admin fields which a non-admin user has no access to + if ($type === null && $schema['section_type'] === 'admin' && !$isAdmin) { + continue; + } + + $s = $schema; + $s['app'] = $app; + + foreach ($s['fields'] as &$field) { + $field['value'] = $this->getValue($user, $app, $schema['id'], $field['id']); + } + unset($field); + + /** @var DeclarativeSettingsFormSchemaWithValues $s */ + $forms[] = $s; + } + } + + usort($forms, [$this, 'sortSchemasByPriorityCallback']); + + return $forms; + } + + private function sortSchemasByPriorityCallback(mixed $a, mixed $b): int { + if ($a['priority'] === $b['priority']) { + return 0; + } + return $a['priority'] > $b['priority'] ? -1 : 1; + } + + /** + * @return DeclarativeSettingsStorageType + */ + private function getStorageType(string $app, string $fieldId): string { + if (array_key_exists($app, $this->appSchemas)) { + foreach ($this->appSchemas[$app] as $schema) { + foreach ($schema['fields'] as $field) { + if ($field['id'] == $fieldId) { + if (array_key_exists('storage_type', $field)) { + return $field['storage_type']; + } + } + } + + if (array_key_exists('storage_type', $schema)) { + return $schema['storage_type']; + } + } + } + + return DeclarativeSettingsTypes::STORAGE_TYPE_INTERNAL; + } + + /** + * @return DeclarativeSettingsSectionType + * @throws Exception + */ + private function getSectionType(string $app, string $fieldId): string { + if (array_key_exists($app, $this->appSchemas)) { + foreach ($this->appSchemas[$app] as $schema) { + foreach ($schema['fields'] as $field) { + if ($field['id'] == $fieldId) { + return $schema['section_type']; + } + } + } + } + + throw new Exception('Unknown fieldId "' . $fieldId . '"'); + } + + /** + * @psalm-param DeclarativeSettingsSectionType $sectionType + * @throws NotAdminException + */ + private function assertAuthorized(IUser $user, string $sectionType): void { + if ($sectionType === 'admin' && !$this->groupManager->isAdmin($user->getUID())) { + throw new NotAdminException('Logged in user does not have permission to access these settings.'); + } + } + + /** + * @return DeclarativeSettingsValueTypes + * @throws Exception + * @throws NotAdminException + */ + private function getValue(IUser $user, string $app, string $formId, string $fieldId): mixed { + $sectionType = $this->getSectionType($app, $fieldId); + $this->assertAuthorized($user, $sectionType); + + $storageType = $this->getStorageType($app, $fieldId); + switch ($storageType) { + case DeclarativeSettingsTypes::STORAGE_TYPE_EXTERNAL: + $event = new DeclarativeSettingsGetValueEvent($user, $app, $formId, $fieldId); + $this->eventDispatcher->dispatchTyped($event); + return $event->getValue(); + case DeclarativeSettingsTypes::STORAGE_TYPE_INTERNAL: + return $this->getInternalValue($user, $app, $formId, $fieldId); + default: + throw new Exception('Unknown storage type "' . $storageType . '"'); + } + } + + /** + * @inheritdoc + */ + public function setValue(IUser $user, string $app, string $formId, string $fieldId, mixed $value): void { + $sectionType = $this->getSectionType($app, $fieldId); + $this->assertAuthorized($user, $sectionType); + + $storageType = $this->getStorageType($app, $fieldId); + switch ($storageType) { + case DeclarativeSettingsTypes::STORAGE_TYPE_EXTERNAL: + $this->eventDispatcher->dispatchTyped(new DeclarativeSettingsSetValueEvent($user, $app, $formId, $fieldId, $value)); + break; + case DeclarativeSettingsTypes::STORAGE_TYPE_INTERNAL: + $this->saveInternalValue($user, $app, $fieldId, $value); + break; + default: + throw new Exception('Unknown storage type "' . $storageType . '"'); + } + } + + private function getInternalValue(IUser $user, string $app, string $formId, string $fieldId): mixed { + $sectionType = $this->getSectionType($app, $fieldId); + $defaultValue = $this->getDefaultValue($app, $formId, $fieldId); + switch ($sectionType) { + case DeclarativeSettingsTypes::SECTION_TYPE_ADMIN: + return $this->config->getAppValue($app, $fieldId, $defaultValue); + case DeclarativeSettingsTypes::SECTION_TYPE_PERSONAL: + return $this->config->getUserValue($user->getUID(), $app, $fieldId, $defaultValue); + default: + throw new Exception('Unknown section type "' . $sectionType . '"'); + } + } + + private function saveInternalValue(IUser $user, string $app, string $fieldId, mixed $value): void { + $sectionType = $this->getSectionType($app, $fieldId); + switch ($sectionType) { + case DeclarativeSettingsTypes::SECTION_TYPE_ADMIN: + $this->appConfig->setValueString($app, $fieldId, $value); + break; + case DeclarativeSettingsTypes::SECTION_TYPE_PERSONAL: + $this->config->setUserValue($user->getUID(), $app, $fieldId, $value); + break; + default: + throw new Exception('Unknown section type "' . $sectionType . '"'); + } + } + + private function getDefaultValue(string $app, string $formId, string $fieldId): mixed { + foreach ($this->appSchemas[$app] as $schema) { + if ($schema['id'] === $formId) { + foreach ($schema['fields'] as $field) { + if ($field['id'] === $fieldId) { + if (isset($field['default'])) { + if (is_array($field['default'])) { + return json_encode($field['default']); + } + return $field['default']; + } + } + } + } + } + return null; + } + + private function validateSchema(string $appId, array $schema): bool { + if (!isset($schema['id'])) { + $this->logger->warning('Attempt to register a declarative settings schema with no id', ['app' => $appId]); + return false; + } + $formId = $schema['id']; + if (!isset($schema['section_type'])) { + $this->logger->warning('Declarative settings: missing section_type', ['app' => $appId, 'form_id' => $formId]); + return false; + } + if (!in_array($schema['section_type'], [DeclarativeSettingsTypes::SECTION_TYPE_ADMIN, DeclarativeSettingsTypes::SECTION_TYPE_PERSONAL])) { + $this->logger->warning('Declarative settings: invalid section_type', ['app' => $appId, 'form_id' => $formId, 'section_type' => $schema['section_type']]); + return false; + } + if (!isset($schema['section_id'])) { + $this->logger->warning('Declarative settings: missing section_id', ['app' => $appId, 'form_id' => $formId]); + return false; + } + if (!isset($schema['storage_type'])) { + $this->logger->warning('Declarative settings: missing storage_type', ['app' => $appId, 'form_id' => $formId]); + return false; + } + if (!in_array($schema['storage_type'], [DeclarativeSettingsTypes::STORAGE_TYPE_EXTERNAL, DeclarativeSettingsTypes::STORAGE_TYPE_INTERNAL])) { + $this->logger->warning('Declarative settings: invalid storage_type', ['app' => $appId, 'form_id' => $formId, 'storage_type' => $schema['storage_type']]); + return false; + } + if (!isset($schema['title'])) { + $this->logger->warning('Declarative settings: missing title', ['app' => $appId, 'form_id' => $formId]); + return false; + } + if (!isset($schema['fields']) || !is_array($schema['fields'])) { + $this->logger->warning('Declarative settings: missing or invalid fields', ['app' => $appId, 'form_id' => $formId]); + return false; + } + foreach ($schema['fields'] as $field) { + if (!isset($field['id'])) { + $this->logger->warning('Declarative settings: missing field id', ['app' => $appId, 'form_id' => $formId, 'field' => $field]); + return false; + } + $fieldId = $field['id']; + if (!isset($field['title'])) { + $this->logger->warning('Declarative settings: missing field title', ['app' => $appId, 'form_id' => $formId, 'field_id' => $fieldId]); + return false; + } + if (!isset($field['type'])) { + $this->logger->warning('Declarative settings: missing field type', ['app' => $appId, 'form_id' => $formId, 'field_id' => $fieldId]); + return false; + } + if (!in_array($field['type'], [ + DeclarativeSettingsTypes::MULTI_SELECT, DeclarativeSettingsTypes::MULTI_CHECKBOX, DeclarativeSettingsTypes::RADIO, + DeclarativeSettingsTypes::SELECT, DeclarativeSettingsTypes::CHECKBOX, + DeclarativeSettingsTypes::URL, DeclarativeSettingsTypes::EMAIL, DeclarativeSettingsTypes::NUMBER, + DeclarativeSettingsTypes::TEL, DeclarativeSettingsTypes::TEXT, DeclarativeSettingsTypes::PASSWORD, + ])) { + $this->logger->warning('Declarative settings: invalid field type', [ + 'app' => $appId, 'form_id' => $formId, 'field_id' => $fieldId, 'type' => $field['type'], + ]); + return false; + } + if (!$this->validateField($appId, $formId, $field)) { + return false; + } + } + + return true; + } + + private function validateField(string $appId, string $formId, array $field): bool { + $fieldId = $field['id']; + if (in_array($field['type'], [ + DeclarativeSettingsTypes::MULTI_SELECT, DeclarativeSettingsTypes::MULTI_CHECKBOX, DeclarativeSettingsTypes::RADIO, + DeclarativeSettingsTypes::SELECT + ])) { + if (!isset($field['options'])) { + $this->logger->warning('Declarative settings: missing field options', ['app' => $appId, 'form_id' => $formId, 'field_id' => $fieldId]); + return false; + } + if (!is_array($field['options'])) { + $this->logger->warning('Declarative settings: field options should be an array', ['app' => $appId, 'form_id' => $formId, 'field_id' => $fieldId]); + return false; + } + } + return true; + } +} diff --git a/lib/private/Settings/Manager.php b/lib/private/Settings/Manager.php index 2d44ac7d3df..c96b73474fa 100644 --- a/lib/private/Settings/Manager.php +++ b/lib/private/Settings/Manager.php @@ -1,33 +1,7 @@ <?php /** - * @copyright Copyright (c) 2016 Arthur Schiwon <blizzz@arthur-schiwon.de> - * - * @author Arthur Schiwon <blizzz@arthur-schiwon.de> - * @author Christoph Wurst <christoph@winzerhof-wurst.at> - * @author Joas Schilling <coding@schilljs.com> - * @author Julius Härtl <jus@bitgrid.net> - * @author Lukas Reschke <lukas@statuscode.ch> - * @author Morris Jobke <hey@morrisjobke.de> - * @author Robin Appelman <robin@icewind.nl> - * @author Roeland Jago Douma <roeland@famdouma.nl> - * @author sualko <klaus@jsxc.org> - * @author Carl Schwan <carl@carlschwan.eu> - * - * @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/>. - * + * SPDX-FileCopyrightText: 2016 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later */ namespace OC\Settings; @@ -90,17 +64,14 @@ class Manager implements IManager { $this->subAdmin = $subAdmin; } - /** @var array */ + /** @var array<self::SETTINGS_*, list<class-string<IIconSection>>> */ protected $sectionClasses = []; - /** @var array */ + /** @var array<self::SETTINGS_*, array<string, IIconSection>> */ protected $sections = []; /** - * @param string $type 'admin' or 'personal' - * @param string $section Class must implement OCP\Settings\IIconSection - * - * @return void + * @inheritdoc */ public function registerSection(string $type, string $section) { if (!isset($this->sectionClasses[$type])) { @@ -111,7 +82,7 @@ class Manager implements IManager { } /** - * @param string $type 'admin' or 'personal' + * @psalm-param self::SETTINGS_* $type * * @return IIconSection[] */ @@ -149,6 +120,9 @@ class Manager implements IManager { return $this->sections[$type]; } + /** + * @inheritdoc + */ public function getSection(string $type, string $sectionId): ?IIconSection { if (isset($this->sections[$type]) && isset($this->sections[$type][$sectionId])) { return $this->sections[$type][$sectionId]; @@ -163,31 +137,27 @@ class Manager implements IManager { ], true); } - /** @var array */ + /** @var array<class-string<ISettings>, self::SETTINGS_*> */ protected $settingClasses = []; - /** @var array */ + /** @var array<self::SETTINGS_*, array<string, list<ISettings>>> */ protected $settings = []; /** - * @psam-param 'admin'|'personal' $type The type of the setting. - * @param string $setting Class must implement OCP\Settings\ISettings - * @param bool $allowedDelegation - * - * @return void + * @inheritdoc */ public function registerSetting(string $type, string $setting) { $this->settingClasses[$setting] = $type; } /** - * @param string $type 'admin' or 'personal' + * @psalm-param self::SETTINGS_* $type The type of the setting. * @param string $section - * @param Closure $filter optional filter to apply on all loaded ISettings + * @param ?Closure $filter optional filter to apply on all loaded ISettings * * @return ISettings[] */ - protected function getSettings(string $type, string $section, Closure $filter = null): array { + protected function getSettings(string $type, string $section, ?Closure $filter = null): array { if (!isset($this->settings[$type])) { $this->settings[$type] = []; } @@ -258,7 +228,7 @@ class Manager implements IManager { /** * @inheritdoc */ - public function getAdminSettings($section, bool $subAdminOnly = false): array { + public function getAdminSettings(string $section, bool $subAdminOnly = false): array { if ($subAdminOnly) { $subAdminSettingsFilter = function (ISettings $settings) { return $settings instanceof ISubAdminSettings; @@ -329,7 +299,7 @@ class Manager implements IManager { /** * @inheritdoc */ - public function getPersonalSettings($section): array { + public function getPersonalSettings(string $section): array { $settings = []; $appSettings = $this->getSettings('personal', $section); @@ -344,6 +314,9 @@ class Manager implements IManager { return $settings; } + /** + * @inheritdoc + */ public function getAllowedAdminSettings(string $section, IUser $user): array { $isAdmin = $this->groupManager->isAdmin($user->getUID()); if ($isAdmin) { @@ -375,6 +348,9 @@ class Manager implements IManager { return $settings; } + /** + * @inheritdoc + */ public function getAllAllowedAdminSettings(IUser $user): array { $this->getSettings('admin', ''); // Make sure all the settings are loaded $settings = []; diff --git a/lib/private/Settings/Section.php b/lib/private/Settings/Section.php index ba94a38e874..4f8234254b1 100644 --- a/lib/private/Settings/Section.php +++ b/lib/private/Settings/Section.php @@ -1,26 +1,7 @@ <?php /** - * @copyright Copyright (c) 2016 Arthur Schiwon <blizzz@arthur-schiwon.de> - * - * @author Arthur Schiwon <blizzz@arthur-schiwon.de> - * @author Joas Schilling <coding@schilljs.com> - * @author Lukas Reschke <lukas@statuscode.ch> - * - * @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/>. - * + * SPDX-FileCopyrightText: 2016 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later */ namespace OC\Settings; diff --git a/lib/private/Setup.php b/lib/private/Setup.php index 0993fe54f47..a5683655447 100644 --- a/lib/private/Setup.php +++ b/lib/private/Setup.php @@ -1,50 +1,11 @@ <?php + +declare(strict_types=1); + /** - * @copyright Copyright (c) 2016, ownCloud, Inc. - * - * @author Administrator "Administrator@WINDOWS-2012" - * @author Arthur Schiwon <blizzz@arthur-schiwon.de> - * @author Bart Visscher <bartv@thisnet.nl> - * @author Bernhard Posselt <dev@bernhard-posselt.com> - * @author Bjoern Schiessle <bjoern@schiessle.org> - * @author Brice Maron <brice@bmaron.net> - * @author Christoph Wurst <christoph@winzerhof-wurst.at> - * @author Dan Callahan <dan.callahan@gmail.com> - * @author Daniel Kesselberg <mail@danielkesselberg.de> - * @author François Kubler <francois@kubler.org> - * @author Frank Isemann <frank@isemann.name> - * @author Jakob Sack <mail@jakobsack.de> - * @author Joas Schilling <coding@schilljs.com> - * @author Julius Härtl <jus@bitgrid.net> - * @author KB7777 <k.burkowski@gmail.com> - * @author Kevin Lanni <therealklanni@gmail.com> - * @author Lukas Reschke <lukas@statuscode.ch> - * @author MichaIng <28480705+MichaIng@users.noreply.github.com> - * @author MichaIng <micha@dietpi.com> - * @author Morris Jobke <hey@morrisjobke.de> - * @author Robin Appelman <robin@icewind.nl> - * @author Roeland Jago Douma <roeland@famdouma.nl> - * @author Sean Comeau <sean@ftlnetworks.ca> - * @author Serge Martin <edb@sigluy.net> - * @author Simounet <contact@simounet.net> - * @author Thomas Müller <thomas.mueller@tmit.eu> - * @author Valdnet <47037905+Valdnet@users.noreply.github.com> - * @author Vincent Petry <vincent@nextcloud.com> - * - * @license AGPL-3.0 - * - * This code is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License, version 3, - * as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License, version 3, - * along with this program. If not, see <http://www.gnu.org/licenses/> - * + * SPDX-FileCopyrightText: 2016-2024 Nextcloud GmbH and Nextcloud contributors + * SPDX-FileCopyrightText: 2016 ownCloud, Inc. + * SPDX-License-Identifier: AGPL-3.0-only */ namespace OC; @@ -53,51 +14,42 @@ use Exception; use InvalidArgumentException; use OC\Authentication\Token\PublicKeyTokenProvider; use OC\Authentication\Token\TokenCleanupJob; -use OC\TextProcessing\RemoveOldTasksBackgroundJob; use OC\Log\Rotate; use OC\Preview\BackgroundCleanupJob; +use OC\TextProcessing\RemoveOldTasksBackgroundJob; use OCP\AppFramework\Utility\ITimeFactory; +use OCP\BackgroundJob\IJobList; use OCP\Defaults; +use OCP\IAppConfig; +use OCP\IConfig; use OCP\IGroup; +use OCP\IGroupManager; use OCP\IL10N; +use OCP\IRequest; +use OCP\IUserManager; +use OCP\IUserSession; +use OCP\L10N\IFactory as IL10NFactory; +use OCP\Migration\IOutput; use OCP\Security\ISecureRandom; +use OCP\Server; use Psr\Log\LoggerInterface; class Setup { - /** @var SystemConfig */ - protected $config; - /** @var IniGetWrapper */ - protected $iniWrapper; - /** @var IL10N */ - protected $l10n; - /** @var Defaults */ - protected $defaults; - /** @var LoggerInterface */ - protected $logger; - /** @var ISecureRandom */ - protected $random; - /** @var Installer */ - protected $installer; + protected IL10N $l10n; public function __construct( - SystemConfig $config, - IniGetWrapper $iniWrapper, - IL10N $l10n, - Defaults $defaults, - LoggerInterface $logger, - ISecureRandom $random, - Installer $installer + protected SystemConfig $config, + protected IniGetWrapper $iniWrapper, + IL10NFactory $l10nFactory, + protected Defaults $defaults, + protected LoggerInterface $logger, + protected ISecureRandom $random, + protected Installer $installer ) { - $this->config = $config; - $this->iniWrapper = $iniWrapper; - $this->l10n = $l10n; - $this->defaults = $defaults; - $this->logger = $logger; - $this->random = $random; - $this->installer = $installer; + $this->l10n = $l10nFactory->get('lib'); } - protected static $dbSetupClasses = [ + protected static array $dbSetupClasses = [ 'mysql' => \OC\Setup\MySQL::class, 'pgsql' => \OC\Setup\PostgreSQL::class, 'oci' => \OC\Setup\OCI::class, @@ -107,30 +59,22 @@ class Setup { /** * Wrapper around the "class_exists" PHP function to be able to mock it - * - * @param string $name - * @return bool */ - protected function class_exists($name) { + protected function class_exists(string $name): bool { return class_exists($name); } /** * Wrapper around the "is_callable" PHP function to be able to mock it - * - * @param string $name - * @return bool */ - protected function is_callable($name) { + protected function is_callable(string $name): bool { return is_callable($name); } /** * Wrapper around \PDO::getAvailableDrivers - * - * @return array */ - protected function getAvailableDbDriversForPdo() { + protected function getAvailableDbDriversForPdo(): array { if (class_exists(\PDO::class)) { return \PDO::getAvailableDrivers(); } @@ -140,11 +84,10 @@ class Setup { /** * Get the available and supported databases of this instance * - * @param bool $allowAllDatabases * @return array * @throws Exception */ - public function getSupportedDatabases($allowAllDatabases = false) { + public function getSupportedDatabases(bool $allowAllDatabases = false): array { $availableDatabases = [ 'sqlite' => [ 'type' => 'pdo', @@ -206,7 +149,7 @@ class Setup { * @return array of system info, including an "errors" value * in case of errors/warnings */ - public function getSystemInfo($allowAllDatabases = false) { + public function getSystemInfo(bool $allowAllDatabases = false): array { $databases = $this->getSupportedDatabases($allowAllDatabases); $dataDir = $this->config->getValue('datadirectory', \OC::$SERVERROOT . '/data'); @@ -226,7 +169,7 @@ class Setup { try { $util = new \OC_Util(); - $htAccessWorking = $util->isHtaccessWorking(\OC::$server->getConfig()); + $htAccessWorking = $util->isHtaccessWorking(Server::get(IConfig::class)); } catch (\OCP\HintException $e) { $errors[] = [ 'error' => $e->getMessage(), @@ -272,17 +215,16 @@ class Setup { } /** - * @param $options - * @return array + * @return array<string|array> errors */ - public function install($options) { + public function install(array $options, ?IOutput $output = null): array { $l = $this->l10n; $error = []; $dbType = $options['dbtype']; if (empty($options['adminlogin'])) { - $error[] = $l->t('Set an admin username.'); + $error[] = $l->t('Set an admin Login.'); } if (empty($options['adminpass'])) { $error[] = $l->t('Set an admin password.'); @@ -313,7 +255,7 @@ class Setup { return $error; } - $request = \OC::$server->getRequest(); + $request = Server::get(IRequest::class); //no errors, good if (isset($options['trusted_domains']) @@ -328,7 +270,7 @@ class Setup { $dbType = 'sqlite3'; } - //generate a random salt that is used to salt the local user passwords + //generate a random salt that is used to salt the local passwords $salt = $this->random->generate(30); // generate a secret $secret = $this->random->generate(48); @@ -349,6 +291,7 @@ class Setup { $this->config->setValues($newConfigValues); + $this->outputDebug($output, 'Configuring database'); $dbSetup->initialize($options); try { $dbSetup->setupDatabase($username); @@ -361,15 +304,17 @@ class Setup { return $error; } catch (Exception $e) { $error[] = [ - 'error' => 'Error while trying to create admin user: ' . $e->getMessage(), + 'error' => 'Error while trying to create admin account: ' . $e->getMessage(), 'exception' => $e, 'hint' => '', ]; return $error; } + + $this->outputDebug($output, 'Run server migrations'); try { // apply necessary migrations - $dbSetup->runMigrations(); + $dbSetup->runMigrations($output); } catch (Exception $e) { $error[] = [ 'error' => 'Error while trying to initialise the database: ' . $e->getMessage(), @@ -379,78 +324,89 @@ class Setup { return $error; } - //create the user and group + $this->outputDebug($output, 'Create admin account'); + + // create the admin account and group $user = null; try { - $user = \OC::$server->getUserManager()->createUser($username, $password); + $user = Server::get(IUserManager::class)->createUser($username, $password); if (!$user) { - $error[] = "User <$username> could not be created."; + $error[] = "Account <$username> could not be created."; + return $error; } } catch (Exception $exception) { $error[] = $exception->getMessage(); + return $error; } - if (empty($error)) { - $config = \OC::$server->getConfig(); - $config->setAppValue('core', 'installedat', (string)microtime(true)); - $config->setAppValue('core', 'lastupdatedat', (string)microtime(true)); + $config = Server::get(IConfig::class); + $config->setAppValue('core', 'installedat', (string)microtime(true)); + $appConfig = Server::get(IAppConfig::class); + $appConfig->setValueInt('core', 'lastupdatedat', time()); - $vendorData = $this->getVendorData(); - $config->setAppValue('core', 'vendor', $vendorData['vendor']); - if ($vendorData['channel'] !== 'stable') { - $config->setSystemValue('updater.release.channel', $vendorData['channel']); - } + $vendorData = $this->getVendorData(); + $config->setAppValue('core', 'vendor', $vendorData['vendor']); + if ($vendorData['channel'] !== 'stable') { + $config->setSystemValue('updater.release.channel', $vendorData['channel']); + } - $group = \OC::$server->getGroupManager()->createGroup('admin'); - if ($group instanceof IGroup) { - $group->addUser($user); - } + $group = Server::get(IGroupManager::class)->createGroup('admin'); + if ($group instanceof IGroup) { + $group->addUser($user); + } - // Install shipped apps and specified app bundles - Installer::installShippedApps(); + // Install shipped apps and specified app bundles + $this->outputDebug($output, 'Install default apps'); + Installer::installShippedApps(false, $output); - // create empty file in data dir, so we can later find - // out that this is indeed an ownCloud data directory - file_put_contents($config->getSystemValueString('datadirectory', \OC::$SERVERROOT . '/data') . '/.ocdata', ''); + // create empty file in data dir, so we can later find + // out that this is indeed an ownCloud data directory + $this->outputDebug($output, 'Setup data directory'); + file_put_contents($config->getSystemValueString('datadirectory', \OC::$SERVERROOT . '/data') . '/.ocdata', ''); - // Update .htaccess files - self::updateHtaccess(); - self::protectDataDirectory(); + // Update .htaccess files + self::updateHtaccess(); + self::protectDataDirectory(); - self::installBackgroundJobs(); + $this->outputDebug($output, 'Install background jobs'); + self::installBackgroundJobs(); - //and we are done - $config->setSystemValue('installed', true); - if (self::shouldRemoveCanInstallFile()) { - unlink(\OC::$configDir.'/CAN_INSTALL'); - } - - $bootstrapCoordinator = \OCP\Server::get(\OC\AppFramework\Bootstrap\Coordinator::class); - $bootstrapCoordinator->runInitialRegistration(); + //and we are done + $config->setSystemValue('installed', true); + if (self::shouldRemoveCanInstallFile()) { + unlink(\OC::$configDir.'/CAN_INSTALL'); + } - // Create a session token for the newly created user - // The token provider requires a working db, so it's not injected on setup - /* @var $userSession User\Session */ - $userSession = \OC::$server->getUserSession(); - $provider = \OCP\Server::get(PublicKeyTokenProvider::class); - $userSession->setTokenProvider($provider); - $userSession->login($username, $password); - $userSession->createSessionToken($request, $userSession->getUser()->getUID(), $username, $password); + $bootstrapCoordinator = \OCP\Server::get(\OC\AppFramework\Bootstrap\Coordinator::class); + $bootstrapCoordinator->runInitialRegistration(); + + // Create a session token for the newly created user + // The token provider requires a working db, so it's not injected on setup + /** @var \OC\User\Session $userSession */ + $userSession = Server::get(IUserSession::class); + $provider = Server::get(PublicKeyTokenProvider::class); + $userSession->setTokenProvider($provider); + $userSession->login($username, $password); + $user = $userSession->getUser(); + if (!$user) { + $error[] = "No account found in session."; + return $error; + } + $userSession->createSessionToken($request, $user->getUID(), $username, $password); - $session = $userSession->getSession(); - $session->set('last-password-confirm', \OCP\Server::get(ITimeFactory::class)->getTime()); + $session = $userSession->getSession(); + $session->set('last-password-confirm', Server::get(ITimeFactory::class)->getTime()); - // Set email for admin - if (!empty($options['adminemail'])) { - $user->setSystemEMailAddress($options['adminemail']); - } + // Set email for admin + if (!empty($options['adminemail'])) { + $user->setSystemEMailAddress($options['adminemail']); } return $error; } - public static function installBackgroundJobs() { - $jobList = \OC::$server->getJobList(); + public static function installBackgroundJobs(): void { + $jobList = Server::get(IJobList::class); $jobList->add(TokenCleanupJob::class); $jobList->add(Rotate::class); $jobList->add(BackgroundCleanupJob::class); @@ -460,15 +416,13 @@ class Setup { /** * @return string Absolute path to htaccess */ - private function pathToHtaccess() { + private function pathToHtaccess(): string { return \OC::$SERVERROOT . '/.htaccess'; } /** * Find webroot from config * - * @param SystemConfig $config - * @return string * @throws InvalidArgumentException when invalid value for overwrite.cli.url */ private static function findWebRoot(SystemConfig $config): string { @@ -495,8 +449,8 @@ class Setup { * @return bool True when success, False otherwise * @throws \OCP\AppFramework\QueryException */ - public static function updateHtaccess() { - $config = \OC::$server->getSystemConfig(); + public static function updateHtaccess(): bool { + $config = Server::get(SystemConfig::class); try { $webRoot = self::findWebRoot($config); @@ -504,15 +458,11 @@ class Setup { return false; } - $setupHelper = new \OC\Setup( - $config, - \OC::$server->get(IniGetWrapper::class), - \OC::$server->getL10N('lib'), - \OCP\Server::get(Defaults::class), - \OC::$server->get(LoggerInterface::class), - \OC::$server->getSecureRandom(), - \OCP\Server::get(Installer::class) - ); + $setupHelper = Server::get(\OC\Setup::class); + + if (!is_writable($setupHelper->pathToHtaccess())) { + return false; + } $htaccessContent = file_get_contents($setupHelper->pathToHtaccess()); $content = "#### DO NOT CHANGE ANYTHING ABOVE THIS LINE ####\n"; @@ -531,13 +481,13 @@ class Setup { $content .= "\n Options -MultiViews"; $content .= "\n RewriteRule ^core/js/oc.js$ index.php [PT,E=PATH_INFO:$1]"; $content .= "\n RewriteRule ^core/preview.png$ index.php [PT,E=PATH_INFO:$1]"; - $content .= "\n RewriteCond %{REQUEST_FILENAME} !\\.(css|js|mjs|svg|gif|png|html|ttf|woff2?|ico|jpg|jpeg|map|webm|mp4|mp3|ogg|wav|wasm|tflite)$"; + $content .= "\n RewriteCond %{REQUEST_FILENAME} !\\.(css|js|mjs|svg|gif|png|html|ttf|woff2?|ico|jpg|jpeg|map|webm|mp4|mp3|ogg|wav|flac|wasm|tflite)$"; $content .= "\n RewriteCond %{REQUEST_FILENAME} !/core/ajax/update\\.php"; $content .= "\n RewriteCond %{REQUEST_FILENAME} !/core/img/(favicon\\.ico|manifest\\.json)$"; $content .= "\n RewriteCond %{REQUEST_FILENAME} !/(cron|public|remote|status)\\.php"; $content .= "\n RewriteCond %{REQUEST_FILENAME} !/ocs/v(1|2)\\.php"; $content .= "\n RewriteCond %{REQUEST_FILENAME} !/robots\\.txt"; - $content .= "\n RewriteCond %{REQUEST_FILENAME} !/(ocm-provider|ocs-provider|updater)/"; + $content .= "\n RewriteCond %{REQUEST_FILENAME} !/(ocs-provider|updater)/"; $content .= "\n RewriteCond %{REQUEST_URI} !^/\\.well-known/(acme-challenge|pki-validation)/.*"; $content .= "\n RewriteCond %{REQUEST_FILENAME} !/richdocumentscode(_arm64)?/proxy.php$"; $content .= "\n RewriteRule . index.php [PT,E=PATH_INFO:$1]"; @@ -551,15 +501,19 @@ class Setup { $content .= "\n</IfModule>"; } - if ($content !== '') { - //suppress errors in case we don't have permissions for it - return (bool)@file_put_contents($setupHelper->pathToHtaccess(), $htaccessContent . $content . "\n"); + // Never write file back if disk space should be too low + if (function_exists('disk_free_space')) { + $df = disk_free_space(\OC::$SERVERROOT); + $size = strlen($content) + 10240; + if ($df !== false && $df < (float)$size) { + throw new \Exception(\OC::$SERVERROOT . " does not have enough space for writing the htaccess file! Not writing it back!"); + } } - - return false; + //suppress errors in case we don't have permissions for it + return (bool)@file_put_contents($setupHelper->pathToHtaccess(), $htaccessContent . $content . "\n"); } - public static function protectDataDirectory() { + public static function protectDataDirectory(): void { //Require all denied $now = date('Y-m-d H:i:s'); $content = "# Generated by Nextcloud on $now\n"; @@ -587,7 +541,7 @@ class Setup { $content .= " IndexIgnore *\n"; $content .= "</IfModule>"; - $baseDir = \OC::$server->getConfig()->getSystemValueString('datadirectory', \OC::$SERVERROOT . '/data'); + $baseDir = Server::get(IConfig::class)->getSystemValueString('datadirectory', \OC::$SERVERROOT . '/data'); file_put_contents($baseDir . '/.htaccess', $content); file_put_contents($baseDir . '/index.html', ''); } @@ -603,17 +557,17 @@ class Setup { ]; } - /** - * @return bool - */ - public function shouldRemoveCanInstallFile() { + public function shouldRemoveCanInstallFile(): bool { return \OC_Util::getChannel() !== 'git' && is_file(\OC::$configDir.'/CAN_INSTALL'); } - /** - * @return bool - */ - public function canInstallFileExists() { + public function canInstallFileExists(): bool { return is_file(\OC::$configDir.'/CAN_INSTALL'); } + + protected function outputDebug(?IOutput $output, string $message): void { + if ($output instanceof IOutput) { + $output->debug($message); + } + } } diff --git a/lib/private/Setup/AbstractDatabase.php b/lib/private/Setup/AbstractDatabase.php index 9ec4137cdef..e96ca8a2f3e 100644 --- a/lib/private/Setup/AbstractDatabase.php +++ b/lib/private/Setup/AbstractDatabase.php @@ -1,30 +1,9 @@ <?php + /** - * @copyright Copyright (c) 2016, ownCloud, Inc. - * - * @author Bart Visscher <bartv@thisnet.nl> - * @author Christoph Wurst <christoph@winzerhof-wurst.at> - * @author Joas Schilling <coding@schilljs.com> - * @author Morris Jobke <hey@morrisjobke.de> - * @author Robin Appelman <robin@icewind.nl> - * @author Roeland Jago Douma <roeland@famdouma.nl> - * @author Thomas Müller <thomas.mueller@tmit.eu> - * @author Thomas Pulzer <t.pulzer@kniel.de> - * - * @license AGPL-3.0 - * - * This code is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License, version 3, - * as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License, version 3, - * along with this program. If not, see <http://www.gnu.org/licenses/> - * + * SPDX-FileCopyrightText: 2016-2024 Nextcloud GmbH and Nextcloud contributors + * SPDX-FileCopyrightText: 2016 ownCloud, Inc. + * SPDX-License-Identifier: AGPL-3.0-only */ namespace OC\Setup; @@ -33,6 +12,7 @@ use OC\DB\ConnectionFactory; use OC\DB\MigrationService; use OC\SystemConfig; use OCP\IL10N; +use OCP\Migration\IOutput; use OCP\Security\ISecureRandom; use Psr\Log\LoggerInterface; @@ -70,9 +50,9 @@ abstract class AbstractDatabase { public function validate($config) { $errors = []; if (empty($config['dbuser']) && empty($config['dbname'])) { - $errors[] = $this->trans->t("Enter the database username and name for %s", [$this->dbprettyname]); + $errors[] = $this->trans->t("Enter the database Login and name for %s", [$this->dbprettyname]); } elseif (empty($config['dbuser'])) { - $errors[] = $this->trans->t("Enter the database username for %s", [$this->dbprettyname]); + $errors[] = $this->trans->t("Enter the database Login for %s", [$this->dbprettyname]); } elseif (empty($config['dbname'])) { $errors[] = $this->trans->t("Enter the database name for %s", [$this->dbprettyname]); } @@ -88,7 +68,7 @@ abstract class AbstractDatabase { $dbName = $config['dbname']; $dbHost = !empty($config['dbhost']) ? $config['dbhost'] : 'localhost'; $dbPort = !empty($config['dbport']) ? $config['dbport'] : ''; - $dbTablePrefix = isset($config['dbtableprefix']) ? $config['dbtableprefix'] : 'oc_'; + $dbTablePrefix = $config['dbtableprefix'] ?? 'oc_'; $createUserConfig = $this->config->getValue("setup_create_db_user", true); // accept `false` both as bool and string, since setting config values from env will result in a string @@ -139,10 +119,12 @@ abstract class AbstractDatabase { } $connectionParams['host'] = $host; } - $connectionParams = array_merge($connectionParams, $configOverwrite); + $connectionParams = array_merge($connectionParams, ['primary' => $connectionParams, 'replica' => [$connectionParams]]); $cf = new ConnectionFactory($this->config); - return $cf->getConnection($this->config->getValue('dbtype', 'sqlite'), $connectionParams); + $connection = $cf->getConnection($this->config->getValue('dbtype', 'sqlite'), $connectionParams); + $connection->ensureConnectedToPrimary(); + return $connection; } /** @@ -150,11 +132,11 @@ abstract class AbstractDatabase { */ abstract public function setupDatabase($username); - public function runMigrations() { + public function runMigrations(?IOutput $output = null) { if (!is_dir(\OC::$SERVERROOT."/core/Migrations")) { return; } - $ms = new MigrationService('core', \OC::$server->get(Connection::class)); + $ms = new MigrationService('core', \OC::$server->get(Connection::class), $output); $ms->migrate('latest', true); } } diff --git a/lib/private/Setup/MySQL.php b/lib/private/Setup/MySQL.php index 50f566728a9..93aee667d18 100644 --- a/lib/private/Setup/MySQL.php +++ b/lib/private/Setup/MySQL.php @@ -1,38 +1,16 @@ <?php + /** - * @copyright Copyright (c) 2016, ownCloud, Inc. - * - * @author Arthur Schiwon <blizzz@arthur-schiwon.de> - * @author Bart Visscher <bartv@thisnet.nl> - * @author Christoph Wurst <christoph@winzerhof-wurst.at> - * @author Hemanth Kumar Veeranki <hems.india1997@gmail.com> - * @author Joas Schilling <coding@schilljs.com> - * @author Michael Göhler <somebody.here@gmx.de> - * @author Morris Jobke <hey@morrisjobke.de> - * @author Thomas Müller <thomas.mueller@tmit.eu> - * @author Vincent Petry <vincent@nextcloud.com> - * - * @license AGPL-3.0 - * - * This code is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License, version 3, - * as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License, version 3, - * along with this program. If not, see <http://www.gnu.org/licenses/> - * + * SPDX-FileCopyrightText: 2016-2024 Nextcloud GmbH and Nextcloud contributors + * SPDX-FileCopyrightText: 2016 ownCloud, Inc. + * SPDX-License-Identifier: AGPL-3.0-only */ namespace OC\Setup; +use Doctrine\DBAL\Platforms\MySQL80Platform; use OC\DB\ConnectionAdapter; use OC\DB\MySqlTools; use OCP\IDBConnection; -use Doctrine\DBAL\Platforms\MySQL80Platform; use OCP\Security\ISecureRandom; class MySQL extends AbstractDatabase { @@ -73,7 +51,7 @@ class MySQL extends AbstractDatabase { $this->logger->error($e->getMessage(), [ 'exception' => $e, ]); - throw new \OC\DatabaseSetupException($this->trans->t('MySQL username and/or password not valid'), + throw new \OC\DatabaseSetupException($this->trans->t('MySQL Login and/or password not valid'), $this->trans->t('You need to enter details of an existing account.'), 0, $e); } } diff --git a/lib/private/Setup/OCI.php b/lib/private/Setup/OCI.php index 477561bbe2a..3a0fa34d8d1 100644 --- a/lib/private/Setup/OCI.php +++ b/lib/private/Setup/OCI.php @@ -1,31 +1,9 @@ <?php + /** - * @copyright Copyright (c) 2016, ownCloud, Inc. - * - * @author Andreas Fischer <bantu@owncloud.com> - * @author Bart Visscher <bartv@thisnet.nl> - * @author Christoph Wurst <christoph@winzerhof-wurst.at> - * @author Joas Schilling <coding@schilljs.com> - * @author Jörn Friedrich Dreyer <jfd@butonic.de> - * @author Morris Jobke <hey@morrisjobke.de> - * @author Robin Appelman <robin@icewind.nl> - * @author Thomas Müller <thomas.mueller@tmit.eu> - * @author Victor Dubiniuk <dubiniuk@owncloud.com> - * - * @license AGPL-3.0 - * - * This code is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License, version 3, - * as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License, version 3, - * along with this program. If not, see <http://www.gnu.org/licenses/> - * + * SPDX-FileCopyrightText: 2016-2024 Nextcloud GmbH and Nextcloud contributors + * SPDX-FileCopyrightText: 2016 ownCloud, Inc. + * SPDX-License-Identifier: AGPL-3.0-only */ namespace OC\Setup; @@ -53,9 +31,9 @@ class OCI extends AbstractDatabase { public function validate($config) { $errors = []; if (empty($config['dbuser']) && empty($config['dbname'])) { - $errors[] = $this->trans->t("Enter the database username and name for %s", [$this->dbprettyname]); + $errors[] = $this->trans->t("Enter the database Login and name for %s", [$this->dbprettyname]); } elseif (empty($config['dbuser'])) { - $errors[] = $this->trans->t("Enter the database username for %s", [$this->dbprettyname]); + $errors[] = $this->trans->t("Enter the database Login for %s", [$this->dbprettyname]); } elseif (empty($config['dbname'])) { $errors[] = $this->trans->t("Enter the database name for %s", [$this->dbprettyname]); } @@ -75,7 +53,7 @@ class OCI extends AbstractDatabase { . ' NLS_LANG=' . getenv('NLS_LANG') . ' tnsnames.ora is ' . (is_readable(getenv('ORACLE_HOME') . '/network/admin/tnsnames.ora') ? '' : 'not ') . 'readable', 0, $e); } - throw new \OC\DatabaseSetupException($this->trans->t('Oracle username and/or password not valid'), + throw new \OC\DatabaseSetupException($this->trans->t('Oracle Login and/or password not valid'), 'Check environment: ORACLE_HOME=' . getenv('ORACLE_HOME') . ' ORACLE_SID=' . getenv('ORACLE_SID') . ' LD_LIBRARY_PATH=' . getenv('LD_LIBRARY_PATH') diff --git a/lib/private/Setup/PostgreSQL.php b/lib/private/Setup/PostgreSQL.php index 490cbba69a9..9cee614b7fa 100644 --- a/lib/private/Setup/PostgreSQL.php +++ b/lib/private/Setup/PostgreSQL.php @@ -1,30 +1,9 @@ <?php + /** - * @copyright Copyright (c) 2016, ownCloud, Inc. - * - * @author Bart Visscher <bartv@thisnet.nl> - * @author Christoph Wurst <christoph@winzerhof-wurst.at> - * @author eduardo <eduardo@vnexu.net> - * @author Joas Schilling <coding@schilljs.com> - * @author Lukas Reschke <lukas@statuscode.ch> - * @author Morris Jobke <hey@morrisjobke.de> - * @author Robin Appelman <robin@icewind.nl> - * @author Vitor Mattos <vitor@php.rio> - * - * @license AGPL-3.0 - * - * This code is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License, version 3, - * as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License, version 3, - * along with this program. If not, see <http://www.gnu.org/licenses/> - * + * SPDX-FileCopyrightText: 2016-2024 Nextcloud GmbH and Nextcloud contributors + * SPDX-FileCopyrightText: 2016 ownCloud, Inc. + * SPDX-License-Identifier: AGPL-3.0-only */ namespace OC\Setup; @@ -114,7 +93,7 @@ class PostgreSQL extends AbstractDatabase { $this->logger->error($e->getMessage(), [ 'exception' => $e, ]); - throw new \OC\DatabaseSetupException($this->trans->t('PostgreSQL username and/or password not valid'), + throw new \OC\DatabaseSetupException($this->trans->t('PostgreSQL Login and/or password not valid'), $this->trans->t('You need to enter details of an existing account.'), 0, $e); } } diff --git a/lib/private/Setup/Sqlite.php b/lib/private/Setup/Sqlite.php index 8ed432b039a..1b90ebd5a5e 100644 --- a/lib/private/Setup/Sqlite.php +++ b/lib/private/Setup/Sqlite.php @@ -1,25 +1,9 @@ <?php + /** - * @copyright Copyright (c) 2016, ownCloud, Inc. - * - * @author Bart Visscher <bartv@thisnet.nl> - * @author Christoph Wurst <christoph@winzerhof-wurst.at> - * @author Daniel Kesselberg <mail@danielkesselberg.de> - * - * @license AGPL-3.0 - * - * This code is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License, version 3, - * as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License, version 3, - * along with this program. If not, see <http://www.gnu.org/licenses/> - * + * SPDX-FileCopyrightText: 2016-2024 Nextcloud GmbH and Nextcloud contributors + * SPDX-FileCopyrightText: 2016 ownCloud, Inc. + * SPDX-License-Identifier: AGPL-3.0-only */ namespace OC\Setup; diff --git a/lib/private/SetupCheck/SetupCheckManager.php b/lib/private/SetupCheck/SetupCheckManager.php new file mode 100644 index 00000000000..41f393fef7a --- /dev/null +++ b/lib/private/SetupCheck/SetupCheckManager.php @@ -0,0 +1,46 @@ +<?php + +declare(strict_types=1); + +/** + * SPDX-FileCopyrightText: 2022 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + +namespace OC\SetupCheck; + +use OC\AppFramework\Bootstrap\Coordinator; +use OCP\Server; +use OCP\SetupCheck\ISetupCheck; +use OCP\SetupCheck\ISetupCheckManager; +use OCP\SetupCheck\SetupResult; +use Psr\Log\LoggerInterface; + +class SetupCheckManager implements ISetupCheckManager { + public function __construct( + private Coordinator $coordinator, + private LoggerInterface $logger, + ) { + } + + public function runAll(): array { + $results = []; + $setupChecks = $this->coordinator->getRegistrationContext()->getSetupChecks(); + foreach ($setupChecks as $setupCheck) { + /** @var ISetupCheck $setupCheckObject */ + $setupCheckObject = Server::get($setupCheck->getService()); + $this->logger->debug('Running check '.get_class($setupCheckObject)); + try { + $setupResult = $setupCheckObject->run(); + } catch (\Throwable $t) { + $setupResult = SetupResult::error("An exception occured while running the setup check:\n$t"); + $this->logger->error('Exception running check '.get_class($setupCheckObject).': '.$t->getMessage(), ['exception' => $t]); + } + $setupResult->setName($setupCheckObject->getName()); + $category = $setupCheckObject->getCategory(); + $results[$category] ??= []; + $results[$category][$setupCheckObject::class] = $setupResult; + } + return $results; + } +} diff --git a/lib/private/Share/Constants.php b/lib/private/Share/Constants.php index 0c8fad17e07..8cfa83f9ea2 100644 --- a/lib/private/Share/Constants.php +++ b/lib/private/Share/Constants.php @@ -1,28 +1,9 @@ <?php + /** - * @copyright Copyright (c) 2016, ownCloud, Inc. - * - * @author Björn Schießle <bjoern@schiessle.org> - * @author Christopher Schäpers <kondou@ts.unde.re> - * @author Christoph Wurst <christoph@winzerhof-wurst.at> - * @author Daniel Calviño Sánchez <danxuliu@gmail.com> - * @author Joas Schilling <coding@schilljs.com> - * @author Julius Härtl <jus@bitgrid.net> - * - * @license AGPL-3.0 - * - * This code is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License, version 3, - * as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License, version 3, - * along with this program. If not, see <http://www.gnu.org/licenses/> - * + * SPDX-FileCopyrightText: 2016-2024 Nextcloud GmbH and Nextcloud contributors + * SPDX-FileCopyrightText: 2016 ownCloud, Inc. + * SPDX-License-Identifier: AGPL-3.0-only */ namespace OC\Share; diff --git a/lib/private/Share/Helper.php b/lib/private/Share/Helper.php index f1b9ae2b9fa..76e7daeb9e5 100644 --- a/lib/private/Share/Helper.php +++ b/lib/private/Share/Helper.php @@ -1,27 +1,9 @@ <?php + /** - * @copyright Copyright (c) 2016, ownCloud, Inc. - * - * @author Björn Schießle <bjoern@schiessle.org> - * @author Christoph Wurst <christoph@winzerhof-wurst.at> - * @author Joas Schilling <coding@schilljs.com> - * @author Miguel Prokop <miguel.prokop@vtu.com> - * @author Morris Jobke <hey@morrisjobke.de> - * - * @license AGPL-3.0 - * - * This code is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License, version 3, - * as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License, version 3, - * along with this program. If not, see <http://www.gnu.org/licenses/> - * + * SPDX-FileCopyrightText: 2016-2024 Nextcloud GmbH and Nextcloud contributors + * SPDX-FileCopyrightText: 2016 ownCloud, Inc. + * SPDX-License-Identifier: AGPL-3.0-only */ namespace OC\Share; diff --git a/lib/private/Share/Share.php b/lib/private/Share/Share.php index 8d14f293e5a..0af264fb968 100644 --- a/lib/private/Share/Share.php +++ b/lib/private/Share/Share.php @@ -1,45 +1,13 @@ <?php + /** - * @copyright Copyright (c) 2016, ownCloud, Inc. - * - * @author Arthur Schiwon <blizzz@arthur-schiwon.de> - * @author Bart Visscher <bartv@thisnet.nl> - * @author Bernhard Reiter <ockham@raz.or.at> - * @author Björn Schießle <bjoern@schiessle.org> - * @author Christoph Wurst <christoph@winzerhof-wurst.at> - * @author Joas Schilling <coding@schilljs.com> - * @author Morris Jobke <hey@morrisjobke.de> - * @author Robin Appelman <robin@icewind.nl> - * @author Robin McCorkell <robin@mccorkell.me.uk> - * @author Roeland Jago Douma <roeland@famdouma.nl> - * @author Sebastian Döll <sebastian.doell@libasys.de> - * @author Thomas Müller <thomas.mueller@tmit.eu> - * @author Vincent Petry <vincent@nextcloud.com> - * @author Volkan Gezer <volkangezer@gmail.com> - * - * @license AGPL-3.0 - * - * This code is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License, version 3, - * as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License, version 3, - * along with this program. If not, see <http://www.gnu.org/licenses/> - * + * SPDX-FileCopyrightText: 2016-2024 Nextcloud GmbH and Nextcloud contributors + * SPDX-FileCopyrightText: 2016 ownCloud, Inc. + * SPDX-License-Identifier: AGPL-3.0-only */ - namespace OC\Share; use OCA\Files_Sharing\ShareBackend\File; -use OCP\DB\Exception; -use OCP\DB\QueryBuilder\IQueryBuilder; -use OCP\IDBConnection; -use OCP\Share\IShare; use Psr\Log\LoggerInterface; /** @@ -94,161 +62,6 @@ class Share extends Constants { } /** - * Get the items of item type shared with the current user - * - * @param string $itemType - * @param int $format (optional) Format type must be defined by the backend - * @param mixed $parameters (optional) - * @param int $limit Number of items to return (optional) Returns all by default - * @param boolean $includeCollections (optional) - * @return mixed Return depends on format - * @deprecated TESTS ONLY - this methods is only used by tests - * called like this: - * \OC\Share\Share::getItemsSharedWith('folder'); (apps/files_sharing/tests/UpdaterTest.php) - */ - public static function getItemsSharedWith() { - return self::getItems('folder', null, self::$shareTypeUserAndGroups, \OC_User::getUser()); - } - - /** - * Get the items of item type shared with a user - * - * @param string $itemType - * @param string $user id for which user we want the shares - * @param int $format (optional) Format type must be defined by the backend - * @param mixed $parameters (optional) - * @param int $limit Number of items to return (optional) Returns all by default - * @param boolean $includeCollections (optional) - * @return mixed Return depends on format - * @deprecated TESTS ONLY - this methods is only used by tests - * called like this: - * \OC\Share\Share::getItemsSharedWithUser('test', $shareWith); (tests/lib/Share/Backend.php) - */ - public static function getItemsSharedWithUser($itemType, $user) { - return self::getItems('test', null, self::$shareTypeUserAndGroups, $user); - } - - /** - * Get the item of item type shared with a given user by source - * - * @param string $itemType - * @param string $itemSource - * @param ?string $user User to whom the item was shared - * @param ?string $owner Owner of the share - * @param ?int $shareType only look for a specific share type - * @return array Return list of items with file_target, permissions and expiration - * @throws Exception - */ - public static function getItemSharedWithUser(string $itemType, string $itemSource, ?string $user = null, ?string $owner = null, ?int $shareType = null) { - $shares = []; - $fileDependent = $itemType === 'file' || $itemType === 'folder'; - $qb = self::getSelectStatement(self::FORMAT_NONE, $fileDependent); - $qb->from('share', 's'); - if ($fileDependent) { - $qb->innerJoin('s', 'filecache', 'f', $qb->expr()->eq('file_source', 'f.fileid')); - $qb->innerJoin('s', 'storages', 'st', $qb->expr()->eq('numeric_id', 'f.storage')); - $column = 'file_source'; - } else { - $column = 'item_source'; - } - - $qb->where($qb->expr()->eq($column, $qb->createNamedParameter($itemSource))) - ->andWhere($qb->expr()->eq('item_type', $qb->createNamedParameter($itemType))); - - // for link shares $user === null - if ($user !== null) { - $qb->andWhere($qb->expr()->eq('share_with', $qb->createNamedParameter($user))); - } - - if ($shareType !== null) { - $qb->andWhere($qb->expr()->eq('share_type', $qb->createNamedParameter($shareType, IQueryBuilder::PARAM_INT))); - } - - if ($owner !== null) { - $qb->andWhere($qb->expr()->eq('uid_owner', $qb->createNamedParameter($owner))); - } - - $result = $qb->executeQuery(); - while ($row = $result->fetch()) { - if ($fileDependent && !self::isFileReachable($row['path'], $row['storage_id'])) { - continue; - } - if ($fileDependent && (int)$row['file_parent'] === -1) { - // if it is a mount point we need to get the path from the mount manager - $mountManager = \OC\Files\Filesystem::getMountManager(); - $mountPoint = $mountManager->findByStorageId($row['storage_id']); - if (!empty($mountPoint)) { - $path = $mountPoint[0]->getMountPoint(); - $path = trim($path, '/'); - $path = substr($path, strlen($owner) + 1); //normalize path to 'files/foo.txt` - $row['path'] = $path; - } else { - \OC::$server->get(LoggerInterface::class)->warning( - 'Could not resolve mount point for ' . $row['storage_id'], - ['app' => 'OCP\Share'] - ); - } - } - $shares[] = $row; - } - $result->closeCursor(); - - // if we didn't found a result then let's look for a group share. - if (empty($shares) && $user !== null) { - $userObject = \OC::$server->getUserManager()->get($user); - $groups = []; - if ($userObject) { - $groups = \OC::$server->getGroupManager()->getUserGroupIds($userObject); - } - - if (!empty($groups)) { - $qb = self::getSelectStatement(self::FORMAT_NONE, $fileDependent); - $qb->from('share', 's'); - - if ($fileDependent) { - $qb->innerJoin('s', 'filecache', 'f', $qb->expr()->eq('file_source', 'f.fileid')) - ->innerJoin('s', 'storages', 'st', $qb->expr()->eq('numeric_id', 'f.storage')); - } - - $qb->where($qb->expr()->eq($column, $qb->createNamedParameter($itemSource))) - ->andWhere($qb->expr()->eq('item_type', $qb->createNamedParameter($itemType, IQueryBuilder::PARAM_STR))) - ->andWhere($qb->expr()->in('share_with', $qb->createNamedParameter($groups, IQueryBuilder::PARAM_STR_ARRAY))); - - if ($owner !== null) { - $qb->andWhere($qb->expr()->eq('uid_owner', $qb->createNamedParameter($owner))); - } - $result = $qb->executeQuery(); - - while ($row = $result->fetch()) { - $shares[] = $row; - } - $result->closeCursor(); - } - } - - return $shares; - } - - /** - * Get the shared item of item type owned by the current user - * - * @param string $itemType - * @param string $itemSource - * @param int $format (optional) Format type must be defined by the backend - * @param mixed $parameters - * @param boolean $includeCollections - * @return mixed Return depends on format - * - * Refactoring notes: - * * defacto $parameters and $format is always the default and therefore is removed in the subsequent call - */ - public static function getItemShared($itemType, $itemSource, $format = self::FORMAT_NONE, - $parameters = null, $includeCollections = false) { - return self::getItems($itemType, $itemSource, null, null, \OC_User::getUser(), self::FORMAT_NONE, - null, -1, $includeCollections); - } - - /** * Get the backend class for the specified item type * * @param string $itemType @@ -256,8 +69,8 @@ class Share extends Constants { * @throws \Exception */ public static function getBackend($itemType) { - $l = \OC::$server->getL10N('lib'); - $logger = \OC::$server->get(LoggerInterface::class); + $l = \OCP\Util::getL10N('lib'); + $logger = \OCP\Server::get(LoggerInterface::class); if (isset(self::$backends[$itemType])) { return self::$backends[$itemType]; } elseif (isset(self::$backendTypes[$itemType]['class'])) { @@ -303,421 +116,6 @@ class Share extends Constants { } /** - * Get a list of collection item types for the specified item type - * - * @param string $itemType - * @return array|false - */ - private static function getCollectionItemTypes(string $itemType) { - $collectionTypes = [$itemType]; - foreach (self::$backendTypes as $type => $backend) { - if (in_array($backend['collectionOf'], $collectionTypes)) { - $collectionTypes[] = $type; - } - } - // TODO Add option for collections to be collection of themselves, only 'folder' does it now... - if (isset(self::$backendTypes[$itemType]) && (!self::getBackend($itemType) instanceof \OCP\Share_Backend_Collection || $itemType != 'folder')) { - unset($collectionTypes[0]); - } - // Return array if collections were found or the item type is a - // collection itself - collections can be inside collections - if (count($collectionTypes) > 0) { - return $collectionTypes; - } - return false; - } - - /** - * Get shared items from the database - * - * @param string $itemType - * @param string $item Item source or target (optional) - * @param int $shareType SHARE_TYPE_USER, SHARE_TYPE_GROUP, SHARE_TYPE_LINK, $shareTypeUserAndGroups, or $shareTypeGroupUserUnique - * @param string $shareWith User or group the item is being shared with - * @param string $uidOwner User that is the owner of shared items (optional) - * @param int $format Format to convert items to with formatItems() (optional) - * @param mixed $parameters to pass to formatItems() (optional) - * @param int $limit Number of items to return, -1 to return all matches (optional) - * @param boolean $includeCollections Include collection item types (optional) - * @param boolean $itemShareWithBySource (optional) - * @param boolean $checkExpireDate - * @return array - * - * See public functions getItem(s)... for parameter usage - * - * Refactoring notes: - * * defacto $limit, $itemsShareWithBySource, $checkExpireDate, $parameters and $format is always the default and therefore is removed in the subsequent call - */ - public static function getItems($itemType, ?string $item = null, ?int $shareType = null, $shareWith = null, - $uidOwner = null, $format = self::FORMAT_NONE, $parameters = null, $limit = -1, - $includeCollections = false, $itemShareWithBySource = false, $checkExpireDate = true) { - if (\OC::$server->getConfig()->getAppValue('core', 'shareapi_enabled', 'yes') != 'yes') { - return []; - } - $fileDependent = $itemType == 'file' || $itemType == 'folder'; - $qb = self::getSelectStatement(self::FORMAT_NONE, $fileDependent, $uidOwner); - $qb->from('share', 's'); - - $backend = self::getBackend($itemType); - $collectionTypes = false; - // Get filesystem root to add it to the file target and remove from the - // file source, match file_source with the file cache - if ($fileDependent) { - if (!is_null($uidOwner)) { - $root = \OC\Files\Filesystem::getRoot(); - } else { - $root = ''; - } - if (isset($item)) { - $qb->innerJoin('s', 'filecache', 'f', $qb->expr()->eq('file_source', 'f.fileid')); - } else { - $qb->innerJoin('s', 'filecache', 'f', $qb->expr()->andX( - $qb->expr()->eq('file_source', 'f.fileid'), - $qb->expr()->isNotNull('file_target') - )); - } - $qb->innerJoin('s', 'storages', 'st', $qb->expr()->eq('numeric_id', 'f.storage')); - } else { - $root = ''; - $collectionTypes = self::getCollectionItemTypes($itemType); - if ($includeCollections && !isset($item) && $collectionTypes) { - // If includeCollections is true, find collections of this item type, e.g. a music album contains songs - if (!in_array($itemType, $collectionTypes)) { - $itemTypes = array_merge([$itemType], $collectionTypes); - } else { - $itemTypes = $collectionTypes; - } - $qb->where($qb->expr()->in('item_type', $qb->createNamedParameter($itemTypes, IQueryBuilder::PARAM_STR_ARRAY))); - } else { - $qb->where($qb->expr()->eq('item_type', $qb->createNamedParameter($itemType))); - } - } - if (\OC::$server->getConfig()->getAppValue('core', 'shareapi_allow_links', 'yes') !== 'yes') { - $qb->andWhere($qb->expr()->neq('share_type', $qb->createNamedParameter(IShare::TYPE_LINK, IQueryBuilder::PARAM_INT))); - } - if (isset($shareType)) { - // Include all user and group items - if ($shareType === self::$shareTypeUserAndGroups && isset($shareWith)) { - $qb->andWhere($qb->expr()->andX( - $qb->expr()->in('share_type', $qb->createNamedParameter([IShare::TYPE_USER, self::$shareTypeGroupUserUnique], IQueryBuilder::PARAM_INT_ARRAY)), - $qb->expr()->eq('share_with', $qb->createNamedParameter($shareWith)) - )); - - $user = \OC::$server->getUserManager()->get($shareWith); - $groups = []; - if ($user) { - $groups = \OC::$server->getGroupManager()->getUserGroupIds($user); - } - if (!empty($groups)) { - $qb->orWhere($qb->expr()->andX( - $qb->expr()->eq('share_type', $qb->createNamedParameter(IShare::TYPE_GROUP, IQueryBuilder::PARAM_INT)), - $qb->expr()->in('share_with', $qb->createNamedParameter($groups, IQueryBuilder::PARAM_STR_ARRAY)) - )); - } - - // Don't include own group shares - $qb->andWhere($qb->expr()->neq('uid_owner', $qb->createNamedParameter($shareWith))); - } else { - $qb->andWhere($qb->expr()->eq('share_type', $qb->createNamedParameter($shareType, IQueryBuilder::PARAM_INT))); - if (isset($shareWith)) { - $qb->andWhere($qb->expr()->eq('share_with', $qb->createNamedParameter($shareWith, IQueryBuilder::PARAM_STR))); - } - } - } - if (isset($uidOwner)) { - $qb->andWhere($qb->expr()->eq('uid_owner', $qb->createNamedParameter($uidOwner))); - if (!isset($shareType)) { - // Prevent unique user targets for group shares from being selected - $qb->andWhere($qb->expr()->neq('share_type', $qb->createNamedParameter(self::$shareTypeGroupUserUnique, IQueryBuilder::PARAM_INT))); - } - if ($fileDependent) { - $column = 'file_source'; - } else { - $column = 'item_source'; - } - } else { - if ($fileDependent) { - $column = 'file_target'; - } else { - $column = 'item_target'; - } - } - if (isset($item)) { - $collectionTypes = self::getCollectionItemTypes($itemType); - // If looking for own shared items, check item_source else check item_target - if (isset($uidOwner)) { - // If item type is a file, file source needs to be checked in case the item was converted - if ($fileDependent) { - $expr = $qb->expr()->eq('file_source', $qb->createNamedParameter($item)); - $column = 'file_source'; - } else { - $expr = $qb->expr()->eq('item_source', $qb->createNamedParameter($item)); - $column = 'item_source'; - } - } else { - if ($fileDependent) { - $item = \OC\Files\Filesystem::normalizePath($item); - $expr = $qb->expr()->eq('file_target', $qb->createNamedParameter($item)); - } else { - $expr = $qb->expr()->eq('item_target', $qb->createNamedParameter($item)); - } - } - if ($includeCollections && $collectionTypes && !in_array('folder', $collectionTypes)) { - $qb->andWhere($qb->expr()->orX( - $expr, - $qb->expr()->in('item_type', $qb->createNamedParameter($collectionTypes, IQueryBuilder::PARAM_STR_ARRAY)) - )); - } else { - $qb->andWhere($expr); - } - } - $qb->orderBy('s.id', 'ASC'); - try { - $result = $qb->executeQuery(); - } catch (\Exception $e) { - \OCP\Server::get(LoggerInterface::class)->error( - 'Error while selecting shares: ' . $qb->getSQL(), - [ - 'app' => 'files_sharing', - 'exception' => $e - ]); - throw new \RuntimeException('Wrong SQL query', 500, $e); - } - - $root = strlen($root); - $items = []; - $targets = []; - $switchedItems = []; - $mounts = []; - while ($row = $result->fetch()) { - //var_dump($row); - self::transformDBResults($row); - // Filter out duplicate group shares for users with unique targets - if ($fileDependent && !self::isFileReachable($row['path'], $row['storage_id'])) { - continue; - } - if ($row['share_type'] == self::$shareTypeGroupUserUnique && isset($items[$row['parent']])) { - $row['share_type'] = IShare::TYPE_GROUP; - $row['unique_name'] = true; // remember that we use a unique name for this user - $row['share_with'] = $items[$row['parent']]['share_with']; - // if the group share was unshared from the user we keep the permission, otherwise - // we take the permission from the parent because this is always the up-to-date - // permission for the group share - if ($row['permissions'] > 0) { - $row['permissions'] = $items[$row['parent']]['permissions']; - } - // Remove the parent group share - unset($items[$row['parent']]); - if ($row['permissions'] == 0) { - continue; - } - } elseif (!isset($uidOwner)) { - // Check if the same target already exists - if (isset($targets[$row['id']])) { - // Check if the same owner shared with the user twice - // through a group and user share - this is allowed - $id = $targets[$row['id']]; - if (isset($items[$id]) && $items[$id]['uid_owner'] == $row['uid_owner']) { - // Switch to group share type to ensure resharing conditions aren't bypassed - if ($items[$id]['share_type'] != IShare::TYPE_GROUP) { - $items[$id]['share_type'] = IShare::TYPE_GROUP; - $items[$id]['share_with'] = $row['share_with']; - } - // Switch ids if sharing permission is granted on only - // one share to ensure correct parent is used if resharing - if (~(int)$items[$id]['permissions'] & \OCP\Constants::PERMISSION_SHARE - && (int)$row['permissions'] & \OCP\Constants::PERMISSION_SHARE) { - $items[$row['id']] = $items[$id]; - $switchedItems[$id] = $row['id']; - unset($items[$id]); - $id = $row['id']; - } - $items[$id]['permissions'] |= (int)$row['permissions']; - } - continue; - } elseif (!empty($row['parent'])) { - $targets[$row['parent']] = $row['id']; - } - } - // Remove root from file source paths if retrieving own shared items - if (isset($uidOwner) && isset($row['path'])) { - if (isset($row['parent'])) { - $query = \OC::$server->getDatabaseConnection()->getQueryBuilder(); - $query->select('file_target') - ->from('share') - ->where($query->expr()->eq('id', $query->createNamedParameter($row['parent']))); - - $parentRow = false; - try { - $parentResult = $query->executeQuery(); - $parentRow = $parentResult->fetchOne(); - $parentResult->closeCursor(); - - $tmpPath = $parentRow['file_target']; - // find the right position where the row path continues from the target path - $pos = strrpos($row['path'], $parentRow['file_target']); - $subPath = substr($row['path'], $pos); - $splitPath = explode('/', $subPath); - foreach (array_slice($splitPath, 2) as $pathPart) { - $tmpPath = $tmpPath . '/' . $pathPart; - } - $row['path'] = $tmpPath; - } catch (Exception $e) { - \OCP\Server::get(LoggerInterface::class) - ->error('Can\'t select parent :' . $e->getMessage() . ' query=' . $query->getSQL(), [ - 'exception' => $e, - 'app' => 'core' - ]); - } - } else { - if (!isset($mounts[$row['storage']])) { - $mountPoints = \OC\Files\Filesystem::getMountByNumericId($row['storage']); - if (is_array($mountPoints) && !empty($mountPoints)) { - $mounts[$row['storage']] = current($mountPoints); - } - } - if (!empty($mounts[$row['storage']])) { - $path = $mounts[$row['storage']]->getMountPoint() . $row['path']; - $relPath = substr($path, $root); // path relative to data/user - $row['path'] = rtrim($relPath, '/'); - } - } - } - - // Check if resharing is allowed, if not remove share permission - if (isset($row['permissions']) && (!self::isResharingAllowed() | \OCP\Util::isSharingDisabledForUser())) { - $row['permissions'] &= ~\OCP\Constants::PERMISSION_SHARE; - } - // Add display names to result - $row['share_with_displayname'] = $row['share_with']; - if (isset($row['share_with']) && $row['share_with'] != '' && - $row['share_type'] === IShare::TYPE_USER) { - $shareWithUser = \OC::$server->getUserManager()->get($row['share_with']); - $row['share_with_displayname'] = $shareWithUser === null ? $row['share_with'] : $shareWithUser->getDisplayName(); - } elseif (isset($row['share_with']) && $row['share_with'] != '' && - $row['share_type'] === IShare::TYPE_REMOTE) { - $addressBookEntries = \OC::$server->getContactsManager()->search($row['share_with'], ['CLOUD'], [ - 'limit' => 1, - 'enumeration' => false, - 'fullmatch' => false, - 'strict_search' => true, - ]); - foreach ($addressBookEntries as $entry) { - foreach ($entry['CLOUD'] as $cloudID) { - if ($cloudID === $row['share_with']) { - $row['share_with_displayname'] = $entry['FN']; - } - } - } - } - if (isset($row['uid_owner']) && $row['uid_owner'] != '') { - $ownerUser = \OC::$server->getUserManager()->get($row['uid_owner']); - $row['displayname_owner'] = $ownerUser === null ? $row['uid_owner'] : $ownerUser->getDisplayName(); - } - - if ($row['permissions'] > 0) { - $items[$row['id']] = $row; - } - } - $result->closeCursor(); - - // group items if we are looking for items shared with the current user - if (isset($shareWith) && $shareWith === \OC_User::getUser()) { - $items = self::groupItems($items, $itemType); - } - - if (!empty($items)) { - $collectionItems = []; - foreach ($items as &$row) { - // Check if this is a collection of the requested item type - if ($includeCollections && $collectionTypes && $row['item_type'] !== 'folder' && in_array($row['item_type'], $collectionTypes)) { - if (($collectionBackend = self::getBackend($row['item_type'])) - && $collectionBackend instanceof \OCP\Share_Backend_Collection) { - // Collections can be inside collections, check if the item is a collection - if (isset($item) && $row['item_type'] == $itemType && $row[$column] == $item) { - $collectionItems[] = $row; - } else { - $collection = []; - $collection['item_type'] = $row['item_type']; - if ($row['item_type'] == 'file' || $row['item_type'] == 'folder') { - $collection['path'] = basename($row['path']); - } - $row['collection'] = $collection; - // Fetch all the children sources - $children = $collectionBackend->getChildren($row[$column]); - foreach ($children as $child) { - $childItem = $row; - $childItem['item_type'] = $itemType; - if ($row['item_type'] != 'file' && $row['item_type'] != 'folder') { - $childItem['item_source'] = $child['source']; - $childItem['item_target'] = $child['target']; - } - if ($backend instanceof \OCP\Share_Backend_File_Dependent) { - if ($row['item_type'] == 'file' || $row['item_type'] == 'folder') { - $childItem['file_source'] = $child['source']; - } else { // TODO is this really needed if we already know that we use the file backend? - $meta = \OC\Files\Filesystem::getFileInfo($child['file_path']); - $childItem['file_source'] = $meta['fileid']; - } - $childItem['file_target'] = - \OC\Files\Filesystem::normalizePath($child['file_path']); - } - if (isset($item)) { - if ($childItem[$column] == $item) { - $collectionItems[] = $childItem; - } - } else { - $collectionItems[] = $childItem; - } - } - } - } - // Remove collection item - $toRemove = $row['id']; - if (array_key_exists($toRemove, $switchedItems)) { - $toRemove = $switchedItems[$toRemove]; - } - unset($items[$toRemove]); - } elseif ($includeCollections && $collectionTypes && in_array($row['item_type'], $collectionTypes)) { - // FIXME: Thats a dirty hack to improve file sharing performance, - // see github issue #10588 for more details - // Need to find a solution which works for all back-ends - $collectionBackend = self::getBackend($row['item_type']); - $sharedParents = $collectionBackend->getParents($row['item_source']); - foreach ($sharedParents as $parent) { - $collectionItems[] = $parent; - } - } - } - if (!empty($collectionItems)) { - $collectionItems = array_unique($collectionItems, SORT_REGULAR); - $items = array_merge($items, $collectionItems); - } - - // filter out invalid items, these can appear when subshare entries exist - // for a group in which the requested user isn't a member any more - $items = array_filter($items, function ($item) { - return $item['share_type'] !== self::$shareTypeGroupUserUnique; - }); - - return self::formatResult($items, $column, $backend); - } elseif ($includeCollections && $collectionTypes && in_array('folder', $collectionTypes)) { - // FIXME: Thats a dirty hack to improve file sharing performance, - // see github issue #10588 for more details - // Need to find a solution which works for all back-ends - $collectionItems = []; - $collectionBackend = self::getBackend('folder'); - $sharedParents = $collectionBackend->getParents($item, $shareWith, $uidOwner); - foreach ($sharedParents as $parent) { - $collectionItems[] = $parent; - } - return self::formatResult($collectionItems, $column, $backend); - } - - return []; - } - - /** * group items with link to the same source * * @param array $items @@ -757,185 +155,6 @@ class Share extends Constants { } /** - * Construct select statement - * - * @param bool $fileDependent ist it a file/folder share or a general share - */ - private static function getSelectStatement(int $format, bool $fileDependent, ?string $uidOwner = null): IQueryBuilder { - /** @var IDBConnection $connection */ - $connection = \OC::$server->get(IDBConnection::class); - $qb = $connection->getQueryBuilder(); - if ($format == self::FORMAT_STATUSES) { - if ($fileDependent) { - return $qb->select( - 's.id', - 's.parent', - 'share_type', - 'path', - 'storage', - 'share_with', - 'uid_owner', - 'file_source', - 'stime', - 's.permissions', - 'uid_initiator' - )->selectAlias('st.id', 'storage_id') - ->selectAlias('f.parent', 'file_parent'); - } - return $qb->select('id', 'parent', 'share_type', 'share_with', 'uid_owner', 'item_source', 'stime', 's.permissions'); - } - - if (isset($uidOwner)) { - if ($fileDependent) { - return $qb->select( - 's.id', - 'item_type', - 'item_source', - 's.parent', - 'share_type', - 'share_with', - 'file_source', - 'file_target', - 'path', - 's.permissions', - 'stime', - 'expiration', - 'token', - 'storage', - 'mail_send', - 'uid_owner', - 'uid_initiator' - )->selectAlias('st.id', 'storage_id') - ->selectAlias('f.parent', 'file_parent'); - } - return $qb->select('id', 'item_type', 'item_source', 'parent', 'share_type', - 'share_with', 'uid_owner', 'file_source', 'stime', 's.permissions', - 'expiration', 'token', 'mail_send'); - } - - if ($fileDependent) { - if ($format == File::FORMAT_GET_FOLDER_CONTENTS || $format == File::FORMAT_FILE_APP_ROOT) { - return $qb->select( - 's.id', - 'item_type', - 'item_source', - 's.parent', - 'uid_owner', - 'share_type', - 'share_with', - 'file_source', - 'path', - 'file_target', - 's.permissions', - 'stime', - 'expiration', - 'storage', - 'name', - 'mtime', - 'mimepart', - 'size', - 'encrypted', - 'etag', - 'mail_send' - )->selectAlias('f.parent', 'file_parent'); - } - return $qb->select( - 's.id', - 'item_type', - 'item_source', - 'item_target', - 's.parent', - 'share_type', - 'share_with', - 'uid_owner', - 'file_source', - 'path', - 'file_target', - 's.permissions', - 'stime', - 'expiration', - 'token', - 'storage', - 'mail_send', - )->selectAlias('f.parent', 'file_parent') - ->selectAlias('st.id', 'storage_id'); - } - return $qb->select('*'); - } - - - /** - * transform db results - * - * @param array $row result - */ - private static function transformDBResults(&$row) { - if (isset($row['id'])) { - $row['id'] = (int)$row['id']; - } - if (isset($row['share_type'])) { - $row['share_type'] = (int)$row['share_type']; - } - if (isset($row['parent'])) { - $row['parent'] = (int)$row['parent']; - } - if (isset($row['file_parent'])) { - $row['file_parent'] = (int)$row['file_parent']; - } - if (isset($row['file_source'])) { - $row['file_source'] = (int)$row['file_source']; - } - if (isset($row['permissions'])) { - $row['permissions'] = (int)$row['permissions']; - } - if (isset($row['storage'])) { - $row['storage'] = (int)$row['storage']; - } - if (isset($row['stime'])) { - $row['stime'] = (int)$row['stime']; - } - if (isset($row['expiration']) && $row['share_type'] !== IShare::TYPE_LINK) { - // discard expiration date for non-link shares, which might have been - // set by ancient bugs - $row['expiration'] = null; - } - } - - /** - * format result - * - * @param array $items result - * @param string $column is it a file share or a general share ('file_target' or 'item_target') - * @param \OCP\Share_Backend $backend sharing backend - * @param int $format - * @param array $parameters additional format parameters - * @return array format result - */ - private static function formatResult($items, $column, $backend, $format = self::FORMAT_NONE, $parameters = null) { - if ($format === self::FORMAT_NONE) { - return $items; - } elseif ($format === self::FORMAT_STATUSES) { - $statuses = []; - foreach ($items as $item) { - if ($item['share_type'] === IShare::TYPE_LINK) { - if ($item['uid_initiator'] !== \OC::$server->getUserSession()->getUser()->getUID()) { - continue; - } - $statuses[$item[$column]]['link'] = true; - } elseif (!isset($statuses[$item[$column]])) { - $statuses[$item[$column]]['link'] = false; - } - if (!empty($item['file_target'])) { - $statuses[$item[$column]]['path'] = $item['path']; - } - } - return $statuses; - } else { - return $backend->formatItems($items, $format, $parameters); - } - } - - /** * remove protocol from URL * * @param string $url @@ -958,29 +177,4 @@ class Share extends Constants { public static function getExpireInterval() { return (int)\OC::$server->getConfig()->getAppValue('core', 'shareapi_expire_after_n_days', '7'); } - - /** - * Checks whether the given path is reachable for the given owner - * - * @param string $path path relative to files - * @param string $ownerStorageId storage id of the owner - * - * @return boolean true if file is reachable, false otherwise - */ - private static function isFileReachable($path, $ownerStorageId) { - // if outside the home storage, file is always considered reachable - if (!(substr($ownerStorageId, 0, 6) === 'home::' || - substr($ownerStorageId, 0, 13) === 'object::user:' - )) { - return true; - } - - // if inside the home storage, the file has to be under "/files/" - $path = ltrim($path, '/'); - if (substr($path, 0, 6) === 'files/') { - return true; - } - - return false; - } } diff --git a/lib/private/Share20/DefaultShareProvider.php b/lib/private/Share20/DefaultShareProvider.php index 5201cf074b1..03202f215b2 100644 --- a/lib/private/Share20/DefaultShareProvider.php +++ b/lib/private/Share20/DefaultShareProvider.php @@ -1,36 +1,9 @@ <?php + /** - * @copyright Copyright (c) 2016, ownCloud, Inc. - * - * @author Arthur Schiwon <blizzz@arthur-schiwon.de> - * @author Bjoern Schiessle <bjoern@schiessle.org> - * @author Björn Schießle <bjoern@schiessle.org> - * @author Christoph Wurst <christoph@winzerhof-wurst.at> - * @author Daniel Calviño Sánchez <danxuliu@gmail.com> - * @author Jan-Philipp Litza <jplitza@users.noreply.github.com> - * @author Joas Schilling <coding@schilljs.com> - * @author Julius Härtl <jus@bitgrid.net> - * @author Lukas Reschke <lukas@statuscode.ch> - * @author Maxence Lange <maxence@artificial-owl.com> - * @author phisch <git@philippschaffrath.de> - * @author Robin Appelman <robin@icewind.nl> - * @author Roeland Jago Douma <roeland@famdouma.nl> - * @author Vincent Petry <vincent@nextcloud.com> - * - * @license AGPL-3.0 - * - * This code is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License, version 3, - * as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License, version 3, - * along with this program. If not, see <http://www.gnu.org/licenses/> - * + * SPDX-FileCopyrightText: 2016-2024 Nextcloud GmbH and Nextcloud contributors + * SPDX-FileCopyrightText: 2016 ownCloud, Inc. + * SPDX-License-Identifier: AGPL-3.0-only */ namespace OC\Share20; @@ -38,12 +11,12 @@ use OC\Files\Cache\Cache; use OC\Share20\Exception\BackendError; use OC\Share20\Exception\InvalidShare; use OC\Share20\Exception\ProviderException; +use OCP\AppFramework\Utility\ITimeFactory; use OCP\DB\QueryBuilder\IQueryBuilder; use OCP\Defaults; use OCP\Files\Folder; use OCP\Files\IRootFolder; use OCP\Files\Node; -use OCP\IConfig; use OCP\IDBConnection; use OCP\IGroupManager; use OCP\IURLGenerator; @@ -55,6 +28,7 @@ use OCP\Share\Exceptions\ShareNotFound; use OCP\Share\IAttributes; use OCP\Share\IShare; use OCP\Share\IShareProvider; +use Psr\Log\LoggerInterface; use function str_starts_with; /** @@ -90,19 +64,19 @@ class DefaultShareProvider implements IShareProvider { /** @var IURLGenerator */ private $urlGenerator; - /** @var IConfig */ - private $config; + private ITimeFactory $timeFactory; public function __construct( - IDBConnection $connection, - IUserManager $userManager, - IGroupManager $groupManager, - IRootFolder $rootFolder, - IMailer $mailer, - Defaults $defaults, - IFactory $l10nFactory, - IURLGenerator $urlGenerator, - IConfig $config) { + IDBConnection $connection, + IUserManager $userManager, + IGroupManager $groupManager, + IRootFolder $rootFolder, + IMailer $mailer, + Defaults $defaults, + IFactory $l10nFactory, + IURLGenerator $urlGenerator, + ITimeFactory $timeFactory, + ) { $this->dbConn = $connection; $this->userManager = $userManager; $this->groupManager = $groupManager; @@ -111,7 +85,7 @@ class DefaultShareProvider implements IShareProvider { $this->defaults = $defaults; $this->l10nFactory = $l10nFactory; $this->urlGenerator = $urlGenerator; - $this->config = $config; + $this->timeFactory = $timeFactory; } /** @@ -216,32 +190,22 @@ class DefaultShareProvider implements IShareProvider { } // Set the time this share was created - $qb->setValue('stime', $qb->createNamedParameter(time())); + $shareTime = $this->timeFactory->now(); + $qb->setValue('stime', $qb->createNamedParameter($shareTime->getTimestamp())); // insert the data and fetch the id of the share - $this->dbConn->beginTransaction(); - $qb->execute(); - $id = $this->dbConn->lastInsertId('*PREFIX*share'); + $qb->executeStatement(); - // Now fetch the inserted share and create a complete share object - $qb = $this->dbConn->getQueryBuilder(); - $qb->select('*') - ->from('share') - ->where($qb->expr()->eq('id', $qb->createNamedParameter($id))); - - $cursor = $qb->execute(); - $data = $cursor->fetch(); - $this->dbConn->commit(); - $cursor->closeCursor(); + // Update mandatory data + $id = $qb->getLastInsertId(); + $share->setId((string)$id); + $share->setProviderId($this->identifier()); - if ($data === false) { - throw new ShareNotFound('Newly created share could not be found'); - } + $share->setShareTime(\DateTime::createFromImmutable($shareTime)); $mailSendValue = $share->getMailSend(); - $data['mail_send'] = ($mailSendValue === null) ? true : $mailSendValue; + $share->setMailSend(($mailSendValue === null) ? true : $mailSendValue); - $share = $this->createShare($data); return $share; } @@ -661,6 +625,10 @@ class DefaultShareProvider implements IShareProvider { } public function getSharesInFolder($userId, Folder $node, $reshares, $shallow = true) { + if (!$shallow) { + throw new \Exception("non-shallow getSharesInFolder is no longer supported"); + } + $qb = $this->dbConn->getQueryBuilder(); $qb->select('s.*', 'f.fileid', 'f.path', 'f.permissions AS f_permissions', 'f.storage', 'f.path_hash', @@ -701,21 +669,12 @@ class DefaultShareProvider implements IShareProvider { }, $childMountNodes); $qb->innerJoin('s', 'filecache', 'f', $qb->expr()->eq('s.file_source', 'f.fileid')); - if ($shallow) { - $qb->andWhere( - $qb->expr()->orX( - $qb->expr()->eq('f.parent', $qb->createNamedParameter($node->getId())), - $qb->expr()->in('f.fileid', $qb->createParameter('chunk')) - ) - ); - } else { - $qb->andWhere( - $qb->expr()->orX( - $qb->expr()->like('f.path', $qb->createNamedParameter($this->dbConn->escapeLikeParameter($node->getInternalPath()) . '/%')), - $qb->expr()->in('f.fileid', $qb->createParameter('chunk')) - ) - ); - } + $qb->andWhere( + $qb->expr()->orX( + $qb->expr()->eq('f.parent', $qb->createNamedParameter($node->getId())), + $qb->expr()->in('f.fileid', $qb->createParameter('chunk')) + ) + ); $qb->orderBy('id'); @@ -831,7 +790,7 @@ class DefaultShareProvider implements IShareProvider { // If the recipient is set for a group share resolve to that user if ($recipientId !== null && $share->getShareType() === IShare::TYPE_GROUP) { - $share = $this->resolveGroupShares([$share], $recipientId)[0]; + $share = $this->resolveGroupShares([(int) $share->getId() => $share], $recipientId)[0]; } return $share; @@ -1008,7 +967,8 @@ class DefaultShareProvider implements IShareProvider { } if ($this->isAccessibleResult($data)) { - $shares2[] = $this->createShare($data); + $share = $this->createShare($data); + $shares2[$share->getId()] = $share; } } $cursor->closeCursor(); @@ -1129,61 +1089,40 @@ class DefaultShareProvider implements IShareProvider { } /** - * @param Share[] $shares + * Update the data from group shares with any per-user modifications + * + * @param array<int, Share> $shareMap shares indexed by share id * @param $userId * @return Share[] The updates shares if no update is found for a share return the original */ - private function resolveGroupShares($shares, $userId) { - $result = []; - - $start = 0; - while (true) { - /** @var Share[] $shareSlice */ - $shareSlice = array_slice($shares, $start, 100); - $start += 100; - - if ($shareSlice === []) { - break; - } - - /** @var int[] $ids */ - $ids = []; - /** @var Share[] $shareMap */ - $shareMap = []; - - foreach ($shareSlice as $share) { - $ids[] = (int)$share->getId(); - $shareMap[$share->getId()] = $share; - } - - $qb = $this->dbConn->getQueryBuilder(); - - $query = $qb->select('*') - ->from('share') - ->where($qb->expr()->in('parent', $qb->createNamedParameter($ids, IQueryBuilder::PARAM_INT_ARRAY))) - ->andWhere($qb->expr()->eq('share_with', $qb->createNamedParameter($userId))) - ->andWhere($qb->expr()->orX( - $qb->expr()->eq('item_type', $qb->createNamedParameter('file')), - $qb->expr()->eq('item_type', $qb->createNamedParameter('folder')) - )); + private function resolveGroupShares($shareMap, $userId) { + $qb = $this->dbConn->getQueryBuilder(); + $query = $qb->select('*') + ->from('share') + ->where($qb->expr()->eq('share_with', $qb->createNamedParameter($userId))) + ->andWhere($qb->expr()->eq('share_type', $qb->createNamedParameter(IShare::TYPE_USERGROUP))) + ->andWhere($qb->expr()->in('item_type', [$qb->createNamedParameter('file'), $qb->createNamedParameter('folder')])); + + // this is called with either all group shares or one group share. + // for all shares it's easier to just only search by share_with, + // for a single share it's efficient to filter by parent + if (count($shareMap) === 1) { + $share = reset($shareMap); + $query->andWhere($qb->expr()->eq('parent', $qb->createNamedParameter($share->getId()))); + } - $stmt = $query->execute(); + $stmt = $query->execute(); - while ($data = $stmt->fetch()) { + while ($data = $stmt->fetch()) { + if (array_key_exists($data['parent'], $shareMap)) { $shareMap[$data['parent']]->setPermissions((int)$data['permissions']); $shareMap[$data['parent']]->setStatus((int)$data['accepted']); $shareMap[$data['parent']]->setTarget($data['file_target']); $shareMap[$data['parent']]->setParent($data['parent']); } - - $stmt->closeCursor(); - - foreach ($shareMap as $share) { - $result[] = $share; - } } - return $result; + return array_values($shareMap); } /** @@ -1247,7 +1186,8 @@ class DefaultShareProvider implements IShareProvider { ) ); } else { - \OC::$server->getLogger()->logException(new \InvalidArgumentException('Default share provider tried to delete all shares for type: ' . $shareType)); + $e = new \InvalidArgumentException('Default share provider tried to delete all shares for type: ' . $shareType); + \OCP\Server::get(LoggerInterface::class)->error($e->getMessage(), ['exception' => $e]); return; } @@ -1374,7 +1314,7 @@ class DefaultShareProvider implements IShareProvider { $type = (int)$row['share_type']; if ($type === IShare::TYPE_USER) { $uid = $row['share_with']; - $users[$uid] = isset($users[$uid]) ? $users[$uid] : []; + $users[$uid] = $users[$uid] ?? []; $users[$uid][$row['id']] = $row; } elseif ($type === IShare::TYPE_GROUP) { $gid = $row['share_with']; @@ -1387,14 +1327,14 @@ class DefaultShareProvider implements IShareProvider { $userList = $group->getUsers(); foreach ($userList as $user) { $uid = $user->getUID(); - $users[$uid] = isset($users[$uid]) ? $users[$uid] : []; + $users[$uid] = $users[$uid] ?? []; $users[$uid][$row['id']] = $row; } } elseif ($type === IShare::TYPE_LINK) { $link = true; } elseif ($type === IShare::TYPE_USERGROUP && $currentAccess === true) { $uid = $row['share_with']; - $users[$uid] = isset($users[$uid]) ? $users[$uid] : []; + $users[$uid] = $users[$uid] ?? []; $users[$uid][$row['id']] = $row; } } diff --git a/lib/private/Share20/Exception/BackendError.php b/lib/private/Share20/Exception/BackendError.php index b3e07495378..60f7dcc1a17 100644 --- a/lib/private/Share20/Exception/BackendError.php +++ b/lib/private/Share20/Exception/BackendError.php @@ -1,23 +1,8 @@ <?php /** - * @copyright Copyright (c) 2016, ownCloud, Inc. - * - * @author Roeland Jago Douma <roeland@famdouma.nl> - * - * @license AGPL-3.0 - * - * This code is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License, version 3, - * as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License, version 3, - * along with this program. If not, see <http://www.gnu.org/licenses/> - * + * SPDX-FileCopyrightText: 2016-2024 Nextcloud GmbH and Nextcloud contributors + * SPDX-FileCopyrightText: 2016 ownCloud, Inc. + * SPDX-License-Identifier: AGPL-3.0-only */ namespace OC\Share20\Exception; diff --git a/lib/private/Share20/Exception/InvalidShare.php b/lib/private/Share20/Exception/InvalidShare.php index 464dc15a9f1..755efdfd2cc 100644 --- a/lib/private/Share20/Exception/InvalidShare.php +++ b/lib/private/Share20/Exception/InvalidShare.php @@ -1,23 +1,8 @@ <?php /** - * @copyright Copyright (c) 2016, ownCloud, Inc. - * - * @author Roeland Jago Douma <roeland@famdouma.nl> - * - * @license AGPL-3.0 - * - * This code is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License, version 3, - * as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License, version 3, - * along with this program. If not, see <http://www.gnu.org/licenses/> - * + * SPDX-FileCopyrightText: 2016-2024 Nextcloud GmbH and Nextcloud contributors + * SPDX-FileCopyrightText: 2016 ownCloud, Inc. + * SPDX-License-Identifier: AGPL-3.0-only */ namespace OC\Share20\Exception; diff --git a/lib/private/Share20/Exception/ProviderException.php b/lib/private/Share20/Exception/ProviderException.php index b8e15dd9db8..cb79ab884b4 100644 --- a/lib/private/Share20/Exception/ProviderException.php +++ b/lib/private/Share20/Exception/ProviderException.php @@ -1,23 +1,9 @@ <?php + /** - * @copyright Copyright (c) 2016, ownCloud, Inc. - * - * @author Roeland Jago Douma <roeland@famdouma.nl> - * - * @license AGPL-3.0 - * - * This code is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License, version 3, - * as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License, version 3, - * along with this program. If not, see <http://www.gnu.org/licenses/> - * + * SPDX-FileCopyrightText: 2016-2024 Nextcloud GmbH and Nextcloud contributors + * SPDX-FileCopyrightText: 2016 ownCloud, Inc. + * SPDX-License-Identifier: AGPL-3.0-only */ namespace OC\Share20\Exception; diff --git a/lib/private/Share20/Hooks.php b/lib/private/Share20/Hooks.php index ae08b20fde6..809b50791e5 100644 --- a/lib/private/Share20/Hooks.php +++ b/lib/private/Share20/Hooks.php @@ -1,32 +1,20 @@ <?php + /** - * @copyright Copyright (c) 2016, ownCloud, Inc. - * - * @author Roeland Jago Douma <roeland@famdouma.nl> - * - * @license AGPL-3.0 - * - * This code is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License, version 3, - * as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License, version 3, - * along with this program. If not, see <http://www.gnu.org/licenses/> - * + * SPDX-FileCopyrightText: 2016-2024 Nextcloud GmbH and Nextcloud contributors + * SPDX-FileCopyrightText: 2016 ownCloud, Inc. + * SPDX-License-Identifier: AGPL-3.0-only */ namespace OC\Share20; +use OCP\Share\IManager as IShareManager; + class Hooks { public static function post_deleteUser($arguments) { - \OC::$server->getShareManager()->userDeleted($arguments['uid']); + \OC::$server->get(IShareManager::class)->userDeleted($arguments['uid']); } public static function post_deleteGroup($arguments) { - \OC::$server->getShareManager()->groupDeleted($arguments['gid']); + \OC::$server->get(IShareManager::class)->groupDeleted($arguments['gid']); } } diff --git a/lib/private/Share20/LegacyHooks.php b/lib/private/Share20/LegacyHooks.php index 24d07167fbd..99c2b0a9a87 100644 --- a/lib/private/Share20/LegacyHooks.php +++ b/lib/private/Share20/LegacyHooks.php @@ -1,28 +1,7 @@ <?php /** - * @copyright 2017, Roeland Jago Douma <roeland@famdouma.nl> - * - * @author Christoph Wurst <christoph@winzerhof-wurst.at> - * @author Joas Schilling <coding@schilljs.com> - * @author Morris Jobke <hey@morrisjobke.de> - * @author Pauli Järvinen <pauli.jarvinen@gmail.com> - * @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/>. - * + * SPDX-FileCopyrightText: 2017 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later */ namespace OC\Share20; diff --git a/lib/private/Share20/Manager.php b/lib/private/Share20/Manager.php index b03608f9872..e10c34fd0d2 100644 --- a/lib/private/Share20/Manager.php +++ b/lib/private/Share20/Manager.php @@ -1,52 +1,16 @@ <?php + /** - * @copyright Copyright (c) 2016, ownCloud, Inc. - * - * @author Arthur Schiwon <blizzz@arthur-schiwon.de> - * @author Bjoern Schiessle <bjoern@schiessle.org> - * @author Björn Schießle <bjoern@schiessle.org> - * @author Christoph Wurst <christoph@winzerhof-wurst.at> - * @author Daniel Calviño Sánchez <danxuliu@gmail.com> - * @author Daniel Kesselberg <mail@danielkesselberg.de> - * @author Jan-Christoph Borchardt <hey@jancborchardt.net> - * @author Joas Schilling <coding@schilljs.com> - * @author John Molakvoæ <skjnldsv@protonmail.com> - * @author Julius Härtl <jus@bitgrid.net> - * @author Lukas Reschke <lukas@statuscode.ch> - * @author Maxence Lange <maxence@artificial-owl.com> - * @author Maxence Lange <maxence@nextcloud.com> - * @author Morris Jobke <hey@morrisjobke.de> - * @author Pauli Järvinen <pauli.jarvinen@gmail.com> - * @author Robin Appelman <robin@icewind.nl> - * @author Roeland Jago Douma <roeland@famdouma.nl> - * @author Samuel <faust64@gmail.com> - * @author szaimen <szaimen@e.mail.de> - * @author Valdnet <47037905+Valdnet@users.noreply.github.com> - * @author Vincent Petry <vincent@nextcloud.com> - * - * @license AGPL-3.0 - * - * This code is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License, version 3, - * as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License, version 3, - * along with this program. If not, see <http://www.gnu.org/licenses/> - * + * SPDX-FileCopyrightText: 2016-2024 Nextcloud GmbH and Nextcloud contributors + * SPDX-FileCopyrightText: 2016 ownCloud, Inc. + * SPDX-License-Identifier: AGPL-3.0-only */ namespace OC\Share20; -use OCP\Cache\CappedMemoryCache; use OC\Files\Mount\MoveableMount; use OC\KnownUser\KnownUserService; use OC\Share20\Exception\ProviderException; use OCA\Files_Sharing\AppInfo\Application; -use OCA\Files_Sharing\ISharedStorage; use OCP\EventDispatcher\IEventDispatcher; use OCP\Files\File; use OCP\Files\Folder; @@ -55,6 +19,7 @@ use OCP\Files\Mount\IMountManager; use OCP\Files\Node; use OCP\HintException; use OCP\IConfig; +use OCP\IDateTimeZone; use OCP\IGroupManager; use OCP\IL10N; use OCP\IURLGenerator; @@ -85,84 +50,34 @@ use Psr\Log\LoggerInterface; * This class is the communication hub for all sharing related operations. */ class Manager implements IManager { - /** @var IProviderFactory */ - private $factory; - private LoggerInterface $logger; - /** @var IConfig */ - private $config; - /** @var ISecureRandom */ - private $secureRandom; - /** @var IHasher */ - private $hasher; - /** @var IMountManager */ - private $mountManager; - /** @var IGroupManager */ - private $groupManager; - /** @var IL10N */ - private $l; - /** @var IFactory */ - private $l10nFactory; - /** @var IUserManager */ - private $userManager; - /** @var IRootFolder */ - private $rootFolder; - /** @var CappedMemoryCache */ - private $sharingDisabledForUsersCache; - /** @var LegacyHooks */ - private $legacyHooks; - /** @var IMailer */ - private $mailer; - /** @var IURLGenerator */ - private $urlGenerator; - /** @var \OC_Defaults */ - private $defaults; - /** @var IEventDispatcher */ - private $dispatcher; - /** @var IUserSession */ - private $userSession; - /** @var KnownUserService */ - private $knownUserService; + + private IL10N|null $l; + private LegacyHooks $legacyHooks; public function __construct( - LoggerInterface $logger, - IConfig $config, - ISecureRandom $secureRandom, - IHasher $hasher, - IMountManager $mountManager, - IGroupManager $groupManager, - IL10N $l, - IFactory $l10nFactory, - IProviderFactory $factory, - IUserManager $userManager, - IRootFolder $rootFolder, - IMailer $mailer, - IURLGenerator $urlGenerator, - \OC_Defaults $defaults, - IEventDispatcher $dispatcher, - IUserSession $userSession, - KnownUserService $knownUserService + private LoggerInterface $logger, + private IConfig $config, + private ISecureRandom $secureRandom, + private IHasher $hasher, + private IMountManager $mountManager, + private IGroupManager $groupManager, + private IFactory $l10nFactory, + private IProviderFactory $factory, + private IUserManager $userManager, + private IRootFolder $rootFolder, + private IMailer $mailer, + private IURLGenerator $urlGenerator, + private \OC_Defaults $defaults, + private IEventDispatcher $dispatcher, + private IUserSession $userSession, + private KnownUserService $knownUserService, + private ShareDisableChecker $shareDisableChecker, + private IDateTimeZone $dateTimeZone ) { - $this->logger = $logger; - $this->config = $config; - $this->secureRandom = $secureRandom; - $this->hasher = $hasher; - $this->mountManager = $mountManager; - $this->groupManager = $groupManager; - $this->l = $l; - $this->l10nFactory = $l10nFactory; - $this->factory = $factory; - $this->userManager = $userManager; - $this->rootFolder = $rootFolder; - $this->sharingDisabledForUsersCache = new CappedMemoryCache(); + $this->l = $this->l10nFactory->get('lib'); // The constructor of LegacyHooks registers the listeners of share events // do not remove if those are not properly migrated - $this->legacyHooks = new LegacyHooks($dispatcher); - $this->mailer = $mailer; - $this->urlGenerator = $urlGenerator; - $this->defaults = $defaults; - $this->dispatcher = $dispatcher; - $this->userSession = $userSession; - $this->knownUserService = $knownUserService; + $this->legacyHooks = new LegacyHooks($this->dispatcher); } /** @@ -208,7 +123,7 @@ class Manager implements IManager { * * @suppress PhanUndeclaredClassMethod */ - protected function generalCreateChecks(IShare $share) { + protected function generalCreateChecks(IShare $share, bool $isUpdate = false) { if ($share->getShareType() === IShare::TYPE_USER) { // We expect a valid user as sharedWith for user shares if (!$this->userManager->userExists($share->getSharedWith())) { @@ -292,50 +207,15 @@ class Manager implements IManager { throw new \InvalidArgumentException('A share requires permissions'); } - $isFederatedShare = $share->getNode()->getStorage()->instanceOfStorage('\OCA\Files_Sharing\External\Storage'); $permissions = 0; - - if (!$isFederatedShare && $share->getNode()->getOwner() && $share->getNode()->getOwner()->getUID() !== $share->getSharedBy()) { - $userMounts = array_filter($userFolder->getById($share->getNode()->getId()), function ($mount) { - // We need to filter since there might be other mountpoints that contain the file - // e.g. if the user has access to the same external storage that the file is originating from - return $mount->getStorage()->instanceOfStorage(ISharedStorage::class); - }); - $userMount = array_shift($userMounts); - if ($userMount === null) { - throw new GenericShareException('Could not get proper share mount for ' . $share->getNode()->getId() . '. Failing since else the next calls are called with null'); - } - $mount = $userMount->getMountPoint(); - // When it's a reshare use the parent share permissions as maximum - $userMountPointId = $mount->getStorageRootId(); - $userMountPoints = $userFolder->getById($userMountPointId); - $userMountPoint = array_shift($userMountPoints); - - if ($userMountPoint === null) { - throw new GenericShareException('Could not get proper user mount for ' . $userMountPointId . '. Failing since else the next calls are called with null'); - } - - /* Check if this is an incoming share */ - $incomingShares = $this->getSharedWith($share->getSharedBy(), IShare::TYPE_USER, $userMountPoint, -1, 0); - $incomingShares = array_merge($incomingShares, $this->getSharedWith($share->getSharedBy(), IShare::TYPE_GROUP, $userMountPoint, -1, 0)); - $incomingShares = array_merge($incomingShares, $this->getSharedWith($share->getSharedBy(), IShare::TYPE_CIRCLE, $userMountPoint, -1, 0)); - $incomingShares = array_merge($incomingShares, $this->getSharedWith($share->getSharedBy(), IShare::TYPE_ROOM, $userMountPoint, -1, 0)); - - /** @var IShare[] $incomingShares */ - if (!empty($incomingShares)) { - foreach ($incomingShares as $incomingShare) { - $permissions |= $incomingShare->getPermissions(); - } - } - } else { - /* - * Quick fix for #23536 - * Non moveable mount points do not have update and delete permissions - * while we 'most likely' do have that on the storage. - */ - $permissions = $share->getNode()->getPermissions(); - if (!($share->getNode()->getMountPoint() instanceof MoveableMount)) { - $permissions |= \OCP\Constants::PERMISSION_DELETE | \OCP\Constants::PERMISSION_UPDATE; + $nodesForUser = $userFolder->getById($share->getNodeId()); + foreach ($nodesForUser as $node) { + if ($node->getInternalPath() === '' && !$node->getMountPoint() instanceof MoveableMount) { + // for the root of non-movable mount, the permissions we see if limited by the mount itself, + // so we instead use the "raw" permissions from the storage + $permissions |= $node->getStorage()->getPermissions(''); + } else { + $permissions |= $node->getPermissions(); } } @@ -382,26 +262,6 @@ class Manager implements IManager { $expirationDate = $share->getExpirationDate(); - if ($expirationDate !== null) { - //Make sure the expiration date is a date - $expirationDate->setTime(0, 0, 0); - - $date = new \DateTime(); - $date->setTime(0, 0, 0); - if ($date >= $expirationDate) { - $message = $this->l->t('Expiration date is in the past'); - throw new GenericShareException($message, $message, 404); - } - } - - // If expiredate is empty set a default one if there is a default - $fullId = null; - try { - $fullId = $share->getFullId(); - } catch (\UnexpectedValueException $e) { - // This is a new share - } - if ($isRemote) { $defaultExpireDate = $this->shareApiRemoteDefaultExpireDate(); $defaultExpireDays = $this->shareApiRemoteDefaultExpireDays(); @@ -413,29 +273,53 @@ class Manager implements IManager { $configProp = 'internal_defaultExpDays'; $isEnforced = $this->shareApiInternalDefaultExpireDateEnforced(); } - if ($fullId === null && $expirationDate === null && $defaultExpireDate) { - $expirationDate = new \DateTime(); - $expirationDate->setTime(0, 0, 0); - $days = (int)$this->config->getAppValue('core', $configProp, (string)$defaultExpireDays); - if ($days > $defaultExpireDays) { - $days = $defaultExpireDays; + // If $expirationDate is falsy, noExpirationDate is true and expiration not enforced + // Then skip expiration date validation as null is accepted + if(!($share->getNoExpirationDate() && !$isEnforced)) { + if ($expirationDate != null) { + $expirationDate->setTimezone($this->dateTimeZone->getTimeZone()); + $expirationDate->setTime(0, 0, 0); + + $date = new \DateTime('now', $this->dateTimeZone->getTimeZone()); + $date->setTime(0, 0, 0); + if ($date >= $expirationDate) { + $message = $this->l->t('Expiration date is in the past'); + throw new GenericShareException($message, $message, 404); + } + } + + // If expiredate is empty set a default one if there is a default + $fullId = null; + try { + $fullId = $share->getFullId(); + } catch (\UnexpectedValueException $e) { + // This is a new share } - $expirationDate->add(new \DateInterval('P' . $days . 'D')); - } - // If we enforce the expiration date check that is does not exceed - if ($isEnforced) { - if ($expirationDate === null) { - throw new \InvalidArgumentException('Expiration date is enforced'); + if ($fullId === null && $expirationDate === null && $defaultExpireDate) { + $expirationDate = new \DateTime('now', $this->dateTimeZone->getTimeZone()); + $expirationDate->setTime(0, 0, 0); + $days = (int)$this->config->getAppValue('core', $configProp, (string)$defaultExpireDays); + if ($days > $defaultExpireDays) { + $days = $defaultExpireDays; + } + $expirationDate->add(new \DateInterval('P' . $days . 'D')); } - $date = new \DateTime(); - $date->setTime(0, 0, 0); - $date->add(new \DateInterval('P' . $defaultExpireDays . 'D')); - if ($date < $expirationDate) { - $message = $this->l->n('Cannot set expiration date more than %n day in the future', 'Cannot set expiration date more than %n days in the future', $defaultExpireDays); - throw new GenericShareException($message, $message, 404); + // If we enforce the expiration date check that is does not exceed + if ($isEnforced) { + if (empty($expirationDate)) { + throw new \InvalidArgumentException('Expiration date is enforced'); + } + + $date = new \DateTime('now', $this->dateTimeZone->getTimeZone()); + $date->setTime(0, 0, 0); + $date->add(new \DateInterval('P' . $defaultExpireDays . 'D')); + if ($date < $expirationDate) { + $message = $this->l->n('Cannot set expiration date more than %n day in the future', 'Cannot set expiration date more than %n days in the future', $defaultExpireDays); + throw new GenericShareException($message, $message, 404); + } } } @@ -468,51 +352,57 @@ class Manager implements IManager { */ protected function validateExpirationDateLink(IShare $share) { $expirationDate = $share->getExpirationDate(); - - if ($expirationDate !== null) { - //Make sure the expiration date is a date - $expirationDate->setTime(0, 0, 0); - - $date = new \DateTime(); - $date->setTime(0, 0, 0); - if ($date >= $expirationDate) { - $message = $this->l->t('Expiration date is in the past'); - throw new GenericShareException($message, $message, 404); + $isEnforced = $this->shareApiLinkDefaultExpireDateEnforced(); + + // If $expirationDate is falsy, noExpirationDate is true and expiration not enforced + // Then skip expiration date validation as null is accepted + if(!($share->getNoExpirationDate() && !$isEnforced)) { + if ($expirationDate !== null) { + $expirationDate->setTimezone($this->dateTimeZone->getTimeZone()); + $expirationDate->setTime(0, 0, 0); + + $date = new \DateTime('now', $this->dateTimeZone->getTimeZone()); + $date->setTime(0, 0, 0); + if ($date >= $expirationDate) { + $message = $this->l->t('Expiration date is in the past'); + throw new GenericShareException($message, $message, 404); + } } - } - - // If expiredate is empty set a default one if there is a default - $fullId = null; - try { - $fullId = $share->getFullId(); - } catch (\UnexpectedValueException $e) { - // This is a new share - } - - if ($fullId === null && $expirationDate === null && $this->shareApiLinkDefaultExpireDate()) { - $expirationDate = new \DateTime(); - $expirationDate->setTime(0, 0, 0); - $days = (int)$this->config->getAppValue('core', 'link_defaultExpDays', (string)$this->shareApiLinkDefaultExpireDays()); - if ($days > $this->shareApiLinkDefaultExpireDays()) { - $days = $this->shareApiLinkDefaultExpireDays(); + // If expiredate is empty set a default one if there is a default + $fullId = null; + try { + $fullId = $share->getFullId(); + } catch (\UnexpectedValueException $e) { + // This is a new share + } + + if ($fullId === null && $expirationDate === null && $this->shareApiLinkDefaultExpireDate()) { + $expirationDate = new \DateTime('now', $this->dateTimeZone->getTimeZone()); + $expirationDate->setTime(0, 0, 0); + + $days = (int)$this->config->getAppValue('core', 'link_defaultExpDays', (string)$this->shareApiLinkDefaultExpireDays()); + if ($days > $this->shareApiLinkDefaultExpireDays()) { + $days = $this->shareApiLinkDefaultExpireDays(); + } + $expirationDate->add(new \DateInterval('P' . $days . 'D')); } - $expirationDate->add(new \DateInterval('P' . $days . 'D')); - } - - // If we enforce the expiration date check that is does not exceed - if ($this->shareApiLinkDefaultExpireDateEnforced()) { - if ($expirationDate === null) { - throw new \InvalidArgumentException('Expiration date is enforced'); + + // If we enforce the expiration date check that is does not exceed + if ($isEnforced) { + if (empty($expirationDate)) { + throw new \InvalidArgumentException('Expiration date is enforced'); + } + + $date = new \DateTime('now', $this->dateTimeZone->getTimeZone()); + $date->setTime(0, 0, 0); + $date->add(new \DateInterval('P' . $this->shareApiLinkDefaultExpireDays() . 'D')); + if ($date < $expirationDate) { + $message = $this->l->n('Cannot set expiration date more than %n day in the future', 'Cannot set expiration date more than %n days in the future', $this->shareApiLinkDefaultExpireDays()); + throw new GenericShareException($message, $message, 404); + } } - $date = new \DateTime(); - $date->setTime(0, 0, 0); - $date->add(new \DateInterval('P' . $this->shareApiLinkDefaultExpireDays() . 'D')); - if ($date < $expirationDate) { - $message = $this->l->n('Cannot set expiration date more than %n day in the future', 'Cannot set expiration date more than %n days in the future', $this->shareApiLinkDefaultExpireDays()); - throw new GenericShareException($message, $message, 404); - } } $accepted = true; @@ -528,6 +418,9 @@ class Manager implements IManager { throw new \Exception($message); } + if ($expirationDate instanceof \DateTime) { + $expirationDate->setTimezone(new \DateTimeZone(date_default_timezone_get())); + } $share->setExpirationDate($expirationDate); return $share; @@ -549,6 +442,11 @@ class Manager implements IManager { $this->groupManager->getUserGroupIds($sharedBy), $this->groupManager->getUserGroupIds($sharedWith) ); + + // optional excluded groups + $excludedGroups = $this->shareWithGroupMembersOnlyExcludeGroupsList(); + $groups = array_diff($groups, $excludedGroups); + if (empty($groups)) { $message_t = $this->l->t('Sharing is only allowed with group members'); throw new \Exception($message_t); @@ -574,7 +472,7 @@ class Manager implements IManager { // Identical share already exists if ($existingShare->getSharedWith() === $share->getSharedWith() && $existingShare->getShareType() === $share->getShareType()) { - $message = $this->l->t('Sharing %s failed, because this item is already shared with user %s', [$share->getNode()->getName(), $share->getSharedWithDisplayName()]); + $message = $this->l->t('Sharing %s failed, because this item is already shared with the account %s', [$share->getNode()->getName(), $share->getSharedWithDisplayName()]); throw new AlreadySharedException($message, $existingShare); } @@ -585,7 +483,7 @@ class Manager implements IManager { $user = $this->userManager->get($share->getSharedWith()); if ($group->inGroup($user) && $existingShare->getShareOwner() !== $share->getShareOwner()) { - $message = $this->l->t('Sharing %s failed, because this item is already shared with user %s', [$share->getNode()->getName(), $share->getSharedWithDisplayName()]); + $message = $this->l->t('Sharing %s failed, because this item is already shared with the account %s', [$share->getNode()->getName(), $share->getSharedWithDisplayName()]); throw new AlreadySharedException($message, $existingShare); } } @@ -609,7 +507,10 @@ class Manager implements IManager { if ($this->shareWithGroupMembersOnly()) { $sharedBy = $this->userManager->get($share->getSharedBy()); $sharedWith = $this->groupManager->get($share->getSharedWith()); - if (is_null($sharedWith) || !$sharedWith->inGroup($sharedBy)) { + + // optional excluded groups + $excludedGroups = $this->shareWithGroupMembersOnlyExcludeGroupsList(); + if (is_null($sharedWith) || in_array($share->getSharedWith(), $excludedGroups) || !$sharedWith->inGroup($sharedBy)) { throw new \Exception('Sharing is only allowed within your own groups'); } } @@ -881,12 +782,12 @@ class Manager implements IManager { * @param \DateTime|null $expiration */ protected function sendMailNotification(IL10N $l, - $filename, - $link, - $initiator, - $shareWith, - \DateTime $expiration = null, - $note = '') { + $filename, + $link, + $initiator, + $shareWith, + ?\DateTime $expiration = null, + $note = '') { $initiatorUser = $this->userManager->get($initiator); $initiatorDisplayName = ($initiatorUser instanceof IUser) ? $initiatorUser->getDisplayName() : $initiator; @@ -988,7 +889,7 @@ class Manager implements IManager { throw new \InvalidArgumentException('Cannot share with the share owner'); } - $this->generalCreateChecks($share); + $this->generalCreateChecks($share, true); if ($share->getShareType() === IShare::TYPE_USER) { $this->userCreateChecks($share); @@ -1300,9 +1201,12 @@ class Manager implements IManager { public function getSharesInFolder($userId, Folder $node, $reshares = false, $shallow = true) { $providers = $this->factory->getAllProviders(); + if (!$shallow) { + throw new \Exception("non-shallow getSharesInFolder is no longer supported"); + } return array_reduce($providers, function ($shares, IShareProvider $provider) use ($userId, $node, $reshares, $shallow) { - $newShares = $provider->getSharesInFolder($userId, $node, $reshares, $shallow); + $newShares = $provider->getSharesInFolder($userId, $node, $reshares); foreach ($newShares as $fid => $data) { if (!isset($shares[$fid])) { $shares[$fid] = []; @@ -1574,14 +1478,8 @@ class Manager implements IManager { * @return bool */ public function checkPassword(IShare $share, $password) { - $passwordProtected = $share->getShareType() !== IShare::TYPE_LINK - || $share->getShareType() !== IShare::TYPE_EMAIL - || $share->getShareType() !== IShare::TYPE_CIRCLE; - if (!$passwordProtected) { - //TODO maybe exception? - return false; - } + // if there is no password on the share object / passsword is null, there is nothing to check if ($password === null || $share->getPassword() === null) { return false; } @@ -1661,9 +1559,10 @@ class Manager implements IManager { * |-folder2 (32) * |-fileA (42) * - * fileA is shared with user1 and user1@server1 + * fileA is shared with user1 and user1@server1 and email1@maildomain1 * folder2 is shared with group2 (user4 is a member of group2) * folder1 is shared with user2 (renamed to "folder (1)") and user2@server2 + * and email2@maildomain2 * * Then the access list to '/folder1/folder2/fileA' with $currentAccess is: * [ @@ -1677,7 +1576,10 @@ class Manager implements IManager { * 'user2@server2' => ['node_id' => 23, 'token' => 'FooBaR'], * ], * public => bool - * mail => bool + * mail => [ + * 'email1@maildomain1' => ['node_id' => 42, 'token' => 'aBcDeFg'], + * 'email2@maildomain2' => ['node_id' => 23, 'token' => 'hIjKlMn'], + * ] * ] * * The access list to '/folder1/folder2/fileA' **without** $currentAccess is: @@ -1685,7 +1587,7 @@ class Manager implements IManager { * users => ['user1', 'user2', 'user4'], * remote => bool, * public => bool - * mail => bool + * mail => ['email1@maildomain1', 'email2@maildomain2'] * ] * * This is required for encryption/activity @@ -1705,9 +1607,9 @@ class Manager implements IManager { $owner = $owner->getUID(); if ($currentAccess) { - $al = ['users' => [], 'remote' => [], 'public' => false]; + $al = ['users' => [], 'remote' => [], 'public' => false, 'mail' => []]; } else { - $al = ['users' => [], 'remote' => false, 'public' => false]; + $al = ['users' => [], 'remote' => false, 'public' => false, 'mail' => []]; } if (!$this->userManager->userExists($owner)) { return $al; @@ -1716,8 +1618,7 @@ class Manager implements IManager { //Get node for the owner and correct the owner in case of external storage $userFolder = $this->rootFolder->getUserFolder($owner); if ($path->getId() !== $userFolder->getId() && !$userFolder->isSubNode($path)) { - $nodes = $userFolder->getById($path->getId()); - $path = array_shift($nodes); + $path = $userFolder->getFirstNodeById($path->getId()); if ($path === null || $path->getOwner() === null) { return []; } @@ -1946,6 +1847,21 @@ class Manager implements IManager { } /** + * If shareWithGroupMembersOnly is enabled, return an optional + * list of groups that must be excluded from the principle of + * belonging to the same group. + * + * @return array + */ + public function shareWithGroupMembersOnlyExcludeGroupsList() { + if (!$this->shareWithGroupMembersOnly()) { + return []; + } + $excludeGroups = $this->config->getAppValue('core', 'shareapi_only_share_with_group_members_exclude_group_list', ''); + return json_decode($excludeGroups, true) ?? []; + } + + /** * Check if users can share with groups * * @return bool @@ -2025,37 +1941,7 @@ class Manager implements IManager { * @return bool */ public function sharingDisabledForUser($userId) { - if ($userId === null) { - return false; - } - - if (isset($this->sharingDisabledForUsersCache[$userId])) { - return $this->sharingDisabledForUsersCache[$userId]; - } - - if ($this->config->getAppValue('core', 'shareapi_exclude_groups', 'no') === 'yes') { - $groupsList = $this->config->getAppValue('core', 'shareapi_exclude_groups_list', ''); - $excludedGroups = json_decode($groupsList); - if (is_null($excludedGroups)) { - $excludedGroups = explode(',', $groupsList); - $newValue = json_encode($excludedGroups); - $this->config->setAppValue('core', 'shareapi_exclude_groups_list', $newValue); - } - $user = $this->userManager->get($userId); - $usersGroups = $this->groupManager->getUserGroupIds($user); - if (!empty($usersGroups)) { - $remainingGroups = array_diff($usersGroups, $excludedGroups); - // if the user is only in groups which are disabled for sharing then - // sharing is also disabled for the user - if (empty($remainingGroups)) { - $this->sharingDisabledForUsersCache[$userId] = true; - return true; - } - } - } - - $this->sharingDisabledForUsersCache[$userId] = false; - return false; + return $this->shareDisableChecker->sharingDisabledForUser($userId); } /** diff --git a/lib/private/Share20/ProviderFactory.php b/lib/private/Share20/ProviderFactory.php index ff6d9b0eacd..2800db8177a 100644 --- a/lib/private/Share20/ProviderFactory.php +++ b/lib/private/Share20/ProviderFactory.php @@ -1,35 +1,9 @@ <?php + /** - * @copyright Copyright (c) 2016, ownCloud, Inc. - * - * @author Bjoern Schiessle <bjoern@schiessle.org> - * @author Björn Schießle <bjoern@schiessle.org> - * @author Christoph Wurst <christoph@winzerhof-wurst.at> - * @author Daniel Calviño Sánchez <danxuliu@gmail.com> - * @author Joas Schilling <coding@schilljs.com> - * @author John Molakvoæ <skjnldsv@protonmail.com> - * @author Julius Härtl <jus@bitgrid.net> - * @author Lukas Reschke <lukas@statuscode.ch> - * @author Maxence Lange <maxence@nextcloud.com> - * @author Morris Jobke <hey@morrisjobke.de> - * @author Robin Appelman <robin@icewind.nl> - * @author Roeland Jago Douma <roeland@famdouma.nl> - * @author Samuel <faust64@gmail.com> - * - * @license AGPL-3.0 - * - * This code is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License, version 3, - * as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License, version 3, - * along with this program. If not, see <http://www.gnu.org/licenses/> - * + * SPDX-FileCopyrightText: 2016-2024 Nextcloud GmbH and Nextcloud contributors + * SPDX-FileCopyrightText: 2016 ownCloud, Inc. + * SPDX-License-Identifier: AGPL-3.0-only */ namespace OC\Share20; @@ -41,10 +15,14 @@ use OCA\FederatedFileSharing\TokenHandler; use OCA\ShareByMail\Settings\SettingsManager; use OCA\ShareByMail\ShareByMailProvider; use OCA\Talk\Share\RoomShareProvider; +use OCP\AppFramework\Utility\ITimeFactory; use OCP\Defaults; use OCP\EventDispatcher\IEventDispatcher; +use OCP\Federation\ICloudFederationFactory; +use OCP\Files\IRootFolder; use OCP\IServerContainer; use OCP\Mail\IMailer; +use OCP\Security\IHasher; use OCP\Share\IManager; use OCP\Share\IProviderFactory; use OCP\Share\IShare; @@ -100,12 +78,12 @@ class ProviderFactory implements IProviderFactory { $this->serverContainer->getDatabaseConnection(), $this->serverContainer->getUserManager(), $this->serverContainer->getGroupManager(), - $this->serverContainer->getLazyRootFolder(), + $this->serverContainer->get(IRootFolder::class), $this->serverContainer->get(IMailer::class), $this->serverContainer->query(Defaults::class), $this->serverContainer->getL10NFactory(), $this->serverContainer->getURLGenerator(), - $this->serverContainer->getConfig() + $this->serverContainer->query(ITimeFactory::class), ); } @@ -142,7 +120,7 @@ class ProviderFactory implements IProviderFactory { $this->serverContainer->query(\OCP\OCS\IDiscoveryService::class), $this->serverContainer->getJobList(), \OC::$server->getCloudFederationProviderManager(), - \OC::$server->getCloudFederationFactory(), + \OC::$server->get(ICloudFederationFactory::class), $this->serverContainer->query(IEventDispatcher::class), $this->serverContainer->get(LoggerInterface::class), ); @@ -156,7 +134,7 @@ class ProviderFactory implements IProviderFactory { $notifications, $tokenHandler, $l, - $this->serverContainer->getLazyRootFolder(), + $this->serverContainer->get(IRootFolder::class), $this->serverContainer->getConfig(), $this->serverContainer->getUserManager(), $this->serverContainer->getCloudIdManager(), @@ -191,15 +169,15 @@ class ProviderFactory implements IProviderFactory { $this->serverContainer->getDatabaseConnection(), $this->serverContainer->getSecureRandom(), $this->serverContainer->getUserManager(), - $this->serverContainer->getLazyRootFolder(), + $this->serverContainer->get(IRootFolder::class), $this->serverContainer->getL10N('sharebymail'), - $this->serverContainer->getLogger(), + $this->serverContainer->get(LoggerInterface::class), $this->serverContainer->get(IMailer::class), $this->serverContainer->getURLGenerator(), $this->serverContainer->getActivityManager(), $settingsManager, $this->serverContainer->query(Defaults::class), - $this->serverContainer->getHasher(), + $this->serverContainer->get(IHasher::class), $this->serverContainer->get(IEventDispatcher::class), $this->serverContainer->get(IManager::class) ); @@ -233,7 +211,7 @@ class ProviderFactory implements IProviderFactory { $this->serverContainer->getDatabaseConnection(), $this->serverContainer->getSecureRandom(), $this->serverContainer->getUserManager(), - $this->serverContainer->getLazyRootFolder(), + $this->serverContainer->get(IRootFolder::class), $this->serverContainer->getL10N('circles'), $this->serverContainer->getLogger(), $this->serverContainer->getURLGenerator() diff --git a/lib/private/Share20/PublicShareTemplateFactory.php b/lib/private/Share20/PublicShareTemplateFactory.php index 222f327496a..34dd9b13b61 100644 --- a/lib/private/Share20/PublicShareTemplateFactory.php +++ b/lib/private/Share20/PublicShareTemplateFactory.php @@ -2,34 +2,18 @@ declare(strict_types=1); /** - * @copyright Copyright (c) 2023 Louis Chemineau <louis@chmn.me> - * - * @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/>. - * + * SPDX-FileCopyrightText: 2023 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later */ - namespace OC\Share20; use Exception; use OC\AppFramework\Bootstrap\Coordinator; use OCA\Files_Sharing\DefaultPublicShareTemplateProvider; use OCP\Server; -use OCP\Share\IShare; use OCP\Share\IPublicShareTemplateFactory; use OCP\Share\IPublicShareTemplateProvider; +use OCP\Share\IShare; class PublicShareTemplateFactory implements IPublicShareTemplateFactory { public function __construct( diff --git a/lib/private/Share20/Share.php b/lib/private/Share20/Share.php index 0a50fa0ccfb..ac95e3ac0d4 100644 --- a/lib/private/Share20/Share.php +++ b/lib/private/Share20/Share.php @@ -1,36 +1,14 @@ <?php + /** - * @copyright Copyright (c) 2016, ownCloud, Inc. - * - * @author Bjoern Schiessle <bjoern@schiessle.org> - * @author Björn Schießle <bjoern@schiessle.org> - * @author Christoph Wurst <christoph@winzerhof-wurst.at> - * @author Daniel Calviño Sánchez <danxuliu@gmail.com> - * @author Joas Schilling <coding@schilljs.com> - * @author John Molakvoæ <skjnldsv@protonmail.com> - * @author Maxence Lange <maxence@nextcloud.com> - * @author Robin Appelman <robin@icewind.nl> - * @author Roeland Jago Douma <roeland@famdouma.nl> - * - * @license AGPL-3.0 - * - * This code is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License, version 3, - * as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License, version 3, - * along with this program. If not, see <http://www.gnu.org/licenses/> - * + * SPDX-FileCopyrightText: 2016-2024 Nextcloud GmbH and Nextcloud contributors + * SPDX-FileCopyrightText: 2016 ownCloud, Inc. + * SPDX-License-Identifier: AGPL-3.0-only */ namespace OC\Share20; -use OCP\Files\File; use OCP\Files\Cache\ICacheEntry; +use OCP\Files\File; use OCP\Files\FileInfo; use OCP\Files\IRootFolder; use OCP\Files\Node; @@ -90,28 +68,24 @@ class Share implements IShare { private $mailSend; /** @var string */ private $label = ''; - - /** @var IRootFolder */ - private $rootFolder; - - /** @var IUserManager */ - private $userManager; - /** @var ICacheEntry|null */ private $nodeCacheEntry; - /** @var bool */ private $hideDownload = false; - public function __construct(IRootFolder $rootFolder, IUserManager $userManager) { - $this->rootFolder = $rootFolder; - $this->userManager = $userManager; + private bool $noExpirationDate = false; + + public function __construct( + private IRootFolder $rootFolder, + private IUserManager $userManager, + ) { } /** * @inheritdoc */ public function setId($id) { + /** @var mixed $id Let's be safe until strong typing */ if (is_int($id)) { $id = (string)$id; } @@ -188,12 +162,12 @@ class Share implements IShare { $userFolder = $this->rootFolder->getUserFolder($this->sharedBy); } - $nodes = $userFolder->getById($this->fileId); - if (empty($nodes)) { + $node = $userFolder->getFirstNodeById($this->fileId); + if (!$node) { throw new NotFoundException('Node for share not found, fileid: ' . $this->fileId); } - $this->node = $nodes[0]; + $this->node = $node; } return $this->node; @@ -211,12 +185,16 @@ class Share implements IShare { /** * @inheritdoc */ - public function getNodeId() { + public function getNodeId(): int { if ($this->fileId === null) { $this->fileId = $this->getNode()->getId(); } - return $this->fileId; + if ($this->fileId === null) { + throw new NotFoundException("Share source not found"); + } else { + return $this->fileId; + } } /** @@ -424,6 +402,21 @@ class Share implements IShare { /** * @inheritdoc */ + public function setNoExpirationDate(bool $noExpirationDate) { + $this->noExpirationDate = $noExpirationDate; + return $this; + } + + /** + * @inheritdoc + */ + public function getNoExpirationDate(): bool { + return $this->noExpirationDate; + } + + /** + * @inheritdoc + */ public function isExpired() { return $this->getExpirationDate() !== null && $this->getExpirationDate() <= new \DateTime(); @@ -534,7 +527,7 @@ class Share implements IShare { /** * Set the parent of this share * - * @param int parent + * @param int $parent * @return IShare * @deprecated The new shares do not have parents. This is just here for legacy reasons. */ diff --git a/lib/private/Share20/ShareAttributes.php b/lib/private/Share20/ShareAttributes.php index 9b97c94275d..abbbd36759b 100644 --- a/lib/private/Share20/ShareAttributes.php +++ b/lib/private/Share20/ShareAttributes.php @@ -1,22 +1,9 @@ <?php + /** - * @author Piotr Mrowczynski <piotr@owncloud.com> - * - * @copyright Copyright (c) 2019, ownCloud GmbH - * @license AGPL-3.0 - * - * This code is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License, version 3, - * as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License, version 3, - * along with this program. If not, see <http://www.gnu.org/licenses/> - * + * SPDX-FileCopyrightText: 2023-2024 Nextcloud GmbH and Nextcloud contributors + * SPDX-FileCopyrightText: 2019-2022 ownCloud GmbH + * SPDX-License-Identifier: AGPL-3.0-only */ namespace OC\Share20; diff --git a/lib/private/Share20/ShareDisableChecker.php b/lib/private/Share20/ShareDisableChecker.php new file mode 100644 index 00000000000..9b8bc01558c --- /dev/null +++ b/lib/private/Share20/ShareDisableChecker.php @@ -0,0 +1,85 @@ +<?php + +/** + * SPDX-FileCopyrightText: 2023 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later + */ +namespace OC\Share20; + +use OCP\Cache\CappedMemoryCache; +use OCP\IConfig; +use OCP\IGroupManager; +use OCP\IUserManager; + +/** + * split of from the share manager to allow using it with minimal DI + */ +class ShareDisableChecker { + private CappedMemoryCache $sharingDisabledForUsersCache; + + public function __construct( + private IConfig $config, + private IUserManager $userManager, + private IGroupManager $groupManager, + ) { + $this->sharingDisabledForUsersCache = new CappedMemoryCache(); + } + + + /** + * @param ?string $userId + * @return bool + */ + public function sharingDisabledForUser(?string $userId) { + if ($userId === null) { + return false; + } + + if (isset($this->sharingDisabledForUsersCache[$userId])) { + return $this->sharingDisabledForUsersCache[$userId]; + } + + $excludeGroups = $this->config->getAppValue('core', 'shareapi_exclude_groups', 'no'); + + if ($excludeGroups && $excludeGroups !== 'no') { + $groupsList = $this->config->getAppValue('core', 'shareapi_exclude_groups_list', ''); + $excludedGroups = json_decode($groupsList); + if (is_null($excludedGroups)) { + $excludedGroups = explode(',', $groupsList); + $newValue = json_encode($excludedGroups); + $this->config->setAppValue('core', 'shareapi_exclude_groups_list', $newValue); + } + $user = $this->userManager->get($userId); + if (!$user) { + return false; + } + $usersGroups = $this->groupManager->getUserGroupIds($user); + if ($excludeGroups !== 'allow') { + if (!empty($usersGroups)) { + $remainingGroups = array_diff($usersGroups, $excludedGroups); + // if the user is only in groups which are disabled for sharing then + // sharing is also disabled for the user + if (empty($remainingGroups)) { + $this->sharingDisabledForUsersCache[$userId] = true; + return true; + } + } + } else { + if (!empty($usersGroups)) { + $remainingGroups = array_intersect($usersGroups, $excludedGroups); + // if the user is in any group which is allowed for sharing then + // sharing is also allowed for the user + if (!empty($remainingGroups)) { + $this->sharingDisabledForUsersCache[$userId] = false; + return false; + } + } + $this->sharingDisabledForUsersCache[$userId] = true; + return true; + } + } + + $this->sharingDisabledForUsersCache[$userId] = false; + return false; + } +} diff --git a/lib/private/Share20/ShareHelper.php b/lib/private/Share20/ShareHelper.php index 3debfe185e0..d4a54f1d687 100644 --- a/lib/private/Share20/ShareHelper.php +++ b/lib/private/Share20/ShareHelper.php @@ -1,25 +1,7 @@ <?php /** - * @copyright Copyright (c) 2016, Roeland Jago Douma <roeland@famdouma.nl> - * - * @author Joas Schilling <coding@schilljs.com> - * @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/>. - * + * SPDX-FileCopyrightText: 2016 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later */ namespace OC\Share20; diff --git a/lib/private/Share20/UserRemovedListener.php b/lib/private/Share20/UserRemovedListener.php index 3af7b5a3650..f06c945b591 100644 --- a/lib/private/Share20/UserRemovedListener.php +++ b/lib/private/Share20/UserRemovedListener.php @@ -3,25 +3,8 @@ declare(strict_types=1); /** - * @copyright Copyright (c) 2020 Joas Schilling <coding@schilljs.com> - * - * @author Joas Schilling <coding@schilljs.com> - * - * @license GNU AGPL version 3 or any later version - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as - * published by the Free Software Foundation, either version 3 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - * + * SPDX-FileCopyrightText: 2020 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later */ namespace OC\Share20; diff --git a/lib/private/SpeechToText/SpeechToTextManager.php b/lib/private/SpeechToText/SpeechToTextManager.php index bdd04ad3651..d6cda473875 100644 --- a/lib/private/SpeechToText/SpeechToTextManager.php +++ b/lib/private/SpeechToText/SpeechToTextManager.php @@ -3,26 +3,8 @@ declare(strict_types=1); /** - * @copyright Copyright (c) 2023 Julius Härtl <jus@bitgrid.net> - * @copyright Copyright (c) 2023 Marcel Klehr <mklehr@gmx.net> - * - * @author Julius Härtl <jus@bitgrid.net> - * @author Marcel Klehr <mklehr@gmx.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/>. + * SPDX-FileCopyrightText: 2023 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later */ @@ -36,9 +18,12 @@ use OCP\Files\InvalidPathException; use OCP\Files\NotFoundException; use OCP\IConfig; use OCP\IServerContainer; +use OCP\IUserSession; use OCP\PreConditionNotMetException; use OCP\SpeechToText\ISpeechToTextManager; use OCP\SpeechToText\ISpeechToTextProvider; +use OCP\SpeechToText\ISpeechToTextProviderWithId; +use OCP\SpeechToText\ISpeechToTextProviderWithUserId; use Psr\Container\ContainerExceptionInterface; use Psr\Container\NotFoundExceptionInterface; use Psr\Log\LoggerInterface; @@ -55,6 +40,7 @@ class SpeechToTextManager implements ISpeechToTextManager { private LoggerInterface $logger, private IJobList $jobList, private IConfig $config, + private IUserSession $userSession, ) { } @@ -108,6 +94,24 @@ class SpeechToTextManager implements ISpeechToTextManager { } } + public function cancelScheduledFileTranscription(File $file, ?string $userId, string $appId): void { + try { + $jobArguments = [ + 'fileId' => $file->getId(), + 'owner' => $file->getOwner()->getUID(), + 'userId' => $userId, + 'appId' => $appId, + ]; + if (!$this->jobList->has(TranscriptionJob::class, $jobArguments)) { + $this->logger->debug('Failed to cancel a Speech-to-text job for file ' . $file->getId() . '. No related job was found.'); + return; + } + $this->jobList->remove(TranscriptionJob::class, $jobArguments); + } catch (NotFoundException|InvalidPathException $e) { + throw new InvalidArgumentException('Invalid file provided to cancel file transcription: ' . $e->getMessage()); + } + } + public function transcribeFile(File $file): string { if (!$this->hasProviders()) { throw new PreConditionNotMetException('No SpeechToText providers have been registered'); @@ -117,8 +121,13 @@ class SpeechToTextManager implements ISpeechToTextManager { $json = $this->config->getAppValue('core', 'ai.stt_provider', ''); if ($json !== '') { - $className = json_decode($json, true); - $provider = current(array_filter($providers, fn ($provider) => $provider::class === $className)); + $classNameOrId = json_decode($json, true); + $provider = current(array_filter($providers, function ($provider) use ($classNameOrId) { + if ($provider instanceof ISpeechToTextProviderWithId) { + return $provider->getId() === $classNameOrId; + } + return $provider::class === $classNameOrId; + })); if ($provider !== false) { $providers = [$provider]; } @@ -126,9 +135,13 @@ class SpeechToTextManager implements ISpeechToTextManager { foreach ($providers as $provider) { try { + if ($provider instanceof ISpeechToTextProviderWithUserId) { + $provider->setUserId($this->userSession->getUser()?->getUID()); + } return $provider->transcribeFile($file); } catch (\Throwable $e) { $this->logger->info('SpeechToText transcription using provider ' . $provider->getName() . ' failed', ['exception' => $e]); + throw new RuntimeException('SpeechToText transcription using provider "' . $provider->getName() . '" failed: ' . $e->getMessage()); } } diff --git a/lib/private/SpeechToText/TranscriptionJob.php b/lib/private/SpeechToText/TranscriptionJob.php index 8921d52ecd1..a46fd737865 100644 --- a/lib/private/SpeechToText/TranscriptionJob.php +++ b/lib/private/SpeechToText/TranscriptionJob.php @@ -3,24 +3,8 @@ declare(strict_types=1); /** - * @copyright Copyright (c) 2023 Marcel Klehr <mklehr@gmx.net> - * - * @author Marcel Klehr <mklehr@gmx.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/>. + * SPDX-FileCopyrightText: 2023 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later */ @@ -65,7 +49,7 @@ class TranscriptionJob extends QueuedJob { try { \OC_Util::setupFS($owner); $userFolder = $this->rootFolder->getUserFolder($owner); - $file = current($userFolder->getById($fileId)); + $file = $userFolder->getFirstNodeById($fileId); if (!($file instanceof File)) { $this->logger->warning('Transcription of file ' . $fileId . ' failed. The file could not be found'); $this->eventDispatcher->dispatchTyped( diff --git a/lib/private/StreamImage.php b/lib/private/StreamImage.php index 33078310d27..9290bf38b0f 100644 --- a/lib/private/StreamImage.php +++ b/lib/private/StreamImage.php @@ -1,30 +1,14 @@ <?php + /** - * @copyright Copyright (c) 2021 Carl Schwan <carl@carlschwan.eu> - * - * @author Carl Schwan <carl@carlschwan.eu> - * - * @license AGPL-3.0-or-later - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as - * published by the Free Software Foundation, either version 3 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - * + * SPDX-FileCopyrightText: 2016-2024 Nextcloud GmbH and Nextcloud contributors + * SPDX-FileCopyrightText: 2021 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later */ - namespace OC; -use OCP\IStreamImage; use OCP\IImage; +use OCP\IStreamImage; /** * Only useful when dealing with transferring streamed previews from an external diff --git a/lib/private/Streamer.php b/lib/private/Streamer.php index aafd3d95dfb..4eca1105002 100644 --- a/lib/private/Streamer.php +++ b/lib/private/Streamer.php @@ -1,30 +1,9 @@ <?php + /** - * @copyright Copyright (c) 2016, ownCloud, Inc. - * - * @author Arthur Schiwon <blizzz@arthur-schiwon.de> - * @author Christoph Wurst <christoph@winzerhof-wurst.at> - * @author Daniel Calviño Sánchez <danxuliu@gmail.com> - * @author Joas Schilling <coding@schilljs.com> - * @author Roeland Jago Douma <roeland@famdouma.nl> - * @author szaimen <szaimen@e.mail.de> - * @author Thomas Müller <thomas.mueller@tmit.eu> - * @author Victor Dubiniuk <dubiniuk@owncloud.com> - * - * @license AGPL-3.0 - * - * This code is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License, version 3, - * as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License, version 3, - * along with this program. If not, see <http://www.gnu.org/licenses/> - * + * SPDX-FileCopyrightText: 2016-2024 Nextcloud GmbH and Nextcloud contributors + * SPDX-FileCopyrightText: 2016 ownCloud, Inc. + * SPDX-License-Identifier: AGPL-3.0-only */ namespace OC; @@ -32,6 +11,7 @@ use OC\Files\Filesystem; use OCP\Files\File; use OCP\Files\Folder; use OCP\Files\InvalidPathException; +use OCP\Files\IRootFolder; use OCP\Files\NotFoundException; use OCP\Files\NotPermittedException; use OCP\IRequest; @@ -118,7 +98,7 @@ class Streamer { // prevent absolute dirs $internalDir = ltrim($internalDir, '/'); - $userFolder = \OC::$server->getRootFolder()->get(Filesystem::getRoot()); + $userFolder = \OC::$server->get(IRootFolder::class)->get(Filesystem::getRoot()); /** @var Folder $dirNode */ $dirNode = $userFolder->get($dir); $files = $dirNode->getDirectoryListing(); diff --git a/lib/private/SubAdmin.php b/lib/private/SubAdmin.php index 54f14b8ab88..9ffa0ab03a3 100644 --- a/lib/private/SubAdmin.php +++ b/lib/private/SubAdmin.php @@ -1,32 +1,9 @@ <?php + /** - * @copyright Copyright (c) 2016, ownCloud, Inc. - * - * @author Arthur Schiwon <blizzz@arthur-schiwon.de> - * @author Bart Visscher <bartv@thisnet.nl> - * @author Christoph Wurst <christoph@winzerhof-wurst.at> - * @author Georg Ehrke <oc.list@georgehrke.com> - * @author Joas Schilling <coding@schilljs.com> - * @author John Molakvoæ <skjnldsv@protonmail.com> - * @author Lukas Reschke <lukas@statuscode.ch> - * @author Mikael Hammarin <mikael@try2.se> - * @author Morris Jobke <hey@morrisjobke.de> - * @author Roeland Jago Douma <roeland@famdouma.nl> - * - * @license AGPL-3.0 - * - * This code is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License, version 3, - * as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License, version 3, - * along with this program. If not, see <http://www.gnu.org/licenses/> - * + * SPDX-FileCopyrightText: 2016-2024 Nextcloud GmbH and Nextcloud contributors + * SPDX-FileCopyrightText: 2016 ownCloud, Inc. + * SPDX-License-Identifier: AGPL-3.0-only */ namespace OC; @@ -60,9 +37,9 @@ class SubAdmin extends PublicEmitter implements ISubAdmin { * @param IDBConnection $dbConn */ public function __construct(IUserManager $userManager, - IGroupManager $groupManager, - IDBConnection $dbConn, - IEventDispatcher $eventDispatcher) { + IGroupManager $groupManager, + IDBConnection $dbConn, + IEventDispatcher $eventDispatcher) { $this->userManager = $userManager; $this->groupManager = $groupManager; $this->dbConn = $dbConn; diff --git a/lib/private/Support/CrashReport/Registry.php b/lib/private/Support/CrashReport/Registry.php index f2a35069288..93969a81265 100644 --- a/lib/private/Support/CrashReport/Registry.php +++ b/lib/private/Support/CrashReport/Registry.php @@ -3,26 +3,8 @@ declare(strict_types=1); /** - * @copyright Copyright (c) 2016 Christoph Wurst <christoph@winzerhof-wurst.at> - * - * @author Christoph Wurst <christoph@winzerhof-wurst.at> - * @author Morris Jobke <hey@morrisjobke.de> - * - * @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/>. - * + * SPDX-FileCopyrightText: 2016 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later */ namespace OC\Support\CrashReport; diff --git a/lib/private/Support/Subscription/Assertion.php b/lib/private/Support/Subscription/Assertion.php index 451afe83bd3..04a0dde739f 100644 --- a/lib/private/Support/Subscription/Assertion.php +++ b/lib/private/Support/Subscription/Assertion.php @@ -1,29 +1,10 @@ <?php declare(strict_types=1); - /** - * @copyright Copyright (c) 2022 Arthur Schiwon <blizzz@arthur-schiwon.de> - * - * @author Arthur Schiwon <blizzz@arthur-schiwon.de> - * - * @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 <https://www.gnu.org/licenses/>. - * + * SPDX-FileCopyrightText: 2022 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later */ - namespace OC\Support\Subscription; use OCP\HintException; diff --git a/lib/private/Support/Subscription/Registry.php b/lib/private/Support/Subscription/Registry.php index eba76ca103e..3d3daa97abb 100644 --- a/lib/private/Support/Subscription/Registry.php +++ b/lib/private/Support/Subscription/Registry.php @@ -3,27 +3,8 @@ declare(strict_types=1); /** - * @copyright Copyright (c) 2016 Morris Jobke <hey@morrisjobke.de> - * - * @author Julius Härtl <jus@bitgrid.net> - * @author Morris Jobke <hey@morrisjobke.de> - * @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/>. - * + * SPDX-FileCopyrightText: 2016 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later */ namespace OC\Support\Subscription; @@ -62,10 +43,10 @@ class Registry implements IRegistry { private $logger; public function __construct(IConfig $config, - IServerContainer $container, - IUserManager $userManager, - IGroupManager $groupManager, - LoggerInterface $logger) { + IServerContainer $container, + IUserManager $userManager, + IGroupManager $groupManager, + LoggerInterface $logger) { $this->config = $config; $this->container = $container; $this->userManager = $userManager; @@ -239,7 +220,7 @@ class Registry implements IRegistry { $notificationManager->notify($notification); } - $this->logger->warning('The user limit was reached and the new user was not created', ['app' => 'lib']); + $this->logger->warning('The account limit was reached and the new account was not created', ['app' => 'lib']); } protected function reIssue(): bool { diff --git a/lib/private/SystemConfig.php b/lib/private/SystemConfig.php index a18c4c2a138..310433474ec 100644 --- a/lib/private/SystemConfig.php +++ b/lib/private/SystemConfig.php @@ -1,28 +1,9 @@ <?php + /** - * @copyright Copyright (c) 2016, ownCloud, Inc. - * - * @author Daniel Kesselberg <mail@danielkesselberg.de> - * @author Joas Schilling <coding@schilljs.com> - * @author Johannes Schlichenmaier <johannes@schlichenmaier.info> - * @author Lukas Reschke <lukas@statuscode.ch> - * @author Morris Jobke <hey@morrisjobke.de> - * @author Roeland Jago Douma <roeland@famdouma.nl> - * - * @license AGPL-3.0 - * - * This code is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License, version 3, - * as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License, version 3, - * along with this program. If not, see <http://www.gnu.org/licenses/> - * + * SPDX-FileCopyrightText: 2016-2024 Nextcloud GmbH and Nextcloud contributors + * SPDX-FileCopyrightText: 2016 ownCloud, Inc. + * SPDX-License-Identifier: AGPL-3.0-only */ namespace OC; @@ -42,6 +23,7 @@ class SystemConfig { 'dbhost' => true, 'dbpassword' => true, 'dbuser' => true, + 'dbreplica' => true, 'activity_dbname' => true, 'activity_dbhost' => true, 'activity_dbpassword' => true, @@ -54,7 +36,10 @@ class SystemConfig { 'passwordsalt' => true, 'secret' => true, 'updater.secret' => true, + 'updater.server.url' => true, 'trusted_proxies' => true, + 'preview_imaginary_url' => true, + 'preview_imaginary_key' => true, 'proxyuserpwd' => true, 'sentry.dsn' => true, 'sentry.public-dsn' => true, @@ -118,13 +103,14 @@ class SystemConfig { ], ], ], + 'onlyoffice' => [ + 'jwt_secret' => true, + ], ]; - /** @var Config */ - private $config; - - public function __construct(Config $config) { - $this->config = $config; + public function __construct( + private Config $config, + ) { } /** diff --git a/lib/private/SystemTag/ManagerFactory.php b/lib/private/SystemTag/ManagerFactory.php index 6670922407e..0fd2ca5fd36 100644 --- a/lib/private/SystemTag/ManagerFactory.php +++ b/lib/private/SystemTag/ManagerFactory.php @@ -1,28 +1,10 @@ <?php declare(strict_types=1); - /** - * @copyright Copyright (c) 2016, ownCloud, Inc. - * - * @author Joas Schilling <coding@schilljs.com> - * @author Roeland Jago Douma <roeland@famdouma.nl> - * @author Vincent Petry <vincent@nextcloud.com> - * - * @license AGPL-3.0 - * - * This code is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License, version 3, - * as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License, version 3, - * along with this program. If not, see <http://www.gnu.org/licenses/> - * + * SPDX-FileCopyrightText: 2016-2024 Nextcloud GmbH and Nextcloud contributors + * SPDX-FileCopyrightText: 2016 ownCloud, Inc. + * SPDX-License-Identifier: AGPL-3.0-only */ namespace OC\SystemTag; @@ -40,25 +22,16 @@ use OCP\SystemTag\ISystemTagObjectMapper; */ class ManagerFactory implements ISystemTagManagerFactory { /** - * Server container - * - * @var IServerContainer - */ - private $serverContainer; - - /** * Constructor for the system tag manager factory - * - * @param IServerContainer $serverContainer server container */ - public function __construct(IServerContainer $serverContainer) { - $this->serverContainer = $serverContainer; + public function __construct( + private IServerContainer $serverContainer, + ) { } /** * Creates and returns an instance of the system tag manager * - * @return ISystemTagManager * @since 9.0.0 */ public function getManager(): ISystemTagManager { @@ -73,7 +46,6 @@ class ManagerFactory implements ISystemTagManagerFactory { * Creates and returns an instance of the system tag object * mapper * - * @return ISystemTagObjectMapper * @since 9.0.0 */ public function getObjectMapper(): ISystemTagObjectMapper { diff --git a/lib/private/SystemTag/SystemTag.php b/lib/private/SystemTag/SystemTag.php index da6d4bd4b11..83fb6bc7f93 100644 --- a/lib/private/SystemTag/SystemTag.php +++ b/lib/private/SystemTag/SystemTag.php @@ -1,68 +1,22 @@ <?php declare(strict_types=1); - /** - * @copyright Copyright (c) 2016, ownCloud, Inc. - * - * @author Joas Schilling <coding@schilljs.com> - * @author Johannes Leuker <j.leuker@hosting.de> - * @author Roeland Jago Douma <roeland@famdouma.nl> - * @author Vincent Petry <vincent@nextcloud.com> - * - * @license AGPL-3.0 - * - * This code is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License, version 3, - * as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License, version 3, - * along with this program. If not, see <http://www.gnu.org/licenses/> - * + * SPDX-FileCopyrightText: 2016-2024 Nextcloud GmbH and Nextcloud contributors + * SPDX-FileCopyrightText: 2016 ownCloud, Inc. + * SPDX-License-Identifier: AGPL-3.0-only */ namespace OC\SystemTag; use OCP\SystemTag\ISystemTag; class SystemTag implements ISystemTag { - /** - * @var string - */ - private $id; - - /** - * @var string - */ - private $name; - - /** - * @var bool - */ - private $userVisible; - - /** - * @var bool - */ - private $userAssignable; - - /** - * Constructor. - * - * @param string $id tag id - * @param string $name tag name - * @param bool $userVisible whether the tag is user visible - * @param bool $userAssignable whether the tag is user assignable - */ - public function __construct(string $id, string $name, bool $userVisible, bool $userAssignable) { - $this->id = $id; - $this->name = $name; - $this->userVisible = $userVisible; - $this->userAssignable = $userAssignable; + public function __construct( + private string $id, + private string $name, + private bool $userVisible, + private bool $userAssignable, + ) { } /** @@ -97,14 +51,14 @@ class SystemTag implements ISystemTag { * {@inheritdoc} */ public function getAccessLevel(): int { - if ($this->userVisible) { - if ($this->userAssignable) { - return self::ACCESS_LEVEL_PUBLIC; - } else { - return self::ACCESS_LEVEL_RESTRICTED; - } - } else { + if (!$this->userVisible) { return self::ACCESS_LEVEL_INVISIBLE; } + + if (!$this->userAssignable) { + return self::ACCESS_LEVEL_RESTRICTED; + } + + return self::ACCESS_LEVEL_PUBLIC; } } diff --git a/lib/private/SystemTag/SystemTagManager.php b/lib/private/SystemTag/SystemTagManager.php index c52c350b6f8..e99213b618b 100644 --- a/lib/private/SystemTag/SystemTagManager.php +++ b/lib/private/SystemTag/SystemTagManager.php @@ -1,31 +1,10 @@ <?php declare(strict_types=1); - /** - * @copyright Copyright (c) 2016, ownCloud, Inc. - * - * @author Christoph Wurst <christoph@winzerhof-wurst.at> - * @author Joas Schilling <coding@schilljs.com> - * @author Jörn Friedrich Dreyer <jfd@butonic.de> - * @author Morris Jobke <hey@morrisjobke.de> - * @author Roeland Jago Douma <roeland@famdouma.nl> - * @author Vincent Petry <vincent@nextcloud.com> - * - * @license AGPL-3.0 - * - * This code is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License, version 3, - * as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License, version 3, - * along with this program. If not, see <http://www.gnu.org/licenses/> - * + * SPDX-FileCopyrightText: 2016-2024 Nextcloud GmbH and Nextcloud contributors + * SPDX-FileCopyrightText: 2016 ownCloud, Inc. + * SPDX-License-Identifier: AGPL-3.0-only */ namespace OC\SystemTag; @@ -50,10 +29,8 @@ class SystemTagManager implements ISystemTagManager { /** * Prepared query for selecting tags directly - * - * @var \OCP\DB\QueryBuilder\IQueryBuilder */ - private $selectTagQuery; + private IQueryBuilder $selectTagQuery; public function __construct( protected IDBConnection $connection, @@ -219,7 +196,12 @@ class SystemTagManager implements ISystemTagManager { /** * {@inheritdoc} */ - public function updateTag(string $tagId, string $newName, bool $userVisible, bool $userAssignable) { + public function updateTag( + string $tagId, + string $newName, + bool $userVisible, + bool $userAssignable, + ): void { try { $tags = $this->getTagsByIds($tagId); } catch (TagNotFoundException $e) { @@ -271,7 +253,7 @@ class SystemTagManager implements ISystemTagManager { /** * {@inheritdoc} */ - public function deleteTags($tagIds) { + public function deleteTags($tagIds): void { if (!\is_array($tagIds)) { $tagIds = [$tagIds]; } @@ -363,14 +345,14 @@ class SystemTagManager implements ISystemTagManager { return false; } - private function createSystemTagFromRow($row) { + private function createSystemTagFromRow($row): SystemTag { return new SystemTag((string)$row['id'], $row['name'], (bool)$row['visibility'], (bool)$row['editable']); } /** * {@inheritdoc} */ - public function setTagGroups(ISystemTag $tag, array $groupIds) { + public function setTagGroups(ISystemTag $tag, array $groupIds): void { // delete relationships first $this->connection->beginTransaction(); try { diff --git a/lib/private/SystemTag/SystemTagObjectMapper.php b/lib/private/SystemTag/SystemTagObjectMapper.php index 66a21e58609..58a9e6a23da 100644 --- a/lib/private/SystemTag/SystemTagObjectMapper.php +++ b/lib/private/SystemTag/SystemTagObjectMapper.php @@ -3,27 +3,9 @@ declare(strict_types=1); /** - * @copyright Copyright (c) 2016, ownCloud, Inc. - * - * @author Christoph Wurst <christoph@winzerhof-wurst.at> - * @author Joas Schilling <coding@schilljs.com> - * @author Roeland Jago Douma <roeland@famdouma.nl> - * @author Vincent Petry <vincent@nextcloud.com> - * - * @license AGPL-3.0 - * - * This code is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License, version 3, - * as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License, version 3, - * along with this program. If not, see <http://www.gnu.org/licenses/> - * + * SPDX-FileCopyrightText: 2016-2024 Nextcloud GmbH and Nextcloud contributors + * SPDX-FileCopyrightText: 2016 ownCloud, Inc. + * SPDX-License-Identifier: AGPL-3.0-only */ namespace OC\SystemTag; @@ -81,7 +63,6 @@ class SystemTagObjectMapper implements ISystemTagObjectMapper { $result->closeCursor(); } - return $mapping; } @@ -128,7 +109,7 @@ class SystemTagObjectMapper implements ISystemTagObjectMapper { /** * {@inheritdoc} */ - public function assignTags(string $objId, string $objectType, $tagIds) { + public function assignTags(string $objId, string $objectType, $tagIds): void { if (!\is_array($tagIds)) { $tagIds = [$tagIds]; } @@ -169,7 +150,7 @@ class SystemTagObjectMapper implements ISystemTagObjectMapper { /** * {@inheritdoc} */ - public function unassignTags(string $objId, string $objectType, $tagIds) { + public function unassignTags(string $objId, string $objectType, $tagIds): void { if (!\is_array($tagIds)) { $tagIds = [$tagIds]; } @@ -241,7 +222,7 @@ class SystemTagObjectMapper implements ISystemTagObjectMapper { * * @throws \OCP\SystemTag\TagNotFoundException if at least one tag did not exist */ - private function assertTagsExist($tagIds) { + private function assertTagsExist(array $tagIds): void { $tags = $this->tagManager->getTagsByIds($tagIds); if (\count($tags) !== \count($tagIds)) { // at least one tag missing, bail out diff --git a/lib/private/SystemTag/SystemTagsInFilesDetector.php b/lib/private/SystemTag/SystemTagsInFilesDetector.php index c9f26c58c02..a8dfec264a0 100644 --- a/lib/private/SystemTag/SystemTagsInFilesDetector.php +++ b/lib/private/SystemTag/SystemTagsInFilesDetector.php @@ -1,29 +1,10 @@ <?php declare(strict_types=1); - /** - * @copyright Copyright (c) 2023 Arthur Schiwon <blizzz@arthur-schiwon.de> - * - * @author Arthur Schiwon <blizzz@arthur-schiwon.de> - * - * @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 <https://www.gnu.org/licenses/>. - * + * SPDX-FileCopyrightText: 2023 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later */ - namespace OC\SystemTag; use OC\Files\Cache\QuerySearchHelper; @@ -36,7 +17,9 @@ use OCP\Files\Search\ISearchBinaryOperator; use OCP\Files\Search\ISearchComparison; class SystemTagsInFilesDetector { - public function __construct(protected QuerySearchHelper $searchHelper) { + public function __construct( + protected QuerySearchHelper $searchHelper, + ) { } public function detectAssignedSystemTagsIn( diff --git a/lib/private/TagManager.php b/lib/private/TagManager.php index 82c4dd2188d..3258ce50bc5 100644 --- a/lib/private/TagManager.php +++ b/lib/private/TagManager.php @@ -1,32 +1,14 @@ <?php + /** - * @copyright Copyright (c) 2016, ownCloud, Inc. - * - * @author Bernhard Reiter <ockham@raz.or.at> - * @author Christoph Wurst <christoph@winzerhof-wurst.at> - * @author Joas Schilling <coding@schilljs.com> - * @author Morris Jobke <hey@morrisjobke.de> - * @author Thomas Tanghus <thomas@tanghus.net> - * @author Vincent Petry <vincent@nextcloud.com> - * - * @license AGPL-3.0 - * - * This code is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License, version 3, - * as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License, version 3, - * along with this program. If not, see <http://www.gnu.org/licenses/> - * + * SPDX-FileCopyrightText: 2016-2024 Nextcloud GmbH and Nextcloud contributors + * SPDX-FileCopyrightText: 2016 ownCloud, Inc. + * SPDX-License-Identifier: AGPL-3.0-only */ namespace OC; use OC\Tagging\TagMapper; +use OCP\Db\Exception as DBException; use OCP\DB\QueryBuilder\IQueryBuilder; use OCP\EventDispatcher\Event; use OCP\EventDispatcher\IEventListener; @@ -35,7 +17,6 @@ use OCP\ITagManager; use OCP\ITags; use OCP\IUserSession; use OCP\User\Events\UserDeletedEvent; -use OCP\Db\Exception as DBException; use Psr\Log\LoggerInterface; /** diff --git a/lib/private/Tagging/Tag.php b/lib/private/Tagging/Tag.php index 17af385418d..ae770beb62f 100644 --- a/lib/private/Tagging/Tag.php +++ b/lib/private/Tagging/Tag.php @@ -1,25 +1,9 @@ <?php + /** - * @copyright Copyright (c) 2016, ownCloud, Inc. - * - * @author Bernhard Reiter <ockham@raz.or.at> - * @author Christoph Wurst <christoph@winzerhof-wurst.at> - * @author Roeland Jago Douma <roeland@famdouma.nl> - * - * @license AGPL-3.0 - * - * This code is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License, version 3, - * as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License, version 3, - * along with this program. If not, see <http://www.gnu.org/licenses/> - * + * SPDX-FileCopyrightText: 2016-2024 Nextcloud GmbH and Nextcloud contributors + * SPDX-FileCopyrightText: 2016 ownCloud, Inc. + * SPDX-License-Identifier: AGPL-3.0-only */ namespace OC\Tagging; diff --git a/lib/private/Tagging/TagMapper.php b/lib/private/Tagging/TagMapper.php index 1ee9c33acf7..efac45a2e3a 100644 --- a/lib/private/Tagging/TagMapper.php +++ b/lib/private/Tagging/TagMapper.php @@ -1,34 +1,16 @@ <?php + /** - * @copyright Copyright (c) 2016, ownCloud, Inc. - * - * @author Bernhard Posselt <dev@bernhard-posselt.com> - * @author Bernhard Reiter <ockham@raz.or.at> - * @author Christoph Wurst <christoph@winzerhof-wurst.at> - * @author Morris Jobke <hey@morrisjobke.de> - * @author Roeland Jago Douma <roeland@famdouma.nl> - * - * @license AGPL-3.0 - * - * This code is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License, version 3, - * as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License, version 3, - * along with this program. If not, see <http://www.gnu.org/licenses/> - * + * SPDX-FileCopyrightText: 2016-2024 Nextcloud GmbH and Nextcloud contributors + * SPDX-FileCopyrightText: 2016 ownCloud, Inc. + * SPDX-License-Identifier: AGPL-3.0-only */ namespace OC\Tagging; use OCP\AppFramework\Db\DoesNotExistException; use OCP\AppFramework\Db\QBMapper; -use OCP\IDBConnection; use OCP\DB\QueryBuilder\IQueryBuilder; +use OCP\IDBConnection; /** * Mapper for Tag entity diff --git a/lib/private/Tags.php b/lib/private/Tags.php index 8da1e7edc3b..420cabc36b9 100644 --- a/lib/private/Tags.php +++ b/lib/private/Tags.php @@ -1,32 +1,9 @@ <?php + /** - * @copyright Copyright (c) 2016, ownCloud, Inc. - * - * @author Arthur Schiwon <blizzz@arthur-schiwon.de> - * @author Bernhard Reiter <ockham@raz.or.at> - * @author Christoph Wurst <christoph@winzerhof-wurst.at> - * @author Daniel Kesselberg <mail@danielkesselberg.de> - * @author Joas Schilling <coding@schilljs.com> - * @author Morris Jobke <hey@morrisjobke.de> - * @author Robin Appelman <robin@icewind.nl> - * @author Roeland Jago Douma <roeland@famdouma.nl> - * @author Thomas Tanghus <thomas@tanghus.net> - * @author Vincent Petry <vincent@nextcloud.com> - * - * @license AGPL-3.0 - * - * This code is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License, version 3, - * as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License, version 3, - * along with this program. If not, see <http://www.gnu.org/licenses/> - * + * SPDX-FileCopyrightText: 2016-2024 Nextcloud GmbH and Nextcloud contributors + * SPDX-FileCopyrightText: 2016 ownCloud, Inc. + * SPDX-License-Identifier: AGPL-3.0-only */ namespace OC; @@ -35,7 +12,6 @@ use OC\Tagging\TagMapper; use OCP\DB\Exception; use OCP\DB\QueryBuilder\IQueryBuilder; use OCP\IDBConnection; -use OCP\ILogger; use OCP\ITags; use OCP\Share_Backend; use Psr\Log\LoggerInterface; @@ -235,7 +211,7 @@ class Tags implements ITags { } if ($tagId === false) { - $l10n = \OC::$server->getL10N('core'); + $l10n = \OCP\Util::getL10N('core'); throw new \Exception( $l10n->t('Could not find category "%s"', [$tag]) ); @@ -486,11 +462,13 @@ class Tags implements ITags { try { return $this->getIdsForTag(ITags::TAG_FAVORITE); } catch (\Exception $e) { - \OC::$server->getLogger()->logException($e, [ - 'message' => __METHOD__, - 'level' => ILogger::ERROR, - 'app' => 'core', - ]); + \OCP\Server::get(LoggerInterface::class)->error( + $e->getMessage(), + [ + 'app' => 'core', + 'exception' => $e, + ] + ); return []; } } @@ -529,7 +507,7 @@ class Tags implements ITags { if (is_string($tag) && !is_numeric($tag)) { $tag = trim($tag); if ($tag === '') { - \OCP\Util::writeLog('core', __METHOD__.', Cannot add an empty tag', ILogger::DEBUG); + $this->logger->debug(__METHOD__.', Cannot add an empty tag'); return false; } if (!$this->hasTag($tag)) { @@ -549,7 +527,7 @@ class Tags implements ITags { try { $qb->executeStatement(); } catch (\Exception $e) { - \OC::$server->getLogger()->error($e->getMessage(), [ + \OCP\Server::get(LoggerInterface::class)->error($e->getMessage(), [ 'app' => 'core', 'exception' => $e, ]); @@ -569,7 +547,7 @@ class Tags implements ITags { if (is_string($tag) && !is_numeric($tag)) { $tag = trim($tag); if ($tag === '') { - \OCP\Util::writeLog('core', __METHOD__.', Tag name is empty', ILogger::DEBUG); + $this->logger->debug(__METHOD__.', Tag name is empty'); return false; } $tagId = $this->getTagId($tag); @@ -609,8 +587,7 @@ class Tags implements ITags { $names = array_map('trim', $names); array_filter($names); - \OCP\Util::writeLog('core', __METHOD__ . ', before: ' - . print_r($this->tags, true), ILogger::DEBUG); + $this->logger->debug(__METHOD__ . ', before: ' . print_r($this->tags, true)); foreach ($names as $name) { $id = null; @@ -625,8 +602,7 @@ class Tags implements ITags { unset($this->tags[$key]); $this->mapper->delete($tag); } else { - \OCP\Util::writeLog('core', __METHOD__ . 'Cannot delete tag ' . $name - . ': not found.', ILogger::ERROR); + $this->logger->error(__METHOD__ . 'Cannot delete tag ' . $name . ': not found.'); } if (!is_null($id) && $id !== false) { try { diff --git a/lib/private/Talk/Broker.php b/lib/private/Talk/Broker.php index 12e6c5fb34b..fc8e0280043 100644 --- a/lib/private/Talk/Broker.php +++ b/lib/private/Talk/Broker.php @@ -2,25 +2,9 @@ declare(strict_types=1); -/* - * @copyright 2022 Christoph Wurst <christoph@winzerhof-wurst.at> - * - * @author 2021 Christoph Wurst <christoph@winzerhof-wurst.at> - * - * @license GNU AGPL version 3 or any later version - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as - * published by the Free Software Foundation, either version 3 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. +/** + * SPDX-FileCopyrightText: 2022 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later */ namespace OC\Talk; @@ -48,8 +32,8 @@ class Broker implements IBroker { private ?ITalkBackend $backend = null; public function __construct(Coordinator $coordinator, - IServerContainer $container, - LoggerInterface $logger) { + IServerContainer $container, + LoggerInterface $logger) { $this->coordinator = $coordinator; $this->container = $container; $this->logger = $logger; @@ -94,8 +78,8 @@ class Broker implements IBroker { } public function createConversation(string $name, - array $moderators, - IConversationOptions $options = null): IConversation { + array $moderators, + ?IConversationOptions $options = null): IConversation { if (!$this->hasBackend()) { throw new NoBackendException("The Talk broker has no registered backend"); } diff --git a/lib/private/Talk/ConversationOptions.php b/lib/private/Talk/ConversationOptions.php index f86c8d037d3..f167fa6d164 100644 --- a/lib/private/Talk/ConversationOptions.php +++ b/lib/private/Talk/ConversationOptions.php @@ -2,25 +2,9 @@ declare(strict_types=1); -/* - * @copyright 2022 Christoph Wurst <christoph@winzerhof-wurst.at> - * - * @author 2022 Christoph Wurst <christoph@winzerhof-wurst.at> - * - * @license GNU AGPL version 3 or any later version - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as - * published by the Free Software Foundation, either version 3 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. +/** + * SPDX-FileCopyrightText: 2022 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later */ namespace OC\Talk; diff --git a/lib/private/TaskProcessing/Db/Task.php b/lib/private/TaskProcessing/Db/Task.php new file mode 100644 index 00000000000..1ac40327899 --- /dev/null +++ b/lib/private/TaskProcessing/Db/Task.php @@ -0,0 +1,119 @@ +<?php + +declare(strict_types=1); + +/** + * SPDX-FileCopyrightText: 2023 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later + */ + +namespace OC\TaskProcessing\Db; + +use OCP\AppFramework\Db\Entity; +use OCP\TaskProcessing\Task as OCPTask; + +/** + * @method setType(string $type) + * @method string getType() + * @method setLastUpdated(int $lastUpdated) + * @method int getLastUpdated() + * @method setStatus(int $status) + * @method int getStatus() + * @method setOutput(string $output) + * @method string getOutput() + * @method setInput(string $input) + * @method string getInput() + * @method setUserId(?string $userId) + * @method string|null getUserId() + * @method setAppId(string $type) + * @method string getAppId() + * @method setCustomId(string $customId) + * @method string getCustomId() + * @method setCompletionExpectedAt(null|\DateTime $completionExpectedAt) + * @method null|\DateTime getCompletionExpectedAt() + * @method setErrorMessage(null|string $error) + * @method null|string getErrorMessage() + * @method setProgress(null|float $progress) + * @method null|float getProgress() + */ +class Task extends Entity { + protected $lastUpdated; + protected $type; + protected $input; + protected $output; + protected $status; + protected $userId; + protected $appId; + protected $customId; + protected $completionExpectedAt; + protected $errorMessage; + protected $progress; + + /** + * @var string[] + */ + public static array $columns = ['id', 'last_updated', 'type', 'input', 'output', 'status', 'user_id', 'app_id', 'custom_id', 'completion_expected_at', 'error_message', 'progress']; + + /** + * @var string[] + */ + public static array $fields = ['id', 'lastUpdated', 'type', 'input', 'output', 'status', 'userId', 'appId', 'customId', 'completionExpectedAt', 'errorMessage', 'progress']; + + + public function __construct() { + // add types in constructor + $this->addType('id', 'integer'); + $this->addType('lastUpdated', 'integer'); + $this->addType('type', 'string'); + $this->addType('input', 'string'); + $this->addType('output', 'string'); + $this->addType('status', 'integer'); + $this->addType('userId', 'string'); + $this->addType('appId', 'string'); + $this->addType('customId', 'string'); + $this->addType('completionExpectedAt', 'datetime'); + $this->addType('errorMessage', 'string'); + $this->addType('progress', 'float'); + } + + public function toRow(): array { + return array_combine(self::$columns, array_map(function ($field) { + return $this->{'get'.ucfirst($field)}(); + }, self::$fields)); + } + + public static function fromPublicTask(OCPTask $task): self { + /** @var Task $taskEntity */ + $taskEntity = self::fromParams([ + 'id' => $task->getId(), + 'type' => $task->getTaskTypeId(), + 'lastUpdated' => time(), + 'status' => $task->getStatus(), + 'input' => json_encode($task->getInput(), JSON_THROW_ON_ERROR), + 'output' => json_encode($task->getOutput(), JSON_THROW_ON_ERROR), + 'errorMessage' => $task->getErrorMessage(), + 'userId' => $task->getUserId(), + 'appId' => $task->getAppId(), + 'customId' => $task->getCustomId(), + 'completionExpectedAt' => $task->getCompletionExpectedAt(), + 'progress' => $task->getProgress(), + ]); + return $taskEntity; + } + + /** + * @return OCPTask + * @throws \JsonException + */ + public function toPublicTask(): OCPTask { + $task = new OCPTask($this->getType(), json_decode($this->getInput(), true, 512, JSON_THROW_ON_ERROR), $this->getAppId(), $this->getuserId(), $this->getCustomId()); + $task->setId($this->getId()); + $task->setStatus($this->getStatus()); + $task->setLastUpdated($this->getLastUpdated()); + $task->setOutput(json_decode($this->getOutput(), true, 512, JSON_THROW_ON_ERROR)); + $task->setCompletionExpectedAt($this->getCompletionExpectedAt()); + $task->setErrorMessage($this->getErrorMessage()); + $task->setProgress($this->getProgress()); + return $task; + } +} diff --git a/lib/private/TaskProcessing/Db/TaskMapper.php b/lib/private/TaskProcessing/Db/TaskMapper.php new file mode 100644 index 00000000000..e66c62007cc --- /dev/null +++ b/lib/private/TaskProcessing/Db/TaskMapper.php @@ -0,0 +1,143 @@ +<?php + +declare(strict_types=1); + +/** + * SPDX-FileCopyrightText: 2023 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later + */ + +namespace OC\TaskProcessing\Db; + +use OCP\AppFramework\Db\DoesNotExistException; +use OCP\AppFramework\Db\Entity; +use OCP\AppFramework\Db\MultipleObjectsReturnedException; +use OCP\AppFramework\Db\QBMapper; +use OCP\AppFramework\Utility\ITimeFactory; +use OCP\DB\Exception; +use OCP\DB\QueryBuilder\IQueryBuilder; +use OCP\IDBConnection; + +/** + * @extends QBMapper<Task> + */ +class TaskMapper extends QBMapper { + public function __construct( + IDBConnection $db, + private ITimeFactory $timeFactory, + ) { + parent::__construct($db, 'taskprocessing_tasks', Task::class); + } + + /** + * @param int $id + * @return Task + * @throws Exception + * @throws DoesNotExistException + * @throws MultipleObjectsReturnedException + */ + public function find(int $id): Task { + $qb = $this->db->getQueryBuilder(); + $qb->select(Task::$columns) + ->from($this->tableName) + ->where($qb->expr()->eq('id', $qb->createPositionalParameter($id))); + return $this->findEntity($qb); + } + + /** + * @param string|null $taskType + * @return Task + * @throws DoesNotExistException + * @throws Exception + */ + public function findOldestScheduledByType(?string $taskType): Task { + $qb = $this->db->getQueryBuilder(); + $qb->select(Task::$columns) + ->from($this->tableName) + ->where($qb->expr()->eq('status', $qb->createPositionalParameter(\OCP\TaskProcessing\Task::STATUS_SCHEDULED, IQueryBuilder::PARAM_INT))) + ->setMaxResults(1) + ->orderBy('last_updated', 'ASC'); + if ($taskType !== null) { + $qb->andWhere($qb->expr()->eq('type', $qb->createPositionalParameter($taskType))); + } + return $this->findEntity($qb); + } + + /** + * @param int $id + * @param string|null $userId + * @return Task + * @throws DoesNotExistException + * @throws Exception + * @throws MultipleObjectsReturnedException + */ + public function findByIdAndUser(int $id, ?string $userId): Task { + $qb = $this->db->getQueryBuilder(); + $qb->select(Task::$columns) + ->from($this->tableName) + ->where($qb->expr()->eq('id', $qb->createPositionalParameter($id))); + if ($userId === null) { + $qb->andWhere($qb->expr()->isNull('user_id')); + } else { + $qb->andWhere($qb->expr()->eq('user_id', $qb->createPositionalParameter($userId))); + } + return $this->findEntity($qb); + } + + /** + * @param string|null $userId + * @param string|null $taskType + * @param string|null $customId + * @return list<Task> + * @throws Exception + */ + public function findByUserAndTaskType(?string $userId, ?string $taskType = null, ?string $customId = null): array { + $qb = $this->db->getQueryBuilder(); + $qb->select(Task::$columns) + ->from($this->tableName) + ->where($qb->expr()->eq('user_id', $qb->createPositionalParameter($userId))); + if ($taskType !== null) { + $qb->andWhere($qb->expr()->eq('type', $qb->createPositionalParameter($taskType))); + } + if ($customId !== null) { + $qb->andWhere($qb->expr()->eq('custom_id', $qb->createPositionalParameter($customId))); + } + return array_values($this->findEntities($qb)); + } + + /** + * @param string $userId + * @param string $appId + * @param string|null $customId + * @return list<Task> + * @throws Exception + */ + public function findUserTasksByApp(?string $userId, string $appId, ?string $customId = null): array { + $qb = $this->db->getQueryBuilder(); + $qb->select(Task::$columns) + ->from($this->tableName) + ->where($qb->expr()->eq('user_id', $qb->createPositionalParameter($userId))) + ->andWhere($qb->expr()->eq('app_id', $qb->createPositionalParameter($appId))); + if ($customId !== null) { + $qb->andWhere($qb->expr()->eq('custom_id', $qb->createPositionalParameter($customId))); + } + return array_values($this->findEntities($qb)); + } + + /** + * @param int $timeout + * @return int the number of deleted tasks + * @throws Exception + */ + public function deleteOlderThan(int $timeout): int { + $qb = $this->db->getQueryBuilder(); + $qb->delete($this->tableName) + ->where($qb->expr()->lt('last_updated', $qb->createPositionalParameter($this->timeFactory->getDateTime()->getTimestamp() - $timeout))); + return $qb->executeStatement(); + } + + public function update(Entity $entity): Entity { + $entity->setLastUpdated($this->timeFactory->now()->getTimestamp()); + return parent::update($entity); + } +} diff --git a/lib/private/TaskProcessing/Manager.php b/lib/private/TaskProcessing/Manager.php new file mode 100644 index 00000000000..7dfdafe0d14 --- /dev/null +++ b/lib/private/TaskProcessing/Manager.php @@ -0,0 +1,866 @@ +<?php + +declare(strict_types=1); + +/** + * SPDX-FileCopyrightText: 2024 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later + */ + +namespace OC\TaskProcessing; + +use OC\AppFramework\Bootstrap\Coordinator; +use OC\Files\SimpleFS\SimpleFile; +use OC\TaskProcessing\Db\TaskMapper; +use OCP\AppFramework\Db\DoesNotExistException; +use OCP\AppFramework\Db\MultipleObjectsReturnedException; +use OCP\BackgroundJob\IJobList; +use OCP\EventDispatcher\IEventDispatcher; +use OCP\Files\AppData\IAppDataFactory; +use OCP\Files\File; +use OCP\Files\GenericFileException; +use OCP\Files\IAppData; +use OCP\Files\IRootFolder; +use OCP\Files\NotPermittedException; +use OCP\Files\SimpleFS\ISimpleFile; +use OCP\IL10N; +use OCP\IServerContainer; +use OCP\L10N\IFactory; +use OCP\Lock\LockedException; +use OCP\SpeechToText\ISpeechToTextProvider; +use OCP\SpeechToText\ISpeechToTextProviderWithId; +use OCP\TaskProcessing\EShapeType; +use OCP\TaskProcessing\Events\TaskFailedEvent; +use OCP\TaskProcessing\Events\TaskSuccessfulEvent; +use OCP\TaskProcessing\Exception\NotFoundException; +use OCP\TaskProcessing\Exception\ProcessingException; +use OCP\TaskProcessing\Exception\UnauthorizedException; +use OCP\TaskProcessing\Exception\ValidationException; +use OCP\TaskProcessing\IManager; +use OCP\TaskProcessing\IProvider; +use OCP\TaskProcessing\ISynchronousProvider; +use OCP\TaskProcessing\ITaskType; +use OCP\TaskProcessing\ShapeDescriptor; +use OCP\TaskProcessing\Task; +use OCP\TaskProcessing\TaskTypes\AudioToText; +use OCP\TaskProcessing\TaskTypes\TextToImage; +use OCP\TaskProcessing\TaskTypes\TextToText; +use OCP\TaskProcessing\TaskTypes\TextToTextHeadline; +use OCP\TaskProcessing\TaskTypes\TextToTextSummary; +use OCP\TaskProcessing\TaskTypes\TextToTextTopics; +use Psr\Log\LoggerInterface; + +class Manager implements IManager { + + public const LEGACY_PREFIX_TEXTPROCESSING = 'legacy:TextProcessing:'; + public const LEGACY_PREFIX_TEXTTOIMAGE = 'legacy:TextToImage:'; + public const LEGACY_PREFIX_SPEECHTOTEXT = 'legacy:SpeechToText:'; + + /** @var list<IProvider>|null */ + private ?array $providers = null; + + /** @var array<string,array{name: string, description: string, inputShape: array<string, ShapeDescriptor>, optionalInputShape: array<string, ShapeDescriptor>, outputShape: array<string, ShapeDescriptor>, optionalOutputShape: array<string, ShapeDescriptor>}>|null */ + private ?array $availableTaskTypes = null; + + private IAppData $appData; + + public function __construct( + private Coordinator $coordinator, + private IServerContainer $serverContainer, + private LoggerInterface $logger, + private TaskMapper $taskMapper, + private IJobList $jobList, + private IEventDispatcher $dispatcher, + IAppDataFactory $appDataFactory, + private IRootFolder $rootFolder, + private \OCP\TextProcessing\IManager $textProcessingManager, + private \OCP\TextToImage\IManager $textToImageManager, + private \OCP\SpeechToText\ISpeechToTextManager $speechToTextManager, + private \OCP\Share\IManager $shareManager, + ) { + $this->appData = $appDataFactory->get('core'); + } + + /** + * @return IProvider[] + */ + private function _getTextProcessingProviders(): array { + $oldProviders = $this->textProcessingManager->getProviders(); + $newProviders = []; + foreach ($oldProviders as $oldProvider) { + $provider = new class($oldProvider) implements IProvider, ISynchronousProvider { + private \OCP\TextProcessing\IProvider $provider; + + public function __construct(\OCP\TextProcessing\IProvider $provider) { + $this->provider = $provider; + } + + public function getId(): string { + if ($this->provider instanceof \OCP\TextProcessing\IProviderWithId) { + return $this->provider->getId(); + } + return Manager::LEGACY_PREFIX_TEXTPROCESSING . $this->provider::class; + } + + public function getName(): string { + return $this->provider->getName(); + } + + public function getTaskTypeId(): string { + return match ($this->provider->getTaskType()) { + \OCP\TextProcessing\FreePromptTaskType::class => TextToText::ID, + \OCP\TextProcessing\HeadlineTaskType::class => TextToTextHeadline::ID, + \OCP\TextProcessing\TopicsTaskType::class => TextToTextTopics::ID, + \OCP\TextProcessing\SummaryTaskType::class => TextToTextSummary::ID, + default => Manager::LEGACY_PREFIX_TEXTPROCESSING . $this->provider->getTaskType(), + }; + } + + public function getExpectedRuntime(): int { + if ($this->provider instanceof \OCP\TextProcessing\IProviderWithExpectedRuntime) { + return $this->provider->getExpectedRuntime(); + } + return 60; + } + + public function getOptionalInputShape(): array { + return []; + } + + public function getOptionalOutputShape(): array { + return []; + } + + public function process(?string $userId, array $input, callable $reportProgress): array { + if ($this->provider instanceof \OCP\TextProcessing\IProviderWithUserId) { + $this->provider->setUserId($userId); + } + try { + return ['output' => $this->provider->process($input['input'])]; + } catch(\RuntimeException $e) { + throw new ProcessingException($e->getMessage(), 0, $e); + } + } + }; + $newProviders[$provider->getId()] = $provider; + } + + return $newProviders; + } + + /** + * @return ITaskType[] + */ + private function _getTextProcessingTaskTypes(): array { + $oldProviders = $this->textProcessingManager->getProviders(); + $newTaskTypes = []; + foreach ($oldProviders as $oldProvider) { + // These are already implemented in the TaskProcessing realm + if (in_array($oldProvider->getTaskType(), [ + \OCP\TextProcessing\FreePromptTaskType::class, + \OCP\TextProcessing\HeadlineTaskType::class, + \OCP\TextProcessing\TopicsTaskType::class, + \OCP\TextProcessing\SummaryTaskType::class + ], true)) { + continue; + } + $taskType = new class($oldProvider->getTaskType()) implements ITaskType { + private string $oldTaskTypeClass; + private \OCP\TextProcessing\ITaskType $oldTaskType; + private IL10N $l; + + public function __construct(string $oldTaskTypeClass) { + $this->oldTaskTypeClass = $oldTaskTypeClass; + $this->oldTaskType = \OCP\Server::get($oldTaskTypeClass); + $this->l = \OCP\Server::get(IFactory::class)->get('core'); + } + + public function getId(): string { + return Manager::LEGACY_PREFIX_TEXTPROCESSING . $this->oldTaskTypeClass; + } + + public function getName(): string { + return $this->oldTaskType->getName(); + } + + public function getDescription(): string { + return $this->oldTaskType->getDescription(); + } + + public function getInputShape(): array { + return ['input' => new ShapeDescriptor($this->l->t('Input text'), $this->l->t('The input text'), EShapeType::Text)]; + } + + public function getOutputShape(): array { + return ['output' => new ShapeDescriptor($this->l->t('Input text'), $this->l->t('The input text'), EShapeType::Text)]; + } + }; + $newTaskTypes[$taskType->getId()] = $taskType; + } + + return $newTaskTypes; + } + + /** + * @return IProvider[] + */ + private function _getTextToImageProviders(): array { + $oldProviders = $this->textToImageManager->getProviders(); + $newProviders = []; + foreach ($oldProviders as $oldProvider) { + $newProvider = new class($oldProvider, $this->appData) implements IProvider, ISynchronousProvider { + private \OCP\TextToImage\IProvider $provider; + private IAppData $appData; + + public function __construct(\OCP\TextToImage\IProvider $provider, IAppData $appData) { + $this->provider = $provider; + $this->appData = $appData; + } + + public function getId(): string { + return Manager::LEGACY_PREFIX_TEXTTOIMAGE . $this->provider->getId(); + } + + public function getName(): string { + return $this->provider->getName(); + } + + public function getTaskTypeId(): string { + return TextToImage::ID; + } + + public function getExpectedRuntime(): int { + return $this->provider->getExpectedRuntime(); + } + + public function getOptionalInputShape(): array { + return []; + } + + public function getOptionalOutputShape(): array { + return []; + } + + public function process(?string $userId, array $input, callable $reportProgress): array { + try { + $folder = $this->appData->getFolder('text2image'); + } catch(\OCP\Files\NotFoundException) { + $folder = $this->appData->newFolder('text2image'); + } + $resources = []; + $files = []; + for ($i = 0; $i < $input['numberOfImages']; $i++) { + $file = $folder->newFile(time() . '-' . rand(1, 100000) . '-' . $i); + $files[] = $file; + $resource = $file->write(); + if ($resource !== false && $resource !== true && is_resource($resource)) { + $resources[] = $resource; + } else { + throw new ProcessingException('Text2Image generation using provider "' . $this->getName() . '" failed: Couldn\'t open file to write.'); + } + } + if ($this->provider instanceof \OCP\TextToImage\IProviderWithUserId) { + $this->provider->setUserId($userId); + } + try { + $this->provider->generate($input['input'], $resources); + } catch (\RuntimeException $e) { + throw new ProcessingException($e->getMessage(), 0, $e); + } + for ($i = 0; $i < $input['numberOfImages']; $i++) { + if (is_resource($resources[$i])) { + // If $resource hasn't been closed yet, we'll do that here + fclose($resources[$i]); + } + } + return ['images' => array_map(fn (ISimpleFile $file) => $file->getContent(), $files)]; + } + }; + $newProviders[$newProvider->getId()] = $newProvider; + } + + return $newProviders; + } + + + /** + * @return IProvider[] + */ + private function _getSpeechToTextProviders(): array { + $oldProviders = $this->speechToTextManager->getProviders(); + $newProviders = []; + foreach ($oldProviders as $oldProvider) { + $newProvider = new class($oldProvider, $this->rootFolder, $this->appData) implements IProvider, ISynchronousProvider { + private ISpeechToTextProvider $provider; + private IAppData $appData; + + private IRootFolder $rootFolder; + + public function __construct(ISpeechToTextProvider $provider, IRootFolder $rootFolder, IAppData $appData) { + $this->provider = $provider; + $this->rootFolder = $rootFolder; + $this->appData = $appData; + } + + public function getId(): string { + if ($this->provider instanceof ISpeechToTextProviderWithId) { + return Manager::LEGACY_PREFIX_SPEECHTOTEXT . $this->provider->getId(); + } + return Manager::LEGACY_PREFIX_SPEECHTOTEXT . $this->provider::class; + } + + public function getName(): string { + return $this->provider->getName(); + } + + public function getTaskTypeId(): string { + return AudioToText::ID; + } + + public function getExpectedRuntime(): int { + return 60; + } + + public function getOptionalInputShape(): array { + return []; + } + + public function getOptionalOutputShape(): array { + return []; + } + + public function process(?string $userId, array $input, callable $reportProgress): array { + try { + $result = $this->provider->transcribeFile($input['input']); + } catch (\RuntimeException $e) { + throw new ProcessingException($e->getMessage(), 0, $e); + } + return ['output' => $result]; + } + }; + $newProviders[$newProvider->getId()] = $newProvider; + } + + return $newProviders; + } + + /** + * @return IProvider[] + */ + private function _getProviders(): array { + $context = $this->coordinator->getRegistrationContext(); + + if ($context === null) { + return []; + } + + $providers = []; + + foreach ($context->getTaskProcessingProviders() as $providerServiceRegistration) { + $class = $providerServiceRegistration->getService(); + try { + /** @var IProvider $provider */ + $provider = $this->serverContainer->get($class); + if (isset($providers[$provider->getId()])) { + $this->logger->warning('Task processing provider ' . $class . ' is using ID ' . $provider->getId() . ' which is already used by ' . $providers[$provider->getId()]::class); + } + $providers[$provider->getId()] = $provider; + } catch (\Throwable $e) { + $this->logger->error('Failed to load task processing provider ' . $class, [ + 'exception' => $e, + ]); + } + } + + $providers += $this->_getTextProcessingProviders() + $this->_getTextToImageProviders() + $this->_getSpeechToTextProviders(); + + return $providers; + } + + /** + * @return ITaskType[] + */ + private function _getTaskTypes(): array { + $context = $this->coordinator->getRegistrationContext(); + + if ($context === null) { + return []; + } + + // Default task types + $taskTypes = [ + \OCP\TaskProcessing\TaskTypes\TextToText::ID => \OCP\Server::get(\OCP\TaskProcessing\TaskTypes\TextToText::class), + \OCP\TaskProcessing\TaskTypes\TextToTextTopics::ID => \OCP\Server::get(\OCP\TaskProcessing\TaskTypes\TextToTextTopics::class), + \OCP\TaskProcessing\TaskTypes\TextToTextHeadline::ID => \OCP\Server::get(\OCP\TaskProcessing\TaskTypes\TextToTextHeadline::class), + \OCP\TaskProcessing\TaskTypes\TextToTextSummary::ID => \OCP\Server::get(\OCP\TaskProcessing\TaskTypes\TextToTextSummary::class), + \OCP\TaskProcessing\TaskTypes\TextToImage::ID => \OCP\Server::get(\OCP\TaskProcessing\TaskTypes\TextToImage::class), + \OCP\TaskProcessing\TaskTypes\AudioToText::ID => \OCP\Server::get(\OCP\TaskProcessing\TaskTypes\AudioToText::class), + ]; + + foreach ($context->getTaskProcessingTaskTypes() as $providerServiceRegistration) { + $class = $providerServiceRegistration->getService(); + try { + /** @var ITaskType $provider */ + $taskType = $this->serverContainer->get($class); + if (isset($taskTypes[$taskType->getId()])) { + $this->logger->warning('Task processing task type ' . $class . ' is using ID ' . $taskType->getId() . ' which is already used by ' . $taskTypes[$taskType->getId()]::class); + } + $taskTypes[$taskType->getId()] = $taskType; + } catch (\Throwable $e) { + $this->logger->error('Failed to load task processing task type ' . $class, [ + 'exception' => $e, + ]); + } + } + + $taskTypes += $this->_getTextProcessingTaskTypes(); + + return $taskTypes; + } + + /** + * @param string $taskType + * @return IProvider + * @throws \OCP\TaskProcessing\Exception\Exception + */ + private function _getPreferredProvider(string $taskType) { + $providers = $this->getProviders(); + foreach ($providers as $provider) { + if ($provider->getTaskTypeId() === $taskType) { + return $provider; + } + } + throw new \OCP\TaskProcessing\Exception\Exception('No matching provider found'); + } + + /** + * @param ShapeDescriptor[] $spec + * @param array $io + * @return void + * @throws ValidationException + */ + private function validateInput(array $spec, array $io, bool $optional = false): void { + foreach ($spec as $key => $descriptor) { + $type = $descriptor->getShapeType(); + if (!isset($io[$key])) { + if ($optional) { + continue; + } + throw new ValidationException('Missing key: "' . $key . '"'); + } + try { + $type->validateInput($io[$key]); + } catch (ValidationException $e) { + throw new ValidationException('Failed to validate input key "' . $key . '": ' . $e->getMessage()); + } + } + } + + /** + * @param ShapeDescriptor[] $spec + * @param array $io + * @param bool $optional + * @return void + * @throws ValidationException + */ + private function validateOutput(array $spec, array $io, bool $optional = false): void { + foreach ($spec as $key => $descriptor) { + $type = $descriptor->getShapeType(); + if (!isset($io[$key])) { + if ($optional) { + continue; + } + throw new ValidationException('Missing key: "' . $key . '"'); + } + try { + $type->validateOutput($io[$key]); + } catch (ValidationException $e) { + throw new ValidationException('Failed to validate output key "' . $key . '": ' . $e->getMessage()); + } + } + } + + /** + * @param array<array-key, T> $array The array to filter + * @param ShapeDescriptor[] ...$specs the specs that define which keys to keep + * @return array<array-key, T> + * @psalm-template T + */ + private function removeSuperfluousArrayKeys(array $array, ...$specs): array { + $keys = array_unique(array_reduce($specs, fn ($carry, $spec) => $carry + array_keys($spec), [])); + $values = array_map(fn (string $key) => $array[$key], $keys); + return array_combine($keys, $values); + } + + public function hasProviders(): bool { + return count($this->getProviders()) !== 0; + } + + public function getProviders(): array { + if ($this->providers === null) { + $this->providers = $this->_getProviders(); + } + + return $this->providers; + } + + public function getAvailableTaskTypes(): array { + if ($this->availableTaskTypes === null) { + $taskTypes = $this->_getTaskTypes(); + $providers = $this->getProviders(); + + $availableTaskTypes = []; + foreach ($providers as $provider) { + if (!isset($taskTypes[$provider->getTaskTypeId()])) { + continue; + } + $taskType = $taskTypes[$provider->getTaskTypeId()]; + $availableTaskTypes[$provider->getTaskTypeId()] = [ + 'name' => $taskType->getName(), + 'description' => $taskType->getDescription(), + 'inputShape' => $taskType->getInputShape(), + 'optionalInputShape' => $provider->getOptionalInputShape(), + 'outputShape' => $taskType->getOutputShape(), + 'optionalOutputShape' => $provider->getOptionalOutputShape(), + ]; + } + + $this->availableTaskTypes = $availableTaskTypes; + } + + return $this->availableTaskTypes; + } + + public function canHandleTask(Task $task): bool { + return isset($this->getAvailableTaskTypes()[$task->getTaskTypeId()]); + } + + public function scheduleTask(Task $task): void { + if (!$this->canHandleTask($task)) { + throw new \OCP\TaskProcessing\Exception\PreConditionNotMetException('No task processing provider is installed that can handle this task type: ' . $task->getTaskTypeId()); + } + $taskTypes = $this->getAvailableTaskTypes(); + $inputShape = $taskTypes[$task->getTaskTypeId()]['inputShape']; + $optionalInputShape = $taskTypes[$task->getTaskTypeId()]['optionalInputShape']; + // validate input + $this->validateInput($inputShape, $task->getInput()); + $this->validateInput($optionalInputShape, $task->getInput(), true); + // authenticate access to mentioned files + $ids = []; + foreach ($inputShape + $optionalInputShape as $key => $descriptor) { + if (in_array(EShapeType::getScalarType($descriptor->getShapeType()), [EShapeType::File, EShapeType::Image, EShapeType::Audio, EShapeType::Video], true)) { + /** @var list<int>|int $inputSlot */ + $inputSlot = $task->getInput()[$key]; + if (is_array($inputSlot)) { + $ids += $inputSlot; + } else { + $ids[] = $inputSlot; + } + } + } + foreach ($ids as $fileId) { + $node = $this->rootFolder->getFirstNodeById($fileId); + if ($node === null) { + $node = $this->rootFolder->getFirstNodeByIdInPath($fileId, '/' . $this->rootFolder->getAppDataDirectoryName() . '/'); + if ($node === null) { + throw new ValidationException('Could not find file ' . $fileId); + } + } + /** @var array{users:array<string,array{node_id:int, node_path: string}>, remote: array<string,array{node_id:int, node_path: string}>, mail: array<string,array{node_id:int, node_path: string}>} $accessList */ + $accessList = $this->shareManager->getAccessList($node, true, true); + $userIds = array_map(fn ($id) => strval($id), array_keys($accessList['users'])); + if (!in_array($task->getUserId(), $userIds)) { + throw new UnauthorizedException('User ' . $task->getUserId() . ' does not have access to file ' . $fileId); + } + } + // remove superfluous keys and set input + $task->setInput($this->removeSuperfluousArrayKeys($task->getInput(), $inputShape, $optionalInputShape)); + $task->setStatus(Task::STATUS_SCHEDULED); + $provider = $this->_getPreferredProvider($task->getTaskTypeId()); + // calculate expected completion time + $completionExpectedAt = new \DateTime('now'); + $completionExpectedAt->add(new \DateInterval('PT'.$provider->getExpectedRuntime().'S')); + $task->setCompletionExpectedAt($completionExpectedAt); + // create a db entity and insert into db table + $taskEntity = \OC\TaskProcessing\Db\Task::fromPublicTask($task); + $this->taskMapper->insert($taskEntity); + // make sure the scheduler knows the id + $task->setId($taskEntity->getId()); + // schedule synchronous job if the provider is synchronous + if ($provider instanceof ISynchronousProvider) { + $this->jobList->add(SynchronousBackgroundJob::class, null); + } + } + + public function deleteTask(Task $task): void { + $taskEntity = \OC\TaskProcessing\Db\Task::fromPublicTask($task); + $this->taskMapper->delete($taskEntity); + } + + public function getTask(int $id): Task { + try { + $taskEntity = $this->taskMapper->find($id); + return $taskEntity->toPublicTask(); + } catch (DoesNotExistException $e) { + throw new NotFoundException('Couldn\'t find task with id ' . $id, 0, $e); + } catch (MultipleObjectsReturnedException|\OCP\DB\Exception $e) { + throw new \OCP\TaskProcessing\Exception\Exception('There was a problem finding the task', 0, $e); + } catch (\JsonException $e) { + throw new \OCP\TaskProcessing\Exception\Exception('There was a problem parsing JSON after finding the task', 0, $e); + } + } + + public function cancelTask(int $id): void { + $task = $this->getTask($id); + if ($task->getStatus() !== Task::STATUS_SCHEDULED && $task->getStatus() !== Task::STATUS_RUNNING) { + return; + } + $task->setStatus(Task::STATUS_CANCELLED); + $taskEntity = \OC\TaskProcessing\Db\Task::fromPublicTask($task); + try { + $this->taskMapper->update($taskEntity); + } catch (\OCP\DB\Exception $e) { + throw new \OCP\TaskProcessing\Exception\Exception('There was a problem finding the task', 0, $e); + } + } + + public function setTaskProgress(int $id, float $progress): bool { + // TODO: Not sure if we should rather catch the exceptions of getTask here and fail silently + $task = $this->getTask($id); + if ($task->getStatus() === Task::STATUS_CANCELLED) { + return false; + } + $task->setStatus(Task::STATUS_RUNNING); + $task->setProgress($progress); + $taskEntity = \OC\TaskProcessing\Db\Task::fromPublicTask($task); + try { + $this->taskMapper->update($taskEntity); + } catch (\OCP\DB\Exception $e) { + throw new \OCP\TaskProcessing\Exception\Exception('There was a problem finding the task', 0, $e); + } + return true; + } + + public function setTaskResult(int $id, ?string $error, ?array $result): void { + // TODO: Not sure if we should rather catch the exceptions of getTask here and fail silently + $task = $this->getTask($id); + if ($task->getStatus() === Task::STATUS_CANCELLED) { + $this->logger->info('A TaskProcessing ' . $task->getTaskTypeId() . ' task with id ' . $id . ' finished but was cancelled in the mean time. Moving on without storing result.'); + return; + } + if ($error !== null) { + $task->setStatus(Task::STATUS_FAILED); + $task->setErrorMessage($error); + $this->logger->warning('A TaskProcessing ' . $task->getTaskTypeId() . ' task with id ' . $id . ' failed with the following message: ' . $error); + } elseif ($result !== null) { + $taskTypes = $this->getAvailableTaskTypes(); + $outputShape = $taskTypes[$task->getTaskTypeId()]['outputShape']; + $optionalOutputShape = $taskTypes[$task->getTaskTypeId()]['optionalOutputShape']; + try { + // validate output + $this->validateOutput($outputShape, $result); + $this->validateOutput($optionalOutputShape, $result, true); + $output = $this->removeSuperfluousArrayKeys($result, $outputShape, $optionalOutputShape); + // extract raw data and put it in files, replace it with file ids + $output = $this->encapsulateOutputFileData($output, $outputShape, $optionalOutputShape); + $task->setOutput($output); + $task->setProgress(1); + $task->setStatus(Task::STATUS_SUCCESSFUL); + } catch (ValidationException $e) { + $task->setProgress(1); + $task->setStatus(Task::STATUS_FAILED); + $error = 'The task was processed successfully but the provider\'s output doesn\'t pass validation against the task type\'s outputShape spec and/or the provider\'s own optionalOutputShape spec'; + $task->setErrorMessage($error); + $this->logger->error($error, ['exception' => $e]); + } catch (NotPermittedException $e) { + $task->setProgress(1); + $task->setStatus(Task::STATUS_FAILED); + $error = 'The task was processed successfully but storing the output in a file failed'; + $task->setErrorMessage($error); + $this->logger->error($error, ['exception' => $e]); + + } + } + $taskEntity = \OC\TaskProcessing\Db\Task::fromPublicTask($task); + try { + $this->taskMapper->update($taskEntity); + } catch (\OCP\DB\Exception $e) { + throw new \OCP\TaskProcessing\Exception\Exception('There was a problem finding the task', 0, $e); + } + if ($task->getStatus() === Task::STATUS_SUCCESSFUL) { + $event = new TaskSuccessfulEvent($task); + } else { + $event = new TaskFailedEvent($task, $error); + } + $this->dispatcher->dispatchTyped($event); + } + + public function getNextScheduledTask(?string $taskTypeId = null): Task { + try { + $taskEntity = $this->taskMapper->findOldestScheduledByType($taskTypeId); + return $taskEntity->toPublicTask(); + } catch (DoesNotExistException $e) { + throw new \OCP\TaskProcessing\Exception\NotFoundException('Could not find the task', 0, $e); + } catch (\OCP\DB\Exception $e) { + throw new \OCP\TaskProcessing\Exception\Exception('There was a problem finding the task', 0, $e); + } catch (\JsonException $e) { + throw new \OCP\TaskProcessing\Exception\Exception('There was a problem parsing JSON after finding the task', 0, $e); + } + } + + /** + * Takes task input or output data and replaces fileIds with base64 data + * + * @param string|null $userId + * @param array<array-key, list<numeric|string>|numeric|string> $input + * @param ShapeDescriptor[] ...$specs the specs + * @return array<array-key, list<File|numeric|string>|numeric|string|File> + * @throws GenericFileException + * @throws LockedException + * @throws NotPermittedException + * @throws ValidationException + */ + public function fillInputFileData(?string $userId, array $input, ...$specs): array { + if ($userId !== null) { + \OC_Util::setupFS($userId); + } + $newInputOutput = []; + $spec = array_reduce($specs, fn ($carry, $spec) => $carry + $spec, []); + foreach($spec as $key => $descriptor) { + $type = $descriptor->getShapeType(); + if (!isset($input[$key])) { + continue; + } + if (!in_array(EShapeType::getScalarType($type), [EShapeType::Image, EShapeType::Audio, EShapeType::Video, EShapeType::File], true)) { + $newInputOutput[$key] = $input[$key]; + continue; + } + if ($type->value < 10) { + $node = $this->rootFolder->getFirstNodeById((int)$input[$key]); + if ($node === null) { + $node = $this->rootFolder->getFirstNodeByIdInPath((int)$input[$key], '/' . $this->rootFolder->getAppDataDirectoryName() . '/'); + if (!$node instanceof File) { + throw new ValidationException('File id given for key "' . $key . '" is not a file'); + } + } elseif (!$node instanceof File) { + throw new ValidationException('File id given for key "' . $key . '" is not a file'); + } + // TODO: Validate if userId has access to this file + $newInputOutput[$key] = $node; + } else { + $newInputOutput[$key] = []; + foreach ($input[$key] as $item) { + $node = $this->rootFolder->getFirstNodeById((int)$item); + if ($node === null) { + $node = $this->rootFolder->getFirstNodeByIdInPath((int)$item, '/' . $this->rootFolder->getAppDataDirectoryName() . '/'); + if (!$node instanceof File) { + throw new ValidationException('File id given for key "' . $key . '" is not a file'); + } + } elseif (!$node instanceof File) { + throw new ValidationException('File id given for key "' . $key . '" is not a file'); + } + // TODO: Validate if userId has access to this file + $newInputOutput[$key][] = $node; + } + } + } + return $newInputOutput; + } + + public function getUserTask(int $id, ?string $userId): Task { + try { + $taskEntity = $this->taskMapper->findByIdAndUser($id, $userId); + return $taskEntity->toPublicTask(); + } catch (DoesNotExistException $e) { + throw new \OCP\TaskProcessing\Exception\NotFoundException('Could not find the task', 0, $e); + } catch (MultipleObjectsReturnedException|\OCP\DB\Exception $e) { + throw new \OCP\TaskProcessing\Exception\Exception('There was a problem finding the task', 0, $e); + } catch (\JsonException $e) { + throw new \OCP\TaskProcessing\Exception\Exception('There was a problem parsing JSON after finding the task', 0, $e); + } + } + + public function getUserTasks(?string $userId, ?string $taskTypeId = null, ?string $customId = null): array { + try { + $taskEntities = $this->taskMapper->findByUserAndTaskType($userId, $taskTypeId, $customId); + return array_map(fn ($taskEntity): Task => $taskEntity->toPublicTask(), $taskEntities); + } catch (\OCP\DB\Exception $e) { + throw new \OCP\TaskProcessing\Exception\Exception('There was a problem finding the tasks', 0, $e); + } catch (\JsonException $e) { + throw new \OCP\TaskProcessing\Exception\Exception('There was a problem parsing JSON after finding the tasks', 0, $e); + } + } + + public function getUserTasksByApp(?string $userId, string $appId, ?string $customId = null): array { + try { + $taskEntities = $this->taskMapper->findUserTasksByApp($userId, $appId, $customId); + return array_map(fn ($taskEntity): Task => $taskEntity->toPublicTask(), $taskEntities); + } catch (\OCP\DB\Exception $e) { + throw new \OCP\TaskProcessing\Exception\Exception('There was a problem finding a task', 0, $e); + } catch (\JsonException $e) { + throw new \OCP\TaskProcessing\Exception\Exception('There was a problem parsing JSON after finding a task', 0, $e); + } + } + + /** + *Takes task input or output and replaces base64 data with file ids + * + * @param array $output + * @param ShapeDescriptor[] ...$specs the specs that define which keys to keep + * @return array + * @throws NotPermittedException + */ + public function encapsulateOutputFileData(array $output, ...$specs): array { + $newOutput = []; + try { + $folder = $this->appData->getFolder('TaskProcessing'); + } catch (\OCP\Files\NotFoundException) { + $folder = $this->appData->newFolder('TaskProcessing'); + } + $spec = array_reduce($specs, fn ($carry, $spec) => $carry + $spec, []); + foreach($spec as $key => $descriptor) { + $type = $descriptor->getShapeType(); + if (!isset($output[$key])) { + continue; + } + if (!in_array(EShapeType::getScalarType($type), [EShapeType::Image, EShapeType::Audio, EShapeType::Video, EShapeType::File], true)) { + $newOutput[$key] = $output[$key]; + continue; + } + if ($type->value < 10) { + /** @var SimpleFile $file */ + $file = $folder->newFile((string) rand(0, 10000000), $output[$key]); + $newOutput[$key] = $file->getId(); // polymorphic call to SimpleFile + } else { + $newOutput = []; + foreach ($output[$key] as $item) { + /** @var SimpleFile $file */ + $file = $folder->newFile((string) rand(0, 10000000), $item); + $newOutput[$key][] = $file->getId(); + } + } + } + return $newOutput; + } + + /** + * @param Task $task + * @return array<array-key, list<numeric|string|File>|numeric|string|File> + * @throws GenericFileException + * @throws LockedException + * @throws NotPermittedException + * @throws ValidationException + */ + public function prepareInputData(Task $task): array { + $taskTypes = $this->getAvailableTaskTypes(); + $inputShape = $taskTypes[$task->getTaskTypeId()]['inputShape']; + $optionalInputShape = $taskTypes[$task->getTaskTypeId()]['optionalInputShape']; + $input = $task->getInput(); + // validate input, again for good measure (should have been validated in scheduleTask) + $this->validateInput($inputShape, $input); + $this->validateInput($optionalInputShape, $input, true); + $input = $this->removeSuperfluousArrayKeys($input, $inputShape, $optionalInputShape); + $input = $this->fillInputFileData($task->getUserId(), $input, $inputShape, $optionalInputShape); + return $input; + } +} diff --git a/lib/private/TaskProcessing/RemoveOldTasksBackgroundJob.php b/lib/private/TaskProcessing/RemoveOldTasksBackgroundJob.php new file mode 100644 index 00000000000..823ea09a622 --- /dev/null +++ b/lib/private/TaskProcessing/RemoveOldTasksBackgroundJob.php @@ -0,0 +1,79 @@ +<?php + +/** + * SPDX-FileCopyrightText: 2024 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later + */ +namespace OC\TaskProcessing; + +use OC\TaskProcessing\Db\TaskMapper; +use OCP\AppFramework\Utility\ITimeFactory; +use OCP\BackgroundJob\TimedJob; +use OCP\Files\AppData\IAppDataFactory; +use OCP\Files\NotFoundException; +use OCP\Files\NotPermittedException; +use OCP\Files\SimpleFS\ISimpleFolder; +use Psr\Log\LoggerInterface; + +class RemoveOldTasksBackgroundJob extends TimedJob { + public const MAX_TASK_AGE_SECONDS = 60 * 50 * 24 * 7 * 4; // 4 weeks + private \OCP\Files\IAppData $appData; + + public function __construct( + ITimeFactory $timeFactory, + private TaskMapper $taskMapper, + private LoggerInterface $logger, + IAppDataFactory $appDataFactory, + ) { + parent::__construct($timeFactory); + $this->setInterval(60 * 60 * 24); + // can be deferred to maintenance window + $this->setTimeSensitivity(TimedJob::TIME_INSENSITIVE); + $this->appData = $appDataFactory->get('core'); + } + + + /** + * @inheritDoc + */ + protected function run($argument): void { + try { + $this->taskMapper->deleteOlderThan(self::MAX_TASK_AGE_SECONDS); + } catch (\OCP\DB\Exception $e) { + $this->logger->warning('Failed to delete stale task processing tasks', ['exception' => $e]); + } + try { + $this->clearFilesOlderThan($this->appData->getFolder('text2image'), self::MAX_TASK_AGE_SECONDS); + } catch (NotFoundException $e) { + // noop + } + try { + $this->clearFilesOlderThan($this->appData->getFolder('audio2text'), self::MAX_TASK_AGE_SECONDS); + } catch (NotFoundException $e) { + // noop + } + try { + $this->clearFilesOlderThan($this->appData->getFolder('TaskProcessing'), self::MAX_TASK_AGE_SECONDS); + } catch (NotFoundException $e) { + // noop + } + } + + /** + * @param ISimpleFolder $folder + * @param int $ageInSeconds + * @return void + */ + private function clearFilesOlderThan(ISimpleFolder $folder, int $ageInSeconds): void { + foreach($folder->getDirectoryListing() as $file) { + if ($file->getMTime() < time() - $ageInSeconds) { + try { + $file->delete(); + } catch (NotPermittedException $e) { + $this->logger->warning('Failed to delete a stale task processing file', ['exception' => $e]); + } + } + } + } + +} diff --git a/lib/private/TaskProcessing/SynchronousBackgroundJob.php b/lib/private/TaskProcessing/SynchronousBackgroundJob.php new file mode 100644 index 00000000000..47c3c83a618 --- /dev/null +++ b/lib/private/TaskProcessing/SynchronousBackgroundJob.php @@ -0,0 +1,106 @@ +<?php + +/** + * SPDX-FileCopyrightText: 2024 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later + */ +namespace OC\TaskProcessing; + +use OCP\AppFramework\Utility\ITimeFactory; +use OCP\BackgroundJob\IJobList; +use OCP\BackgroundJob\QueuedJob; +use OCP\Files\GenericFileException; +use OCP\Files\NotPermittedException; +use OCP\Lock\LockedException; +use OCP\TaskProcessing\Exception\Exception; +use OCP\TaskProcessing\Exception\NotFoundException; +use OCP\TaskProcessing\Exception\ProcessingException; +use OCP\TaskProcessing\Exception\ValidationException; +use OCP\TaskProcessing\IManager; +use OCP\TaskProcessing\ISynchronousProvider; +use Psr\Log\LoggerInterface; + +class SynchronousBackgroundJob extends QueuedJob { + public function __construct( + ITimeFactory $timeFactory, + private readonly IManager $taskProcessingManager, + private readonly IJobList $jobList, + private readonly LoggerInterface $logger, + ) { + parent::__construct($timeFactory); + } + + + /** + * @inheritDoc + */ + protected function run($argument) { + $providers = $this->taskProcessingManager->getProviders(); + + foreach ($providers as $provider) { + if (!$provider instanceof ISynchronousProvider) { + continue; + } + $taskType = $provider->getTaskTypeId(); + try { + $task = $this->taskProcessingManager->getNextScheduledTask($taskType); + } catch (NotFoundException $e) { + continue; + } catch (Exception $e) { + $this->logger->error('Unknown error while retrieving scheduled TaskProcessing tasks', ['exception' => $e]); + continue; + } + try { + try { + $input = $this->taskProcessingManager->prepareInputData($task); + } catch (GenericFileException|NotPermittedException|LockedException|ValidationException $e) { + $this->logger->warning('Failed to prepare input data for a TaskProcessing task with synchronous provider ' . $provider->getId(), ['exception' => $e]); + $this->taskProcessingManager->setTaskResult($task->getId(), $e->getMessage(), null); + // Schedule again + $this->jobList->add(self::class, $argument); + return; + } + try { + $output = $provider->process($task->getUserId(), $input, fn (float $progress) => $this->taskProcessingManager->setTaskProgress($task->getId(), $progress)); + } catch (ProcessingException $e) { + $this->logger->warning('Failed to process a TaskProcessing task with synchronous provider ' . $provider->getId(), ['exception' => $e]); + $this->taskProcessingManager->setTaskResult($task->getId(), $e->getMessage(), null); + // Schedule again + $this->jobList->add(self::class, $argument); + return; + } catch (\Throwable $e) { + $this->logger->error('Unknown error while processing TaskProcessing task', ['exception' => $e]); + $this->taskProcessingManager->setTaskResult($task->getId(), $e->getMessage(), null); + // Schedule again + $this->jobList->add(self::class, $argument); + return; + } + $this->taskProcessingManager->setTaskResult($task->getId(), null, $output); + } catch (NotFoundException $e) { + $this->logger->info('Could not find task anymore after execution. Moving on.', ['exception' => $e]); + } catch (Exception $e) { + $this->logger->error('Failed to report result of TaskProcessing task', ['exception' => $e]); + } + } + + $synchronousProviders = array_filter($providers, fn ($provider) => + $provider instanceof ISynchronousProvider); + $taskTypes = array_values(array_map(fn ($provider) => + $provider->getTaskTypeId(), + $synchronousProviders + )); + $taskTypesWithTasks = array_filter($taskTypes, function ($taskType) { + try { + $this->taskProcessingManager->getNextScheduledTask($taskType); + return true; + } catch (NotFoundException|Exception $e) { + return false; + } + }); + + if (count($taskTypesWithTasks) > 0) { + // Schedule again + $this->jobList->add(self::class, $argument); + } + } +} diff --git a/lib/private/Teams/TeamManager.php b/lib/private/Teams/TeamManager.php new file mode 100644 index 00000000000..4fe0e60b7d1 --- /dev/null +++ b/lib/private/Teams/TeamManager.php @@ -0,0 +1,103 @@ +<?php +/** + * SPDX-FileCopyrightText: 2024 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later + */ + +namespace OC\Teams; + +use OC\AppFramework\Bootstrap\Coordinator; +use OCA\Circles\CirclesManager; +use OCA\Circles\Exceptions\CircleNotFoundException; +use OCA\Circles\Model\Circle; +use OCA\Circles\Model\Member; +use OCP\IURLGenerator; +use OCP\Server; +use OCP\Teams\ITeamManager; +use OCP\Teams\ITeamResourceProvider; +use OCP\Teams\Team; +use Psr\Container\ContainerExceptionInterface; +use Psr\Container\NotFoundExceptionInterface; + +class TeamManager implements ITeamManager { + + /** @var ?ITeamResourceProvider[] */ + private ?array $providers = null; + + public function __construct( + private Coordinator $bootContext, + private IURLGenerator $urlGenerator, + private ?CirclesManager $circlesManager, + ) { + } + + public function hasTeamSupport(): bool { + return $this->circlesManager !== null; + } + + public function getProviders(): array { + if ($this->providers !== null) { + return $this->providers; + } + + $this->providers = []; + foreach ($this->bootContext->getRegistrationContext()->getTeamResourceProviders() as $providerRegistration) { + try { + /** @var ITeamResourceProvider $provider */ + $provider = Server::get($providerRegistration->getService()); + $this->providers[$provider->getId()] = $provider; + } catch (NotFoundExceptionInterface|ContainerExceptionInterface $e) { + } + } + return $this->providers; + } + + public function getProvider(string $providerId): ITeamResourceProvider { + $providers = $this->getProviders(); + if (isset($providers[$providerId])) { + return $providers[$providerId]; + } + + throw new \RuntimeException('No provider found for id ' .$providerId); + } + + public function getSharedWith(string $teamId, string $userId): array { + if ($this->getTeam($teamId, $userId) === null) { + return []; + } + + $resources = []; + + foreach ($this->getProviders() as $provider) { + array_push($resources, ...$provider->getSharedWith($teamId)); + } + + return $resources; + } + + public function getTeamsForResource(string $providerId, string $resourceId, string $userId): array { + $provider = $this->getProvider($providerId); + return array_values(array_filter(array_map(function ($teamId) use ($userId) { + $team = $this->getTeam($teamId, $userId); + if ($team === null) { + return null; + } + + return new Team( + $teamId, + $team->getDisplayName(), + $this->urlGenerator->linkToRouteAbsolute('contacts.contacts.directcircle', ['singleId' => $teamId]), + ); + }, $provider->getTeamsForResource($resourceId)))); + } + + private function getTeam(string $teamId, string $userId): ?Circle { + try { + $federatedUser = $this->circlesManager->getFederatedUser($userId, Member::TYPE_USER); + $this->circlesManager->startSession($federatedUser); + return $this->circlesManager->getCircle($teamId); + } catch (CircleNotFoundException) { + return null; + } + } +} diff --git a/lib/private/TempManager.php b/lib/private/TempManager.php index 0df31dce3ff..68a53f493db 100644 --- a/lib/private/TempManager.php +++ b/lib/private/TempManager.php @@ -1,33 +1,9 @@ <?php + /** - * @copyright Copyright (c) 2016, ownCloud, Inc. - * - * @author Christoph Wurst <christoph@winzerhof-wurst.at> - * @author Joas Schilling <coding@schilljs.com> - * @author Lars <winnetou+github@catolic.de> - * @author Lukas Reschke <lukas@statuscode.ch> - * @author Martin Mattel <martin.mattel@diemattels.at> - * @author Morris Jobke <hey@morrisjobke.de> - * @author Olivier Paroz <github@oparoz.com> - * @author Robin Appelman <robin@icewind.nl> - * @author Robin McCorkell <robin@mccorkell.me.uk> - * @author Roeland Jago Douma <roeland@famdouma.nl> - * @author Stefan Weil <sw@weilnetz.de> - * - * @license AGPL-3.0 - * - * This code is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License, version 3, - * as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License, version 3, - * along with this program. If not, see <http://www.gnu.org/licenses/> - * + * SPDX-FileCopyrightText: 2016-2024 Nextcloud GmbH and Nextcloud contributors + * SPDX-FileCopyrightText: 2016 ownCloud, Inc. + * SPDX-License-Identifier: AGPL-3.0-only */ namespace OC; diff --git a/lib/private/Template/Base.php b/lib/private/Template/Base.php index 71ce2ed9d86..2adf9172f6b 100644 --- a/lib/private/Template/Base.php +++ b/lib/private/Template/Base.php @@ -1,31 +1,9 @@ <?php + /** - * @copyright Copyright (c) 2016, ownCloud, Inc. - * - * @author Bart Visscher <bartv@thisnet.nl> - * @author Björn Schießle <bjoern@schiessle.org> - * @author Christoph Wurst <christoph@winzerhof-wurst.at> - * @author Joas Schilling <coding@schilljs.com> - * @author Jörn Friedrich Dreyer <jfd@butonic.de> - * @author Julius Härtl <jus@bitgrid.net> - * @author Lukas Reschke <lukas@statuscode.ch> - * @author Morris Jobke <hey@morrisjobke.de> - * @author Thomas Müller <thomas.mueller@tmit.eu> - * - * @license AGPL-3.0 - * - * This code is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License, version 3, - * as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License, version 3, - * along with this program. If not, see <http://www.gnu.org/licenses/> - * + * SPDX-FileCopyrightText: 2016-2024 Nextcloud GmbH and Nextcloud contributors + * SPDX-FileCopyrightText: 2016 ownCloud, Inc. + * SPDX-License-Identifier: AGPL-3.0-only */ namespace OC\Template; diff --git a/lib/private/Template/CSSResourceLocator.php b/lib/private/Template/CSSResourceLocator.php index 4ebe054ca7b..e9aa38ef2cc 100644 --- a/lib/private/Template/CSSResourceLocator.php +++ b/lib/private/Template/CSSResourceLocator.php @@ -1,33 +1,9 @@ <?php + /** - * @copyright Copyright (c) 2016, ownCloud, Inc. - * - * @author Axel Helmert <axel.helmert@luka.de> - * @author Bart Visscher <bartv@thisnet.nl> - * @author Christoph Wurst <christoph@winzerhof-wurst.at> - * @author Joas Schilling <coding@schilljs.com> - * @author John Molakvoæ <skjnldsv@protonmail.com> - * @author Kyle Fazzari <kyrofa@ubuntu.com> - * @author Morris Jobke <hey@morrisjobke.de> - * @author Robin Appelman <robin@icewind.nl> - * @author Roeland Jago Douma <roeland@famdouma.nl> - * @author Thomas Müller <thomas.mueller@tmit.eu> - * @author tux-rampage <tux-rampage@users.noreply.github.com> - * - * @license AGPL-3.0 - * - * This code is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License, version 3, - * as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License, version 3, - * along with this program. If not, see <http://www.gnu.org/licenses/> - * + * SPDX-FileCopyrightText: 2016-2024 Nextcloud GmbH and Nextcloud contributors + * SPDX-FileCopyrightText: 2016 ownCloud, Inc. + * SPDX-License-Identifier: AGPL-3.0-only */ namespace OC\Template; diff --git a/lib/private/Template/JSCombiner.php b/lib/private/Template/JSCombiner.php index b87829360d5..5fce3effb3f 100644 --- a/lib/private/Template/JSCombiner.php +++ b/lib/private/Template/JSCombiner.php @@ -1,28 +1,7 @@ <?php /** - * @copyright 2017, Roeland Jago Douma <roeland@famdouma.nl> - * - * @author Christoph Wurst <christoph@winzerhof-wurst.at> - * @author Julius Härtl <jus@bitgrid.net> - * @author Lukas Reschke <lukas@statuscode.ch> - * @author Morris Jobke <hey@morrisjobke.de> - * @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/>. - * + * SPDX-FileCopyrightText: 2017 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later */ namespace OC\Template; @@ -55,10 +34,10 @@ class JSCombiner { private $cacheFactory; public function __construct(IAppData $appData, - IURLGenerator $urlGenerator, - ICacheFactory $cacheFactory, - SystemConfig $config, - LoggerInterface $logger) { + IURLGenerator $urlGenerator, + ICacheFactory $cacheFactory, + SystemConfig $config, + LoggerInterface $logger) { $this->appData = $appData; $this->urlGenerator = $urlGenerator; $this->cacheFactory = $cacheFactory; diff --git a/lib/private/Template/JSConfigHelper.php b/lib/private/Template/JSConfigHelper.php index 7b6d0a6a346..2f2ff9d64e3 100644 --- a/lib/private/Template/JSConfigHelper.php +++ b/lib/private/Template/JSConfigHelper.php @@ -2,34 +2,8 @@ declare(strict_types=1); /** - * @copyright Copyright (c) 2016, Roeland Jago Douma <roeland@famdouma.nl> - * - * @author Abijeet <abijeetpatro@gmail.com> - * @author Arthur Schiwon <blizzz@arthur-schiwon.de> - * @author Bjoern Schiessle <bjoern@schiessle.org> - * @author Christoph Wurst <christoph@winzerhof-wurst.at> - * @author Joas Schilling <coding@schilljs.com> - * @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 Vincent Petry <vincent@nextcloud.com> - * - * @license GNU AGPL version 3 or any later version - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as - * published by the Free Software Foundation, either version 3 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - * + * SPDX-FileCopyrightText: 2016 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later */ namespace OC\Template; @@ -45,10 +19,11 @@ use OCP\IConfig; use OCP\IGroupManager; use OCP\IInitialStateService; use OCP\IL10N; +use OCP\ILogger; use OCP\ISession; use OCP\IURLGenerator; -use OCP\ILogger; use OCP\IUser; +use OCP\Share\IManager as IShareManager; use OCP\User\Backend\IPasswordConfirmationBackend; use OCP\Util; @@ -69,16 +44,16 @@ class JSConfigHelper { private $excludedUserBackEnds = ['user_saml' => true, 'user_globalsiteselector' => true]; public function __construct(IL10N $l, - Defaults $defaults, - IAppManager $appManager, - ISession $session, - ?IUser $currentUser, - IConfig $config, - IGroupManager $groupManager, - IniGetWrapper $iniWrapper, - IURLGenerator $urlGenerator, - CapabilitiesManager $capabilitiesManager, - IInitialStateService $initialStateService) { + Defaults $defaults, + IAppManager $appManager, + ISession $session, + ?IUser $currentUser, + IConfig $config, + IGroupManager $groupManager, + IniGetWrapper $iniWrapper, + IURLGenerator $urlGenerator, + CapabilitiesManager $capabilitiesManager, + IInitialStateService $initialStateService) { $this->l = $l; $this->defaults = $defaults; $this->appManager = $appManager; @@ -168,6 +143,7 @@ class JSConfigHelper { $config = [ 'auto_logout' => $this->config->getSystemValue('auto_logout', false), 'blacklist_files_regex' => FileInfo::BLACKLIST_FILES_REGEX, + 'forbidden_filename_characters' => Util::getForbiddenFileNameChars(), 'loglevel' => $this->config->getSystemValue('loglevel_frontend', $this->config->getSystemValue('loglevel', ILogger::WARN) ), @@ -179,7 +155,8 @@ class JSConfigHelper { 'sharing.maxAutocompleteResults' => max(0, $this->config->getSystemValueInt('sharing.maxAutocompleteResults', Constants::SHARING_MAX_AUTOCOMPLETE_RESULTS_DEFAULT)), 'sharing.minSearchStringLength' => $this->config->getSystemValueInt('sharing.minSearchStringLength', 0), 'version' => implode('.', Util::getVersion()), - 'versionstring' => \OC_Util::getVersionString() + 'versionstring' => \OC_Util::getVersionString(), + 'enable_non-accessible_features' => $this->config->getSystemValueBool('enable_non-accessible_features', true), ]; $array = [ @@ -260,7 +237,7 @@ class JSConfigHelper { 'resharingAllowed' => Share::isResharingAllowed(), 'remoteShareAllowed' => $outgoingServer2serverShareEnabled, 'federatedCloudShareDoc' => $this->urlGenerator->linkToDocs('user-sharing-federated'), - 'allowGroupSharing' => \OC::$server->getShareManager()->allowGroupSharing(), + 'allowGroupSharing' => \OC::$server->get(IShareManager::class)->allowGroupSharing(), 'defaultInternalExpireDateEnabled' => $defaultInternalExpireDateEnabled, 'defaultInternalExpireDate' => $defaultInternalExpireDate, 'defaultInternalExpireDateEnforced' => $defaultInternalExpireDateEnforced, diff --git a/lib/private/Template/JSResourceLocator.php b/lib/private/Template/JSResourceLocator.php index b283f0b610f..b9a2fce6cd5 100644 --- a/lib/private/Template/JSResourceLocator.php +++ b/lib/private/Template/JSResourceLocator.php @@ -1,29 +1,9 @@ <?php + /** - * @copyright Copyright (c) 2016, ownCloud, Inc. - * - * @author Bart Visscher <bartv@thisnet.nl> - * @author Christoph Wurst <christoph@winzerhof-wurst.at> - * @author Joas Schilling <coding@schilljs.com> - * @author Kyle Fazzari <kyrofa@ubuntu.com> - * @author Morris Jobke <hey@morrisjobke.de> - * @author Roeland Jago Douma <roeland@famdouma.nl> - * @author Thomas Müller <thomas.mueller@tmit.eu> - * - * @license AGPL-3.0 - * - * This code is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License, version 3, - * as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License, version 3, - * along with this program. If not, see <http://www.gnu.org/licenses/> - * + * SPDX-FileCopyrightText: 2016-2024 Nextcloud GmbH and Nextcloud contributors + * SPDX-FileCopyrightText: 2016 ownCloud, Inc. + * SPDX-License-Identifier: AGPL-3.0-only */ namespace OC\Template; @@ -51,6 +31,22 @@ class JSResourceLocator extends ResourceLocator { // Extracting the appId and the script file name $app = substr($script, 0, strpos($script, '/')); $scriptName = basename($script); + // Get the app root path + $appRoot = $this->serverroot . '/apps/'; + $appWebRoot = null; + try { + // We need the dir name as getAppPath appends the appid + $appRoot = dirname($this->appManager->getAppPath($app)); + // Only do this if $app_path is set, because an empty argument to realpath gets turned into cwd. + if ($appRoot) { + // Handle symlinks + $appRoot = realpath($appRoot); + } + // Get the app webroot + $appWebRoot = dirname($this->appManager->getAppWebPath($app)); + } catch (AppPathNotFoundException $e) { + // ignore + } if (str_contains($script, '/l10n/')) { // For language files we try to load them all, so themes can overwrite @@ -60,7 +56,7 @@ class JSResourceLocator extends ResourceLocator { $found += $this->appendScriptIfExist($this->serverroot, $theme_dir.'core/'.$script); $found += $this->appendScriptIfExist($this->serverroot, $script); $found += $this->appendScriptIfExist($this->serverroot, $theme_dir.$script); - $found += $this->appendScriptIfExist($this->serverroot, 'apps/'.$script); + $found += $this->appendScriptIfExist($appRoot, $script, $appWebRoot); $found += $this->appendScriptIfExist($this->serverroot, $theme_dir.'apps/'.$script); if ($found) { @@ -71,8 +67,9 @@ class JSResourceLocator extends ResourceLocator { || $this->appendScriptIfExist($this->serverroot, $script) || $this->appendScriptIfExist($this->serverroot, $theme_dir."dist/$app-$scriptName") || $this->appendScriptIfExist($this->serverroot, "dist/$app-$scriptName") - || $this->appendScriptIfExist($this->serverroot, 'apps/'.$script) + || $this->appendScriptIfExist($appRoot, $script, $appWebRoot) || $this->cacheAndAppendCombineJsonIfExist($this->serverroot, $script.'.json') + || $this->cacheAndAppendCombineJsonIfExist($appRoot, $script.'.json', $appWebRoot) || $this->appendScriptIfExist($this->serverroot, $theme_dir.'core/'.$script) || $this->appendScriptIfExist($this->serverroot, 'core/'.$script) || (strpos($scriptName, '/') === -1 && ($this->appendScriptIfExist($this->serverroot, $theme_dir."dist/core-$scriptName") @@ -82,43 +79,13 @@ class JSResourceLocator extends ResourceLocator { return; } - $script = substr($script, strpos($script, '/') + 1); - $app_url = null; - - try { - $app_url = $this->appManager->getAppWebPath($app); - } catch (AppPathNotFoundException) { - // pass - } - - try { - $app_path = $this->appManager->getAppPath($app); - - // Account for the possibility of having symlinks in app path. Only - // do this if $app_path is set, because an empty argument to realpath - // gets turned into cwd. - $app_path = realpath($app_path); - - // check combined files - if (!str_starts_with($script, 'l10n/') && $this->cacheAndAppendCombineJsonIfExist($app_path, $script.'.json', $app)) { - return; - } - - // fallback to plain file location - if ($this->appendScriptIfExist($app_path, $script, $app_url)) { - return; - } - } catch (AppPathNotFoundException) { - // pass (general error handling happens below) - } - // missing translations files will be ignored - if (str_starts_with($script, 'l10n/')) { + if (str_contains($script, '/l10n/')) { return; } $this->logger->error('Could not find resource {resource} to load', [ - 'resource' => $app . '/' . $script . '.js', + 'resource' => $script . '.js', 'app' => 'jsresourceloader', ]); } @@ -133,7 +100,7 @@ class JSResourceLocator extends ResourceLocator { * Try to find ES6 script file (`.mjs`) with fallback to plain javascript (`.js`) * @see appendIfExist() */ - protected function appendScriptIfExist(string $root, string $file, string $webRoot = null) { + protected function appendScriptIfExist(string $root, string $file, ?string $webRoot = null) { if (!$this->appendIfExist($root, $file . '.mjs', $webRoot)) { return $this->appendIfExist($root, $file . '.js', $webRoot); } diff --git a/lib/private/Template/ResourceLocator.php b/lib/private/Template/ResourceLocator.php index 9e6e2056e6b..377f0e0b6a8 100755 --- a/lib/private/Template/ResourceLocator.php +++ b/lib/private/Template/ResourceLocator.php @@ -1,31 +1,9 @@ <?php + /** - * @copyright Copyright (c) 2016, ownCloud, Inc. - * - * @author Bart Visscher <bartv@thisnet.nl> - * @author Christoph Wurst <christoph@winzerhof-wurst.at> - * @author Joas Schilling <coding@schilljs.com> - * @author John Molakvoæ <skjnldsv@protonmail.com> - * @author Jörn Friedrich Dreyer <jfd@butonic.de> - * @author Morris Jobke <hey@morrisjobke.de> - * @author Robin Appelman <robin@icewind.nl> - * @author Robin McCorkell <robin@mccorkell.me.uk> - * @author tux-rampage <tux-rampage@users.noreply.github.com> - * - * @license AGPL-3.0 - * - * This code is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License, version 3, - * as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License, version 3, - * along with this program. If not, see <http://www.gnu.org/licenses/> - * + * SPDX-FileCopyrightText: 2016-2024 Nextcloud GmbH and Nextcloud contributors + * SPDX-FileCopyrightText: 2016 ownCloud, Inc. + * SPDX-License-Identifier: AGPL-3.0-only */ namespace OC\Template; diff --git a/lib/private/Template/ResourceNotFoundException.php b/lib/private/Template/ResourceNotFoundException.php index c8ed33f569c..e51dfb5cb89 100644 --- a/lib/private/Template/ResourceNotFoundException.php +++ b/lib/private/Template/ResourceNotFoundException.php @@ -1,24 +1,9 @@ <?php + /** - * @copyright Copyright (c) 2016, ownCloud, Inc. - * - * @author Joas Schilling <coding@schilljs.com> - * @author Morris Jobke <hey@morrisjobke.de> - * - * @license AGPL-3.0 - * - * This code is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License, version 3, - * as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License, version 3, - * along with this program. If not, see <http://www.gnu.org/licenses/> - * + * SPDX-FileCopyrightText: 2016-2024 Nextcloud GmbH and Nextcloud contributors + * SPDX-FileCopyrightText: 2016 ownCloud, Inc. + * SPDX-License-Identifier: AGPL-3.0-only */ namespace OC\Template; diff --git a/lib/private/Template/TemplateFileLocator.php b/lib/private/Template/TemplateFileLocator.php index 027144e2f43..164fcd503c1 100644 --- a/lib/private/Template/TemplateFileLocator.php +++ b/lib/private/Template/TemplateFileLocator.php @@ -1,27 +1,9 @@ <?php + /** - * @copyright Copyright (c) 2016, ownCloud, Inc. - * - * @author Bart Visscher <bartv@thisnet.nl> - * @author Christoph Wurst <christoph@winzerhof-wurst.at> - * @author Jörn Friedrich Dreyer <jfd@butonic.de> - * @author Morris Jobke <hey@morrisjobke.de> - * @author Thomas Müller <thomas.mueller@tmit.eu> - * - * @license AGPL-3.0 - * - * This code is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License, version 3, - * as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License, version 3, - * along with this program. If not, see <http://www.gnu.org/licenses/> - * + * SPDX-FileCopyrightText: 2016-2024 Nextcloud GmbH and Nextcloud contributors + * SPDX-FileCopyrightText: 2016 ownCloud, Inc. + * SPDX-License-Identifier: AGPL-3.0-only */ namespace OC\Template; diff --git a/lib/private/TemplateLayout.php b/lib/private/TemplateLayout.php index 658a85152bf..0ff56ece635 100644 --- a/lib/private/TemplateLayout.php +++ b/lib/private/TemplateLayout.php @@ -1,44 +1,9 @@ <?php + /** - * @copyright Copyright (c) 2016, ownCloud, Inc. - * - * @author Bart Visscher <bartv@thisnet.nl> - * @author Christopher Schäpers <kondou@ts.unde.re> - * @author Christoph Wurst <christoph@winzerhof-wurst.at> - * @author Clark Tomlinson <fallen013@gmail.com> - * @author Daniel Calviño Sánchez <danxuliu@gmail.com> - * @author Guillaume COMPAGNON <gcompagnon@outlook.com> - * @author Hendrik Leppelsack <hendrik@leppelsack.de> - * @author Joas Schilling <coding@schilljs.com> - * @author John Molakvoæ <skjnldsv@protonmail.com> - * @author Jörn Friedrich Dreyer <jfd@butonic.de> - * @author Julius Haertl <jus@bitgrid.net> - * @author Julius Härtl <jus@bitgrid.net> - * @author Lukas Reschke <lukas@statuscode.ch> - * @author Michael Gapczynski <GapczynskiM@gmail.com> - * @author Morris Jobke <hey@morrisjobke.de> - * @author Nils <git@to.nilsschnabel.de> - * @author Remco Brenninkmeijer <requist1@starmail.nl> - * @author Robin Appelman <robin@icewind.nl> - * @author Robin McCorkell <robin@mccorkell.me.uk> - * @author Roeland Jago Douma <roeland@famdouma.nl> - * @author Thomas Citharel <nextcloud@tcit.fr> - * @author Thomas Müller <thomas.mueller@tmit.eu> - * - * @license AGPL-3.0 - * - * This code is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License, version 3, - * as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License, version 3, - * along with this program. If not, see <http://www.gnu.org/licenses/> - * + * SPDX-FileCopyrightText: 2016-2024 Nextcloud GmbH and Nextcloud contributors + * SPDX-FileCopyrightText: 2016 ownCloud, Inc. + * SPDX-License-Identifier: AGPL-3.0-only */ namespace OC; @@ -47,11 +12,13 @@ use OC\Search\SearchQuery; use OC\Template\CSSResourceLocator; use OC\Template\JSConfigHelper; use OC\Template\JSResourceLocator; +use OCP\App\IAppManager; use OCP\AppFramework\Http\TemplateResponse; use OCP\Defaults; use OCP\IConfig; use OCP\IInitialStateService; use OCP\INavigationManager; +use OCP\IURLGenerator; use OCP\IUserSession; use OCP\Support\Subscription\IRegistry; use OCP\Util; @@ -106,11 +73,15 @@ class TemplateLayout extends \OC_Template { $this->initialState->provideInitialState('core', 'active-app', $this->navigationManager->getActiveEntry()); $this->initialState->provideInitialState('core', 'apps', $this->navigationManager->getAll()); - $this->initialState->provideInitialState('unified-search', 'limit-default', (int)$this->config->getAppValue('core', 'unified-search.limit-default', (string)SearchQuery::LIMIT_DEFAULT)); - $this->initialState->provideInitialState('unified-search', 'min-search-length', (int)$this->config->getAppValue('core', 'unified-search.min-search-length', (string)1)); - $this->initialState->provideInitialState('unified-search', 'live-search', $this->config->getAppValue('core', 'unified-search.live-search', 'yes') === 'yes'); - Util::addScript('core', 'unified-search', 'core'); + if ($this->config->getSystemValueBool('unified_search.enabled', false) || !$this->config->getSystemValueBool('enable_non-accessible_features', true)) { + $this->initialState->provideInitialState('unified-search', 'limit-default', (int)$this->config->getAppValue('core', 'unified-search.limit-default', (string)SearchQuery::LIMIT_DEFAULT)); + $this->initialState->provideInitialState('unified-search', 'min-search-length', (int)$this->config->getAppValue('core', 'unified-search.min-search-length', (string)1)); + $this->initialState->provideInitialState('unified-search', 'live-search', $this->config->getAppValue('core', 'unified-search.live-search', 'yes') === 'yes'); + Util::addScript('core', 'legacy-unified-search', 'core'); + } else { + Util::addScript('core', 'unified-search', 'core'); + } // Set body data-theme $this->assign('enabledThemes', []); if (\OC::$server->getAppManager()->isEnabledForUser('theming') && class_exists('\OCA\Theming\Service\ThemesService')) { @@ -189,13 +160,31 @@ class TemplateLayout extends \OC_Template { $this->assign('appid', $appId); $this->assign('bodyid', 'body-public'); + // Set logo link target + $logoUrl = $this->config->getSystemValueString('logo_url', ''); + $this->assign('logoUrl', $logoUrl); + /** @var IRegistry $subscription */ $subscription = \OCP\Server::get(IRegistry::class); $showSimpleSignup = $this->config->getSystemValueBool('simpleSignUpLink.shown', true); if ($showSimpleSignup && $subscription->delegateHasValidSubscription()) { $showSimpleSignup = false; } + + $defaultSignUpLink = 'https://nextcloud.com/signup/'; + $signUpLink = $this->config->getSystemValueString('registration_link', $defaultSignUpLink); + if ($signUpLink !== $defaultSignUpLink) { + $showSimpleSignup = true; + } + + $appManager = \OCP\Server::get(IAppManager::class); + if ($appManager->isEnabledForUser('registration')) { + $urlGenerator = \OCP\Server::get(IURLGenerator::class); + $signUpLink = $urlGenerator->getAbsoluteURL('/index.php/apps/registration/'); + } + $this->assign('showSimpleSignUpLink', $showSimpleSignup); + $this->assign('signUpLink', $signUpLink); } else { parent::__construct('core', 'layout.base'); } @@ -225,7 +214,7 @@ class TemplateLayout extends \OC_Template { // this is on purpose outside of the if statement below so that the initial state is prefilled (done in the getConfig() call) // see https://github.com/nextcloud/server/pull/22636 for details $jsConfigHelper = new JSConfigHelper( - \OC::$server->getL10N('lib'), + \OCP\Util::getL10N('lib'), \OCP\Server::get(Defaults::class), \OC::$server->getAppManager(), \OC::$server->getSession(), @@ -234,7 +223,7 @@ class TemplateLayout extends \OC_Template { \OC::$server->getGroupManager(), \OC::$server->get(IniGetWrapper::class), \OC::$server->getURLGenerator(), - \OC::$server->getCapabilitiesManager(), + \OC::$server->get(CapabilitiesManager::class), \OCP\Server::get(IInitialStateService::class) ); $config = $jsConfigHelper->getConfig(); @@ -279,7 +268,7 @@ class TemplateLayout extends \OC_Template { $web = $info[1]; $file = $info[2]; - if (substr($file, -strlen('print.css')) === 'print.css') { + if (str_ends_with($file, 'print.css')) { $this->append('printcssfiles', $web.'/'.$file . $this->getVersionHashSuffix()); } else { $suffix = $this->getVersionHashSuffix($web, $file); diff --git a/lib/private/TextProcessing/Db/Task.php b/lib/private/TextProcessing/Db/Task.php index 9c6f16d11ae..31c9aab345b 100644 --- a/lib/private/TextProcessing/Db/Task.php +++ b/lib/private/TextProcessing/Db/Task.php @@ -3,24 +3,8 @@ declare(strict_types=1); /** - * @copyright Copyright (c) 2023 Marcel Klehr <mklehr@gmx.net> - * - * @author Marcel Klehr <mklehr@gmx.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/>. + * SPDX-FileCopyrightText: 2023 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later */ namespace OC\TextProcessing\Db; @@ -45,6 +29,8 @@ use OCP\TextProcessing\Task as OCPTask; * @method string getAppId() * @method setIdentifier(string $identifier) * @method string getIdentifier() + * @method setCompletionExpectedAt(null|\DateTime $completionExpectedAt) + * @method null|\DateTime getCompletionExpectedAt() */ class Task extends Entity { protected $lastUpdated; @@ -55,16 +41,17 @@ class Task extends Entity { protected $userId; protected $appId; protected $identifier; + protected $completionExpectedAt; /** * @var string[] */ - public static array $columns = ['id', 'last_updated', 'type', 'input', 'output', 'status', 'user_id', 'app_id', 'identifier']; + public static array $columns = ['id', 'last_updated', 'type', 'input', 'output', 'status', 'user_id', 'app_id', 'identifier', 'completion_expected_at']; /** * @var string[] */ - public static array $fields = ['id', 'lastUpdated', 'type', 'input', 'output', 'status', 'userId', 'appId', 'identifier']; + public static array $fields = ['id', 'lastUpdated', 'type', 'input', 'output', 'status', 'userId', 'appId', 'identifier', 'completionExpectedAt']; public function __construct() { @@ -78,6 +65,7 @@ class Task extends Entity { $this->addType('userId', 'string'); $this->addType('appId', 'string'); $this->addType('identifier', 'string'); + $this->addType('completionExpectedAt', 'datetime'); } public function toRow(): array { @@ -98,6 +86,7 @@ class Task extends Entity { 'userId' => $task->getUserId(), 'appId' => $task->getAppId(), 'identifier' => $task->getIdentifier(), + 'completionExpectedAt' => $task->getCompletionExpectedAt(), ]); return $task; } @@ -107,6 +96,7 @@ class Task extends Entity { $task->setId($this->getId()); $task->setStatus($this->getStatus()); $task->setOutput($this->getOutput()); + $task->setCompletionExpectedAt($this->getCompletionExpectedAt()); return $task; } } diff --git a/lib/private/TextProcessing/Db/TaskMapper.php b/lib/private/TextProcessing/Db/TaskMapper.php index 62dabea544f..b03e5833958 100644 --- a/lib/private/TextProcessing/Db/TaskMapper.php +++ b/lib/private/TextProcessing/Db/TaskMapper.php @@ -3,24 +3,8 @@ declare(strict_types=1); /** - * @copyright Copyright (c) 2023 Marcel Klehr <mklehr@gmx.net> - * - * @author Marcel Klehr <mklehr@gmx.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/>. + * SPDX-FileCopyrightText: 2023 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later */ namespace OC\TextProcessing\Db; diff --git a/lib/private/TextProcessing/Manager.php b/lib/private/TextProcessing/Manager.php index b9cb06c298e..44387662937 100644 --- a/lib/private/TextProcessing/Manager.php +++ b/lib/private/TextProcessing/Manager.php @@ -3,43 +3,30 @@ declare(strict_types=1); /** - * @copyright Copyright (c) 2023 Marcel Klehr <mklehr@gmx.net> - * - * @author Marcel Klehr <mklehr@gmx.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/>. + * SPDX-FileCopyrightText: 2023 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later */ namespace OC\TextProcessing; use OC\AppFramework\Bootstrap\Coordinator; use OC\TextProcessing\Db\Task as DbTask; -use OCP\IConfig; -use OCP\TextProcessing\Task; -use OCP\TextProcessing\Task as OCPTask; use OC\TextProcessing\Db\TaskMapper; use OCP\AppFramework\Db\DoesNotExistException; use OCP\AppFramework\Db\MultipleObjectsReturnedException; use OCP\BackgroundJob\IJobList; use OCP\Common\Exception\NotFoundException; use OCP\DB\Exception; +use OCP\IConfig; use OCP\IServerContainer; +use OCP\PreConditionNotMetException; +use OCP\TextProcessing\Exception\TaskFailureException; use OCP\TextProcessing\IManager; use OCP\TextProcessing\IProvider; -use OCP\PreConditionNotMetException; +use OCP\TextProcessing\IProviderWithExpectedRuntime; +use OCP\TextProcessing\IProviderWithId; +use OCP\TextProcessing\Task; +use OCP\TextProcessing\Task as OCPTask; use Psr\Log\LoggerInterface; use RuntimeException; use Throwable; @@ -114,26 +101,16 @@ class Manager implements IManager { if (!$this->canHandleTask($task)) { throw new PreConditionNotMetException('No text processing provider is installed that can handle this task'); } - $providers = $this->getProviders(); - $json = $this->config->getAppValue('core', 'ai.textprocessing_provider_preferences', ''); - if ($json !== '') { - $preferences = json_decode($json, true); - if (isset($preferences[$task->getType()])) { - // If a preference for this task type is set, move the preferred provider to the start - $provider = current(array_filter($providers, fn ($provider) => $provider::class === $preferences[$task->getType()])); - if ($provider !== false) { - $providers = array_filter($providers, fn ($p) => $p !== $provider); - array_unshift($providers, $provider); - } - } - } + $providers = $this->getPreferredProviders($task); foreach ($providers as $provider) { - if (!$task->canUseProvider($provider)) { - continue; - } try { $task->setStatus(OCPTask::STATUS_RUNNING); + if ($provider instanceof IProviderWithExpectedRuntime) { + $completionExpectedAt = new \DateTime('now'); + $completionExpectedAt->add(new \DateInterval('PT'.$provider->getExpectedRuntime().'S')); + $task->setCompletionExpectedAt($completionExpectedAt); + } if ($task->getId() === null) { $taskEntity = $this->taskMapper->insert(DbTask::fromPublicTask($task)); $task->setId($taskEntity->getId()); @@ -145,31 +122,37 @@ class Manager implements IManager { $task->setStatus(OCPTask::STATUS_SUCCESSFUL); $this->taskMapper->update(DbTask::fromPublicTask($task)); return $output; - } catch (\RuntimeException $e) { - $this->logger->info('LanguageModel call using provider ' . $provider->getName() . ' failed', ['exception' => $e]); - $task->setStatus(OCPTask::STATUS_FAILED); - $this->taskMapper->update(DbTask::fromPublicTask($task)); - throw $e; } catch (\Throwable $e) { $this->logger->info('LanguageModel call using provider ' . $provider->getName() . ' failed', ['exception' => $e]); $task->setStatus(OCPTask::STATUS_FAILED); $this->taskMapper->update(DbTask::fromPublicTask($task)); - throw new RuntimeException('LanguageModel call using provider ' . $provider->getName() . ' failed: ' . $e->getMessage(), 0, $e); + throw new TaskFailureException('LanguageModel call using provider ' . $provider->getName() . ' failed: ' . $e->getMessage(), 0, $e); } } - throw new RuntimeException('Could not run task'); + $task->setStatus(OCPTask::STATUS_FAILED); + $this->taskMapper->update(DbTask::fromPublicTask($task)); + throw new TaskFailureException('Could not run task'); } /** * @inheritDoc - * @throws Exception */ public function scheduleTask(OCPTask $task): void { if (!$this->canHandleTask($task)) { throw new PreConditionNotMetException('No LanguageModel provider is installed that can handle this task'); } $task->setStatus(OCPTask::STATUS_SCHEDULED); + $providers = $this->getPreferredProviders($task); + if (count($providers) === 0) { + throw new PreConditionNotMetException('No LanguageModel provider is installed that can handle this task'); + } + [$provider,] = $providers; + if ($provider instanceof IProviderWithExpectedRuntime) { + $completionExpectedAt = new \DateTime('now'); + $completionExpectedAt->add(new \DateInterval('PT'.$provider->getExpectedRuntime().'S')); + $task->setCompletionExpectedAt($completionExpectedAt); + } $taskEntity = DbTask::fromPublicTask($task); $this->taskMapper->insert($taskEntity); $task->setId($taskEntity->getId()); @@ -181,6 +164,25 @@ class Manager implements IManager { /** * @inheritDoc */ + public function runOrScheduleTask(OCPTask $task): bool { + if (!$this->canHandleTask($task)) { + throw new PreConditionNotMetException('No LanguageModel provider is installed that can handle this task'); + } + [$provider,] = $this->getPreferredProviders($task); + $maxExecutionTime = (int) ini_get('max_execution_time'); + // Offload the task to a background job if the expected runtime of the likely provider is longer than 80% of our max execution time + // or if the provider doesn't provide a getExpectedRuntime() method + if (!$provider instanceof IProviderWithExpectedRuntime || $provider->getExpectedRuntime() > $maxExecutionTime * 0.8) { + $this->scheduleTask($task); + return false; + } + $this->runTask($task); + return true; + } + + /** + * @inheritDoc + */ public function deleteTask(Task $task): void { $taskEntity = DbTask::fromPublicTask($task); $this->taskMapper->delete($taskEntity); @@ -253,4 +255,30 @@ class Manager implements IManager { throw new RuntimeException('Failure while trying to find tasks by appId and identifier: ' . $e->getMessage(), 0, $e); } } + + /** + * @param OCPTask $task + * @return IProvider[] + */ + public function getPreferredProviders(OCPTask $task): array { + $providers = $this->getProviders(); + $json = $this->config->getAppValue('core', 'ai.textprocessing_provider_preferences', ''); + if ($json !== '') { + $preferences = json_decode($json, true); + if (isset($preferences[$task->getType()])) { + // If a preference for this task type is set, move the preferred provider to the start + $provider = current(array_values(array_filter($providers, function ($provider) use ($preferences, $task) { + if ($provider instanceof IProviderWithId) { + return $provider->getId() === $preferences[$task->getType()]; + } + return $provider::class === $preferences[$task->getType()]; + }))); + if ($provider !== false) { + $providers = array_filter($providers, fn ($p) => $p !== $provider); + array_unshift($providers, $provider); + } + } + } + return array_values(array_filter($providers, fn (IProvider $provider) => $task->canUseProvider($provider))); + } } diff --git a/lib/private/TextProcessing/RemoveOldTasksBackgroundJob.php b/lib/private/TextProcessing/RemoveOldTasksBackgroundJob.php index 89d329acfbb..2a71256c492 100644 --- a/lib/private/TextProcessing/RemoveOldTasksBackgroundJob.php +++ b/lib/private/TextProcessing/RemoveOldTasksBackgroundJob.php @@ -3,24 +3,8 @@ declare(strict_types=1); /** - * @copyright Copyright (c) 2023 Marcel Klehr <mklehr@gmx.net> - * - * @author Marcel Klehr <mklehr@gmx.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/>. + * SPDX-FileCopyrightText: 2023 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later */ diff --git a/lib/private/TextProcessing/TaskBackgroundJob.php b/lib/private/TextProcessing/TaskBackgroundJob.php index 4c24b3e531f..781d8207934 100644 --- a/lib/private/TextProcessing/TaskBackgroundJob.php +++ b/lib/private/TextProcessing/TaskBackgroundJob.php @@ -3,24 +3,8 @@ declare(strict_types=1); /** - * @copyright Copyright (c) 2023 Marcel Klehr <mklehr@gmx.net> - * - * @author Marcel Klehr <mklehr@gmx.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/>. + * SPDX-FileCopyrightText: 2023 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later */ diff --git a/lib/private/TextToImage/Db/Task.php b/lib/private/TextToImage/Db/Task.php new file mode 100644 index 00000000000..d7eb0a22014 --- /dev/null +++ b/lib/private/TextToImage/Db/Task.php @@ -0,0 +1,101 @@ +<?php + +declare(strict_types=1); + +/** + * SPDX-FileCopyrightText: 2023 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later + */ + +namespace OC\TextToImage\Db; + +use DateTime; +use OCP\AppFramework\Db\Entity; +use OCP\AppFramework\Utility\ITimeFactory; +use OCP\TextToImage\Task as OCPTask; + +/** + * @method setLastUpdated(DateTime $lastUpdated) + * @method DateTime getLastUpdated() + * @method setInput(string $type) + * @method string getInput() + * @method setResultPath(string $resultPath) + * @method string getResultPath() + * @method setStatus(int $type) + * @method int getStatus() + * @method setUserId(?string $userId) + * @method string|null getUserId() + * @method setAppId(string $type) + * @method string getAppId() + * @method setIdentifier(string $identifier) + * @method string|null getIdentifier() + * @method setNumberOfImages(int $numberOfImages) + * @method int getNumberOfImages() + * @method setCompletionExpectedAt(DateTime $at) + * @method DateTime getCompletionExpectedAt() + */ +class Task extends Entity { + protected $lastUpdated; + protected $type; + protected $input; + protected $status; + protected $userId; + protected $appId; + protected $identifier; + protected $numberOfImages; + protected $completionExpectedAt; + + /** + * @var string[] + */ + public static array $columns = ['id', 'last_updated', 'input', 'status', 'user_id', 'app_id', 'identifier', 'number_of_images', 'completion_expected_at']; + + /** + * @var string[] + */ + public static array $fields = ['id', 'lastUpdated', 'input', 'status', 'userId', 'appId', 'identifier', 'numberOfImages', 'completionExpectedAt']; + + + public function __construct() { + // add types in constructor + $this->addType('id', 'integer'); + $this->addType('lastUpdated', 'datetime'); + $this->addType('input', 'string'); + $this->addType('status', 'integer'); + $this->addType('userId', 'string'); + $this->addType('appId', 'string'); + $this->addType('identifier', 'string'); + $this->addType('numberOfImages', 'integer'); + $this->addType('completionExpectedAt', 'datetime'); + } + + public function toRow(): array { + return array_combine(self::$columns, array_map(function ($field) { + return $this->{'get'.ucfirst($field)}(); + }, self::$fields)); + } + + public static function fromPublicTask(OCPTask $task): Task { + /** @var Task $dbTask */ + $dbTask = Task::fromParams([ + 'id' => $task->getId(), + 'lastUpdated' => \OCP\Server::get(ITimeFactory::class)->getDateTime(), + 'status' => $task->getStatus(), + 'numberOfImages' => $task->getNumberOfImages(), + 'input' => $task->getInput(), + 'userId' => $task->getUserId(), + 'appId' => $task->getAppId(), + 'identifier' => $task->getIdentifier(), + 'completionExpectedAt' => $task->getCompletionExpectedAt(), + ]); + return $dbTask; + } + + public function toPublicTask(): OCPTask { + $task = new OCPTask($this->getInput(), $this->getAppId(), $this->getNumberOfImages(), $this->getuserId(), $this->getIdentifier()); + $task->setId($this->getId()); + $task->setStatus($this->getStatus()); + $task->setCompletionExpectedAt($this->getCompletionExpectedAt()); + return $task; + } +} diff --git a/lib/private/TextToImage/Db/TaskMapper.php b/lib/private/TextToImage/Db/TaskMapper.php new file mode 100644 index 00000000000..4c0357884e3 --- /dev/null +++ b/lib/private/TextToImage/Db/TaskMapper.php @@ -0,0 +1,111 @@ +<?php + +declare(strict_types=1); + +/** + * SPDX-FileCopyrightText: 2023 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later + */ + +namespace OC\TextToImage\Db; + +use OCP\AppFramework\Db\DoesNotExistException; +use OCP\AppFramework\Db\Entity; +use OCP\AppFramework\Db\MultipleObjectsReturnedException; +use OCP\AppFramework\Db\QBMapper; +use OCP\AppFramework\Utility\ITimeFactory; +use OCP\DB\Exception; +use OCP\DB\QueryBuilder\IQueryBuilder; +use OCP\IDBConnection; + +/** + * @extends QBMapper<Task> + */ +class TaskMapper extends QBMapper { + public function __construct( + IDBConnection $db, + private ITimeFactory $timeFactory, + ) { + parent::__construct($db, 'text2image_tasks', Task::class); + } + + /** + * @param int $id + * @return Task + * @throws Exception + * @throws DoesNotExistException + * @throws MultipleObjectsReturnedException + */ + public function find(int $id): Task { + $qb = $this->db->getQueryBuilder(); + $qb->select(Task::$columns) + ->from($this->tableName) + ->where($qb->expr()->eq('id', $qb->createPositionalParameter($id))); + return $this->findEntity($qb); + } + + /** + * @param int $id + * @param string|null $userId + * @return Task + * @throws DoesNotExistException + * @throws Exception + * @throws MultipleObjectsReturnedException + */ + public function findByIdAndUser(int $id, ?string $userId): Task { + $qb = $this->db->getQueryBuilder(); + $qb->select(Task::$columns) + ->from($this->tableName) + ->where($qb->expr()->eq('id', $qb->createPositionalParameter($id))); + if ($userId === null) { + $qb->andWhere($qb->expr()->isNull('user_id')); + } else { + $qb->andWhere($qb->expr()->eq('user_id', $qb->createPositionalParameter($userId))); + } + return $this->findEntity($qb); + } + + /** + * @param string $userId + * @param string $appId + * @param string|null $identifier + * @return array + * @throws Exception + */ + public function findUserTasksByApp(?string $userId, string $appId, ?string $identifier = null): array { + $qb = $this->db->getQueryBuilder(); + $qb->select(Task::$columns) + ->from($this->tableName) + ->where($qb->expr()->eq('user_id', $qb->createPositionalParameter($userId))) + ->andWhere($qb->expr()->eq('app_id', $qb->createPositionalParameter($appId))); + if ($identifier !== null) { + $qb->andWhere($qb->expr()->eq('identifier', $qb->createPositionalParameter($identifier))); + } + return $this->findEntities($qb); + } + + /** + * @param int $timeout + * @return Task[] the deleted tasks + * @throws Exception + */ + public function deleteOlderThan(int $timeout): array { + $datetime = $this->timeFactory->getDateTime(); + $datetime->sub(new \DateInterval('PT'.$timeout.'S')); + $qb = $this->db->getQueryBuilder(); + $qb->select('*') + ->from($this->tableName) + ->where($qb->expr()->lt('last_updated', $qb->createPositionalParameter($datetime, IQueryBuilder::PARAM_DATE))); + $deletedTasks = $this->findEntities($qb); + $qb = $this->db->getQueryBuilder(); + $qb->delete($this->tableName) + ->where($qb->expr()->lt('last_updated', $qb->createPositionalParameter($datetime, IQueryBuilder::PARAM_DATE))); + $qb->executeStatement(); + return $deletedTasks; + } + + public function update(Entity $entity): Entity { + $entity->setLastUpdated($this->timeFactory->getDateTime()); + return parent::update($entity); + } +} diff --git a/lib/private/TextToImage/Manager.php b/lib/private/TextToImage/Manager.php new file mode 100644 index 00000000000..6ad3592a1b7 --- /dev/null +++ b/lib/private/TextToImage/Manager.php @@ -0,0 +1,325 @@ +<?php + +declare(strict_types=1); + +/** + * SPDX-FileCopyrightText: 2023 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later + */ + +namespace OC\TextToImage; + +use OC\AppFramework\Bootstrap\Coordinator; +use OC\TextToImage\Db\Task as DbTask; +use OC\TextToImage\Db\TaskMapper; +use OCP\AppFramework\Db\DoesNotExistException; +use OCP\AppFramework\Db\MultipleObjectsReturnedException; +use OCP\BackgroundJob\IJobList; +use OCP\DB\Exception; +use OCP\Files\AppData\IAppDataFactory; +use OCP\Files\IAppData; +use OCP\Files\NotFoundException; +use OCP\Files\NotPermittedException; +use OCP\IConfig; +use OCP\IServerContainer; +use OCP\PreConditionNotMetException; +use OCP\TextToImage\Exception\TaskFailureException; +use OCP\TextToImage\Exception\TaskNotFoundException; +use OCP\TextToImage\IManager; +use OCP\TextToImage\IProvider; +use OCP\TextToImage\IProviderWithUserId; +use OCP\TextToImage\Task; +use Psr\Log\LoggerInterface; +use RuntimeException; +use Throwable; + +class Manager implements IManager { + /** @var ?list<IProvider> */ + private ?array $providers = null; + private IAppData $appData; + + public function __construct( + private IServerContainer $serverContainer, + private Coordinator $coordinator, + private LoggerInterface $logger, + private IJobList $jobList, + private TaskMapper $taskMapper, + private IConfig $config, + IAppDataFactory $appDataFactory, + ) { + $this->appData = $appDataFactory->get('core'); + } + + /** + * @inheritDoc + */ + public function getProviders(): array { + $context = $this->coordinator->getRegistrationContext(); + if ($context === null) { + return []; + } + + if ($this->providers !== null) { + return $this->providers; + } + + $this->providers = []; + + foreach ($context->getTextToImageProviders() as $providerServiceRegistration) { + $class = $providerServiceRegistration->getService(); + try { + /** @var IProvider $provider */ + $provider = $this->serverContainer->get($class); + $this->providers[] = $provider; + } catch (Throwable $e) { + $this->logger->error('Failed to load Text to image provider ' . $class, [ + 'exception' => $e, + ]); + } + } + + return $this->providers; + } + + /** + * @inheritDoc + */ + public function hasProviders(): bool { + $context = $this->coordinator->getRegistrationContext(); + if ($context === null) { + return false; + } + return count($context->getTextToImageProviders()) > 0; + } + + /** + * @inheritDoc + */ + public function runTask(Task $task): void { + $this->logger->debug('Running TextToImage Task'); + if (!$this->hasProviders()) { + throw new PreConditionNotMetException('No text to image provider is installed that can handle this task'); + } + $providers = $this->getPreferredProviders(); + + foreach ($providers as $provider) { + $this->logger->debug('Trying to run Text2Image provider '.$provider::class); + try { + $task->setStatus(Task::STATUS_RUNNING); + $completionExpectedAt = new \DateTime('now'); + $completionExpectedAt->add(new \DateInterval('PT'.$provider->getExpectedRuntime().'S')); + $task->setCompletionExpectedAt($completionExpectedAt); + if ($task->getId() === null) { + $this->logger->debug('Inserting Text2Image task into DB'); + $taskEntity = $this->taskMapper->insert(DbTask::fromPublicTask($task)); + $task->setId($taskEntity->getId()); + } else { + $this->logger->debug('Updating Text2Image task in DB'); + $this->taskMapper->update(DbTask::fromPublicTask($task)); + } + try { + $folder = $this->appData->getFolder('text2image'); + } catch(NotFoundException) { + $this->logger->debug('Creating folder in appdata for Text2Image results'); + $folder = $this->appData->newFolder('text2image'); + } + try { + $folder = $folder->getFolder((string) $task->getId()); + } catch(NotFoundException) { + $this->logger->debug('Creating new folder in appdata Text2Image results folder'); + $folder = $folder->newFolder((string) $task->getId()); + } + $this->logger->debug('Creating result files for Text2Image task'); + $resources = []; + $files = []; + for ($i = 0; $i < $task->getNumberOfImages(); $i++) { + $file = $folder->newFile((string) $i); + $files[] = $file; + $resource = $file->write(); + if ($resource !== false && $resource !== true && is_resource($resource)) { + $resources[] = $resource; + } else { + throw new RuntimeException('Text2Image generation using provider "' . $provider->getName() . '" failed: Couldn\'t open file to write.'); + } + } + $this->logger->debug('Calling Text2Image provider\'s generate method'); + if ($provider instanceof IProviderWithUserId) { + $provider->setUserId($task->getUserId()); + } + $provider->generate($task->getInput(), $resources); + for ($i = 0; $i < $task->getNumberOfImages(); $i++) { + if (is_resource($resources[$i])) { + // If $resource hasn't been closed yet, we'll do that here + fclose($resources[$i]); + } + } + $task->setStatus(Task::STATUS_SUCCESSFUL); + $this->logger->debug('Updating Text2Image task in DB'); + $this->taskMapper->update(DbTask::fromPublicTask($task)); + return; + } catch (\RuntimeException|\Throwable $e) { + for ($i = 0; $i < $task->getNumberOfImages(); $i++) { + if (isset($files, $files[$i])) { + try { + $files[$i]->delete(); + } catch(NotPermittedException $e) { + $this->logger->warning('Failed to clean up Text2Image result file after error', ['exception' => $e]); + } + } + } + + $this->logger->info('Text2Image generation using provider "' . $provider->getName() . '" failed', ['exception' => $e]); + $task->setStatus(Task::STATUS_FAILED); + try { + $this->taskMapper->update(DbTask::fromPublicTask($task)); + } catch (Exception $e) { + $this->logger->warning('Failed to update database after Text2Image error', ['exception' => $e]); + } + throw new TaskFailureException('Text2Image generation using provider "' . $provider->getName() . '" failed: ' . $e->getMessage(), 0, $e); + } + } + + $task->setStatus(Task::STATUS_FAILED); + try { + $this->taskMapper->update(DbTask::fromPublicTask($task)); + } catch (Exception $e) { + $this->logger->warning('Failed to update database after Text2Image error', ['exception' => $e]); + } + throw new TaskFailureException('Could not run task'); + } + + /** + * @inheritDoc + */ + public function scheduleTask(Task $task): void { + if (!$this->hasProviders()) { + throw new PreConditionNotMetException('No text to image provider is installed that can handle this task'); + } + $this->logger->debug('Scheduling Text2Image Task'); + $task->setStatus(Task::STATUS_SCHEDULED); + $completionExpectedAt = new \DateTime('now'); + $completionExpectedAt->add(new \DateInterval('PT'.$this->getPreferredProviders()[0]->getExpectedRuntime().'S')); + $task->setCompletionExpectedAt($completionExpectedAt); + $taskEntity = DbTask::fromPublicTask($task); + $this->taskMapper->insert($taskEntity); + $task->setId($taskEntity->getId()); + $this->jobList->add(TaskBackgroundJob::class, [ + 'taskId' => $task->getId() + ]); + } + + /** + * @inheritDoc + */ + public function runOrScheduleTask(Task $task) : void { + if (!$this->hasProviders()) { + throw new PreConditionNotMetException('No text to image provider is installed that can handle this task'); + } + $providers = $this->getPreferredProviders(); + $maxExecutionTime = (int) ini_get('max_execution_time'); + // Offload the task to a background job if the expected runtime of the likely provider is longer than 80% of our max execution time + if ($providers[0]->getExpectedRuntime() > $maxExecutionTime * 0.8) { + $this->scheduleTask($task); + return; + } + $this->runTask($task); + } + + /** + * @inheritDoc + */ + public function deleteTask(Task $task): void { + $taskEntity = DbTask::fromPublicTask($task); + $this->taskMapper->delete($taskEntity); + $this->jobList->remove(TaskBackgroundJob::class, [ + 'taskId' => $task->getId() + ]); + } + + /** + * Get a task from its id + * + * @param int $id The id of the task + * @return Task + * @throws RuntimeException If the query failed + * @throws TaskNotFoundException If the task could not be found + */ + public function getTask(int $id): Task { + try { + $taskEntity = $this->taskMapper->find($id); + return $taskEntity->toPublicTask(); + } catch (DoesNotExistException $e) { + throw new TaskNotFoundException('Could not find task with the provided id'); + } catch (MultipleObjectsReturnedException $e) { + throw new RuntimeException('Could not uniquely identify task with given id', 0, $e); + } catch (Exception $e) { + throw new RuntimeException('Failure while trying to find task by id: ' . $e->getMessage(), 0, $e); + } + } + + /** + * Get a task from its user id and task id + * If userId is null, this can only get a task that was scheduled anonymously + * + * @param int $id The id of the task + * @param string|null $userId The user id that scheduled the task + * @return Task + * @throws RuntimeException If the query failed + * @throws TaskNotFoundException If the task could not be found + */ + public function getUserTask(int $id, ?string $userId): Task { + try { + $taskEntity = $this->taskMapper->findByIdAndUser($id, $userId); + return $taskEntity->toPublicTask(); + } catch (DoesNotExistException $e) { + throw new TaskNotFoundException('Could not find task with the provided id and user id'); + } catch (MultipleObjectsReturnedException $e) { + throw new RuntimeException('Could not uniquely identify task with given id and user id', 0, $e); + } catch (Exception $e) { + throw new RuntimeException('Failure while trying to find task by id and user id: ' . $e->getMessage(), 0, $e); + } + } + + /** + * Get a list of tasks scheduled by a specific user for a specific app + * and optionally with a specific identifier. + * This cannot be used to get anonymously scheduled tasks + * + * @param string $userId + * @param string $appId + * @param string|null $identifier + * @return Task[] + * @throws RuntimeException + */ + public function getUserTasksByApp(?string $userId, string $appId, ?string $identifier = null): array { + try { + $taskEntities = $this->taskMapper->findUserTasksByApp($userId, $appId, $identifier); + return array_map(static function (DbTask $taskEntity) { + return $taskEntity->toPublicTask(); + }, $taskEntities); + } catch (Exception $e) { + throw new RuntimeException('Failure while trying to find tasks by appId and identifier: ' . $e->getMessage(), 0, $e); + } + } + + /** + * @return list<IProvider> + */ + private function getPreferredProviders() { + $providers = $this->getProviders(); + $json = $this->config->getAppValue('core', 'ai.text2image_provider', ''); + if ($json !== '') { + try { + $id = json_decode($json, true, 512, JSON_THROW_ON_ERROR); + $provider = current(array_filter($providers, fn ($provider) => $provider->getId() === $id)); + if ($provider !== false && $provider !== null) { + $providers = [$provider]; + } + } catch (\JsonException $e) { + $this->logger->warning('Failed to decode Text2Image setting `ai.text2image_provider`', ['exception' => $e]); + } + } + + return $providers; + } +} diff --git a/lib/private/TextToImage/RemoveOldTasksBackgroundJob.php b/lib/private/TextToImage/RemoveOldTasksBackgroundJob.php new file mode 100644 index 00000000000..525d68df22b --- /dev/null +++ b/lib/private/TextToImage/RemoveOldTasksBackgroundJob.php @@ -0,0 +1,62 @@ +<?php + +declare(strict_types=1); + +/** + * SPDX-FileCopyrightText: 2023 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later + */ + + +namespace OC\TextToImage; + +use OC\TextToImage\Db\TaskMapper; +use OCP\AppFramework\Utility\ITimeFactory; +use OCP\BackgroundJob\TimedJob; +use OCP\DB\Exception; +use OCP\Files\AppData\IAppDataFactory; +use OCP\Files\IAppData; +use OCP\Files\NotFoundException; +use OCP\Files\NotPermittedException; +use Psr\Log\LoggerInterface; + +class RemoveOldTasksBackgroundJob extends TimedJob { + public const MAX_TASK_AGE_SECONDS = 60 * 50 * 24 * 7; // 1 week + + private IAppData $appData; + + public function __construct( + ITimeFactory $timeFactory, + private TaskMapper $taskMapper, + private LoggerInterface $logger, + IAppDataFactory $appDataFactory, + ) { + parent::__construct($timeFactory); + $this->appData = $appDataFactory->get('core'); + $this->setInterval(60 * 60 * 24); + } + + /** + * @param mixed $argument + * @inheritDoc + */ + protected function run($argument) { + try { + $deletedTasks = $this->taskMapper->deleteOlderThan(self::MAX_TASK_AGE_SECONDS); + $folder = $this->appData->getFolder('text2image'); + foreach ($deletedTasks as $deletedTask) { + try { + $folder->getFolder((string)$deletedTask->getId())->delete(); + } catch (NotFoundException) { + // noop + } catch (NotPermittedException $e) { + $this->logger->warning('Failed to delete stale text to image task files', ['exception' => $e]); + } + } + } catch (Exception $e) { + $this->logger->warning('Failed to delete stale text to image tasks', ['exception' => $e]); + } catch(NotFoundException) { + // noop + } + } +} diff --git a/lib/private/TextToImage/TaskBackgroundJob.php b/lib/private/TextToImage/TaskBackgroundJob.php new file mode 100644 index 00000000000..16990005530 --- /dev/null +++ b/lib/private/TextToImage/TaskBackgroundJob.php @@ -0,0 +1,47 @@ +<?php + +declare(strict_types=1); + +/** + * SPDX-FileCopyrightText: 2023 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later + */ + + +namespace OC\TextToImage; + +use OCP\AppFramework\Utility\ITimeFactory; +use OCP\BackgroundJob\QueuedJob; +use OCP\EventDispatcher\IEventDispatcher; +use OCP\TextToImage\Events\TaskFailedEvent; +use OCP\TextToImage\Events\TaskSuccessfulEvent; +use OCP\TextToImage\IManager; + +class TaskBackgroundJob extends QueuedJob { + public function __construct( + ITimeFactory $timeFactory, + private IManager $text2imageManager, + private IEventDispatcher $eventDispatcher, + ) { + parent::__construct($timeFactory); + // We want to avoid overloading the machine with these jobs + // so we only allow running one job at a time + $this->setAllowParallelRuns(false); + } + + /** + * @param array{taskId: int} $argument + * @inheritDoc + */ + protected function run($argument) { + $taskId = $argument['taskId']; + $task = $this->text2imageManager->getTask($taskId); + try { + $this->text2imageManager->runTask($task); + $event = new TaskSuccessfulEvent($task); + } catch (\Throwable $e) { + $event = new TaskFailedEvent($task, $e->getMessage()); + } + $this->eventDispatcher->dispatchTyped($event); + } +} diff --git a/lib/private/Translation/TranslationManager.php b/lib/private/Translation/TranslationManager.php index 48a0e2cdebd..08f9f36678a 100644 --- a/lib/private/Translation/TranslationManager.php +++ b/lib/private/Translation/TranslationManager.php @@ -3,24 +3,8 @@ declare(strict_types=1); /** - * @copyright Copyright (c) 2023 Julius Härtl <jus@bitgrid.net> - * - * @author Julius Härtl <jus@bitgrid.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/>. + * SPDX-FileCopyrightText: 2023 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later */ @@ -30,11 +14,14 @@ use InvalidArgumentException; use OC\AppFramework\Bootstrap\Coordinator; use OCP\IConfig; use OCP\IServerContainer; +use OCP\IUserSession; use OCP\PreConditionNotMetException; use OCP\Translation\CouldNotTranslateException; use OCP\Translation\IDetectLanguageProvider; use OCP\Translation\ITranslationManager; use OCP\Translation\ITranslationProvider; +use OCP\Translation\ITranslationProviderWithId; +use OCP\Translation\ITranslationProviderWithUserId; use Psr\Container\ContainerExceptionInterface; use Psr\Container\NotFoundExceptionInterface; use Psr\Log\LoggerInterface; @@ -50,6 +37,7 @@ class TranslationManager implements ITranslationManager { private Coordinator $coordinator, private LoggerInterface $logger, private IConfig $config, + private IUserSession $userSession, ) { } @@ -73,19 +61,26 @@ class TranslationManager implements ITranslationManager { $precedence = json_decode($json, true); $newProviders = []; foreach ($precedence as $className) { - $provider = current(array_filter($providers, fn ($provider) => $provider::class === $className)); + $provider = current(array_filter($providers, function ($provider) use ($className) { + return $provider instanceof ITranslationProviderWithId ? $provider->getId() === $className : $provider::class === $className; + })); if ($provider !== false) { $newProviders[] = $provider; } } // Add all providers that haven't been added so far - $newProviders += array_udiff($providers, $newProviders, fn ($a, $b) => $a::class > $b::class ? 1 : ($a::class < $b::class ? -1 : 0)); + $newProviders += array_udiff($providers, $newProviders, function ($a, $b) { + return ($a instanceof ITranslationProviderWithId ? $a->getId() : $a::class) <=> ($b instanceof ITranslationProviderWithId ? $b->getId() : $b::class); + }); $providers = $newProviders; } if ($fromLanguage === null) { foreach ($providers as $provider) { if ($provider instanceof IDetectLanguageProvider) { + if ($provider instanceof ITranslationProviderWithUserId) { + $provider->setUserId($this->userSession->getUser()?->getUID()); + } $fromLanguage = $provider->detectLanguage($text); } @@ -105,6 +100,9 @@ class TranslationManager implements ITranslationManager { foreach ($providers as $provider) { try { + if ($provider instanceof ITranslationProviderWithUserId) { + $provider->setUserId($this->userSession->getUser()?->getUID()); + } return $provider->translate($fromLanguage, $toLanguage, $text); } catch (RuntimeException $e) { $this->logger->warning("Failed to translate from {$fromLanguage} to {$toLanguage} using provider {$provider->getName()}", ['exception' => $e]); diff --git a/lib/private/URLGenerator.php b/lib/private/URLGenerator.php index 3a52b99889c..ab568a3296d 100644 --- a/lib/private/URLGenerator.php +++ b/lib/private/URLGenerator.php @@ -1,42 +1,10 @@ <?php declare(strict_types=1); - /** - * @copyright Copyright (c) 2016, ownCloud, Inc. - * - * @author Arthur Schiwon <blizzz@arthur-schiwon.de> - * @author Bart Visscher <bartv@thisnet.nl> - * @author Christoph Wurst <christoph@winzerhof-wurst.at> - * @author Daniel Rudolf <github.com@daniel-rudolf.de> - * @author Felix Epp <work@felixepp.de> - * @author Joas Schilling <coding@schilljs.com> - * @author Jörn Friedrich Dreyer <jfd@butonic.de> - * @author Julius Haertl <jus@bitgrid.net> - * @author Julius Härtl <jus@bitgrid.net> - * @author Lukas Reschke <lukas@statuscode.ch> - * @author mmccarn <mmccarn-github@mmsionline.us> - * @author Morris Jobke <hey@morrisjobke.de> - * @author Robin Appelman <robin@icewind.nl> - * @author Robin McCorkell <robin@mccorkell.me.uk> - * @author Roeland Jago Douma <roeland@famdouma.nl> - * @author Thomas Müller <thomas.mueller@tmit.eu> - * @author Thomas Tanghus <thomas@tanghus.net> - * - * @license AGPL-3.0 - * - * This code is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License, version 3, - * as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License, version 3, - * along with this program. If not, see <http://www.gnu.org/licenses/> - * + * SPDX-FileCopyrightText: 2016-2024 Nextcloud GmbH and Nextcloud contributors + * SPDX-FileCopyrightText: 2016 ownCloud, Inc. + * SPDX-License-Identifier: AGPL-3.0-only */ namespace OC; @@ -70,10 +38,10 @@ class URLGenerator implements IURLGenerator { private ?IAppManager $appManager = null; public function __construct(IConfig $config, - IUserSession $userSession, - ICacheFactory $cacheFactory, - IRequest $request, - Router $router + IUserSession $userSession, + ICacheFactory $cacheFactory, + IRequest $request, + Router $router ) { $this->config = $config; $this->userSession = $userSession; @@ -116,16 +84,25 @@ class URLGenerator implements IURLGenerator { } public function linkToOCSRouteAbsolute(string $routeName, array $arguments = []): string { + // Returns `/subfolder/index.php/ocsapp/…` with `'htaccess.IgnoreFrontController' => false` in config.php + // And `/subfolder/ocsapp/…` with `'htaccess.IgnoreFrontController' => true` in config.php $route = $this->router->generate('ocs.'.$routeName, $arguments, false); - $indexPhpPos = strpos($route, '/index.php/'); - if ($indexPhpPos !== false) { - $route = substr($route, $indexPhpPos + 10); + // Cut off `/subfolder` + if (\OC::$WEBROOT !== '' && str_starts_with($route, \OC::$WEBROOT)) { + $route = substr($route, \strlen(\OC::$WEBROOT)); + } + + if (str_starts_with($route, '/index.php/')) { + $route = substr($route, 10); } + // Remove `ocsapp/` bit $route = substr($route, 7); + // Prefix with ocs/v2.php endpoint $route = '/ocs/v2.php' . $route; + // Turn into an absolute URL return $this->getAbsoluteURL($route); } @@ -147,7 +124,7 @@ class URLGenerator implements IURLGenerator { $app_path = $this->getAppManager()->getAppPath($appName); // Check if the app is in the app folder if (file_exists($app_path . '/' . $file)) { - if (substr($file, -3) === 'php') { + if (str_ends_with($file, 'php')) { $urlLinkTo = \OC::$WEBROOT . '/index.php/apps/' . $appName; if ($frontControllerActive) { $urlLinkTo = \OC::$WEBROOT . '/apps/' . $appName; @@ -217,7 +194,7 @@ class URLGenerator implements IURLGenerator { $themingEnabled = $this->config->getSystemValueBool('installed', false) && $this->getAppManager()->isEnabledForUser('theming'); $themingImagePath = false; if ($themingEnabled) { - $themingDefaults = \OC::$server->getThemingDefaults(); + $themingDefaults = \OC::$server->get('ThemingDefaults'); if ($themingDefaults instanceof ThemingDefaults) { $themingImagePath = $themingDefaults->replaceImagePath($appName, $file); } @@ -290,7 +267,7 @@ class URLGenerator implements IURLGenerator { * @return string url to the online documentation */ public function linkToDocs(string $key): string { - $theme = \OC::$server->getThemingDefaults(); + $theme = \OC::$server->get('ThemingDefaults'); return $theme->buildDocLinkToKey($key); } diff --git a/lib/private/Updater.php b/lib/private/Updater.php index 5a14bb17507..6d23e81aa63 100644 --- a/lib/private/Updater.php +++ b/lib/private/Updater.php @@ -3,51 +3,14 @@ declare(strict_types=1); /** - * @copyright Copyright (c) 2016, ownCloud, Inc. - * @copyright Copyright (c) 2016, Lukas Reschke <lukas@statuscode.ch> - * - * @author Arthur Schiwon <blizzz@arthur-schiwon.de> - * @author Bjoern Schiessle <bjoern@schiessle.org> - * @author Christoph Wurst <christoph@winzerhof-wurst.at> - * @author Frank Karlitschek <frank@karlitschek.de> - * @author Georg Ehrke <oc.list@georgehrke.com> - * @author J0WI <J0WI@users.noreply.github.com> - * @author Joas Schilling <coding@schilljs.com> - * @author Julius Härtl <jus@bitgrid.net> - * @author Lukas Reschke <lukas@statuscode.ch> - * @author Morris Jobke <hey@morrisjobke.de> - * @author Robin Appelman <robin@icewind.nl> - * @author Roeland Jago Douma <roeland@famdouma.nl> - * @author Steffen Lindner <mail@steffen-lindner.de> - * @author Thomas Müller <thomas.mueller@tmit.eu> - * @author Victor Dubiniuk <dubiniuk@owncloud.com> - * @author Vincent Petry <vincent@nextcloud.com> - * - * @license AGPL-3.0 - * - * This code is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License, version 3, - * as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License, version 3, - * along with this program. If not, see <http://www.gnu.org/licenses/> - * + * SPDX-FileCopyrightText: 2016 Nextcloud GmbH and Nextcloud contributors + * SPDX-FileCopyrightText: 2016 ownCloud, Inc. + * SPDX-License-Identifier: AGPL-3.0-only */ namespace OC; -use OCP\App\IAppManager; -use OCP\EventDispatcher\Event; -use OCP\EventDispatcher\IEventDispatcher; -use OCP\HintException; -use OCP\IConfig; -use OCP\ILogger; -use OCP\Util; use OC\App\AppManager; +use OC\App\AppStore\Fetcher\AppFetcher; use OC\DB\Connection; use OC\DB\MigrationService; use OC\DB\MigratorExecuteSqlEvent; @@ -61,6 +24,14 @@ use OC\Repair\Events\RepairStartEvent; use OC\Repair\Events\RepairStepEvent; use OC\Repair\Events\RepairWarningEvent; use OC_App; +use OCP\App\IAppManager; +use OCP\EventDispatcher\Event; +use OCP\EventDispatcher\IEventDispatcher; +use OCP\HintException; +use OCP\IAppConfig; +use OCP\IConfig; +use OCP\ILogger; +use OCP\Util; use Psr\Log\LoggerInterface; /** @@ -73,19 +44,7 @@ use Psr\Log\LoggerInterface; * - failure(string $message) */ class Updater extends BasicEmitter { - /** @var LoggerInterface */ - private $log; - - /** @var IConfig */ - private $config; - - /** @var Checker */ - private $checker; - - /** @var Installer */ - private $installer; - - private $logLevelNames = [ + private array $logLevelNames = [ 0 => 'Debug', 1 => 'Info', 2 => 'Warning', @@ -93,14 +52,13 @@ class Updater extends BasicEmitter { 4 => 'Fatal', ]; - public function __construct(IConfig $config, - Checker $checker, - ?LoggerInterface $log, - Installer $installer) { - $this->log = $log; - $this->config = $config; - $this->checker = $checker; - $this->installer = $installer; + public function __construct( + private IConfig $config, + private IAppConfig $appConfig, + private Checker $checker, + private ?LoggerInterface $log, + private Installer $installer + ) { } /** @@ -255,7 +213,8 @@ class Updater extends BasicEmitter { file_put_contents($this->config->getSystemValueString('datadirectory', \OC::$SERVERROOT . '/data') . '/.ocdata', ''); // pre-upgrade repairs - $repair = new Repair(Repair::getBeforeUpgradeRepairSteps(), \OC::$server->get(\OCP\EventDispatcher\IEventDispatcher::class), \OC::$server->get(LoggerInterface::class)); + $repair = \OCP\Server::get(Repair::class); + $repair->setRepairSteps(Repair::getBeforeUpgradeRepairSteps()); $repair->run(); $this->doCoreUpgrade(); @@ -272,7 +231,7 @@ class Updater extends BasicEmitter { $this->doAppUpgrade(); // Update the appfetchers version so it downloads the correct list from the appstore - \OC::$server->getAppFetcher()->setVersion($currentVersion); + \OC::$server->get(AppFetcher::class)->setVersion($currentVersion); /** @var AppManager $appManager */ $appManager = \OC::$server->getAppManager(); @@ -296,11 +255,12 @@ class Updater extends BasicEmitter { } // post-upgrade repairs - $repair = new Repair(Repair::getRepairSteps(), \OC::$server->get(\OCP\EventDispatcher\IEventDispatcher::class), \OC::$server->get(LoggerInterface::class)); + $repair = \OCP\Server::get(Repair::class); + $repair->setRepairSteps(Repair::getRepairSteps()); $repair->run(); //Invalidate update feed - $this->config->setAppValue('core', 'lastupdatedat', '0'); + $this->appConfig->setValueInt('core', 'lastupdatedat', 0); // Check for code integrity if not disabled if (\OC::$server->getIntegrityCodeChecker()->isCodeCheckEnforced()) { diff --git a/lib/private/Updater/Changes.php b/lib/private/Updater/Changes.php index 81a57c0c871..bdf799a0100 100644 --- a/lib/private/Updater/Changes.php +++ b/lib/private/Updater/Changes.php @@ -3,25 +3,8 @@ declare(strict_types=1); /** - * @copyright Copyright (c) 2018 Arthur Schiwon <blizzz@arthur-schiwon.de> - * - * @author Arthur Schiwon <blizzz@arthur-schiwon.de> - * - * @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/>. - * + * SPDX-FileCopyrightText: 2018 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later */ namespace OC\Updater; diff --git a/lib/private/Updater/ChangesCheck.php b/lib/private/Updater/ChangesCheck.php index ee4d1f1fcee..df017b09040 100644 --- a/lib/private/Updater/ChangesCheck.php +++ b/lib/private/Updater/ChangesCheck.php @@ -3,27 +3,8 @@ declare(strict_types=1); /** - * @copyright Copyright (c) 2018 Arthur Schiwon <blizzz@arthur-schiwon.de> - * - * @author Arthur Schiwon <blizzz@arthur-schiwon.de> - * @author Christoph Wurst <christoph@winzerhof-wurst.at> - * @author Daniel Kesselberg <mail@danielkesselberg.de> - * - * @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/>. - * + * SPDX-FileCopyrightText: 2018 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later */ namespace OC\Updater; diff --git a/lib/private/Updater/ChangesMapper.php b/lib/private/Updater/ChangesMapper.php index 33d50f5844f..5583cf5a1a6 100644 --- a/lib/private/Updater/ChangesMapper.php +++ b/lib/private/Updater/ChangesMapper.php @@ -3,26 +3,8 @@ declare(strict_types=1); /** - * @copyright Copyright (c) 2018 Arthur Schiwon <blizzz@arthur-schiwon.de> - * - * @author Arthur Schiwon <blizzz@arthur-schiwon.de> - * @author Christoph Wurst <christoph@winzerhof-wurst.at> - * - * @license GNU AGPL version 3 or any later version - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as - * published by the Free Software Foundation, either version 3 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - * + * SPDX-FileCopyrightText: 2018 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later */ namespace OC\Updater; diff --git a/lib/private/Updater/VersionCheck.php b/lib/private/Updater/VersionCheck.php index 2aab260716a..9ad129db1a4 100644 --- a/lib/private/Updater/VersionCheck.php +++ b/lib/private/Updater/VersionCheck.php @@ -1,43 +1,28 @@ <?php + /** - * @copyright Copyright (c) 2016, ownCloud, Inc. - * - * @author Arthur Schiwon <blizzz@arthur-schiwon.de> - * @author Joas Schilling <coding@schilljs.com> - * @author Lukas Reschke <lukas@statuscode.ch> - * @author Morris Jobke <hey@morrisjobke.de> - * @author Roeland Jago Douma <roeland@famdouma.nl> - * @author Thomas Müller <thomas.mueller@tmit.eu> - * - * @license AGPL-3.0 - * - * This code is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License, version 3, - * as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License, version 3, - * along with this program. If not, see <http://www.gnu.org/licenses/> - * + * SPDX-FileCopyrightText: 2016-2024 Nextcloud GmbH and Nextcloud contributors + * SPDX-FileCopyrightText: 2016 ownCloud, Inc. + * SPDX-License-Identifier: AGPL-3.0-only */ namespace OC\Updater; use OCP\Http\Client\IClientService; +use OCP\IAppConfig; use OCP\IConfig; use OCP\IUserManager; use OCP\Support\Subscription\IRegistry; use OCP\Util; +use Psr\Log\LoggerInterface; class VersionCheck { public function __construct( private IClientService $clientService, private IConfig $config, + private IAppConfig $appConfig, private IUserManager $userManager, private IRegistry $registry, + private LoggerInterface $logger, ) { } @@ -54,13 +39,13 @@ class VersionCheck { } // Look up the cache - it is invalidated all 30 minutes - if (((int)$this->config->getAppValue('core', 'lastupdatedat') + 1800) > time()) { + if (($this->appConfig->getValueInt('core', 'lastupdatedat') + 1800) > time()) { return json_decode($this->config->getAppValue('core', 'lastupdateResult'), true); } $updaterUrl = $this->config->getSystemValueString('updater.server.url', 'https://updates.nextcloud.com/updater_server/'); - $this->config->setAppValue('core', 'lastupdatedat', (string)time()); + $this->appConfig->setValueInt('core', 'lastupdatedat', time()); if ($this->config->getAppValue('core', 'installedat', '') === '') { $this->config->setAppValue('core', 'installedat', (string)microtime(true)); @@ -68,7 +53,7 @@ class VersionCheck { $version = Util::getVersion(); $version['installed'] = $this->config->getAppValue('core', 'installedat'); - $version['updated'] = $this->config->getAppValue('core', 'lastupdatedat'); + $version['updated'] = $this->appConfig->getValueInt('core', 'lastupdatedat'); $version['updatechannel'] = \OC_Util::getChannel(); $version['edition'] = ''; $version['build'] = \OC_Util::getBuild(); @@ -86,6 +71,8 @@ class VersionCheck { try { $xml = $this->getUrlContent($url); } catch (\Exception $e) { + $this->logger->info('Version could not be fetched from updater server: ' . $url, ['exception' => $e]); + return false; } @@ -123,7 +110,9 @@ class VersionCheck { */ protected function getUrlContent($url) { $client = $this->clientService->newClient(); - $response = $client->get($url); + $response = $client->get($url, [ + 'timeout' => 5, + ]); return $response->getBody(); } diff --git a/lib/private/User/AvailabilityCoordinator.php b/lib/private/User/AvailabilityCoordinator.php new file mode 100644 index 00000000000..5017a85c28b --- /dev/null +++ b/lib/private/User/AvailabilityCoordinator.php @@ -0,0 +1,122 @@ +<?php + +declare(strict_types=1); + +/** + * SPDX-FileCopyrightText: 2023 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later + */ + +namespace OC\User; + +use JsonException; +use OCA\DAV\AppInfo\Application; +use OCA\DAV\CalDAV\TimezoneService; +use OCA\DAV\Service\AbsenceService; +use OCP\ICache; +use OCP\ICacheFactory; +use OCP\IConfig; +use OCP\IUser; +use OCP\User\IAvailabilityCoordinator; +use OCP\User\IOutOfOfficeData; +use Psr\Log\LoggerInterface; + +class AvailabilityCoordinator implements IAvailabilityCoordinator { + private ICache $cache; + + public function __construct( + ICacheFactory $cacheFactory, + private IConfig $config, + private AbsenceService $absenceService, + private LoggerInterface $logger, + private TimezoneService $timezoneService, + ) { + $this->cache = $cacheFactory->createLocal('OutOfOfficeData'); + } + + public function isEnabled(): bool { + return $this->config->getAppValue(Application::APP_ID, 'hide_absence_settings', 'no') === 'no'; + } + + private function getCachedOutOfOfficeData(IUser $user): ?OutOfOfficeData { + $cachedString = $this->cache->get($user->getUID()); + if ($cachedString === null) { + return null; + } + + try { + $cachedData = json_decode($cachedString, true, 10, JSON_THROW_ON_ERROR); + } catch (JsonException $e) { + $this->logger->error('Failed to deserialize cached out-of-office data: ' . $e->getMessage(), [ + 'exception' => $e, + 'json' => $cachedString, + ]); + return null; + } + + return new OutOfOfficeData( + $cachedData['id'], + $user, + $cachedData['startDate'], + $cachedData['endDate'], + $cachedData['shortMessage'], + $cachedData['message'], + ); + } + + private function setCachedOutOfOfficeData(IOutOfOfficeData $data): void { + try { + $cachedString = json_encode([ + 'id' => $data->getId(), + 'startDate' => $data->getStartDate(), + 'endDate' => $data->getEndDate(), + 'shortMessage' => $data->getShortMessage(), + 'message' => $data->getMessage(), + ], JSON_THROW_ON_ERROR); + } catch (JsonException $e) { + $this->logger->error('Failed to serialize out-of-office data: ' . $e->getMessage(), [ + 'exception' => $e, + ]); + return; + } + + $this->cache->set($data->getUser()->getUID(), $cachedString, 300); + } + + public function getCurrentOutOfOfficeData(IUser $user): ?IOutOfOfficeData { + $timezone = $this->getCachedTimezone($user->getUID()); + if ($timezone === null) { + $timezone = $this->timezoneService->getUserTimezone($user->getUID()) ?? $this->timezoneService->getDefaultTimezone(); + $this->setCachedTimezone($user->getUID(), $timezone); + } + + $data = $this->getCachedOutOfOfficeData($user); + if ($data === null) { + $absenceData = $this->absenceService->getAbsence($user->getUID()); + if ($absenceData === null) { + return null; + } + $data = $absenceData->toOutOufOfficeData($user, $timezone); + } + + $this->setCachedOutOfOfficeData($data); + return $data; + } + + private function getCachedTimezone(string $userId): ?string { + return $this->cache->get($userId . '_timezone') ?? null; + } + + private function setCachedTimezone(string $userId, string $timezone): void { + $this->cache->set($userId . '_timezone', $timezone, 3600); + } + + public function clearCache(string $userId): void { + $this->cache->set($userId, null, 300); + $this->cache->set($userId . '_timezone', null, 3600); + } + + public function isInEffect(IOutOfOfficeData $data): bool { + return $this->absenceService->isInEffect($data); + } +} diff --git a/lib/private/User/Backend.php b/lib/private/User/Backend.php index b68e4c2e541..98b291e5dee 100644 --- a/lib/private/User/Backend.php +++ b/lib/private/User/Backend.php @@ -1,26 +1,9 @@ <?php + /** - * @copyright Copyright (c) 2016, ownCloud, Inc. - * - * @author Christoph Wurst <christoph@winzerhof-wurst.at> - * @author Joas Schilling <coding@schilljs.com> - * @author Jörn Friedrich Dreyer <jfd@butonic.de> - * @author Roeland Jago Douma <roeland@famdouma.nl> - * - * @license AGPL-3.0 - * - * This code is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License, version 3, - * as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License, version 3, - * along with this program. If not, see <http://www.gnu.org/licenses/> - * + * SPDX-FileCopyrightText: 2016-2024 Nextcloud GmbH and Nextcloud contributors + * SPDX-FileCopyrightText: 2016 ownCloud, Inc. + * SPDX-License-Identifier: AGPL-3.0-only */ namespace OC\User; diff --git a/lib/private/User/Database.php b/lib/private/User/Database.php index 9ef1bc67a58..cc7050f2da8 100644 --- a/lib/private/User/Database.php +++ b/lib/private/User/Database.php @@ -1,47 +1,10 @@ <?php declare(strict_types=1); - /** - * @copyright Copyright (c) 2016, ownCloud, Inc. - * - * @author adrien <adrien.waksberg@believedigital.com> - * @author Aldo "xoen" Giambelluca <xoen@xoen.org> - * @author Arthur Schiwon <blizzz@arthur-schiwon.de> - * @author Bart Visscher <bartv@thisnet.nl> - * @author Bjoern Schiessle <bjoern@schiessle.org> - * @author Björn Schießle <bjoern@schiessle.org> - * @author Christoph Wurst <christoph@winzerhof-wurst.at> - * @author Daniel Calviño Sánchez <danxuliu@gmail.com> - * @author fabian <fabian@web2.0-apps.de> - * @author Georg Ehrke <oc.list@georgehrke.com> - * @author Jakob Sack <mail@jakobsack.de> - * @author Joas Schilling <coding@schilljs.com> - * @author Jörn Friedrich Dreyer <jfd@butonic.de> - * @author Loki3000 <github@labcms.ru> - * @author Lukas Reschke <lukas@statuscode.ch> - * @author Morris Jobke <hey@morrisjobke.de> - * @author nishiki <nishiki@yaegashi.fr> - * @author Robin Appelman <robin@icewind.nl> - * @author Robin McCorkell <robin@mccorkell.me.uk> - * @author Roeland Jago Douma <roeland@famdouma.nl> - * @author Thomas Müller <thomas.mueller@tmit.eu> - * @author Vincent Petry <vincent@nextcloud.com> - * - * @license AGPL-3.0 - * - * This code is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License, version 3, - * as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License, version 3, - * along with this program. If not, see <http://www.gnu.org/licenses/> - * + * SPDX-FileCopyrightText: 2016-2024 Nextcloud GmbH and Nextcloud contributors + * SPDX-FileCopyrightText: 2016 ownCloud, Inc. + * SPDX-License-Identifier: AGPL-3.0-only */ namespace OC\User; @@ -50,6 +13,7 @@ use OCP\Cache\CappedMemoryCache; use OCP\EventDispatcher\IEventDispatcher; use OCP\IDBConnection; use OCP\Security\Events\ValidatePasswordPolicyEvent; +use OCP\Security\IHasher; use OCP\User\Backend\ABackend; use OCP\User\Backend\ICheckPasswordBackend; use OCP\User\Backend\ICountUsersBackend; @@ -130,7 +94,7 @@ class Database extends ABackend implements $qb->insert($this->table) ->values([ 'uid' => $qb->createNamedParameter($uid), - 'password' => $qb->createNamedParameter(\OC::$server->getHasher()->hash($password)), + 'password' => $qb->createNamedParameter(\OC::$server->get(IHasher::class)->hash($password)), 'uid_lower' => $qb->createNamedParameter(mb_strtolower($uid)), ]); @@ -197,7 +161,7 @@ class Database extends ABackend implements if ($this->userExists($uid)) { $this->eventDispatcher->dispatchTyped(new ValidatePasswordPolicyEvent($password)); - $hasher = \OC::$server->getHasher(); + $hasher = \OC::$server->get(IHasher::class); $hashedPassword = $hasher->hash($password); $return = $this->updatePassword($uid, $hashedPassword); @@ -353,7 +317,7 @@ class Database extends ABackend implements if ($found && is_array($this->cache[$loginName])) { $storedHash = $this->cache[$loginName]['password']; $newHash = ''; - if (\OC::$server->getHasher()->verify($password, $storedHash, $newHash)) { + if (\OC::$server->get(IHasher::class)->verify($password, $storedHash, $newHash)) { if (!empty($newHash)) { $this->updatePassword($loginName, $newHash); } diff --git a/lib/private/User/DisplayNameCache.php b/lib/private/User/DisplayNameCache.php index 6ee74cc9f6c..34db783de68 100644 --- a/lib/private/User/DisplayNameCache.php +++ b/lib/private/User/DisplayNameCache.php @@ -1,26 +1,10 @@ <?php declare(strict_types=1); - /** - * @copyright Copyright 2022 Carl Schwan <carl@carlschwan.eu> - * @license AGPL-3.0-or-later - * - * This code is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License, version 3, - * as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License, version 3, - * along with this program. If not, see <http://www.gnu.org/licenses/> - * + * SPDX-FileCopyrightText: 2022 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later */ - - namespace OC\User; use OCP\EventDispatcher\Event; @@ -37,6 +21,7 @@ use OCP\User\Events\UserDeletedEvent; * This saves fetching the user from a user backend and later on fetching * their preferences. It's generally not an issue if this data is slightly * outdated. + * @template-implements IEventListener<UserChangedEvent|UserDeletedEvent> */ class DisplayNameCache implements IEventListener { private array $cache = []; diff --git a/lib/private/User/LazyUser.php b/lib/private/User/LazyUser.php index 396d3c252f1..80b2bfe510f 100644 --- a/lib/private/User/LazyUser.php +++ b/lib/private/User/LazyUser.php @@ -2,23 +2,8 @@ declare(strict_types=1); /** - * @copyright Copyright (c) 2022 Robin Appelman <robin@icewind.nl> - * - * @license GNU AGPL version 3 or any later version - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as - * published by the Free Software Foundation, either version 3 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - * + * SPDX-FileCopyrightText: 2022 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later */ namespace OC\User; diff --git a/lib/private/User/Listeners/BeforeUserDeletedListener.php b/lib/private/User/Listeners/BeforeUserDeletedListener.php index ec1f80c5413..50dc9835400 100644 --- a/lib/private/User/Listeners/BeforeUserDeletedListener.php +++ b/lib/private/User/Listeners/BeforeUserDeletedListener.php @@ -3,31 +3,17 @@ declare(strict_types=1); /** - * @copyright Copyright (c) 2022 Carl Schwan <carl@carlschwan.eu> - * - * @license AGPL-3.0-or-later - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as - * published by the Free Software Foundation, either version 3 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - * + * SPDX-FileCopyrightText: 2022 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later */ namespace OC\User\Listeners; use OCP\EventDispatcher\Event; use OCP\EventDispatcher\IEventListener; -use OCP\User\Events\BeforeUserDeletedEvent; use OCP\Files\NotFoundException; use OCP\IAvatarManager; +use OCP\Security\ICredentialsManager; +use OCP\User\Events\BeforeUserDeletedEvent; use Psr\Log\LoggerInterface; /** @@ -35,10 +21,12 @@ use Psr\Log\LoggerInterface; */ class BeforeUserDeletedListener implements IEventListener { private IAvatarManager $avatarManager; + private ICredentialsManager $credentialsManager; private LoggerInterface $logger; - public function __construct(LoggerInterface $logger, IAvatarManager $avatarManager) { + public function __construct(LoggerInterface $logger, IAvatarManager $avatarManager, ICredentialsManager $credentialsManager) { $this->avatarManager = $avatarManager; + $this->credentialsManager = $credentialsManager; $this->logger = $logger; } @@ -61,5 +49,7 @@ class BeforeUserDeletedListener implements IEventListener { 'exception' => $e, ]); } + // Delete storages credentials on user deletion + $this->credentialsManager->erase($user->getUID()); } } diff --git a/lib/private/User/Listeners/UserChangedListener.php b/lib/private/User/Listeners/UserChangedListener.php index a561db2423d..983a4e81233 100644 --- a/lib/private/User/Listeners/UserChangedListener.php +++ b/lib/private/User/Listeners/UserChangedListener.php @@ -3,31 +3,16 @@ declare(strict_types=1); /** - * @copyright Copyright (c) 2022 Carl Schwan <carl@carlschwan.eu> - * - * @license AGPL-3.0-or-later - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as - * published by the Free Software Foundation, either version 3 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - * + * SPDX-FileCopyrightText: 2022 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later */ namespace OC\User\Listeners; use OCP\EventDispatcher\Event; use OCP\EventDispatcher\IEventListener; -use OCP\User\Events\UserChangedEvent; use OCP\Files\NotFoundException; use OCP\IAvatarManager; +use OCP\User\Events\UserChangedEvent; /** * @template-implements IEventListener<UserChangedEvent> diff --git a/lib/private/User/LoginException.php b/lib/private/User/LoginException.php index 7133b7b76dc..6f0e98513dd 100644 --- a/lib/private/User/LoginException.php +++ b/lib/private/User/LoginException.php @@ -1,23 +1,9 @@ <?php + /** - * @copyright Copyright (c) 2016, ownCloud, Inc. - * - * @author Robin Appelman <robin@icewind.nl> - * - * @license AGPL-3.0 - * - * This code is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License, version 3, - * as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License, version 3, - * along with this program. If not, see <http://www.gnu.org/licenses/> - * + * SPDX-FileCopyrightText: 2016-2024 Nextcloud GmbH and Nextcloud contributors + * SPDX-FileCopyrightText: 2016 ownCloud, Inc. + * SPDX-License-Identifier: AGPL-3.0-only */ namespace OC\User; diff --git a/lib/private/User/Manager.php b/lib/private/User/Manager.php index fb1afb65825..d93431a2699 100644 --- a/lib/private/User/Manager.php +++ b/lib/private/User/Manager.php @@ -1,35 +1,9 @@ <?php + /** - * @copyright Copyright (c) 2016, ownCloud, Inc. - * - * @author Arthur Schiwon <blizzz@arthur-schiwon.de> - * @author Bjoern Schiessle <bjoern@schiessle.org> - * @author Christoph Wurst <christoph@winzerhof-wurst.at> - * @author Georg Ehrke <oc.list@georgehrke.com> - * @author Joas Schilling <coding@schilljs.com> - * @author John Molakvoæ <skjnldsv@protonmail.com> - * @author Jörn Friedrich Dreyer <jfd@butonic.de> - * @author Lukas Reschke <lukas@statuscode.ch> - * @author Morris Jobke <hey@morrisjobke.de> - * @author Robin Appelman <robin@icewind.nl> - * @author Roeland Jago Douma <roeland@famdouma.nl> - * @author Thomas Müller <thomas.mueller@tmit.eu> - * @author Vincent Chan <plus.vincchan@gmail.com> - * - * @license AGPL-3.0 - * - * This code is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License, version 3, - * as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License, version 3, - * along with this program. If not, see <http://www.gnu.org/licenses/> - * + * SPDX-FileCopyrightText: 2016-2024 Nextcloud GmbH and Nextcloud contributors + * SPDX-FileCopyrightText: 2016 ownCloud, Inc. + * SPDX-License-Identifier: AGPL-3.0-only */ namespace OC\User; @@ -48,13 +22,15 @@ use OCP\IUserManager; use OCP\L10N\IFactory; use OCP\Server; use OCP\Support\Subscription\IAssertion; -use OCP\User\Backend\IGetRealUIDBackend; -use OCP\User\Backend\ISearchKnownUsersBackend; use OCP\User\Backend\ICheckPasswordBackend; use OCP\User\Backend\ICountUsersBackend; +use OCP\User\Backend\IGetRealUIDBackend; +use OCP\User\Backend\IProvideEnabledStateBackend; +use OCP\User\Backend\ISearchKnownUsersBackend; use OCP\User\Events\BeforeUserCreatedEvent; use OCP\User\Events\UserCreatedEvent; use OCP\UserInterface; +use Psr\Log\LoggerInterface; /** * Class Manager @@ -77,35 +53,26 @@ class Manager extends PublicEmitter implements IUserManager { /** * @var \OCP\UserInterface[] $backends */ - private $backends = []; + private array $backends = []; /** - * @var \OC\User\User[] $cachedUsers + * @var array<string,\OC\User\User> $cachedUsers */ - private $cachedUsers = []; - - /** @var IConfig */ - private $config; + private array $cachedUsers = []; - /** @var ICache */ - private $cache; - - /** @var IEventDispatcher */ - private $eventDispatcher; + private ICache $cache; private DisplayNameCache $displayNameCache; - public function __construct(IConfig $config, - ICacheFactory $cacheFactory, - IEventDispatcher $eventDispatcher) { - $this->config = $config; + public function __construct( + private IConfig $config, + ICacheFactory $cacheFactory, + private IEventDispatcher $eventDispatcher, + ) { $this->cache = new WithLocalCache($cacheFactory->createDistributed('user_backend_map')); - $cachedUsers = &$this->cachedUsers; - $this->listen('\OC\User', 'postDelete', function ($user) use (&$cachedUsers) { - /** @var \OC\User\User $user */ - unset($cachedUsers[$user->getUID()]); + $this->listen('\OC\User', 'postDelete', function (IUser $user): void { + unset($this->cachedUsers[$user->getUID()]); }); - $this->eventDispatcher = $eventDispatcher; $this->displayNameCache = new DisplayNameCache($cacheFactory, $this); } @@ -234,7 +201,7 @@ class Manager extends PublicEmitter implements IUserManager { $result = $this->checkPasswordNoLogging($loginName, $password); if ($result === false) { - \OC::$server->getLogger()->warning('Login failed: \''. $loginName .'\' (Remote IP: \''. \OC::$server->getRequest()->getRemoteAddress(). '\')', ['app' => 'core']); + \OCP\Server::get(LoggerInterface::class)->warning('Login failed: \''. $loginName .'\' (Remote IP: \''. \OC::$server->getRequest()->getRemoteAddress(). '\')', ['app' => 'core']); } return $result; @@ -338,6 +305,44 @@ class Manager extends PublicEmitter implements IUserManager { } /** + * @return IUser[] + */ + public function getDisabledUsers(?int $limit = null, int $offset = 0, string $search = ''): array { + $users = $this->config->getUsersForUserValue('core', 'enabled', 'false'); + $users = array_combine( + $users, + array_map( + fn (string $uid): IUser => new LazyUser($uid, $this), + $users + ) + ); + if ($search !== '') { + $users = array_filter( + $users, + fn (IUser $user): bool => + mb_stripos($user->getUID(), $search) !== false || + mb_stripos($user->getDisplayName(), $search) !== false || + mb_stripos($user->getEMailAddress() ?? '', $search) !== false, + ); + } + + $tempLimit = ($limit === null ? null : $limit + $offset); + foreach ($this->backends as $backend) { + if (($tempLimit !== null) && (count($users) >= $tempLimit)) { + break; + } + if ($backend instanceof IProvideEnabledStateBackend) { + $backendUsers = $backend->getDisabledUserList(($tempLimit === null ? null : $tempLimit - count($users)), 0, $search); + foreach ($backendUsers as $uid) { + $users[$uid] = new LazyUser($uid, $this, null, $backend); + } + } + } + + return array_slice($users, $offset, $limit); + } + + /** * Search known users (from phonebook sync) by displayName * * @param string $searcher @@ -415,7 +420,7 @@ class Manager extends PublicEmitter implements IUserManager { * @throws \InvalidArgumentException */ public function createUserFromBackend($uid, $password, UserInterface $backend) { - $l = \OC::$server->getL10N('lib'); + $l = \OCP\Util::getL10N('lib'); $this->validateUserId($uid, true); @@ -426,7 +431,7 @@ class Manager extends PublicEmitter implements IUserManager { // Check if user already exists if ($this->userExists($uid)) { - throw new \InvalidArgumentException($l->t('The username is already being used')); + throw new \InvalidArgumentException($l->t('The Login is already being used')); } /** @deprecated 21.0.0 use BeforeUserCreatedEvent event with the IEventDispatcher instead */ @@ -434,7 +439,7 @@ class Manager extends PublicEmitter implements IUserManager { $this->eventDispatcher->dispatchTyped(new BeforeUserCreatedEvent($uid, $password)); $state = $backend->createUser($uid, $password); if ($state === false) { - throw new \InvalidArgumentException($l->t('Could not create user')); + throw new \InvalidArgumentException($l->t('Could not create account')); } $user = $this->getUserObject($uid, $backend); if ($user instanceof IUser) { @@ -704,27 +709,27 @@ class Manager extends PublicEmitter implements IUserManager { // Check the name for bad characters // Allowed are: "a-z", "A-Z", "0-9", spaces and "_.@-'" if (preg_match('/[^a-zA-Z0-9 _.@\-\']/', $uid)) { - throw new \InvalidArgumentException($l->t('Only the following characters are allowed in a username:' + throw new \InvalidArgumentException($l->t('Only the following characters are allowed in an Login:' . ' "a-z", "A-Z", "0-9", spaces and "_.@-\'"')); } // No empty username if (trim($uid) === '') { - throw new \InvalidArgumentException($l->t('A valid username must be provided')); + throw new \InvalidArgumentException($l->t('A valid Login must be provided')); } // No whitespace at the beginning or at the end if (trim($uid) !== $uid) { - throw new \InvalidArgumentException($l->t('Username contains whitespace at the beginning or at the end')); + throw new \InvalidArgumentException($l->t('Login contains whitespace at the beginning or at the end')); } // Username only consists of 1 or 2 dots (directory traversal) if ($uid === '.' || $uid === '..') { - throw new \InvalidArgumentException($l->t('Username must not consist of dots only')); + throw new \InvalidArgumentException($l->t('Login must not consist of dots only')); } if (!$this->verifyUid($uid, $checkDataDirectory)) { - throw new \InvalidArgumentException($l->t('Username is invalid because files already exist for this user')); + throw new \InvalidArgumentException($l->t('Login is invalid because files already exist for this user')); } } @@ -738,6 +743,8 @@ class Manager extends PublicEmitter implements IUserManager { '.ocdata', 'owncloud.log', 'nextcloud.log', + 'updater.log', + 'audit.log', $appdata], true)) { return false; } diff --git a/lib/private/User/NoUserException.php b/lib/private/User/NoUserException.php index d9149318467..c0a5c51b08d 100644 --- a/lib/private/User/NoUserException.php +++ b/lib/private/User/NoUserException.php @@ -1,24 +1,9 @@ <?php + /** - * @copyright Copyright (c) 2016, ownCloud, Inc. - * - * @author Christoph Wurst <christoph@winzerhof-wurst.at> - * @author Jörn Friedrich Dreyer <jfd@butonic.de> - * - * @license AGPL-3.0 - * - * This code is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License, version 3, - * as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License, version 3, - * along with this program. If not, see <http://www.gnu.org/licenses/> - * + * SPDX-FileCopyrightText: 2016-2024 Nextcloud GmbH and Nextcloud contributors + * SPDX-FileCopyrightText: 2016 ownCloud, Inc. + * SPDX-License-Identifier: AGPL-3.0-only */ namespace OC\User; diff --git a/lib/private/User/OutOfOfficeData.php b/lib/private/User/OutOfOfficeData.php new file mode 100644 index 00000000000..65d7c9c24cb --- /dev/null +++ b/lib/private/User/OutOfOfficeData.php @@ -0,0 +1,58 @@ +<?php + +declare(strict_types=1); + +/** + * SPDX-FileCopyrightText: 2023 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later + */ + +namespace OC\User; + +use OCP\IUser; +use OCP\User\IOutOfOfficeData; + +class OutOfOfficeData implements IOutOfOfficeData { + public function __construct(private string $id, + private IUser $user, + private int $startDate, + private int $endDate, + private string $shortMessage, + private string $message) { + } + + public function getId(): string { + return $this->id; + } + + public function getUser(): IUser { + return $this->user; + } + + public function getStartDate(): int { + return $this->startDate; + } + + public function getEndDate(): int { + return $this->endDate; + } + + public function getShortMessage(): string { + return $this->shortMessage; + } + + public function getMessage(): string { + return $this->message; + } + + public function jsonSerialize(): array { + return [ + 'id' => $this->getId(), + 'userId' => $this->getUser()->getUID(), + 'startDate' => $this->getStartDate(), + 'endDate' => $this->getEndDate(), + 'shortMessage' => $this->getShortMessage(), + 'message' => $this->getMessage(), + ]; + } +} diff --git a/lib/private/User/Session.php b/lib/private/User/Session.php index 82887f8d029..9d77a90c541 100644 --- a/lib/private/User/Session.php +++ b/lib/private/User/Session.php @@ -1,56 +1,26 @@ <?php /** - * @copyright Copyright (c) 2017, Sandro Lutz <sandro.lutz@temparus.ch> - * @copyright Copyright (c) 2016, ownCloud, Inc. - * - * @author Arthur Schiwon <blizzz@arthur-schiwon.de> - * @author Bernhard Posselt <dev@bernhard-posselt.com> - * @author Bjoern Schiessle <bjoern@schiessle.org> - * @author Christoph Wurst <christoph@winzerhof-wurst.at> - * @author Felix Rupp <github@felixrupp.com> - * @author Greta Doci <gretadoci@gmail.com> - * @author Joas Schilling <coding@schilljs.com> - * @author Jörn Friedrich Dreyer <jfd@butonic.de> - * @author Lionel Elie Mamane <lionel@mamane.lu> - * @author Lukas Reschke <lukas@statuscode.ch> - * @author Morris Jobke <hey@morrisjobke.de> - * @author Robin Appelman <robin@icewind.nl> - * @author Robin McCorkell <robin@mccorkell.me.uk> - * @author Roeland Jago Douma <roeland@famdouma.nl> - * @author Sandro Lutz <sandro.lutz@temparus.ch> - * @author Thomas Müller <thomas.mueller@tmit.eu> - * @author Vincent Petry <vincent@nextcloud.com> - * - * @license AGPL-3.0 - * - * This code is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License, version 3, - * as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License, version 3, - * along with this program. If not, see <http://www.gnu.org/licenses/> - * + * SPDX-FileCopyrightText: 2017 Nextcloud GmbH and Nextcloud contributors + * SPDX-FileCopyrightText: 2016 ownCloud, Inc. + * SPDX-License-Identifier: AGPL-3.0-only */ namespace OC\User; use OC; -use OC\Authentication\Exceptions\ExpiredTokenException; -use OC\Authentication\Exceptions\InvalidTokenException; use OC\Authentication\Exceptions\PasswordlessTokenException; use OC\Authentication\Exceptions\PasswordLoginForbiddenException; use OC\Authentication\Token\IProvider; use OC\Authentication\Token\IToken; +use OC\Authentication\Token\PublicKeyToken; +use OC\Authentication\TwoFactorAuth\Manager as TwoFactorAuthManager; use OC\Hooks\Emitter; use OC\Hooks\PublicEmitter; use OC_User; use OC_Util; use OCA\DAV\Connector\Sabre\Auth; use OCP\AppFramework\Utility\ITimeFactory; +use OCP\Authentication\Exceptions\ExpiredTokenException; +use OCP\Authentication\Exceptions\InvalidTokenException; use OCP\EventDispatcher\GenericEvent; use OCP\EventDispatcher\IEventDispatcher; use OCP\Files\NotPermittedException; @@ -120,14 +90,14 @@ class Session implements IUserSession, Emitter { private $dispatcher; public function __construct(Manager $manager, - ISession $session, - ITimeFactory $timeFactory, - ?IProvider $tokenProvider, - IConfig $config, - ISecureRandom $random, - ILockdownManager $lockdownManager, - LoggerInterface $logger, - IEventDispatcher $dispatcher + ISession $session, + ITimeFactory $timeFactory, + ?IProvider $tokenProvider, + IConfig $config, + ISecureRandom $random, + ILockdownManager $lockdownManager, + LoggerInterface $logger, + IEventDispatcher $dispatcher ) { $this->manager = $manager; $this->session = $session; @@ -161,7 +131,7 @@ class Session implements IUserSession, Emitter { * @param string $method optional * @param callable $callback optional */ - public function removeListener($scope = null, $method = null, callable $callback = null) { + public function removeListener($scope = null, $method = null, ?callable $callback = null) { $this->manager->removeListener($scope, $method, $callback); } @@ -211,6 +181,15 @@ class Session implements IUserSession, Emitter { } /** + * Temporarily set the currently active user without persisting in the session + * + * @param IUser|null $user + */ + public function setVolatileActiveUser(?IUser $user): void { + $this->activeUser = $user; + } + + /** * get the current active user * * @return IUser|null Current user, otherwise null @@ -367,7 +346,7 @@ class Session implements IUserSession, Emitter { if (!$user->isEnabled()) { // disabled users can not log in // injecting l10n does not work - there is a circular dependency between session and \OCP\L10N\IFactory - $message = \OC::$server->getL10N('lib')->t('User disabled'); + $message = \OCP\Util::getL10N('lib')->t('Account disabled'); throw new LoginException($message); } @@ -406,7 +385,7 @@ class Session implements IUserSession, Emitter { return true; } - $message = \OC::$server->getL10N('lib')->t('Login canceled by app'); + $message = \OCP\Util::getL10N('lib')->t('Login canceled by app'); throw new LoginException($message); } @@ -425,9 +404,9 @@ class Session implements IUserSession, Emitter { * @return boolean */ public function logClientIn($user, - $password, - IRequest $request, - IThrottler $throttler) { + $password, + IRequest $request, + IThrottler $throttler) { $remoteAddress = $request->getRemoteAddress(); $currentDelay = $throttler->sleepDelayOrThrowOnMax($remoteAddress, 'login'); @@ -456,8 +435,18 @@ class Session implements IUserSession, Emitter { $this->handleLoginFailed($throttler, $currentDelay, $remoteAddress, $user, $password); return false; } - $users = $this->manager->getByEmail($user); - if (!(\count($users) === 1 && $this->login($users[0]->getUID(), $password))) { + + if ($isTokenPassword) { + $dbToken = $this->tokenProvider->getToken($password); + $userFromToken = $this->manager->get($dbToken->getUID()); + $isValidEmailLogin = $userFromToken->getEMailAddress() === $user + && $this->validateTokenLoginName($userFromToken->getEMailAddress(), $dbToken); + } else { + $users = $this->manager->getByEmail($user); + $isValidEmailLogin = (\count($users) === 1 && $this->login($users[0]->getUID(), $password)); + } + + if (!$isValidEmailLogin) { $this->handleLoginFailed($throttler, $currentDelay, $remoteAddress, $user, $password); return false; } @@ -514,7 +503,7 @@ class Session implements IUserSession, Emitter { $user = $users[0]; } // DI not possible due to cyclic dependencies :'-/ - return OC::$server->getTwoFactorAuthManager()->isTwoFactorAuthenticated($user); + return OC::$server->get(TwoFactorAuthManager::class)->isTwoFactorAuthenticated($user); } /** @@ -576,7 +565,7 @@ class Session implements IUserSession, Emitter { * @return boolean if the login was successful */ public function tryBasicAuthLogin(IRequest $request, - IThrottler $throttler) { + IThrottler $throttler) { if (!empty($request->server['PHP_AUTH_USER']) && !empty($request->server['PHP_AUTH_PW'])) { try { if ($this->logClientIn($request->server['PHP_AUTH_USER'], $request->server['PHP_AUTH_PW'], $request, $throttler)) { @@ -747,8 +736,6 @@ class Session implements IUserSession, Emitter { return false; } - $dbToken->setLastCheck($now); - $this->tokenProvider->updateToken($dbToken); return true; } @@ -766,6 +753,9 @@ class Session implements IUserSession, Emitter { } $dbToken->setLastCheck($now); + if ($dbToken instanceof PublicKeyToken) { + $dbToken->setLastActivity($now); + } $this->tokenProvider->updateToken($dbToken); return true; } @@ -783,23 +773,23 @@ class Session implements IUserSession, Emitter { try { $dbToken = $this->tokenProvider->getToken($token); } catch (InvalidTokenException $ex) { + $this->logger->debug('Session token is invalid because it does not exist', [ + 'app' => 'core', + 'user' => $user, + 'exception' => $ex, + ]); return false; } - // Check if login names match - if (!is_null($user) && $dbToken->getLoginName() !== $user) { - // TODO: this makes it impossible to use different login names on browser and client - // e.g. login by e-mail 'user@example.com' on browser for generating the token will not - // allow to use the client token with the login name 'user'. - $this->logger->error('App token login name does not match', [ - 'tokenLoginName' => $dbToken->getLoginName(), - 'sessionLoginName' => $user, - ]); - + if (!is_null($user) && !$this->validateTokenLoginName($user, $dbToken)) { return false; } if (!$this->checkTokenCredentials($dbToken, $token)) { + $this->logger->warning('Session token credentials are invalid', [ + 'app' => 'core', + 'user' => $user, + ]); return false; } @@ -812,6 +802,27 @@ class Session implements IUserSession, Emitter { } /** + * Check if login names match + */ + private function validateTokenLoginName(?string $loginName, IToken $token): bool { + if ($token->getLoginName() !== $loginName) { + // TODO: this makes it impossible to use different login names on browser and client + // e.g. login by e-mail 'user@example.com' on browser for generating the token will not + // allow to use the client token with the login name 'user'. + $this->logger->error('App token login name does not match', [ + 'tokenLoginName' => $token->getLoginName(), + 'sessionLoginName' => $loginName, + 'app' => 'core', + 'user' => $token->getUID(), + ]); + + return false; + } + + return true; + } + + /** * Tries to login the user with auth token header * * @param IRequest $request @@ -822,13 +833,16 @@ class Session implements IUserSession, Emitter { $authHeader = $request->getHeader('Authorization'); if (str_starts_with($authHeader, 'Bearer ')) { $token = substr($authHeader, 7); - } else { - // No auth header, let's try session id + } elseif ($request->getCookie($this->config->getSystemValueString('instanceid')) !== null) { + // No auth header, let's try session id, but only if this is an existing + // session and the request has a session cookie try { $token = $this->session->getId(); } catch (SessionNotAvailableException $ex) { return false; } + } else { + return false; } if (!$this->loginWithToken($token)) { @@ -875,9 +889,9 @@ class Session implements IUserSession, Emitter { $tokens = $this->config->getUserKeys($uid, 'login_token'); // test cookies token against stored tokens if (!in_array($currentToken, $tokens, true)) { - $this->logger->info('Tried to log in {uid} but could not verify token', [ + $this->logger->info('Tried to log in but could not verify token', [ 'app' => 'core', - 'uid' => $uid, + 'user' => $uid, ]); return false; } @@ -885,18 +899,31 @@ class Session implements IUserSession, Emitter { $this->config->deleteUserValue($uid, 'login_token', $currentToken); $newToken = $this->random->generate(32); $this->config->setUserValue($uid, 'login_token', $newToken, (string)$this->timeFactory->getTime()); + $this->logger->debug('Remember-me token replaced', [ + 'app' => 'core', + 'user' => $uid, + ]); try { $sessionId = $this->session->getId(); $token = $this->tokenProvider->renewSessionToken($oldSessionId, $sessionId); + $this->logger->debug('Session token replaced', [ + 'app' => 'core', + 'user' => $uid, + ]); } catch (SessionNotAvailableException $ex) { - $this->logger->warning('Could not renew session token for {uid} because the session is unavailable', [ + $this->logger->critical('Could not renew session token for {uid} because the session is unavailable', [ 'app' => 'core', 'uid' => $uid, + 'user' => $uid, ]); return false; } catch (InvalidTokenException $ex) { - $this->logger->warning('Renewing session token failed', ['app' => 'core']); + $this->logger->error('Renewing session token failed: ' . $ex->getMessage(), [ + 'app' => 'core', + 'user' => $uid, + 'exception' => $ex, + ]); return false; } @@ -935,10 +962,17 @@ class Session implements IUserSession, Emitter { $this->manager->emit('\OC\User', 'logout', [$user]); if ($user !== null) { try { - $this->tokenProvider->invalidateToken($this->session->getId()); + $token = $this->session->getId(); + $this->tokenProvider->invalidateToken($token); + $this->logger->debug('Session token invalidated before logout', [ + 'user' => $user->getUID(), + ]); } catch (SessionNotAvailableException $ex) { } } + $this->logger->debug('Logging out', [ + 'user' => $user === null ? null : $user->getUID(), + ]); $this->setUser(null); $this->setLoginName(null); $this->setToken(null); diff --git a/lib/private/User/User.php b/lib/private/User/User.php index d1185e17aef..06ec6db6066 100644 --- a/lib/private/User/User.php +++ b/lib/private/User/User.php @@ -1,36 +1,9 @@ <?php + /** - * @copyright Copyright (c) 2016, ownCloud, Inc. - * - * @author Arthur Schiwon <blizzz@arthur-schiwon.de> - * @author Bart Visscher <bartv@thisnet.nl> - * @author Björn Schießle <bjoern@schiessle.org> - * @author Christoph Wurst <christoph@winzerhof-wurst.at> - * @author Joas Schilling <coding@schilljs.com> - * @author John Molakvoæ <skjnldsv@protonmail.com> - * @author Jörn Friedrich Dreyer <jfd@butonic.de> - * @author Julius Härtl <jus@bitgrid.net> - * @author Leon Klingele <leon@struktur.de> - * @author Lukas Reschke <lukas@statuscode.ch> - * @author Morris Jobke <hey@morrisjobke.de> - * @author Robin Appelman <robin@icewind.nl> - * @author Roeland Jago Douma <roeland@famdouma.nl> - * @author Thomas Müller <thomas.mueller@tmit.eu> - * - * @license AGPL-3.0 - * - * This code is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License, version 3, - * as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License, version 3, - * along with this program. If not, see <http://www.gnu.org/licenses/> - * + * SPDX-FileCopyrightText: 2016-2024 Nextcloud GmbH and Nextcloud contributors + * SPDX-FileCopyrightText: 2016 ownCloud, Inc. + * SPDX-License-Identifier: AGPL-3.0-only */ namespace OC\User; @@ -40,6 +13,7 @@ use OC\Avatar\AvatarManager; use OC\Hooks\Emitter; use OC_Helper; use OCP\Accounts\IAccountManager; +use OCP\Comments\ICommentsManager; use OCP\EventDispatcher\IEventDispatcher; use OCP\Group\Events\BeforeUserRemovedEvent; use OCP\Group\Events\UserRemovedEvent; @@ -49,17 +23,17 @@ use OCP\IImage; use OCP\IURLGenerator; use OCP\IUser; use OCP\IUserBackend; +use OCP\User\Backend\IGetHomeBackend; +use OCP\User\Backend\IProvideAvatarBackend; +use OCP\User\Backend\IProvideEnabledStateBackend; +use OCP\User\Backend\ISetDisplayNameBackend; +use OCP\User\Backend\ISetPasswordBackend; use OCP\User\Events\BeforePasswordUpdatedEvent; use OCP\User\Events\BeforeUserDeletedEvent; use OCP\User\Events\PasswordUpdatedEvent; use OCP\User\Events\UserChangedEvent; use OCP\User\Events\UserDeletedEvent; use OCP\User\GetQuotaEvent; -use OCP\User\Backend\ISetDisplayNameBackend; -use OCP\User\Backend\ISetPasswordBackend; -use OCP\User\Backend\IProvideAvatarBackend; -use OCP\User\Backend\IProvideEnabledStateBackend; -use OCP\User\Backend\IGetHomeBackend; use OCP\UserInterface; use function json_decode; use function json_encode; @@ -102,7 +76,7 @@ class User implements IUser { /** @var IURLGenerator */ private $urlGenerator; - public function __construct(string $uid, ?UserInterface $backend, IEventDispatcher $dispatcher, $emitter = null, IConfig $config = null, $urlGenerator = null) { + public function __construct(string $uid, ?UserInterface $backend, IEventDispatcher $dispatcher, $emitter = null, ?IConfig $config = null, $urlGenerator = null) { $this->uid = $uid; $this->backend = $backend; $this->emitter = $emitter; @@ -290,8 +264,8 @@ class User implements IUser { // Delete the user's keys in preferences \OC::$server->getConfig()->deleteAllUserValues($this->uid); - \OC::$server->getCommentsManager()->deleteReferencesOfActor('users', $this->uid); - \OC::$server->getCommentsManager()->deleteReadMarksFromUser($this); + \OC::$server->get(ICommentsManager::class)->deleteReferencesOfActor('users', $this->uid); + \OC::$server->get(ICommentsManager::class)->deleteReadMarksFromUser($this); /** @var AvatarManager $avatarManager */ $avatarManager = \OCP\Server::get(AvatarManager::class); @@ -576,7 +550,7 @@ class User implements IUser { public function getAvatarImage($size) { // delay the initialization if (is_null($this->avatarManager)) { - $this->avatarManager = \OC::$server->getAvatarManager(); + $this->avatarManager = \OC::$server->get(IAvatarManager::class); } $avatar = $this->avatarManager->getAvatar($this->uid); @@ -597,7 +571,7 @@ class User implements IUser { public function getCloudId() { $uid = $this->getUID(); $server = rtrim($this->urlGenerator->getAbsoluteURL('/'), '/'); - if (substr($server, -10) === '/index.php') { + if (str_ends_with($server, '/index.php')) { $server = substr($server, 0, -10); } $server = $this->removeProtocolFromUrl($server); diff --git a/lib/private/UserStatus/ISettableProvider.php b/lib/private/UserStatus/ISettableProvider.php index 88a107d1f86..1a2e7cfe7cf 100644 --- a/lib/private/UserStatus/ISettableProvider.php +++ b/lib/private/UserStatus/ISettableProvider.php @@ -3,25 +3,8 @@ declare(strict_types=1); /** - * @copyright Copyright (c) 2021 Carl Schwan <carl@carlschwan.eu> - * - * @author Carl Schwan <carl@carlschwan.eu> - * - * @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/>. - * + * SPDX-FileCopyrightText: 2021 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later */ namespace OC\UserStatus; @@ -39,8 +22,9 @@ interface ISettableProvider extends IProvider { * @param string $messageId The new message id. * @param string $status The new status. * @param bool $createBackup If true, this will store the old status so that it is possible to revert it later (e.g. after a call). + * @param string|null $customMessage */ - public function setUserStatus(string $userId, string $messageId, string $status, bool $createBackup): void; + public function setUserStatus(string $userId, string $messageId, string $status, bool $createBackup, ?string $customMessage = null): void; /** * Revert an automatically set user status. For example after leaving a call, diff --git a/lib/private/UserStatus/Manager.php b/lib/private/UserStatus/Manager.php index 89a1bb455c7..4658f61df82 100644 --- a/lib/private/UserStatus/Manager.php +++ b/lib/private/UserStatus/Manager.php @@ -3,25 +3,8 @@ declare(strict_types=1); /** - * @copyright Copyright (c) 2020, Georg Ehrke - * - * @author Georg Ehrke <oc.list@georgehrke.com> - * - * @license GNU AGPL version 3 or any later version - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as - * published by the Free Software Foundation, either version 3 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - * + * SPDX-FileCopyrightText: 2020 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later */ namespace OC\UserStatus; @@ -51,7 +34,7 @@ class Manager implements IManager { * @param LoggerInterface $logger */ public function __construct(IServerContainer $container, - LoggerInterface $logger) { + LoggerInterface $logger) { $this->container = $container; $this->logger = $logger; } @@ -104,13 +87,13 @@ class Manager implements IManager { $this->provider = $provider; } - public function setUserStatus(string $userId, string $messageId, string $status, bool $createBackup = false): void { + public function setUserStatus(string $userId, string $messageId, string $status, bool $createBackup = false, ?string $customMessage = null): void { $this->setupProvider(); if (!$this->provider || !($this->provider instanceof ISettableProvider)) { return; } - $this->provider->setUserStatus($userId, $messageId, $status, $createBackup); + $this->provider->setUserStatus($userId, $messageId, $status, $createBackup, $customMessage); } public function revertUserStatus(string $userId, string $messageId, string $status): void { diff --git a/lib/private/legacy/OC_API.php b/lib/private/legacy/OC_API.php index 275e02986c4..bb2376d4ea0 100644 --- a/lib/private/legacy/OC_API.php +++ b/lib/private/legacy/OC_API.php @@ -1,32 +1,9 @@ <?php + /** - * @copyright Copyright (c) 2016, ownCloud, Inc. - * - * @author Bart Visscher <bartv@thisnet.nl> - * @author Björn Schießle <bjoern@schiessle.org> - * @author Christoph Wurst <christoph@winzerhof-wurst.at> - * @author Jörn Friedrich Dreyer <jfd@butonic.de> - * @author Lukas Reschke <lukas@statuscode.ch> - * @author Michael Gapczynski <GapczynskiM@gmail.com> - * @author Morris Jobke <hey@morrisjobke.de> - * @author Roeland Jago Douma <roeland@famdouma.nl> - * @author Thomas Müller <thomas.mueller@tmit.eu> - * @author Tom Needham <tom@owncloud.com> - * - * @license AGPL-3.0 - * - * This code is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License, version 3, - * as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License, version 3, - * along with this program. If not, see <http://www.gnu.org/licenses/> - * + * SPDX-FileCopyrightText: 2016-2024 Nextcloud GmbH and Nextcloud contributors + * SPDX-FileCopyrightText: 2016 ownCloud, Inc. + * SPDX-License-Identifier: AGPL-3.0-only */ use OCP\API; use OCP\AppFramework\Http; @@ -130,7 +107,7 @@ class OC_API { protected static function isV2(\OCP\IRequest $request) { $script = $request->getScriptName(); - return substr($script, -11) === '/ocs/v2.php'; + return str_ends_with($script, '/ocs/v2.php'); } /** @@ -171,7 +148,7 @@ class OC_API { ], ]; if ($format == 'json') { - return OC_JSON::encode($response); + return json_encode($response, JSON_HEX_TAG); } $writer = new XMLWriter(); diff --git a/lib/private/legacy/OC_App.php b/lib/private/legacy/OC_App.php index ac449a62a4f..c9d1f86cb0e 100644 --- a/lib/private/legacy/OC_App.php +++ b/lib/private/legacy/OC_App.php @@ -1,69 +1,24 @@ <?php declare(strict_types=1); - /** - * @copyright Copyright (c) 2016, ownCloud, Inc. - * @copyright Copyright (c) 2016, Lukas Reschke <lukas@statuscode.ch> - * - * @author Arthur Schiwon <blizzz@arthur-schiwon.de> - * @author Bart Visscher <bartv@thisnet.nl> - * @author Bernhard Posselt <dev@bernhard-posselt.com> - * @author Borjan Tchakaloff <borjan@tchakaloff.fr> - * @author Brice Maron <brice@bmaron.net> - * @author Christopher Schäpers <kondou@ts.unde.re> - * @author Christoph Wurst <christoph@winzerhof-wurst.at> - * @author Daniel Rudolf <github.com@daniel-rudolf.de> - * @author Frank Karlitschek <frank@karlitschek.de> - * @author Georg Ehrke <oc.list@georgehrke.com> - * @author Jakob Sack <mail@jakobsack.de> - * @author Joas Schilling <coding@schilljs.com> - * @author Jörn Friedrich Dreyer <jfd@butonic.de> - * @author Julius Haertl <jus@bitgrid.net> - * @author Julius Härtl <jus@bitgrid.net> - * @author Kamil Domanski <kdomanski@kdemail.net> - * @author Lukas Reschke <lukas@statuscode.ch> - * @author Markus Goetz <markus@woboq.com> - * @author Morris Jobke <hey@morrisjobke.de> - * @author RealRancor <Fisch.666@gmx.de> - * @author Robin Appelman <robin@icewind.nl> - * @author Robin McCorkell <robin@mccorkell.me.uk> - * @author Roeland Jago Douma <roeland@famdouma.nl> - * @author Sam Tuke <mail@samtuke.com> - * @author Sebastian Wessalowski <sebastian@wessalowski.org> - * @author Thomas Müller <thomas.mueller@tmit.eu> - * @author Thomas Tanghus <thomas@tanghus.net> - * @author Vincent Petry <vincent@nextcloud.com> - * - * @license AGPL-3.0 - * - * This code is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License, version 3, - * as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License, version 3, - * along with this program. If not, see <http://www.gnu.org/licenses/> - * + * SPDX-FileCopyrightText: 2016 Nextcloud GmbH and Nextcloud contributors + * SPDX-FileCopyrightText: 2016 ownCloud, Inc. + * SPDX-License-Identifier: AGPL-3.0-only */ - -use OCP\App\Events\AppUpdateEvent; -use OCP\App\IAppManager; -use OCP\App\ManagerEvent; -use OCP\Authentication\IAlternativeLogin; -use OCP\EventDispatcher\IEventDispatcher; -use OCP\ILogger; -use OC\AppFramework\Bootstrap\Coordinator; use OC\App\DependencyAnalyzer; use OC\App\Platform; +use OC\AppFramework\Bootstrap\Coordinator; use OC\DB\MigrationService; use OC\Installer; use OC\Repair; use OC\Repair\Events\RepairErrorEvent; +use OCP\App\Events\AppUpdateEvent; +use OCP\App\IAppManager; +use OCP\App\ManagerEvent; +use OCP\Authentication\IAlternativeLogin; +use OCP\EventDispatcher\IEventDispatcher; +use OCP\IAppConfig; use Psr\Container\ContainerExceptionInterface; use Psr\Log\LoggerInterface; @@ -117,6 +72,8 @@ class OC_App { * exists. * * if $types is set to non-empty array, only apps of those types will be loaded + * + * @deprecated 29.0.0 use IAppManager::loadApps instead */ public static function loadApps(array $types = []): bool { if (!\OC::$server->getSystemConfig()->getValue('installed', false)) { @@ -252,7 +209,7 @@ class OC_App { * This function set an app as enabled in appconfig. */ public function enable(string $appId, - array $groups = []) { + array $groups = []) { // Check if app is already downloaded /** @var Installer $installer */ $installer = \OCP\Server::get(Installer::class); @@ -282,17 +239,15 @@ class OC_App { /** * Get the path where to install apps - * - * @return string|false */ - public static function getInstallPath() { + public static function getInstallPath(): string|null { foreach (OC::$APPSROOTS as $dir) { if (isset($dir['writable']) && $dir['writable'] === true) { return $dir['path']; } } - \OCP\Util::writeLog('core', 'No application directories are marked as writable.', ILogger::ERROR); + \OCP\Server::get(LoggerInterface::class)->error('No application directories are marked as writable.', ['app' => 'core']); return null; } @@ -354,7 +309,7 @@ class OC_App { * @param string $appId * @param bool $refreshAppPath should be set to true only during install/upgrade * @return string|false - * @deprecated 11.0.0 use \OC::$server->getAppManager()->getAppPath() + * @deprecated 11.0.0 use \OCP\Server::get(IAppManager)->getAppPath() */ public static function getAppPath(string $appId, bool $refreshAppPath = false) { if ($appId === null || trim($appId) === '') { @@ -391,7 +346,7 @@ class OC_App { public static function getAppVersionByPath(string $path): string { $infoFile = $path . '/appinfo/info.xml'; $appData = \OC::$server->getAppManager()->getAppInfo($infoFile, true); - return isset($appData['version']) ? $appData['version'] : ''; + return $appData['version'] ?? ''; } /** @@ -517,7 +472,7 @@ class OC_App { foreach (OC::$APPSROOTS as $apps_dir) { if (!is_readable($apps_dir['path'])) { - \OCP\Util::writeLog('core', 'unable to read app folder : ' . $apps_dir['path'], ILogger::WARN); + \OCP\Server::get(LoggerInterface::class)->warning('unable to read app folder : ' . $apps_dir['path'], ['app' => 'core']); continue; } $dh = opendir($apps_dir['path']); @@ -565,15 +520,15 @@ class OC_App { $supportedApps = $this->getSupportedApps(); foreach ($installedApps as $app) { - if (array_search($app, $blacklist) === false) { + if (!in_array($app, $blacklist)) { $info = $appManager->getAppInfo($app, false, $langCode); if (!is_array($info)) { - \OCP\Util::writeLog('core', 'Could not read app info file for app "' . $app . '"', ILogger::ERROR); + \OCP\Server::get(LoggerInterface::class)->error('Could not read app info file for app "' . $app . '"', ['app' => 'core']); continue; } if (!isset($info['name'])) { - \OCP\Util::writeLog('core', 'App id "' . $app . '" has no name in appinfo', ILogger::ERROR); + \OCP\Server::get(LoggerInterface::class)->error('App id "' . $app . '" has no name in appinfo', ['app' => 'core']); continue; } @@ -731,8 +686,9 @@ class OC_App { static $versions; if (!$versions) { - $appConfig = \OC::$server->getAppConfig(); - $versions = $appConfig->getValues(false, 'installed_version'); + /** @var IAppConfig $appConfig */ + $appConfig = \OCP\Server::get(IAppConfig::class); + $versions = $appConfig->searchValues('installed_version'); } return $versions; } @@ -824,7 +780,7 @@ class OC_App { $dispatcher = \OC::$server->get(IEventDispatcher::class); // load the steps - $r = new Repair([], $dispatcher, \OC::$server->get(LoggerInterface::class)); + $r = \OCP\Server::get(Repair::class); foreach ($steps as $step) { try { $r->addStep($step); @@ -870,11 +826,11 @@ class OC_App { } return new \OC\Files\View('/' . OC_User::getUser() . '/' . $appId); } else { - \OCP\Util::writeLog('core', 'Can\'t get app storage, app ' . $appId . ', user not logged in', ILogger::ERROR); + \OCP\Server::get(LoggerInterface::class)->error('Can\'t get app storage, app ' . $appId . ', user not logged in', ['app' => 'core']); return false; } } else { - \OCP\Util::writeLog('core', 'Can\'t get app storage, app ' . $appId . ' not enabled', ILogger::ERROR); + \OCP\Server::get(LoggerInterface::class)->error('Can\'t get app storage, app ' . $appId . ' not enabled', ['app' => 'core']); return false; } } diff --git a/lib/private/legacy/OC_Defaults.php b/lib/private/legacy/OC_Defaults.php index ce89a642741..54640a892c9 100644 --- a/lib/private/legacy/OC_Defaults.php +++ b/lib/private/legacy/OC_Defaults.php @@ -1,40 +1,9 @@ <?php + /** - * @copyright Copyright (c) 2016, ownCloud, Inc. - * - * @author Björn Schießle <bjoern@schiessle.org> - * @author Christoph Wurst <christoph@winzerhof-wurst.at> - * @author Daniel Kesselberg <mail@danielkesselberg.de> - * @author Jan-Christoph Borchardt <hey@jancborchardt.net> - * @author Jörn Friedrich Dreyer <jfd@butonic.de> - * @author Julius Haertl <jus@bitgrid.net> - * @author Julius Härtl <jus@bitgrid.net> - * @author Lukas Reschke <lukas@statuscode.ch> - * @author Markus Staab <markus.staab@redaxo.de> - * @author Michael Weimann <mail@michael-weimann.eu> - * @author Morris Jobke <hey@morrisjobke.de> - * @author Pascal de Bruijn <pmjdebruijn@pcode.nl> - * @author Robin Appelman <robin@icewind.nl> - * @author Robin McCorkell <robin@mccorkell.me.uk> - * @author Roeland Jago Douma <roeland@famdouma.nl> - * @author scolebrook <scolebrook@mac.com> - * @author Thomas Müller <thomas.mueller@tmit.eu> - * @author Volkan Gezer <volkangezer@gmail.com> - * - * @license AGPL-3.0 - * - * This code is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License, version 3, - * as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License, version 3, - * along with this program. If not, see <http://www.gnu.org/licenses/> - * + * SPDX-FileCopyrightText: 2016-2024 Nextcloud GmbH and Nextcloud contributors + * SPDX-FileCopyrightText: 2016 ownCloud, Inc. + * SPDX-License-Identifier: AGPL-3.0-only */ class OC_Defaults { @@ -52,6 +21,7 @@ class OC_Defaults { private $defaultDocBaseUrl; private $defaultDocVersion; private $defaultSlogan; + private $defaultColorBackground; private $defaultColorPrimary; private $defaultTextColorPrimary; private $defaultProductName; @@ -70,7 +40,8 @@ class OC_Defaults { $this->defaultFDroidClientUrl = $config->getSystemValue('customclient_fdroid', 'https://f-droid.org/packages/com.nextcloud.client/'); $this->defaultDocBaseUrl = 'https://docs.nextcloud.com'; $this->defaultDocVersion = \OC_Util::getVersion()[0]; // used to generate doc links - $this->defaultColorPrimary = '#0082c9'; + $this->defaultColorBackground = '#00679e'; + $this->defaultColorPrimary = '#00679e'; $this->defaultTextColorPrimary = '#ffffff'; $this->defaultProductName = 'Nextcloud'; @@ -300,6 +271,17 @@ class OC_Defaults { } /** + * Returns primary color + * @return string + */ + public function getColorBackground() { + if ($this->themeExist('getColorBackground')) { + return $this->theme->getColorBackground(); + } + return $this->defaultColorBackground; + } + + /** * @return array scss variables to overwrite */ public function getScssVariables() { diff --git a/lib/private/legacy/OC_FileChunking.php b/lib/private/legacy/OC_FileChunking.php index 58bdd0af3d0..7600d810657 100644 --- a/lib/private/legacy/OC_FileChunking.php +++ b/lib/private/legacy/OC_FileChunking.php @@ -1,32 +1,9 @@ <?php + /** - * @copyright Copyright (c) 2016, ownCloud, Inc. - * - * @author Bart Visscher <bartv@thisnet.nl> - * @author Christoph Wurst <christoph@winzerhof-wurst.at> - * @author Felix Moeller <mail@felixmoeller.de> - * @author Jörn Friedrich Dreyer <jfd@butonic.de> - * @author Morris Jobke <hey@morrisjobke.de> - * @author Robin Appelman <robin@icewind.nl> - * @author Roeland Jago Douma <roeland@famdouma.nl> - * @author Thomas Müller <thomas.mueller@tmit.eu> - * @author Thomas Tanghus <thomas@tanghus.net> - * @author Vincent Petry <vincent@nextcloud.com> - * - * @license AGPL-3.0 - * - * This code is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License, version 3, - * as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License, version 3, - * along with this program. If not, see <http://www.gnu.org/licenses/> - * + * SPDX-FileCopyrightText: 2016-2024 Nextcloud GmbH and Nextcloud contributors + * SPDX-FileCopyrightText: 2016 ownCloud, Inc. + * SPDX-License-Identifier: AGPL-3.0-only */ class OC_FileChunking { protected $info; diff --git a/lib/private/legacy/OC_Files.php b/lib/private/legacy/OC_Files.php index 7bc1fab94b6..76d61a98558 100644 --- a/lib/private/legacy/OC_Files.php +++ b/lib/private/legacy/OC_Files.php @@ -1,52 +1,18 @@ <?php + /** - * @copyright Copyright (c) 2016, ownCloud, Inc. - * - * @author Arthur Schiwon <blizzz@arthur-schiwon.de> - * @author Björn Schießle <bjoern@schiessle.org> - * @author Christoph Wurst <christoph@winzerhof-wurst.at> - * @author Daniel Calviño Sánchez <danxuliu@gmail.com> - * @author Frank Karlitschek <frank@karlitschek.de> - * @author Jakob Sack <mail@jakobsack.de> - * @author Joas Schilling <coding@schilljs.com> - * @author Jörn Friedrich Dreyer <jfd@butonic.de> - * @author Julius Härtl <jus@bitgrid.net> - * @author Ko- <k.stoffelen@cs.ru.nl> - * @author Michael Gapczynski <GapczynskiM@gmail.com> - * @author Morris Jobke <hey@morrisjobke.de> - * @author Nicolai Ehemann <en@enlightened.de> - * @author Piotr Filiciak <piotr@filiciak.pl> - * @author Robin Appelman <robin@icewind.nl> - * @author Robin McCorkell <robin@mccorkell.me.uk> - * @author Roeland Jago Douma <roeland@famdouma.nl> - * @author Thibaut GRIDEL <tgridel@free.fr> - * @author Thomas Müller <thomas.mueller@tmit.eu> - * @author Valdnet <47037905+Valdnet@users.noreply.github.com> - * @author Victor Dubiniuk <dubiniuk@owncloud.com> - * @author Vincent Petry <vincent@nextcloud.com> - * - * @license AGPL-3.0 - * - * This code is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License, version 3, - * as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License, version 3, - * along with this program. If not, see <http://www.gnu.org/licenses/> - * + * SPDX-FileCopyrightText: 2016-2024 Nextcloud GmbH and Nextcloud contributors + * SPDX-FileCopyrightText: 2016 ownCloud, Inc. + * SPDX-License-Identifier: AGPL-3.0-only */ use bantu\IniGetWrapper\IniGetWrapper; use OC\Files\View; use OC\Streamer; -use OCP\Lock\ILockingProvider; -use OCP\Files\Events\BeforeZipCreatedEvent; -use OCP\Files\Events\BeforeDirectFileDownloadEvent; use OCP\EventDispatcher\IEventDispatcher; +use OCP\Files\Events\BeforeDirectFileDownloadEvent; +use OCP\Files\Events\BeforeZipCreatedEvent; +use OCP\Files\IRootFolder; +use OCP\Lock\ILockingProvider; /** * Class for file server access @@ -76,7 +42,6 @@ class OC_Files { private static function sendHeaders($filename, $name, array $rangeArray): void { OC_Response::setContentDispositionHeader($name, 'attachment'); header('Content-Transfer-Encoding: binary', true); - header('Pragma: public');// enable caching in IE header('Expires: 0'); header("Cache-Control: must-revalidate, post-check=0, pre-check=0"); $fileSize = \OC\Files\Filesystem::filesize($filename); @@ -87,7 +52,7 @@ class OC_Files { header('Accept-Ranges: bytes', true); if (count($rangeArray) > 1) { $type = 'multipart/byteranges; boundary='.self::getBoundary(); - // no Content-Length header here + // no Content-Length header here } else { header(sprintf('Content-Range: bytes %d-%d/%d', $rangeArray[0]['from'], $rangeArray[0]['to'], $fileSize), true); OC_Response::setContentLengthHeader($rangeArray[0]['to'] - $rangeArray[0]['from'] + 1); @@ -104,7 +69,7 @@ class OC_Files { * return the content of a file or return a zip file containing multiple files * * @param string $dir - * @param string $files ; separated list of files to download + * @param string|array $files ; separated list of files to download * @param array $params ; 'head' boolean to only send header of the request ; 'range' http range header */ public static function get($dir, $files, $params = null) { @@ -188,7 +153,7 @@ class OC_Files { foreach ($files as $file) { $file = $dir . '/' . $file; if (\OC\Files\Filesystem::is_file($file)) { - $userFolder = \OC::$server->getRootFolder()->get(\OC\Files\Filesystem::getRoot()); + $userFolder = \OC::$server->get(IRootFolder::class)->get(\OC\Files\Filesystem::getRoot()); $file = $userFolder->get($file); if ($file instanceof \OC\Files\Node\File) { try { @@ -230,6 +195,10 @@ class OC_Files { OC::$server->getLogger()->logException($ex); $l = \OC::$server->getL10N('lib'); \OC_Template::printErrorPage($l->t('Cannot download file'), $ex->getMessage(), 200); + } catch (\OCP\Files\ConnectionLostException $ex) { + self::unlockAllTheFiles($dir, $files, $getType, $view, $filename); + OC::$server->getLogger()->logException($ex, ['level' => \OCP\ILogger::DEBUG]); + \OC_Template::printErrorPage('Connection lost', $ex->getMessage(), 200); } catch (\Exception $ex) { self::unlockAllTheFiles($dir, $files, $getType, $view, $filename); OC::$server->getLogger()->logException($ex); @@ -304,7 +273,7 @@ class OC_Files { $file = null; try { - $userFolder = \OC::$server->getRootFolder()->get(\OC\Files\Filesystem::getRoot()); + $userFolder = \OC::$server->get(IRootFolder::class)->get(\OC\Files\Filesystem::getRoot()); $file = $userFolder->get($filename); if (!$file instanceof \OC\Files\Node\File || !$file->isReadable()) { http_response_code(403); diff --git a/lib/private/legacy/OC_Helper.php b/lib/private/legacy/OC_Helper.php index cf39d045ae9..0c913709111 100644 --- a/lib/private/legacy/OC_Helper.php +++ b/lib/private/legacy/OC_Helper.php @@ -1,53 +1,15 @@ <?php + /** - * @copyright Copyright (c) 2016, ownCloud, Inc. - * - * @author Ardinis <Ardinis@users.noreply.github.com> - * @author Arthur Schiwon <blizzz@arthur-schiwon.de> - * @author Bart Visscher <bartv@thisnet.nl> - * @author Björn Schießle <bjoern@schiessle.org> - * @author Christoph Wurst <christoph@winzerhof-wurst.at> - * @author Daniel Kesselberg <mail@danielkesselberg.de> - * @author Felix Moeller <mail@felixmoeller.de> - * @author J0WI <J0WI@users.noreply.github.com> - * @author Jakob Sack <mail@jakobsack.de> - * @author Jan-Christoph Borchardt <hey@jancborchardt.net> - * @author Joas Schilling <coding@schilljs.com> - * @author Jörn Friedrich Dreyer <jfd@butonic.de> - * @author Julius Härtl <jus@bitgrid.net> - * @author Lukas Reschke <lukas@statuscode.ch> - * @author Morris Jobke <hey@morrisjobke.de> - * @author Olivier Paroz <github@oparoz.com> - * @author Pellaeon Lin <nfsmwlin@gmail.com> - * @author RealRancor <fisch.666@gmx.de> - * @author Robin Appelman <robin@icewind.nl> - * @author Robin McCorkell <robin@mccorkell.me.uk> - * @author Roeland Jago Douma <roeland@famdouma.nl> - * @author Simon Könnecke <simonkoennecke@gmail.com> - * @author Thomas Müller <thomas.mueller@tmit.eu> - * @author Thomas Tanghus <thomas@tanghus.net> - * @author Vincent Petry <vincent@nextcloud.com> - * - * @license AGPL-3.0 - * - * This code is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License, version 3, - * as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License, version 3, - * along with this program. If not, see <http://www.gnu.org/licenses/> - * + * SPDX-FileCopyrightText: 2016-2024 Nextcloud GmbH and Nextcloud contributors + * SPDX-FileCopyrightText: 2016 ownCloud, Inc. + * SPDX-License-Identifier: AGPL-3.0-only */ use bantu\IniGetWrapper\IniGetWrapper; use OC\Files\Filesystem; use OCP\Files\Mount\IMountPoint; -use OCP\ICacheFactory; use OCP\IBinaryFinder; +use OCP\ICacheFactory; use OCP\IUser; use OCP\Util; use Psr\Log\LoggerInterface; @@ -138,7 +100,7 @@ class OC_Helper { $bytes = (float)$str; - if (preg_match('#([kmgtp]?b?)$#si', $str, $matches) && !empty($bytes_array[$matches[1]])) { + if (preg_match('#([kmgtp]?b?)$#si', $str, $matches) && isset($bytes_array[$matches[1]])) { $bytes *= $bytes_array[$matches[1]]; } else { return false; @@ -596,6 +558,11 @@ class OC_Helper { 'mountPoint' => trim($mountPoint, '/'), ]; + if ($ownerId && $path === '/') { + // If path is root, store this as last known quota usage for this user + \OCP\Server::get(\OCP\IConfig::class)->setUserValue($ownerId, 'files', 'lastSeenQuotaUsage', (string)$relative); + } + $memcache->set($cacheKey, $info, 5 * 60); return $info; diff --git a/lib/private/legacy/OC_Hook.php b/lib/private/legacy/OC_Hook.php index a7a0f755673..2f6686f9126 100644 --- a/lib/private/legacy/OC_Hook.php +++ b/lib/private/legacy/OC_Hook.php @@ -1,32 +1,9 @@ <?php + /** - * @copyright Copyright (c) 2016, ownCloud, Inc. - * - * @author Bart Visscher <bartv@thisnet.nl> - * @author Christoph Wurst <christoph@winzerhof-wurst.at> - * @author Jakob Sack <mail@jakobsack.de> - * @author Jörn Friedrich Dreyer <jfd@butonic.de> - * @author Morris Jobke <hey@morrisjobke.de> - * @author Robin Appelman <robin@icewind.nl> - * @author Robin McCorkell <robin@mccorkell.me.uk> - * @author Sam Tuke <mail@samtuke.com> - * @author Thomas Müller <thomas.mueller@tmit.eu> - * @author Vincent Petry <vincent@nextcloud.com> - * - * @license AGPL-3.0 - * - * This code is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License, version 3, - * as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License, version 3, - * along with this program. If not, see <http://www.gnu.org/licenses/> - * + * SPDX-FileCopyrightText: 2016-2024 Nextcloud GmbH and Nextcloud contributors + * SPDX-FileCopyrightText: 2016 ownCloud, Inc. + * SPDX-License-Identifier: AGPL-3.0-only */ class OC_Hook { public static $thrownExceptions = []; diff --git a/lib/private/legacy/OC_Image.php b/lib/private/legacy/OC_Image.php index aac0c496734..cc6968839d4 100644 --- a/lib/private/legacy/OC_Image.php +++ b/lib/private/legacy/OC_Image.php @@ -1,47 +1,10 @@ <?php declare(strict_types=1); - /** - * @copyright Copyright (c) 2016, ownCloud, Inc. - * - * @author Bartek Przybylski <bart.p.pl@gmail.com> - * @author Bart Visscher <bartv@thisnet.nl> - * @author Björn Schießle <bjoern@schiessle.org> - * @author Byron Marohn <combustible@live.com> - * @author Côme Chilliet <come.chilliet@nextcloud.com> - * @author Christopher Schäpers <kondou@ts.unde.re> - * @author Christoph Wurst <christoph@winzerhof-wurst.at> - * @author Georg Ehrke <oc.list@georgehrke.com> - * @author J0WI <J0WI@users.noreply.github.com> - * @author j-ed <juergen@eisfair.org> - * @author Joas Schilling <coding@schilljs.com> - * @author Johannes Willnecker <johannes@willnecker.com> - * @author Jörn Friedrich Dreyer <jfd@butonic.de> - * @author Julius Härtl <jus@bitgrid.net> - * @author Lukas Reschke <lukas@statuscode.ch> - * @author Morris Jobke <hey@morrisjobke.de> - * @author Olivier Paroz <github@oparoz.com> - * @author Robin Appelman <robin@icewind.nl> - * @author Roeland Jago Douma <roeland@famdouma.nl> - * @author Samuel CHEMLA <chemla.samuel@gmail.com> - * @author Thomas Müller <thomas.mueller@tmit.eu> - * @author Thomas Tanghus <thomas@tanghus.net> - * - * @license AGPL-3.0 - * - * This code is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License, version 3, - * as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License, version 3, - * along with this program. If not, see <http://www.gnu.org/licenses/> - * + * SPDX-FileCopyrightText: 2016-2024 Nextcloud GmbH and Nextcloud contributors + * SPDX-FileCopyrightText: 2016 ownCloud, Inc. + * SPDX-License-Identifier: AGPL-3.0-only */ use OCP\IImage; @@ -55,7 +18,10 @@ class OC_Image implements \OCP\IImage { // Default quality for jpeg images protected const DEFAULT_JPEG_QUALITY = 80; - /** @var false|resource|\GdImage */ + // Default quality for webp images + protected const DEFAULT_WEBP_QUALITY = 80; + + /** @var false|\GdImage */ protected $resource = false; // tmp resource. /** @var int */ protected $imageType = IMAGETYPE_PNG; // Default to png if file type isn't evident. @@ -63,25 +29,25 @@ class OC_Image implements \OCP\IImage { protected $mimeType = 'image/png'; // Default to png /** @var null|string */ protected $filePath = null; - /** @var finfo */ - private $fileInfo; + /** @var ?finfo */ + private $fileInfo = null; /** @var \OCP\ILogger */ private $logger; /** @var \OCP\IConfig */ private $config; - /** @var array */ - private $exif; + /** @var ?array */ + private $exif = null; /** * Constructor. * - * @param resource|string|\GdImage $imageRef The path to a local file, a base64 encoded string or a resource created by - * an imagecreate* function. + * @param mixed $imageRef Deprecated, should be null + * @psalm-assert null $imageRef * @param \OCP\ILogger $logger * @param \OCP\IConfig $config * @throws \InvalidArgumentException in case the $imageRef parameter is not null */ - public function __construct($imageRef = null, \OCP\ILogger $logger = null, \OCP\IConfig $config = null) { + public function __construct($imageRef = null, ?\OCP\ILogger $logger = null, ?\OCP\IConfig $config = null) { $this->logger = $logger; if ($logger === null) { $this->logger = \OC::$server->getLogger(); @@ -103,11 +69,11 @@ class OC_Image implements \OCP\IImage { /** * Determine whether the object contains an image resource. * + * @psalm-assert-if-true \GdImage $this->resource * @return bool */ public function valid(): bool { - if ((is_resource($this->resource) && get_resource_type($this->resource) === 'gd') || - (is_object($this->resource) && get_class($this->resource) === \GdImage::class)) { + if (is_object($this->resource) && get_class($this->resource) === \GdImage::class) { return true; } @@ -130,10 +96,7 @@ class OC_Image implements \OCP\IImage { */ public function width(): int { if ($this->valid()) { - $width = imagesx($this->resource); - if ($width !== false) { - return $width; - } + return imagesx($this->resource); } return -1; } @@ -145,10 +108,7 @@ class OC_Image implements \OCP\IImage { */ public function height(): int { if ($this->valid()) { - $height = imagesy($this->resource); - if ($height !== false) { - return $height; - } + return imagesy($this->resource); } return -1; } @@ -207,7 +167,7 @@ class OC_Image implements \OCP\IImage { * @param string $mimeType * @return bool */ - public function show(string $mimeType = null): bool { + public function show(?string $mimeType = null): bool { if ($mimeType === null) { $mimeType = $this->mimeType(); } @@ -283,6 +243,9 @@ class OC_Image implements \OCP\IImage { case 'image/x-ms-bmp': $imageType = IMAGETYPE_BMP; break; + case 'image/webp': + $imageType = IMAGETYPE_WEBP; + break; default: throw new Exception('\OC_Image::_output(): "' . $mimeType . '" is not supported when forcing a specific output format'); } @@ -293,8 +256,7 @@ class OC_Image implements \OCP\IImage { $retVal = imagegif($this->resource, $filePath); break; case IMAGETYPE_JPEG: - /** @psalm-suppress InvalidScalarArgument */ - imageinterlace($this->resource, (PHP_VERSION_ID >= 80000 ? true : 1)); + imageinterlace($this->resource, true); $retVal = imagejpeg($this->resource, $filePath, $this->getJpegQuality()); break; case IMAGETYPE_PNG: @@ -314,6 +276,9 @@ class OC_Image implements \OCP\IImage { case IMAGETYPE_BMP: $retVal = imagebmp($this->resource, $filePath); break; + case IMAGETYPE_WEBP: + $retVal = imagewebp($this->resource, null, $this->getWebpQuality()); + break; default: $retVal = imagepng($this->resource, $filePath); } @@ -328,25 +293,14 @@ class OC_Image implements \OCP\IImage { } /** - * @param resource|\GdImage $resource - * @throws \InvalidArgumentException in case the supplied resource does not have the type "gd" + * @param \GdImage $resource */ - public function setResource($resource) { - // For PHP<8 - if (is_resource($resource) && get_resource_type($resource) === 'gd') { - $this->resource = $resource; - return; - } - // PHP 8 has real objects for GD stuff - if (is_object($resource) && get_class($resource) === \GdImage::class) { - $this->resource = $resource; - return; - } - throw new \InvalidArgumentException('Supplied resource is not of type "gd".'); + public function setResource(\GdImage $resource): void { + $this->resource = $resource; } /** - * @return false|resource|\GdImage Returns the image resource if any + * @return false|\GdImage Returns the image resource if any */ public function resource() { return $this->resource; @@ -364,6 +318,7 @@ class OC_Image implements \OCP\IImage { case 'image/png': case 'image/jpeg': case 'image/gif': + case 'image/webp': return $this->mimeType; default: return 'image/png'; @@ -383,14 +338,16 @@ class OC_Image implements \OCP\IImage { $res = imagepng($this->resource); break; case "image/jpeg": - /** @psalm-suppress InvalidScalarArgument */ - imageinterlace($this->resource, (PHP_VERSION_ID >= 80000 ? true : 1)); + imageinterlace($this->resource, true); $quality = $this->getJpegQuality(); $res = imagejpeg($this->resource, null, $quality); break; case "image/gif": $res = imagegif($this->resource); break; + case "image/webp": + $res = imagewebp($this->resource, null, $this->getWebpQuality()); + break; default: $res = imagepng($this->resource); $this->logger->info('OC_Image->data. Could not guess mime-type, defaulting to png', ['app' => 'core']); @@ -405,7 +362,7 @@ class OC_Image implements \OCP\IImage { /** * @return string - base64 encoded, which is suitable for embedding in a VCard. */ - public function __toString() { + public function __toString(): string { return base64_encode($this->data()); } @@ -422,6 +379,18 @@ class OC_Image implements \OCP\IImage { } /** + * @return int + */ + protected function getWebpQuality(): int { + $quality = $this->config->getAppValue('preview', 'webp_quality', (string) self::DEFAULT_WEBP_QUALITY); + // TODO: remove when getAppValue is type safe + if ($quality === null) { + $quality = self::DEFAULT_WEBP_QUALITY; + } + return min(100, max(10, (int) $quality)); + } + + /** * (I'm open for suggestions on better method name ;) * Get the orientation based on EXIF data. * @@ -558,7 +527,7 @@ class OC_Image implements \OCP\IImage { * It is the responsibility of the caller to position the pointer at the correct place and to close the handle again. * * @param resource $handle - * @return resource|\GdImage|false An image resource or false on error + * @return \GdImage|false An image resource or false on error */ public function loadFromFileHandle($handle) { $contents = stream_get_contents($handle); @@ -637,7 +606,7 @@ class OC_Image implements \OCP\IImage { * Loads an image from a local file. * * @param bool|string $imagePath The path to a local file. - * @return bool|resource|\GdImage An image resource or false on error + * @return bool|\GdImage An image resource or false on error */ public function loadFromFile($imagePath = false) { // exif_imagetype throws "read error!" if file is less than 12 byte @@ -775,7 +744,7 @@ class OC_Image implements \OCP\IImage { * Loads an image from a string of data. * * @param string $str A string of image data as read from a file. - * @return bool|resource|\GdImage An image resource or false on error + * @return bool|\GdImage An image resource or false on error */ public function loadFromData(string $str) { if (!$this->checkImageDataSize($str)) { @@ -801,7 +770,7 @@ class OC_Image implements \OCP\IImage { * Loads an image from a base64 encoded string. * * @param string $str A string base64 encoded string of image data. - * @return bool|resource|\GdImage An image resource or false on error + * @return bool|\GdImage An image resource or false on error */ public function loadFromBase64(string $str) { $data = base64_decode($str); @@ -842,7 +811,7 @@ class OC_Image implements \OCP\IImage { /** * @param $maxSize - * @return resource|bool|\GdImage + * @return bool|\GdImage */ private function resizeNew(int $maxSize) { if (!$this->valid()) { @@ -883,7 +852,7 @@ class OC_Image implements \OCP\IImage { /** * @param int $width * @param int $height - * @return resource|bool|\GdImage + * @return bool|\GdImage */ public function preciseResizeNew(int $width, int $height) { if (!($width > 0) || !($height > 0)) { @@ -959,13 +928,13 @@ class OC_Image implements \OCP\IImage { // preserve transparency if ($this->imageType == IMAGETYPE_GIF or $this->imageType == IMAGETYPE_PNG) { - imagecolortransparent($process, imagecolorallocatealpha($process, 0, 0, 0, 127)); + imagecolortransparent($process, imagecolorallocatealpha($process, 0, 0, 0, 127) ?: null); imagealphablending($process, false); imagesavealpha($process, true); } - imagecopyresampled($process, $this->resource, 0, 0, $x, $y, $targetWidth, $targetHeight, $width, $height); - if ($process === false) { + $result = imagecopyresampled($process, $this->resource, 0, 0, $x, $y, $targetWidth, $targetHeight, $width, $height); + if ($result === false) { $this->logger->debug('OC_Image->centerCrop, Error re-sampling process image ' . $width . 'x' . $height, ['app' => 'core']); return false; } @@ -1001,7 +970,7 @@ class OC_Image implements \OCP\IImage { * @param int $y Vertical position * @param int $w Width * @param int $h Height - * @return resource|\GdImage|false + * @return \GdImage|false */ public function cropNew(int $x, int $y, int $w, int $h) { if (!$this->valid()) { @@ -1016,13 +985,13 @@ class OC_Image implements \OCP\IImage { // preserve transparency if ($this->imageType == IMAGETYPE_GIF or $this->imageType == IMAGETYPE_PNG) { - imagecolortransparent($process, imagecolorallocatealpha($process, 0, 0, 0, 127)); + imagecolortransparent($process, imagecolorallocatealpha($process, 0, 0, 0, 127) ?: null); imagealphablending($process, false); imagesavealpha($process, true); } - imagecopyresampled($process, $this->resource, 0, 0, $x, $y, $w, $h, $w, $h); - if ($process === false) { + $result = imagecopyresampled($process, $this->resource, 0, 0, $x, $y, $w, $h, $w, $h); + if ($result === false) { $this->logger->debug(__METHOD__ . '(): Error re-sampling process image ' . $w . 'x' . $h, ['app' => 'core']); return false; } @@ -1141,7 +1110,7 @@ if (!function_exists('exif_imagetype')) { * * @link https://www.php.net/manual/en/function.exif-imagetype.php#80383 * @param string $fileName - * @return string|boolean + * @return int|false */ function exif_imagetype(string $fileName) { if (($info = getimagesize($fileName)) !== false) { diff --git a/lib/private/legacy/OC_JSON.php b/lib/private/legacy/OC_JSON.php index b9cfb8210e0..a9682e2cce7 100644 --- a/lib/private/legacy/OC_JSON.php +++ b/lib/private/legacy/OC_JSON.php @@ -1,32 +1,13 @@ <?php + /** - * @copyright Copyright (c) 2016, ownCloud, Inc. - * - * @author Bart Visscher <bartv@thisnet.nl> - * @author Christoph Wurst <christoph@winzerhof-wurst.at> - * @author Lukas Reschke <lukas@statuscode.ch> - * @author Morris Jobke <hey@morrisjobke.de> - * @author Robin Appelman <robin@icewind.nl> - * @author Roeland Jago Douma <roeland@famdouma.nl> - * @author Thomas Müller <thomas.mueller@tmit.eu> - * @author Thomas Tanghus <thomas@tanghus.net> - * @author Vincent Petry <vincent@nextcloud.com> - * - * @license AGPL-3.0 - * - * This code is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License, version 3, - * as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License, version 3, - * along with this program. If not, see <http://www.gnu.org/licenses/> - * + * SPDX-FileCopyrightText: 2016-2024 Nextcloud GmbH and Nextcloud contributors + * SPDX-FileCopyrightText: 2016 ownCloud, Inc. + * SPDX-License-Identifier: AGPL-3.0-only */ + +use OC\Authentication\TwoFactorAuth\Manager as TwoFactorAuthManager; + class OC_JSON { /** * Check if the app is enabled, send json error msg if not @@ -48,7 +29,7 @@ class OC_JSON { * @suppress PhanDeprecatedFunction */ public static function checkLoggedIn() { - $twoFactorAuthManger = \OC::$server->getTwoFactorAuthManager(); + $twoFactorAuthManger = \OC::$server->get(TwoFactorAuthManager::class); if (!\OC::$server->getUserSession()->isLoggedIn() || $twoFactorAuthManger->needsSecondFactor(\OC::$server->getUserSession()->getUser())) { $l = \OC::$server->getL10N('lib'); @@ -114,22 +95,10 @@ class OC_JSON { } /** - * Convert OC_L10N_String to string, for use in json encodings - */ - protected static function to_string(&$value) { - if ($value instanceof \OC\L10N\L10NString) { - $value = (string)$value; - } - } - - /** * Encode JSON * @deprecated Use a AppFramework JSONResponse instead */ - public static function encode($data) { - if (is_array($data)) { - array_walk_recursive($data, ['OC_JSON', 'to_string']); - } + private static function encode($data) { return json_encode($data, JSON_HEX_TAG); } } diff --git a/lib/private/legacy/OC_Response.php b/lib/private/legacy/OC_Response.php index 9440feae3cd..8c8890c74c4 100644 --- a/lib/private/legacy/OC_Response.php +++ b/lib/private/legacy/OC_Response.php @@ -1,31 +1,9 @@ <?php + /** - * @copyright Copyright (c) 2016, ownCloud, Inc. - * - * @author Andreas Fischer <bantu@owncloud.com> - * @author Bart Visscher <bartv@thisnet.nl> - * @author Christoph Wurst <christoph@winzerhof-wurst.at> - * @author J0WI <J0WI@users.noreply.github.com> - * @author Lukas Reschke <lukas@statuscode.ch> - * @author Morris Jobke <hey@morrisjobke.de> - * @author Roeland Jago Douma <roeland@famdouma.nl> - * @author Thomas Müller <thomas.mueller@tmit.eu> - * @author Vincent Petry <vincent@nextcloud.com> - * - * @license AGPL-3.0 - * - * This code is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License, version 3, - * as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License, version 3, - * along with this program. If not, see <http://www.gnu.org/licenses/> - * + * SPDX-FileCopyrightText: 2016-2024 Nextcloud GmbH and Nextcloud contributors + * SPDX-FileCopyrightText: 2016 ownCloud, Inc. + * SPDX-License-Identifier: AGPL-3.0-only */ class OC_Response { /** diff --git a/lib/private/legacy/OC_Template.php b/lib/private/legacy/OC_Template.php index 974f26fa381..5caa733b115 100644 --- a/lib/private/legacy/OC_Template.php +++ b/lib/private/legacy/OC_Template.php @@ -1,44 +1,14 @@ <?php + /** - * @copyright Copyright (c) 2016, ownCloud, Inc. - * - * @author Bart Visscher <bartv@thisnet.nl> - * @author Brice Maron <brice@bmaron.net> - * @author Christoph Wurst <christoph@winzerhof-wurst.at> - * @author Frank Karlitschek <frank@karlitschek.de> - * @author Individual IT Services <info@individual-it.net> - * @author Jakob Sack <mail@jakobsack.de> - * @author Jan-Christoph Borchardt <hey@jancborchardt.net> - * @author Joas Schilling <coding@schilljs.com> - * @author John Molakvoæ <skjnldsv@protonmail.com> - * @author Jörn Friedrich Dreyer <jfd@butonic.de> - * @author Julius Härtl <jus@bitgrid.net> - * @author Lukas Reschke <lukas@statuscode.ch> - * @author Marin Treselj <marin@pixelipo.com> - * @author Michael Letzgus <www@chronos.michael-letzgus.de> - * @author Morris Jobke <hey@morrisjobke.de> - * @author Robin Appelman <robin@icewind.nl> - * @author Roeland Jago Douma <roeland@famdouma.nl> - * @author Thomas Müller <thomas.mueller@tmit.eu> - * @author Vincent Petry <vincent@nextcloud.com> - * - * @license AGPL-3.0 - * - * This code is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License, version 3, - * as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License, version 3, - * along with this program. If not, see <http://www.gnu.org/licenses/> - * + * SPDX-FileCopyrightText: 2016-2024 Nextcloud GmbH and Nextcloud contributors + * SPDX-FileCopyrightText: 2016 ownCloud, Inc. + * SPDX-License-Identifier: AGPL-3.0-only */ use OC\TemplateLayout; +use OCP\AppFramework\Http\Events\BeforeTemplateRenderedEvent; use OCP\AppFramework\Http\TemplateResponse; +use OCP\EventDispatcher\IEventDispatcher; require_once __DIR__.'/template/functions.php'; @@ -249,20 +219,44 @@ class OC_Template extends \OC\Template\Base { // If the hint is the same as the message there is no need to display it twice. $hint = ''; } + $errors = [['error' => $error_msg, 'hint' => $hint]]; http_response_code($statusCode); try { - $content = new \OC_Template('', 'error', 'error', false); - $errors = [['error' => $error_msg, 'hint' => $hint]]; - $content->assign('errors', $errors); - $content->printPage(); - } catch (\Exception $e) { + // Try rendering themed html error page + $response = new TemplateResponse( + '', + 'error', + ['errors' => $errors], + TemplateResponse::RENDER_AS_ERROR, + $statusCode, + ); + $event = new BeforeTemplateRenderedEvent(false, $response); + \OC::$server->get(IEventDispatcher::class)->dispatchTyped($event); + print($response->render()); + } catch (\Throwable $e1) { $logger = \OC::$server->getLogger(); - $logger->error("$error_msg $hint", ['app' => 'core']); - $logger->logException($e, ['app' => 'core']); + $logger->logException($e1, [ + 'app' => 'core', + 'message' => 'Rendering themed error page failed. Falling back to unthemed error page.' + ]); - header('Content-Type: text/plain; charset=utf-8'); - print("$error_msg $hint"); + try { + // Try rendering unthemed html error page + $content = new \OC_Template('', 'error', 'error', false); + $content->assign('errors', $errors); + $content->printPage(); + } catch (\Exception $e2) { + // If nothing else works, fall back to plain text error page + $logger->error("$error_msg $hint", ['app' => 'core']); + $logger->logException($e2, [ + 'app' => 'core', + 'message' => 'Rendering unthemed error page failed. Falling back to plain text error page.' + ]); + + header('Content-Type: text/plain; charset=utf-8'); + print("$error_msg $hint"); + } } die(); } @@ -275,8 +269,11 @@ class OC_Template extends \OC\Template\Base { * @suppress PhanAccessMethodInternal */ public static function printExceptionErrorPage($exception, $statusCode = 503) { + $debug = false; http_response_code($statusCode); try { + $debug = \OC::$server->getSystemConfig()->getValue('debug', false); + $serverLogsDocumentation = \OC::$server->getSystemConfig()->getValue('documentation_url.server_logs', ''); $request = \OC::$server->getRequest(); $content = new \OC_Template('', 'exception', 'error', false); $content->assign('errorClass', get_class($exception)); @@ -285,7 +282,8 @@ class OC_Template extends \OC\Template\Base { $content->assign('file', $exception->getFile()); $content->assign('line', $exception->getLine()); $content->assign('exception', $exception); - $content->assign('debugMode', \OC::$server->getSystemConfig()->getValue('debug', false)); + $content->assign('debugMode', $debug); + $content->assign('serverLogsDocumentation', $serverLogsDocumentation); $content->assign('remoteAddr', $request->getRemoteAddress()); $content->assign('requestID', $request->getId()); $content->printPage(); @@ -296,22 +294,28 @@ class OC_Template extends \OC\Template\Base { $logger->logException($e, ['app' => 'core']); } catch (Throwable $e) { // no way to log it properly - but to avoid a white page of death we send some output - header('Content-Type: text/plain; charset=utf-8'); - print("Internal Server Error\n\n"); - print("The server encountered an internal error and was unable to complete your request.\n"); - print("Please contact the server administrator if this error reappears multiple times, please include the technical details below in your report.\n"); - print("More details can be found in the server log.\n"); + self::printPlainErrorPage($e, $debug); // and then throw it again to log it at least to the web server error log throw $e; } - header('Content-Type: text/plain; charset=utf-8'); - print("Internal Server Error\n\n"); - print("The server encountered an internal error and was unable to complete your request.\n"); - print("Please contact the server administrator if this error reappears multiple times, please include the technical details below in your report.\n"); - print("More details can be found in the server log.\n"); + self::printPlainErrorPage($e, $debug); } die(); } + + private static function printPlainErrorPage(\Throwable $exception, bool $debug = false) { + header('Content-Type: text/plain; charset=utf-8'); + print("Internal Server Error\n\n"); + print("The server encountered an internal error and was unable to complete your request.\n"); + print("Please contact the server administrator if this error reappears multiple times, please include the technical details below in your report.\n"); + print("More details can be found in the server log.\n"); + + if ($debug) { + print("\n"); + print($exception->getMessage() . ' ' . $exception->getFile() . ' at ' . $exception->getLine() . "\n"); + print($exception->getTraceAsString()); + } + } } diff --git a/lib/private/legacy/OC_User.php b/lib/private/legacy/OC_User.php index caa4f5dca65..e7708525c76 100644 --- a/lib/private/legacy/OC_User.php +++ b/lib/private/legacy/OC_User.php @@ -1,47 +1,20 @@ <?php + /** - * @copyright Copyright (c) 2016, ownCloud, Inc. - * - * @author Aldo "xoen" Giambelluca <xoen@xoen.org> - * @author Andreas Fischer <bantu@owncloud.com> - * @author Arthur Schiwon <blizzz@arthur-schiwon.de> - * @author Bartek Przybylski <bart.p.pl@gmail.com> - * @author Björn Schießle <bjoern@schiessle.org> - * @author Christoph Wurst <christoph@winzerhof-wurst.at> - * @author Georg Ehrke <oc.list@georgehrke.com> - * @author Jakob Sack <mail@jakobsack.de> - * @author Jörn Friedrich Dreyer <jfd@butonic.de> - * @author Lukas Reschke <lukas@statuscode.ch> - * @author Morris Jobke <hey@morrisjobke.de> - * @author Robin Appelman <robin@icewind.nl> - * @author Robin McCorkell <robin@mccorkell.me.uk> - * @author Roeland Jago Douma <roeland@famdouma.nl> - * @author shkdee <louis.traynard@m4x.org> - * @author Thomas Müller <thomas.mueller@tmit.eu> - * @author Vincent Petry <vincent@nextcloud.com> - * - * @license AGPL-3.0 - * - * This code is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License, version 3, - * as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License, version 3, - * along with this program. If not, see <http://www.gnu.org/licenses/> - * + * SPDX-FileCopyrightText: 2016-2024 Nextcloud GmbH and Nextcloud contributors + * SPDX-FileCopyrightText: 2016 ownCloud, Inc. + * SPDX-License-Identifier: AGPL-3.0-only */ - use OC\User\LoginException; use OCP\EventDispatcher\IEventDispatcher; -use OCP\ILogger; +use OCP\IGroupManager; +use OCP\ISession; +use OCP\IUser; use OCP\IUserManager; +use OCP\Server; use OCP\User\Events\BeforeUserLoggedInEvent; use OCP\User\Events\UserLoggedInEvent; +use Psr\Log\LoggerInterface; /** * This class provides wrapper methods for user management. Multiple backends are @@ -84,7 +57,7 @@ class OC_User { \OC::$server->getUserManager()->registerBackend($backend); } else { // You'll never know what happens - if (null === $backend or !is_string($backend)) { + if ($backend === null or !is_string($backend)) { $backend = 'database'; } @@ -93,7 +66,7 @@ class OC_User { case 'database': case 'mysql': case 'sqlite': - \OCP\Util::writeLog('core', 'Adding user backend ' . $backend . '.', ILogger::DEBUG); + Server::get(LoggerInterface::class)->debug('Adding user backend ' . $backend . '.', ['app' => 'core']); self::$_usedBackends[$backend] = new \OC\User\Database(); \OC::$server->getUserManager()->registerBackend(self::$_usedBackends[$backend]); break; @@ -102,7 +75,7 @@ class OC_User { \OC::$server->getUserManager()->registerBackend(self::$_usedBackends[$backend]); break; default: - \OCP\Util::writeLog('core', 'Adding default user backend ' . $backend . '.', ILogger::DEBUG); + Server::get(LoggerInterface::class)->debug('Adding default user backend ' . $backend . '.', ['app' => 'core']); $className = 'OC_USER_' . strtoupper($backend); self::$_usedBackends[$backend] = new $className(); \OC::$server->getUserManager()->registerBackend(self::$_usedBackends[$backend]); @@ -138,7 +111,7 @@ class OC_User { $class = $config['class']; $arguments = $config['arguments']; if (class_exists($class)) { - if (array_search($i, self::$_setupedBackends) === false) { + if (!in_array($i, self::$_setupedBackends)) { // make a reflection object $reflectionObj = new ReflectionClass($class); @@ -147,10 +120,10 @@ class OC_User { self::useBackend($backend); self::$_setupedBackends[] = $i; } else { - \OCP\Util::writeLog('core', 'User backend ' . $class . ' already initialized.', ILogger::DEBUG); + Server::get(LoggerInterface::class)->debug('User backend ' . $class . ' already initialized.', ['app' => 'core']); } } else { - \OCP\Util::writeLog('core', 'User backend ' . $class . ' not found.', ILogger::ERROR); + Server::get(LoggerInterface::class)->error('User backend ' . $class . ' not found.', ['app' => 'core']); } } } @@ -178,7 +151,7 @@ class OC_User { $dispatcher = \OC::$server->get(IEventDispatcher::class); if ($userSession->getUser() && !$userSession->getUser()->isEnabled()) { - $message = \OC::$server->getL10N('lib')->t('User disabled'); + $message = \OC::$server->getL10N('lib')->t('Account disabled'); throw new LoginException($message); } $userSession->setLoginName($uid); @@ -303,7 +276,7 @@ class OC_User { } $user = \OC::$server->getUserSession()->getUser(); - if ($user instanceof \OCP\IUser) { + if ($user instanceof IUser) { $backend = $user->getBackend(); if ($backend instanceof \OCP\User\Backend\ICustomLogout) { return $backend->getLogoutUrl(); @@ -323,12 +296,9 @@ class OC_User { * @return bool */ public static function isAdminUser($uid) { - $group = \OC::$server->getGroupManager()->get('admin'); - $user = \OC::$server->getUserManager()->get($uid); - if ($group && $user && $group->inGroup($user) && self::$incognitoMode === false) { - return true; - } - return false; + $user = Server::get(IUserManager::class)->get($uid); + $isAdmin = $user && Server::get(IGroupManager::class)->isAdmin($user->getUID()); + return $isAdmin && self::$incognitoMode === false; } @@ -338,7 +308,7 @@ class OC_User { * @return string|false uid or false */ public static function getUser() { - $uid = \OC::$server->getSession() ? \OC::$server->getSession()->get('user_id') : null; + $uid = Server::get(ISession::class)?->get('user_id'); if (!is_null($uid) && self::$incognitoMode === false) { return $uid; } else { diff --git a/lib/private/legacy/OC_Util.php b/lib/private/legacy/OC_Util.php index 9d62c46137e..8be4c81c2cd 100644 --- a/lib/private/legacy/OC_Util.php +++ b/lib/private/legacy/OC_Util.php @@ -1,70 +1,12 @@ <?php + /** - * @copyright Copyright (c) 2016, ownCloud, Inc. - * - * @author Arthur Schiwon <blizzz@arthur-schiwon.de> - * @author Bart Visscher <bartv@thisnet.nl> - * @author Bernhard Posselt <dev@bernhard-posselt.com> - * @author Birk Borkason <daniel.niccoli@gmail.com> - * @author Bjoern Schiessle <bjoern@schiessle.org> - * @author Björn Schießle <bjoern@schiessle.org> - * @author Brice Maron <brice@bmaron.net> - * @author Christopher Schäpers <kondou@ts.unde.re> - * @author Christoph Wurst <christoph@winzerhof-wurst.at> - * @author Clark Tomlinson <fallen013@gmail.com> - * @author cmeh <cmeh@users.noreply.github.com> - * @author Eric Masseran <rico.masseran@gmail.com> - * @author Felix Epp <work@felixepp.de> - * @author Florin Peter <github@florin-peter.de> - * @author Frank Karlitschek <frank@karlitschek.de> - * @author Georg Ehrke <oc.list@georgehrke.com> - * @author helix84 <helix84@centrum.sk> - * @author Ilja Neumann <ineumann@owncloud.com> - * @author Individual IT Services <info@individual-it.net> - * @author Jakob Sack <mail@jakobsack.de> - * @author Joas Schilling <coding@schilljs.com> - * @author John Molakvoæ <skjnldsv@protonmail.com> - * @author Jörn Friedrich Dreyer <jfd@butonic.de> - * @author Julius Härtl <jus@bitgrid.net> - * @author Kawohl <john@owncloud.com> - * @author Lukas Reschke <lukas@statuscode.ch> - * @author Markus Goetz <markus@woboq.com> - * @author Martin Mattel <martin.mattel@diemattels.at> - * @author Marvin Thomas Rabe <mrabe@marvinrabe.de> - * @author Michael Gapczynski <GapczynskiM@gmail.com> - * @author Morris Jobke <hey@morrisjobke.de> - * @author rakekniven <mark.ziegler@rakekniven.de> - * @author Robert Dailey <rcdailey@gmail.com> - * @author Robin Appelman <robin@icewind.nl> - * @author Robin McCorkell <robin@mccorkell.me.uk> - * @author Roeland Jago Douma <roeland@famdouma.nl> - * @author Sebastian Wessalowski <sebastian@wessalowski.org> - * @author Stefan Rado <owncloud@sradonia.net> - * @author Stefan Weil <sw@weilnetz.de> - * @author Thomas Müller <thomas.mueller@tmit.eu> - * @author Thomas Tanghus <thomas@tanghus.net> - * @author Valdnet <47037905+Valdnet@users.noreply.github.com> - * @author Victor Dubiniuk <dubiniuk@owncloud.com> - * @author Vincent Petry <vincent@nextcloud.com> - * @author Volkan Gezer <volkangezer@gmail.com> - * - * @license AGPL-3.0 - * - * This code is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License, version 3, - * as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License, version 3, - * along with this program. If not, see <http://www.gnu.org/licenses/> - * + * SPDX-FileCopyrightText: 2016-2024 Nextcloud GmbH and Nextcloud contributors + * SPDX-FileCopyrightText: 2016 ownCloud, Inc. + * SPDX-License-Identifier: AGPL-3.0-only */ - use bantu\IniGetWrapper\IniGetWrapper; +use OC\Authentication\TwoFactorAuth\Manager as TwoFactorAuthManager; use OC\Files\SetupManager; use OCP\Files\Template\ITemplateManager; use OCP\IConfig; @@ -325,9 +267,10 @@ class OC_Util { return; } + $timestamp = filemtime(OC::$SERVERROOT . '/version.php'); require OC::$SERVERROOT . '/version.php'; /** @var int $timestamp */ - self::$versionCache['OC_Version_Timestamp'] = \OC::$VERSION_MTIME; + self::$versionCache['OC_Version_Timestamp'] = $timestamp; /** @var string $OC_Version */ self::$versionCache['OC_Version'] = $OC_Version; /** @var string $OC_VersionString */ @@ -512,15 +455,7 @@ class OC_Util { } $webServerRestart = false; - $setup = new \OC\Setup( - $config, - \OC::$server->get(IniGetWrapper::class), - \OC::$server->getL10N('lib'), - \OC::$server->get(\OCP\Defaults::class), - \OC::$server->get(LoggerInterface::class), - \OC::$server->getSecureRandom(), - \OC::$server->get(\OC\Installer::class) - ); + $setup = \OCP\Server::get(\OC\Setup::class); $urlGenerator = \OC::$server->getURLGenerator(); @@ -739,8 +674,8 @@ class OC_Util { if ($perms[2] !== '0') { $l = \OC::$server->getL10N('lib'); return [[ - 'error' => $l->t('Your data directory is readable by other users.'), - 'hint' => $l->t('Please change the permissions to 0770 so that the directory cannot be listed by other users.'), + 'error' => $l->t('Your data directory is readable by other people.'), + 'hint' => $l->t('Please change the permissions to 0770 so that the directory cannot be listed by other people.'), ]]; } } @@ -792,7 +727,7 @@ class OC_Util { exit(); } // Redirect to 2FA challenge selection if 2FA challenge was not solved yet - if (\OC::$server->getTwoFactorAuthManager()->needsSecondFactor(\OC::$server->getUserSession()->getUser())) { + if (\OC::$server->get(TwoFactorAuthManager::class)->needsSecondFactor(\OC::$server->getUserSession()->getUser())) { header('Location: ' . \OC::$server->getURLGenerator()->linkToRoute('core.TwoFactorChallenge.selectChallenge')); exit(); } @@ -976,11 +911,11 @@ class OC_Util { */ private static function isNonUTF8Locale() { if (function_exists('escapeshellcmd')) { - return '' === escapeshellcmd('§'); + return escapeshellcmd('§') === ''; } elseif (function_exists('escapeshellarg')) { - return '\'\'' === escapeshellarg('§'); + return escapeshellarg('§') === '\'\''; } else { - return 0 === preg_match('/utf-?8/i', setlocale(LC_CTYPE, 0)); + return preg_match('/utf-?8/i', setlocale(LC_CTYPE, 0)) === 0; } } @@ -1119,8 +1054,8 @@ class OC_Util { return false; } - foreach (str_split($trimmed) as $char) { - if (str_contains(\OCP\Constants::FILENAME_INVALID_CHARS, $char)) { + foreach (\OCP\Util::getForbiddenFileNameChars() as $char) { + if (str_contains($trimmed, $char)) { return false; } } diff --git a/lib/private/legacy/template/functions.php b/lib/private/legacy/template/functions.php index bcc0906dcdf..84ada2aa6d8 100644 --- a/lib/private/legacy/template/functions.php +++ b/lib/private/legacy/template/functions.php @@ -1,39 +1,12 @@ <?php -use OCP\Util; - /** - * @copyright Copyright (c) 2016, ownCloud, Inc. - * - * @author Bart Visscher <bartv@thisnet.nl> - * @author Bernhard Posselt <dev@bernhard-posselt.com> - * @author Christoph Wurst <christoph@winzerhof-wurst.at> - * @author Joas Schilling <coding@schilljs.com> - * @author Jörn Friedrich Dreyer <jfd@butonic.de> - * @author Julius Härtl <jus@bitgrid.net> - * @author Lukas Reschke <lukas@statuscode.ch> - * @author Michael Letzgus <www@chronos.michael-letzgus.de> - * @author Morris Jobke <hey@morrisjobke.de> - * @author Robin McCorkell <robin@mccorkell.me.uk> - * @author Roeland Jago Douma <roeland@famdouma.nl> - * @author Thomas Müller <thomas.mueller@tmit.eu> - * @author Vincent Petry <vincent@nextcloud.com> - * - * @license AGPL-3.0 - * - * This code is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License, version 3, - * as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License, version 3, - * along with this program. If not, see <http://www.gnu.org/licenses/> - * + * SPDX-FileCopyrightText: 2016-2024 Nextcloud GmbH and Nextcloud contributors + * SPDX-FileCopyrightText: 2016 ownCloud, Inc. + * SPDX-License-Identifier: AGPL-3.0-only */ +use OCP\Util; + function p($string) { print(\OCP\Util::sanitizeHTML($string)); } |