refactor: Remove old Share backend

This has been implicitely deprecated for a while with Share20 containing
the new implementation.

The only use was to determine whether remote sharing was enabled or not,
which we can do much more easily.

Signed-off-by: Carl Schwan <carlschwan@kde.org>
This commit is contained in:
Carl Schwan
2026-02-26 16:47:55 +01:00
parent 346c4bd69a
commit e60036b45f
26 changed files with 17 additions and 1084 deletions

View File

@@ -96,8 +96,6 @@ return array(
'OCA\\Files_Sharing\\ResponseDefinitions' => $baseDir . '/../lib/ResponseDefinitions.php',
'OCA\\Files_Sharing\\Scanner' => $baseDir . '/../lib/Scanner.php',
'OCA\\Files_Sharing\\Settings\\Personal' => $baseDir . '/../lib/Settings/Personal.php',
'OCA\\Files_Sharing\\ShareBackend\\File' => $baseDir . '/../lib/ShareBackend/File.php',
'OCA\\Files_Sharing\\ShareBackend\\Folder' => $baseDir . '/../lib/ShareBackend/Folder.php',
'OCA\\Files_Sharing\\ShareTargetValidator' => $baseDir . '/../lib/ShareTargetValidator.php',
'OCA\\Files_Sharing\\SharedMount' => $baseDir . '/../lib/SharedMount.php',
'OCA\\Files_Sharing\\SharedStorage' => $baseDir . '/../lib/SharedStorage.php',

View File

@@ -111,8 +111,6 @@ class ComposerStaticInitFiles_Sharing
'OCA\\Files_Sharing\\ResponseDefinitions' => __DIR__ . '/..' . '/../lib/ResponseDefinitions.php',
'OCA\\Files_Sharing\\Scanner' => __DIR__ . '/..' . '/../lib/Scanner.php',
'OCA\\Files_Sharing\\Settings\\Personal' => __DIR__ . '/..' . '/../lib/Settings/Personal.php',
'OCA\\Files_Sharing\\ShareBackend\\File' => __DIR__ . '/..' . '/../lib/ShareBackend/File.php',
'OCA\\Files_Sharing\\ShareBackend\\Folder' => __DIR__ . '/..' . '/../lib/ShareBackend/Folder.php',
'OCA\\Files_Sharing\\ShareTargetValidator' => __DIR__ . '/..' . '/../lib/ShareTargetValidator.php',
'OCA\\Files_Sharing\\SharedMount' => __DIR__ . '/..' . '/../lib/SharedMount.php',
'OCA\\Files_Sharing\\SharedStorage' => __DIR__ . '/..' . '/../lib/SharedStorage.php',

View File

@@ -8,7 +8,6 @@
namespace OCA\Files_Sharing\AppInfo;
use OC\Group\DisplayNameCache as GroupDisplayNameCache;
use OC\Share\Share;
use OC\User\DisplayNameCache;
use OCA\Files\Event\LoadAdditionalScriptsEvent;
use OCA\Files\Event\LoadSidebar;
@@ -34,8 +33,6 @@ use OCA\Files_Sharing\Middleware\SharingCheckMiddleware;
use OCA\Files_Sharing\MountProvider;
use OCA\Files_Sharing\Notification\Listener;
use OCA\Files_Sharing\Notification\Notifier;
use OCA\Files_Sharing\ShareBackend\File;
use OCA\Files_Sharing\ShareBackend\Folder;
use OCP\AppFramework\App;
use OCP\AppFramework\Bootstrap\IBootContext;
use OCP\AppFramework\Bootstrap\IBootstrap;
@@ -130,9 +127,6 @@ class Application extends App implements IBootstrap {
$context->injectFn([$this, 'registerEventsScripts']);
Helper::registerHooks();
Share::registerBackend('file', File::class);
Share::registerBackend('folder', Folder::class, 'file');
}

View File

@@ -128,11 +128,11 @@ class ShareesAPIController extends OCSController {
$shareTypes[] = IShare::TYPE_GROUP;
}
if ($this->isRemoteSharingAllowed($itemType)) {
if ($this->isRemoteSharingAllowed()) {
$shareTypes[] = IShare::TYPE_REMOTE;
}
if ($this->isRemoteGroupSharingAllowed($itemType)) {
if ($this->isRemoteGroupSharingAllowed()) {
$shareTypes[] = IShare::TYPE_REMOTE_GROUP;
}
@@ -309,11 +309,11 @@ class ShareesAPIController extends OCSController {
$shareTypes[] = IShare::TYPE_GROUP;
}
if ($this->isRemoteSharingAllowed($itemType)) {
if ($this->isRemoteSharingAllowed()) {
$shareTypes[] = IShare::TYPE_REMOTE;
}
if ($this->isRemoteGroupSharingAllowed($itemType)) {
if ($this->isRemoteGroupSharingAllowed()) {
$shareTypes[] = IShare::TYPE_REMOTE_GROUP;
}
@@ -353,24 +353,12 @@ class ShareesAPIController extends OCSController {
* @param string $itemType
* @return bool
*/
protected function isRemoteSharingAllowed(string $itemType): bool {
try {
// FIXME: static foo makes unit testing unnecessarily difficult
$backend = Share::getBackend($itemType);
return $backend->isShareTypeAllowed(IShare::TYPE_REMOTE);
} catch (\Exception $e) {
return false;
}
protected function isRemoteSharingAllowed(): bool {
return $this->federatedShareProvider->isOutgoingServer2serverShareEnabled();
}
protected function isRemoteGroupSharingAllowed(string $itemType): bool {
try {
// FIXME: static foo makes unit testing unnecessarily difficult
$backend = Share::getBackend($itemType);
return $backend->isShareTypeAllowed(IShare::TYPE_REMOTE_GROUP);
} catch (\Exception $e) {
return false;
}
protected function isRemoteGroupSharingAllowed(): bool {
return $this->federatedShareProvider->isOutgoingServer2serverGroupShareEnabled();
}

View File

@@ -330,7 +330,7 @@ class Storage extends DAV implements ISharedStorage, IDisableEncryptionStorage,
}
public function isSharable(string $path): bool {
if (Util::isSharingDisabledForUser() || !Share::isResharingAllowed()) {
if (Util::isSharingDisabledForUser() || $this->config->getAppValue('core', 'shareapi_allow_resharing', 'yes') !== 'yes') {
return false;
}
return (bool)($this->getPermissions($path) & Constants::PERMISSION_SHARE);

View File

@@ -29,7 +29,7 @@ class Helper {
* @param View $view
* @return string $path
*/
public static function generateUniqueTarget($path, $view) {
public static function generateUniqueTarget(string $path, View $view): string {
$pathinfo = pathinfo($path);
$ext = isset($pathinfo['extension']) ? '.' . $pathinfo['extension'] : '';
$name = $pathinfo['filename'];
@@ -82,13 +82,4 @@ class Helper {
return $shareFolder;
}
/**
* set default share folder
*
* @param string $shareFolder
*/
public static function setShareFolder($shareFolder) {
Server::get(IConfig::class)->setSystemValue('share_folder', $shareFolder);
}
}

View File

@@ -1,228 +0,0 @@
<?php
/**
* SPDX-FileCopyrightText: 2017-2024 Nextcloud GmbH and Nextcloud contributors
* SPDX-FileCopyrightText: 2016 ownCloud, Inc.
* SPDX-License-Identifier: AGPL-3.0-only
*/
namespace OCA\Files_Sharing\ShareBackend;
use OC\Files\Filesystem;
use OC\Files\View;
use OCA\FederatedFileSharing\FederatedShareProvider;
use OCA\Files_Sharing\Helper;
use OCP\Files\NotFoundException;
use OCP\IDBConnection;
use OCP\Server;
use OCP\Share\IShare;
use OCP\Share_Backend_File_Dependent;
use Psr\Log\LoggerInterface;
class File implements Share_Backend_File_Dependent {
public const FORMAT_SHARED_STORAGE = 0;
public const FORMAT_GET_FOLDER_CONTENTS = 1;
public const FORMAT_FILE_APP_ROOT = 2;
public const FORMAT_OPENDIR = 3;
public const FORMAT_GET_ALL = 4;
public const FORMAT_PERMISSIONS = 5;
public const FORMAT_TARGET_NAMES = 6;
private $path;
public function __construct(
private ?FederatedShareProvider $federatedShareProvider = null,
) {
if ($federatedShareProvider) {
$this->federatedShareProvider = $federatedShareProvider;
} else {
$this->federatedShareProvider = Server::get(FederatedShareProvider::class);
}
}
public function isValidSource($itemSource, $uidOwner) {
try {
$path = Filesystem::getPath($itemSource);
// FIXME: attributes should not be set here,
// keeping this pattern for now to avoid unexpected
// regressions
$this->path = Filesystem::normalizePath(basename($path));
return true;
} catch (NotFoundException $e) {
return false;
}
}
public function getFilePath($itemSource, $uidOwner) {
if (isset($this->path)) {
$path = $this->path;
$this->path = null;
return $path;
} else {
try {
$path = Filesystem::getPath($itemSource);
return $path;
} catch (NotFoundException $e) {
return false;
}
}
}
/**
* create unique target
*
* @param string $itemSource
* @param string $shareWith
* @return string
*/
public function generateTarget($itemSource, $shareWith) {
$shareFolder = Helper::getShareFolder();
$target = Filesystem::normalizePath($shareFolder . '/' . basename($itemSource));
Filesystem::initMountPoints($shareWith);
$view = new View('/' . $shareWith . '/files');
if (!$view->is_dir($shareFolder)) {
$dir = '';
$subdirs = explode('/', $shareFolder);
foreach ($subdirs as $subdir) {
$dir = $dir . '/' . $subdir;
if (!$view->is_dir($dir)) {
$view->mkdir($dir);
}
}
}
return Helper::generateUniqueTarget($target, $view);
}
public function formatItems($items, $format, $parameters = null) {
if ($format === self::FORMAT_SHARED_STORAGE) {
// Only 1 item should come through for this format call
$item = array_shift($items);
return [
'parent' => $item['parent'],
'path' => $item['path'],
'storage' => $item['storage'],
'permissions' => $item['permissions'],
'uid_owner' => $item['uid_owner'],
];
} elseif ($format === self::FORMAT_GET_FOLDER_CONTENTS) {
$files = [];
foreach ($items as $item) {
$file = [];
$file['fileid'] = $item['file_source'];
$file['storage'] = $item['storage'];
$file['path'] = $item['file_target'];
$file['parent'] = $item['file_parent'];
$file['name'] = basename($item['file_target']);
$file['mimetype'] = $item['mimetype'];
$file['mimepart'] = $item['mimepart'];
$file['mtime'] = $item['mtime'];
$file['encrypted'] = $item['encrypted'];
$file['etag'] = $item['etag'];
$file['uid_owner'] = $item['uid_owner'];
$file['displayname_owner'] = $item['displayname_owner'];
$storage = Filesystem::getStorage('/');
$cache = $storage->getCache();
$file['size'] = $item['size'];
$files[] = $file;
}
return $files;
} elseif ($format === self::FORMAT_OPENDIR) {
$files = [];
foreach ($items as $item) {
$files[] = basename($item['file_target']);
}
return $files;
} elseif ($format === self::FORMAT_GET_ALL) {
$ids = [];
foreach ($items as $item) {
$ids[] = $item['file_source'];
}
return $ids;
} elseif ($format === self::FORMAT_PERMISSIONS) {
$filePermissions = [];
foreach ($items as $item) {
$filePermissions[$item['file_source']] = $item['permissions'];
}
return $filePermissions;
} elseif ($format === self::FORMAT_TARGET_NAMES) {
$targets = [];
foreach ($items as $item) {
$targets[] = $item['file_target'];
}
return $targets;
}
return [];
}
/**
* check if server2server share is enabled
*
* @param int $shareType
* @return boolean
*/
public function isShareTypeAllowed($shareType) {
if ($shareType === IShare::TYPE_REMOTE) {
return $this->federatedShareProvider->isOutgoingServer2serverShareEnabled();
}
if ($shareType === IShare::TYPE_REMOTE_GROUP) {
return $this->federatedShareProvider->isOutgoingServer2serverGroupShareEnabled();
}
return true;
}
/**
* resolve reshares to return the correct source item
* @param array $source
* @return array source item
*/
protected static function resolveReshares($source) {
if (isset($source['parent'])) {
$parent = $source['parent'];
while (isset($parent)) {
$qb = Server::get(IDBConnection::class)->getQueryBuilder();
$qb->select('parent', 'uid_owner')
->from('share')
->where(
$qb->expr()->eq('id', $qb->createNamedParameter($parent))
);
$result = $qb->executeQuery();
$item = $result->fetchAssociative();
$result->closeCursor();
if (isset($item['parent'])) {
$parent = $item['parent'];
} else {
$fileOwner = $item['uid_owner'];
break;
}
}
} else {
$fileOwner = $source['uid_owner'];
}
if (isset($fileOwner)) {
$source['fileOwner'] = $fileOwner;
} else {
Server::get(LoggerInterface::class)->error('No owner found for reshare', ['app' => 'files_sharing']);
}
return $source;
}
/**
* @param string $target
* @param array $share
* @return array|false source item
*/
public static function getSource($target, $share) {
if ($share['item_type'] === 'folder' && $target !== '') {
// note: in case of ext storage mount points the path might be empty
// which would cause a leading slash to appear
$share['path'] = ltrim($share['path'] . '/' . $target, '/');
}
return self::resolveReshares($share);
}
}

View File

@@ -1,61 +0,0 @@
<?php
/**
* SPDX-FileCopyrightText: 2017-2024 Nextcloud GmbH and Nextcloud contributors
* SPDX-FileCopyrightText: 2016 ownCloud, Inc.
* SPDX-License-Identifier: AGPL-3.0-only
*/
namespace OCA\Files_Sharing\ShareBackend;
use OCP\IDBConnection;
use OCP\Server;
use OCP\Share_Backend_Collection;
class Folder extends File implements Share_Backend_Collection {
public function getChildren($itemSource): array {
$children = [];
$parents = [$itemSource];
$qb = Server::get(IDBConnection::class)->getQueryBuilder();
$qb->select('id')
->from('mimetypes')
->where(
$qb->expr()->eq('mimetype', $qb->createNamedParameter('httpd/unix-directory'))
);
$result = $qb->executeQuery();
if (($row = $result->fetchAssociative()) !== false) {
$mimetype = (int)$row['id'];
} else {
$mimetype = -1;
}
$result->closeCursor();
while (!empty($parents)) {
$qb = Server::get(IDBConnection::class)->getQueryBuilder();
$parents = array_map(function ($parent) use ($qb) {
return $qb->createNamedParameter($parent);
}, $parents);
$qb->select('`fileid', 'name', '`mimetype')
->from('filecache')
->where(
$qb->expr()->in('parent', $parents)
);
$result = $qb->executeQuery();
$parents = [];
foreach ($result->iterateAssociative() as $file) {
$children[] = ['source' => $file['fileid'], 'file_path' => $file['name']];
// If a child folder is found look inside it
if ((int)$file['mimetype'] === $mimetype) {
$parents[] = $file['fileid'];
}
}
$result->closeCursor();
}
return $children;
}
}

View File

@@ -36,6 +36,7 @@ use OCP\Files\Storage\IDisableEncryptionStorage;
use OCP\Files\Storage\ILockingStorage;
use OCP\Files\Storage\ISharedStorage;
use OCP\Files\Storage\IStorage;
use OCP\IAppConfig;
use OCP\Lock\ILockingProvider;
use OCP\Server;
use OCP\Share\IShare;
@@ -279,7 +280,8 @@ class SharedStorage extends Jail implements LegacyISharedStorage, ISharedStorage
}
public function isSharable(string $path): bool {
if (Util::isSharingDisabledForUser() || !Share::isResharingAllowed()) {
$appConfig = \OCP\Server::get(IAppConfig::class);
if (Util::isSharingDisabledForUser() || !$appConfig->getValueBool('core', 'shareapi_allow_resharing', true)) {
return false;
}
return (bool)($this->getPermissions($path) & Constants::PERMISSION_SHARE);

View File

@@ -282,13 +282,11 @@ class ShareesAPIControllerTest extends TestCase {
$sharees->expects($this->any())
->method('isRemoteSharingAllowed')
->with($itemType)
->willReturn($remoteSharingEnabled);
$sharees->expects($this->any())
->method('isRemoteGroupSharingAllowed')
->with($itemType)
->willReturn($isRemoteGroupSharingEnabled);
$this->shareManager->expects($this->any())
@@ -385,25 +383,6 @@ class ShareesAPIControllerTest extends TestCase {
}
}
public static function dataIsRemoteSharingAllowed() {
return [
['file', true],
['folder', true],
['', false],
['contacts', false],
];
}
/**
*
* @param string $itemType
* @param bool $expected
*/
#[\PHPUnit\Framework\Attributes\DataProvider(methodName: 'dataIsRemoteSharingAllowed')]
public function testIsRemoteSharingAllowed($itemType, $expected): void {
$this->assertSame($expected, $this->invokePrivate($this->sharees, 'isRemoteSharingAllowed', [$itemType]));
}
public function testSearchSharingDisabled(): void {
$this->shareManager->expects($this->once())
->method('sharingDisabledForUser')

View File

@@ -1,36 +0,0 @@
<?php
/**
* SPDX-FileCopyrightText: 2017-2024 Nextcloud GmbH and Nextcloud contributors
* SPDX-FileCopyrightText: 2016 ownCloud, Inc.
* SPDX-License-Identifier: AGPL-3.0-only
*/
namespace OCA\Files_Sharing\Tests;
use OC\Files\Filesystem;
use OCA\Files_Sharing\Helper;
use OCP\IConfig;
use OCP\Server;
/**
* Class HelperTest
*/
#[\PHPUnit\Framework\Attributes\Group(name: 'DB')]
class HelperTest extends TestCase {
/**
* test set and get share folder
*/
public function testSetGetShareFolder(): void {
$this->assertSame('/', Helper::getShareFolder());
Helper::setShareFolder('/Shared/Folder');
$sharedFolder = Helper::getShareFolder();
$this->assertSame('/Shared/Folder', Helper::getShareFolder());
$this->assertTrue(Filesystem::is_dir($sharedFolder));
// cleanup
Server::get(IConfig::class)->deleteSystemValue('share_folder');
}
}

View File

@@ -9,7 +9,6 @@ namespace OCA\Files_Sharing\Tests;
use OC\Files\FileInfo;
use OC\Files\Filesystem;
use OCA\Files_Sharing\Helper;
use OCP\Constants;
use OCP\IConfig;
use OCP\IGroupManager;
@@ -128,7 +127,7 @@ class ShareTest extends TestCase {
Constants::PERMISSION_READ | Constants::PERMISSION_UPDATE | Constants::PERMISSION_SHARE
);
Helper::setShareFolder('/Shared/subfolder');
Server::get(IConfig::class)->setSystemValue('share_folder', '/Shared/subfolder');
$share = $this->share(
IShare::TYPE_USER,

View File

@@ -1538,6 +1538,7 @@
</DeprecatedInterface>
<DeprecatedMethod>
<code><![CDATA[Util::isSharingDisabledForUser()]]></code>
<code><![CDATA[getAppValue]]></code>
</DeprecatedMethod>
</file>
<file src="apps/files_sharing/lib/Helper.php">
@@ -1619,24 +1620,6 @@
<code><![CDATA[getUserValue]]></code>
</DeprecatedMethod>
</file>
<file src="apps/files_sharing/lib/ShareBackend/File.php">
<InternalClass>
<code><![CDATA[new View('/' . $shareWith . '/files')]]></code>
</InternalClass>
<InternalMethod>
<code><![CDATA[is_dir]]></code>
<code><![CDATA[is_dir]]></code>
<code><![CDATA[mkdir]]></code>
<code><![CDATA[new View('/' . $shareWith . '/files')]]></code>
</InternalMethod>
<InvalidArgument>
<code><![CDATA[$itemSource]]></code>
<code><![CDATA[$itemSource]]></code>
</InvalidArgument>
<MoreSpecificImplementedParamType>
<code><![CDATA[$shareWith]]></code>
</MoreSpecificImplementedParamType>
</file>
<file src="apps/files_sharing/lib/SharedMount.php">
<InvalidReturnType>
<code><![CDATA[bool]]></code>

View File

@@ -871,9 +871,6 @@ return array(
'OCP\\Share\\IShareProviderSupportsAccept' => $baseDir . '/lib/public/Share/IShareProviderSupportsAccept.php',
'OCP\\Share\\IShareProviderSupportsAllSharesInFolder' => $baseDir . '/lib/public/Share/IShareProviderSupportsAllSharesInFolder.php',
'OCP\\Share\\IShareProviderWithNotification' => $baseDir . '/lib/public/Share/IShareProviderWithNotification.php',
'OCP\\Share_Backend' => $baseDir . '/lib/public/Share_Backend.php',
'OCP\\Share_Backend_Collection' => $baseDir . '/lib/public/Share_Backend_Collection.php',
'OCP\\Share_Backend_File_Dependent' => $baseDir . '/lib/public/Share_Backend_File_Dependent.php',
'OCP\\Snowflake\\ISnowflakeDecoder' => $baseDir . '/lib/public/Snowflake/ISnowflakeDecoder.php',
'OCP\\Snowflake\\ISnowflakeGenerator' => $baseDir . '/lib/public/Snowflake/ISnowflakeGenerator.php',
'OCP\\Snowflake\\Snowflake' => $baseDir . '/lib/public/Snowflake/Snowflake.php',
@@ -2187,7 +2184,6 @@ return array(
'OC\\Share20\\UserDeletedListener' => $baseDir . '/lib/private/Share20/UserDeletedListener.php',
'OC\\Share20\\UserRemovedListener' => $baseDir . '/lib/private/Share20/UserRemovedListener.php',
'OC\\Share\\Constants' => $baseDir . '/lib/private/Share/Constants.php',
'OC\\Share\\Share' => $baseDir . '/lib/private/Share/Share.php',
'OC\\Snowflake\\APCuSequence' => $baseDir . '/lib/private/Snowflake/APCuSequence.php',
'OC\\Snowflake\\FileSequence' => $baseDir . '/lib/private/Snowflake/FileSequence.php',
'OC\\Snowflake\\ISequence' => $baseDir . '/lib/private/Snowflake/ISequence.php',

View File

@@ -912,9 +912,6 @@ class ComposerStaticInit749170dad3f5e7f9ca158f5a9f04f6a2
'OCP\\Share\\IShareProviderSupportsAccept' => __DIR__ . '/../../..' . '/lib/public/Share/IShareProviderSupportsAccept.php',
'OCP\\Share\\IShareProviderSupportsAllSharesInFolder' => __DIR__ . '/../../..' . '/lib/public/Share/IShareProviderSupportsAllSharesInFolder.php',
'OCP\\Share\\IShareProviderWithNotification' => __DIR__ . '/../../..' . '/lib/public/Share/IShareProviderWithNotification.php',
'OCP\\Share_Backend' => __DIR__ . '/../../..' . '/lib/public/Share_Backend.php',
'OCP\\Share_Backend_Collection' => __DIR__ . '/../../..' . '/lib/public/Share_Backend_Collection.php',
'OCP\\Share_Backend_File_Dependent' => __DIR__ . '/../../..' . '/lib/public/Share_Backend_File_Dependent.php',
'OCP\\Snowflake\\ISnowflakeDecoder' => __DIR__ . '/../../..' . '/lib/public/Snowflake/ISnowflakeDecoder.php',
'OCP\\Snowflake\\ISnowflakeGenerator' => __DIR__ . '/../../..' . '/lib/public/Snowflake/ISnowflakeGenerator.php',
'OCP\\Snowflake\\Snowflake' => __DIR__ . '/../../..' . '/lib/public/Snowflake/Snowflake.php',
@@ -2228,7 +2225,6 @@ class ComposerStaticInit749170dad3f5e7f9ca158f5a9f04f6a2
'OC\\Share20\\UserDeletedListener' => __DIR__ . '/../../..' . '/lib/private/Share20/UserDeletedListener.php',
'OC\\Share20\\UserRemovedListener' => __DIR__ . '/../../..' . '/lib/private/Share20/UserRemovedListener.php',
'OC\\Share\\Constants' => __DIR__ . '/../../..' . '/lib/private/Share/Constants.php',
'OC\\Share\\Share' => __DIR__ . '/../../..' . '/lib/private/Share/Share.php',
'OC\\Snowflake\\APCuSequence' => __DIR__ . '/../../..' . '/lib/private/Snowflake/APCuSequence.php',
'OC\\Snowflake\\FileSequence' => __DIR__ . '/../../..' . '/lib/private/Snowflake/FileSequence.php',
'OC\\Snowflake\\ISequence' => __DIR__ . '/../../..' . '/lib/private/Snowflake/ISequence.php',

View File

@@ -20,7 +20,6 @@ use OC\Files\Storage\Wrapper\PermissionsMask;
use OC\Files\Storage\Wrapper\Quota;
use OC\Lockdown\Filesystem\NullStorage;
use OC\ServerNotAvailableException;
use OC\Share\Share;
use OC\Share20\ShareDisableChecker;
use OC_Hook;
use OCA\Files_External\Config\ExternalMountPoint;
@@ -167,7 +166,7 @@ class SetupManager implements ISetupManager {
return $storage;
});
$reSharingEnabled = Share::isResharingAllowed();
$reSharingEnabled = $this->config->getAppValue('core', 'shareapi_allow_resharing', 'yes') === 'yes';
$user = $this->userSession->getUser();
$sharingEnabledForUser = $user ? !$this->shareDisableChecker->sharingDisabledForUser($user->getUID()) : true;
Filesystem::addStorageWrapper(

View File

@@ -10,20 +10,8 @@ declare(strict_types=1);
namespace OC\Share;
class Constants {
public const FORMAT_NONE = -1;
public const FORMAT_STATUSES = -2;
public const FORMAT_SOURCES = -3; // ToDo Check if it is still in use otherwise remove it
public const RESPONSE_FORMAT = 'json'; // default response format for ocs calls
public const MIN_TOKEN_LENGTH = 6; // 19,770,609,664 different possible variations
public const DEFAULT_TOKEN_LENGTH = 15; // 54,960,434,128,018,667,122,720,768 different possible variations
public const MAX_TOKEN_LENGTH = 32; // 8,167,835,760,036,914,488,254,418,108,462,708,901,695,678,621,570,564,096 different possible variations
public const TOKEN_LENGTH = self::DEFAULT_TOKEN_LENGTH; // old (oc7) length is 32, keep token length in db at least that for compatibility
protected static $shareTypeUserAndGroups = -1;
protected static $shareTypeGroupUserUnique = 2;
protected static $backends = [];
protected static $backendTypes = [];
protected static $isResharingAllowed;
}

View File

@@ -1,184 +0,0 @@
<?php
/**
* 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\IConfig;
use OCP\Server;
use OCP\Share_Backend;
use OCP\Util;
use Psr\Log\LoggerInterface;
/**
* This class provides the ability for apps to share their content between users.
* Apps must create a backend class that implements OCP\Share_Backend and register it with this class.
*
* It provides the following hooks:
* - post_shared
*/
class Share extends Constants {
/** CRUDS permissions (Create, Read, Update, Delete, Share) using a bitmask
* Construct permissions for share() and setPermissions with Or (|) e.g.
* Give user read and update permissions: PERMISSION_READ | PERMISSION_UPDATE
*
* Check if permission is granted with And (&) e.g. Check if delete is
* granted: if ($permissions & PERMISSION_DELETE)
*
* Remove permissions with And (&) and Not (~) e.g. Remove the update
* permission: $permissions &= ~PERMISSION_UPDATE
*
* Apps are required to handle permissions on their own, this class only
* stores and manages the permissions of shares
*
* @see lib/public/Constants.php
*/
/**
* Register a sharing backend class that implements OCP\Share_Backend for an item type
*
* @param string $itemType Item type
* @param string $class Backend class
* @param string $collectionOf (optional) Depends on item type
* @param array $supportedFileExtensions (optional) List of supported file extensions if this item type depends on files
* @return boolean true if backend is registered or false if error
*/
public static function registerBackend($itemType, $class, $collectionOf = null, $supportedFileExtensions = null) {
if (Server::get(IConfig::class)->getAppValue('core', 'shareapi_enabled', 'yes') == 'yes') {
if (!isset(self::$backendTypes[$itemType])) {
self::$backendTypes[$itemType] = [
'class' => $class,
'collectionOf' => $collectionOf,
'supportedFileExtensions' => $supportedFileExtensions
];
return true;
}
Server::get(LoggerInterface::class)->warning(
'Sharing backend ' . $class . ' not registered, ' . self::$backendTypes[$itemType]['class']
. ' is already registered for ' . $itemType,
['app' => 'files_sharing']);
}
return false;
}
/**
* Get the backend class for the specified item type
*
* @param string $itemType
* @return Share_Backend
* @throws \Exception
*/
public static function getBackend($itemType) {
$l = Util::getL10N('lib');
$logger = Server::get(LoggerInterface::class);
if (isset(self::$backends[$itemType])) {
return self::$backends[$itemType];
} elseif (isset(self::$backendTypes[$itemType]['class'])) {
$class = self::$backendTypes[$itemType]['class'];
if (class_exists($class)) {
self::$backends[$itemType] = new $class;
if (!(self::$backends[$itemType] instanceof Share_Backend)) {
$message = 'Sharing backend %s must implement the interface OCP\Share_Backend';
$message_t = $l->t('Sharing backend %s must implement the interface OCP\Share_Backend', [$class]);
$logger->error(sprintf($message, $class), ['app' => 'OCP\Share']);
throw new \Exception($message_t);
}
return self::$backends[$itemType];
} else {
$message = 'Sharing backend %s not found';
$message_t = $l->t('Sharing backend %s not found', [$class]);
$logger->error(sprintf($message, $class), ['app' => 'OCP\Share']);
throw new \Exception($message_t);
}
}
$message = 'Sharing backend for %s not found';
$message_t = $l->t('Sharing backend for %s not found', [$itemType]);
$logger->error(sprintf($message, $itemType), ['app' => 'OCP\Share']);
throw new \Exception($message_t);
}
/**
* Check if resharing is allowed
*
* @return boolean true if allowed or false
*
* Resharing is allowed by default if not configured
*/
public static function isResharingAllowed() {
if (!isset(self::$isResharingAllowed)) {
if (Server::get(IConfig::class)->getAppValue('core', 'shareapi_allow_resharing', 'yes') == 'yes') {
self::$isResharingAllowed = true;
} else {
self::$isResharingAllowed = false;
}
}
return self::$isResharingAllowed;
}
/**
* group items with link to the same source
*
* @param array $items
* @param string $itemType
* @return array of grouped items
*/
protected static function groupItems($items, $itemType) {
$fileSharing = $itemType === 'file' || $itemType === 'folder';
$result = [];
foreach ($items as $item) {
$grouped = false;
foreach ($result as $key => $r) {
// for file/folder shares we need to compare file_source, otherwise we compare item_source
// only group shares if they already point to the same target, otherwise the file where shared
// before grouping of shares was added. In this case we don't group them to avoid confusions
if (($fileSharing && $item['file_source'] === $r['file_source'] && $item['file_target'] === $r['file_target'])
|| (!$fileSharing && $item['item_source'] === $r['item_source'] && $item['item_target'] === $r['item_target'])) {
// add the first item to the list of grouped shares
if (!isset($result[$key]['grouped'])) {
$result[$key]['grouped'][] = $result[$key];
}
$result[$key]['permissions'] = (int)$item['permissions'] | (int)$r['permissions'];
$result[$key]['grouped'][] = $item;
$grouped = true;
break;
}
}
if (!$grouped) {
$result[] = $item;
}
}
return $result;
}
/**
* remove protocol from URL
*
* @param string $url
* @return string
*/
public static function removeProtocolFromUrl($url) {
if (str_starts_with($url, 'https://')) {
return substr($url, strlen('https://'));
} elseif (str_starts_with($url, 'http://')) {
return substr($url, strlen('http://'));
}
return $url;
}
/**
* @return int
*/
public static function getExpireInterval() {
return (int)Server::get(IConfig::class)->getAppValue('core', 'shareapi_expire_after_n_days', '7');
}
}

View File

@@ -19,7 +19,6 @@ use OCP\IDBConnection;
use OCP\ITags;
use OCP\IUserSession;
use OCP\Server;
use OCP\Share_Backend;
use OCP\Util;
use Psr\Log\LoggerInterface;
@@ -30,23 +29,12 @@ class Tags implements ITags {
private static array $relations = [];
private array $tags = [];
/**
* Are we including tags for shared items?
*/
private bool $includeShared = false;
/**
* The current user, plus any owners of the items shared with the current
* user, if $this->includeShared === true.
*/
private array $owners = [];
/**
* The sharing backend for objects of $this->type. Required if
* $this->includeShared === true to determine ownership of items.
*/
private ?Share_Backend $backend = null;
public const TAG_TABLE = 'vcategory';
public const RELATION_TABLE = 'vcategory_to_object';

View File

@@ -12,7 +12,6 @@ use OC\Authentication\Token\IProvider;
use OC\CapabilitiesManager;
use OC\Core\AppInfo\ConfigLexicon;
use OC\Files\FilenameValidator;
use OC\Share\Share;
use OCA\Provisioning_API\Controller\AUserDataOCSController;
use OCP\App\AppPathNotFoundException;
use OCP\App\IAppManager;
@@ -247,7 +246,7 @@ class JSConfigHelper {
'enforcePasswordForPublicLink' => Util::isPublicLinkPasswordRequired(),
'enableLinkPasswordByDefault' => $enableLinkPasswordByDefault,
'sharingDisabledForUser' => $shareManager->sharingDisabledForUser($uid),
'resharingAllowed' => Share::isResharingAllowed(),
'resharingAllowed' => $this->appConfig->getValueBool('core', 'shareapi_allow_resharing', true),
'remoteShareAllowed' => $outgoingServer2serverShareEnabled,
'federatedCloudShareDoc' => $this->urlGenerator->linkToDocs('user-sharing-federated'),
'allowGroupSharing' => $shareManager->allowGroupSharing(),

View File

@@ -1,78 +0,0 @@
<?php
/**
* SPDX-FileCopyrightText: 2016-2024 Nextcloud GmbH and Nextcloud contributors
* SPDX-FileCopyrightText: 2016 ownCloud, Inc.
* SPDX-License-Identifier: AGPL-3.0-only
*/
// use OCP namespace for all classes that are considered public.
// This means that they should be used by apps instead of the internal Nextcloud classes
namespace OCP;
/**
* Interface that apps must implement to share content.
* @since 5.0.0
*/
interface Share_Backend {
/**
* Check if this $itemSource exist for the user
* @param string $itemSource
* @param string $uidOwner Owner of the item
* @return boolean|null Source
*
* Return false if the item does not exist for the user
* @since 5.0.0
*/
public function isValidSource($itemSource, $uidOwner);
/**
* Get a unique name of the item for the specified user
* @param string $itemSource
* @param string|false $shareWith User the item is being shared with
* @return string Target name
*
* This function needs to verify that the user does not already have an item with this name.
* If it does generate a new name e.g. name_#
* @since 5.0.0
* @deprecated 31.0.0
*/
public function generateTarget($itemSource, $shareWith);
/**
* Converts the shared item sources back into the item in the specified format
* @param array $items Shared items
* @param int $format
* @return array
*
* The items array is a 3-dimensional array with the item_source as the
* first key and the share id as the second key to an array with the share
* info.
*
* The key/value pairs included in the share info depend on the function originally called:
* If called by getItem(s)Shared: id, item_type, item, item_source,
* share_type, share_with, permissions, stime, file_source
*
* If called by getItem(s)SharedWith: id, item_type, item, item_source,
* item_target, share_type, share_with, permissions, stime, file_source,
* file_target
*
* This function allows the backend to control the output of shared items with custom formats.
* It is only called through calls to the public getItem(s)Shared(With) functions.
* @since 5.0.0
*/
public function formatItems($items, $format, $parameters = null);
/**
* Check if a given share type is allowed by the back-end
*
* @param int $shareType share type
* @return boolean
*
* The back-end can enable/disable specific share types. Just return true if
* the back-end doesn't provide any specific settings for it and want to allow
* all share types defined by the share API
* @since 8.0.0
*/
public function isShareTypeAllowed($shareType);
}

View File

@@ -1,26 +0,0 @@
<?php
/**
* SPDX-FileCopyrightText: 2016-2024 Nextcloud GmbH and Nextcloud contributors
* SPDX-FileCopyrightText: 2016 ownCloud, Inc.
* SPDX-License-Identifier: AGPL-3.0-only
*/
// use OCP namespace for all classes that are considered public.
// This means that they should be used by apps instead of the internal Nextcloud classes
namespace OCP;
/**
* Interface for collections of items implemented by another share backend.
* Extends the Share_Backend interface.
* @since 5.0.0
*/
interface Share_Backend_Collection extends Share_Backend {
/**
* Get the sources of the children of the item
* @param string $itemSource
* @return array Returns an array of children each inside an array with the keys: source, target, and file_path if applicable
* @since 5.0.0
*/
public function getChildren($itemSource);
}

View File

@@ -1,27 +0,0 @@
<?php
/**
* SPDX-FileCopyrightText: 2016-2024 Nextcloud GmbH and Nextcloud contributors
* SPDX-FileCopyrightText: 2016 ownCloud, Inc.
* SPDX-License-Identifier: AGPL-3.0-only
*/
// use OCP namespace for all classes that are considered public.
// This means that they should be used by apps instead of the internal Nextcloud classes
namespace OCP;
/**
* Interface for share backends that share content that is dependent on files.
* Extends the Share_Backend interface.
* @since 5.0.0
*/
interface Share_Backend_File_Dependent extends Share_Backend {
/**
* Get the file path of the item
* @param string $itemSource
* @param string $uidOwner User that is the owner of shared item
* @return string|false
* @since 5.0.0
*/
public function getFilePath($itemSource, $uidOwner);
}

View File

@@ -10,7 +10,6 @@ namespace Test\Files;
use OC\Files\Filesystem;
use OC\Files\Utils\Scanner;
use OC\Share\Share;
use OCA\Files_Sharing\AppInfo\Application;
use OCP\EventDispatcher\IEventDispatcher;
use OCP\IConfig;
@@ -44,9 +43,6 @@ class EtagTest extends \Test\TestCase {
// init files sharing
new Application();
Share::registerBackend('file', 'OCA\Files_Sharing\ShareBackend\File');
Share::registerBackend('folder', 'OCA\Files_Sharing\ShareBackend\Folder', 'file');
$config = Server::get(IConfig::class);
$this->datadir = $config->getSystemValueString('datadirectory');
$this->tmpDir = Server::get(ITempManager::class)->getTemporaryFolder();

View File

@@ -1,86 +0,0 @@
<?php
/**
* SPDX-FileCopyrightText: 2017-2024 Nextcloud GmbH and Nextcloud contributors
* SPDX-FileCopyrightText: 2016 ownCloud, Inc.
* SPDX-License-Identifier: AGPL-3.0-or-later
*/
namespace Test\Share;
use OC\Share20\Manager;
use OCP\Server;
use OCP\Share\IShare;
use OCP\Share_Backend;
class Backend implements Share_Backend {
public const FORMAT_SOURCE = 0;
public const FORMAT_TARGET = 1;
public const FORMAT_PERMISSIONS = 2;
private $testItem1 = 'test.txt';
private $testItem2 = 'share.txt';
private $testId = 1;
public function isValidSource($itemSource, $uidOwner) {
if ($itemSource == $this->testItem1 || $itemSource == $this->testItem2 || $itemSource == 1) {
return true;
}
}
public function generateTarget($itemSource, $shareWith, $exclude = null) {
// Always make target be test.txt to cause conflicts
if (substr($itemSource, 0, strlen('test')) !== 'test') {
$target = 'test.txt';
} else {
$target = $itemSource;
}
$shareManager = Server::get(Manager::class);
$shares = array_merge(
$shareManager->getSharedWith($shareWith, IShare::TYPE_USER),
$shareManager->getSharedWith($shareWith, IShare::TYPE_GROUP),
);
$knownTargets = [];
foreach ($shares as $share) {
$knownTargets[] = $share['item_target'];
}
if (in_array($target, $knownTargets)) {
$pos = strrpos($target, '.');
$name = substr($target, 0, $pos);
$ext = substr($target, $pos);
$append = '';
$i = 1;
while (in_array($name . $append . $ext, $knownTargets)) {
$append = $i;
$i++;
}
$target = $name . $append . $ext;
}
return $target;
}
public function formatItems($items, $format, $parameters = null) {
$testItems = [];
foreach ($items as $item) {
if ($format === self::FORMAT_SOURCE) {
$testItems[] = $item['item_source'];
} elseif ($format === self::FORMAT_TARGET) {
$testItems[] = $item['item_target'];
} elseif ($format === self::FORMAT_PERMISSIONS) {
$testItems[] = $item['permissions'];
}
}
return $testItems;
}
public function isShareTypeAllowed($shareType) {
return true;
}
}

View File

@@ -1,235 +0,0 @@
<?php
/**
* SPDX-FileCopyrightText: 2016-2024 Nextcloud GmbH and Nextcloud contributors
* SPDX-FileCopyrightText: 2016 ownCloud, Inc.
* SPDX-License-Identifier: AGPL-3.0-or-later
*/
namespace Test\Share;
use OC\Share\Share;
use OC\SystemConfig;
use OCP\Constants;
use OCP\IConfig;
use OCP\IDBConnection;
use OCP\IGroup;
use OCP\IGroupManager;
use OCP\IUser;
use OCP\IUserManager;
use OCP\Server;
/**
* Class Test_Share
*/
#[\PHPUnit\Framework\Attributes\Group('DB')]
class ShareTest extends \Test\TestCase {
protected $itemType;
protected IUser $user1;
protected IUser $user2;
protected IUser $user3;
protected IUser $user4;
protected IUser $user5;
protected IUser $user6;
protected IUser $groupAndUser_user;
protected IGroup $group1;
protected IGroup $group2;
protected IGroup $groupAndUser_group;
protected string $resharing;
protected string $dateInFuture;
protected string $dateInPast;
protected IGroupManager $groupManager;
protected IUserManager $userManager;
private IDBConnection $connection;
protected function setUp(): void {
parent::setUp();
$this->groupManager = Server::get(IGroupManager::class);
$this->userManager = Server::get(IUserManager::class);
$this->userManager->clearBackends();
$this->userManager->registerBackend(new \Test\Util\User\Dummy());
$this->user1 = $this->userManager->createUser($this->getUniqueID('user1_'), 'pass');
$this->user2 = $this->userManager->createUser($this->getUniqueID('user2_'), 'pass');
$this->user3 = $this->userManager->createUser($this->getUniqueID('user3_'), 'pass');
$this->user4 = $this->userManager->createUser($this->getUniqueID('user4_'), 'pass');
$this->user5 = $this->userManager->createUser($this->getUniqueID('user5_'), 'pass');
$this->user6 = $this->userManager->createUser($this->getUniqueID('user6_'), 'pass');
$groupAndUserId = $this->getUniqueID('groupAndUser_');
$this->groupAndUser_user = $this->userManager->createUser($groupAndUserId, 'pass');
\OC_User::setUserId($this->user1->getUID());
$this->groupManager->clearBackends();
$this->groupManager->addBackend(new \Test\Util\Group\Dummy());
$this->group1 = $this->groupManager->createGroup($this->getUniqueID('group1_'));
$this->group2 = $this->groupManager->createGroup($this->getUniqueID('group2_'));
$this->groupAndUser_group = $this->groupManager->createGroup($groupAndUserId);
$this->connection = Server::get(IDBConnection::class);
$this->group1->addUser($this->user1);
$this->group1->addUser($this->user2);
$this->group1->addUser($this->user3);
$this->group2->addUser($this->user2);
$this->group2->addUser($this->user4);
$this->groupAndUser_group->addUser($this->user2);
$this->groupAndUser_group->addUser($this->user3);
Share::registerBackend('test', 'Test\Share\Backend');
\OC_Hook::clear('OCP\\Share');
\OC::registerShareHooks(Server::get(SystemConfig::class));
$this->resharing = Server::get(IConfig::class)->getAppValue('core', 'shareapi_allow_resharing', 'yes');
Server::get(IConfig::class)->setAppValue('core', 'shareapi_allow_resharing', 'yes');
// 20 Minutes in the past, 20 minutes in the future.
$now = time();
$dateFormat = 'Y-m-d H:i:s';
$this->dateInPast = date($dateFormat, $now - 20 * 60);
$this->dateInFuture = date($dateFormat, $now + 20 * 60);
}
protected function tearDown(): void {
$query = $this->connection->getQueryBuilder();
$query->delete('share')->andWhere($query->expr()->eq('item_type', $query->createNamedParameter('test')));
$query->executeStatement();
Server::get(IConfig::class)->setAppValue('core', 'shareapi_allow_resharing', $this->resharing);
$this->user1->delete();
$this->user2->delete();
$this->user3->delete();
$this->user4->delete();
$this->user5->delete();
$this->user6->delete();
$this->groupAndUser_user->delete();
$this->group1->delete();
$this->group2->delete();
$this->groupAndUser_group->delete();
$this->logout();
parent::tearDown();
}
public function verifyResult($result, $expected) {
foreach ($result as $r) {
if (in_array($r['item_target'], $expected)) {
$key = array_search($r['item_target'], $expected);
unset($expected[$key]);
}
}
$this->assertEmpty($expected, 'did not found all expected values');
}
/**
* @param string $url
* @param string $expectedResult
*/
#[\PHPUnit\Framework\Attributes\DataProvider('urls')]
public function testRemoveProtocolFromUrl($url, $expectedResult): void {
$share = new Share();
$result = self::invokePrivate($share, 'removeProtocolFromUrl', [$url]);
$this->assertSame($expectedResult, $result);
}
public static function urls(): array {
return [
['http://owncloud.org', 'owncloud.org'],
['https://owncloud.org', 'owncloud.org'],
['owncloud.org', 'owncloud.org'],
];
}
/**
* @param array $ungrouped
* @param array $grouped
*/
#[\PHPUnit\Framework\Attributes\DataProvider('dataProviderTestGroupItems')]
public function testGroupItems($ungrouped, $grouped): void {
$result = DummyShareClass::groupItemsTest($ungrouped);
$this->compareArrays($grouped, $result);
}
public function compareArrays($result, $expectedResult) {
foreach ($expectedResult as $key => $value) {
if (is_array($value)) {
$this->compareArrays($result[$key], $value);
} else {
$this->assertSame($value, $result[$key]);
}
}
}
public static function dataProviderTestGroupItems(): array {
return [
// one array with one share
[
[ // input
['item_source' => 1, 'permissions' => Constants::PERMISSION_ALL, 'item_target' => 't1']],
[ // expected result
['item_source' => 1, 'permissions' => Constants::PERMISSION_ALL, 'item_target' => 't1']]],
// two shares both point to the same source
[
[ // input
['item_source' => 1, 'permissions' => Constants::PERMISSION_READ, 'item_target' => 't1'],
['item_source' => 1, 'permissions' => Constants::PERMISSION_UPDATE, 'item_target' => 't1'],
],
[ // expected result
['item_source' => 1, 'permissions' => Constants::PERMISSION_READ | Constants::PERMISSION_UPDATE, 'item_target' => 't1',
'grouped' => [
['item_source' => 1, 'permissions' => Constants::PERMISSION_READ, 'item_target' => 't1'],
['item_source' => 1, 'permissions' => Constants::PERMISSION_UPDATE, 'item_target' => 't1'],
]
],
]
],
// two shares both point to the same source but with different targets
[
[ // input
['item_source' => 1, 'permissions' => Constants::PERMISSION_READ, 'item_target' => 't1'],
['item_source' => 1, 'permissions' => Constants::PERMISSION_UPDATE, 'item_target' => 't2'],
],
[ // expected result
['item_source' => 1, 'permissions' => Constants::PERMISSION_READ, 'item_target' => 't1'],
['item_source' => 1, 'permissions' => Constants::PERMISSION_UPDATE, 'item_target' => 't2'],
]
],
// three shares two point to the same source
[
[ // input
['item_source' => 1, 'permissions' => Constants::PERMISSION_READ, 'item_target' => 't1'],
['item_source' => 2, 'permissions' => Constants::PERMISSION_CREATE, 'item_target' => 't2'],
['item_source' => 1, 'permissions' => Constants::PERMISSION_UPDATE, 'item_target' => 't1'],
],
[ // expected result
['item_source' => 1, 'permissions' => Constants::PERMISSION_READ | Constants::PERMISSION_UPDATE, 'item_target' => 't1',
'grouped' => [
['item_source' => 1, 'permissions' => Constants::PERMISSION_READ, 'item_target' => 't1'],
['item_source' => 1, 'permissions' => Constants::PERMISSION_UPDATE, 'item_target' => 't1'],
]
],
['item_source' => 2, 'permissions' => Constants::PERMISSION_CREATE, 'item_target' => 't2'],
]
],
];
}
}
class DummyShareClass extends Share {
public static function groupItemsTest($items) {
return parent::groupItems($items, 'test');
}
}
class DummyHookListener {
public static $shareType = null;
public static function listen($params) {
self::$shareType = $params['shareType'];
}
}