aboutsummaryrefslogtreecommitdiffstats
path: root/lib/private/Share
diff options
context:
space:
mode:
Diffstat (limited to 'lib/private/Share')
-rw-r--r--lib/private/Share/Constants.php27
-rw-r--r--lib/private/Share/Helper.php138
-rw-r--r--lib/private/Share/Share.php180
3 files changed, 345 insertions, 0 deletions
diff --git a/lib/private/Share/Constants.php b/lib/private/Share/Constants.php
new file mode 100644
index 00000000000..c55caee6f0a
--- /dev/null
+++ b/lib/private/Share/Constants.php
@@ -0,0 +1,27 @@
+<?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;
+
+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;
+}
diff --git a/lib/private/Share/Helper.php b/lib/private/Share/Helper.php
new file mode 100644
index 00000000000..d53f9d6ed94
--- /dev/null
+++ b/lib/private/Share/Helper.php
@@ -0,0 +1,138 @@
+<?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;
+
+class Helper extends \OC\Share\Constants {
+ /**
+ * get default expire settings defined by the admin
+ * @return array contains 'defaultExpireDateSet', 'enforceExpireDate', 'expireAfterDays'
+ */
+ public static function getDefaultExpireSetting() {
+ $config = \OC::$server->getConfig();
+
+ $defaultExpireSettings = ['defaultExpireDateSet' => false];
+
+ // get default expire settings
+ $defaultExpireDate = $config->getAppValue('core', 'shareapi_default_expire_date', 'no');
+ if ($defaultExpireDate === 'yes') {
+ $enforceExpireDate = $config->getAppValue('core', 'shareapi_enforce_expire_date', 'no');
+ $defaultExpireSettings['defaultExpireDateSet'] = true;
+ $defaultExpireSettings['expireAfterDays'] = (int)$config->getAppValue('core', 'shareapi_expire_after_n_days', '7');
+ $defaultExpireSettings['enforceExpireDate'] = $enforceExpireDate === 'yes';
+ }
+
+ return $defaultExpireSettings;
+ }
+
+ public static function calcExpireDate() {
+ $expireAfter = \OC\Share\Share::getExpireInterval() * 24 * 60 * 60;
+ $expireAt = time() + $expireAfter;
+ $date = new \DateTime();
+ $date->setTimestamp($expireAt);
+ $date->setTime(0, 0, 0);
+ //$dateString = $date->format('Y-m-d') . ' 00:00:00';
+
+ return $date;
+ }
+
+ /**
+ * calculate expire date
+ * @param array $defaultExpireSettings contains 'defaultExpireDateSet', 'enforceExpireDate', 'expireAfterDays'
+ * @param int $creationTime timestamp when the share was created
+ * @param int $userExpireDate expire timestamp set by the user
+ * @return mixed integer timestamp or False
+ */
+ public static function calculateExpireDate($defaultExpireSettings, $creationTime, $userExpireDate = null) {
+ $expires = false;
+ $defaultExpires = null;
+
+ if (!empty($defaultExpireSettings['defaultExpireDateSet'])) {
+ $defaultExpires = $creationTime + $defaultExpireSettings['expireAfterDays'] * 86400;
+ }
+
+
+ if (isset($userExpireDate)) {
+ // if the admin decided to enforce the default expire date then we only take
+ // the user defined expire date of it is before the default expire date
+ if ($defaultExpires && !empty($defaultExpireSettings['enforceExpireDate'])) {
+ $expires = min($userExpireDate, $defaultExpires);
+ } else {
+ $expires = $userExpireDate;
+ }
+ } elseif ($defaultExpires && !empty($defaultExpireSettings['enforceExpireDate'])) {
+ $expires = $defaultExpires;
+ }
+
+ return $expires;
+ }
+
+ /**
+ * Strips away a potential file names and trailing slashes:
+ * - http://localhost
+ * - http://localhost/
+ * - http://localhost/index.php
+ * - http://localhost/index.php/s/{shareToken}
+ *
+ * all return: http://localhost
+ *
+ * @param string $remote
+ * @return string
+ */
+ protected static function fixRemoteURL($remote) {
+ $remote = str_replace('\\', '/', $remote);
+ if ($fileNamePosition = strpos($remote, '/index.php')) {
+ $remote = substr($remote, 0, $fileNamePosition);
+ }
+ $remote = rtrim($remote, '/');
+
+ return $remote;
+ }
+
+ /**
+ * check if two federated cloud IDs refer to the same user
+ *
+ * @param string $user1
+ * @param string $server1
+ * @param string $user2
+ * @param string $server2
+ * @return bool true if both users and servers are the same
+ */
+ public static function isSameUserOnSameServer($user1, $server1, $user2, $server2) {
+ $normalizedServer1 = strtolower(\OC\Share\Share::removeProtocolFromUrl($server1));
+ $normalizedServer2 = strtolower(\OC\Share\Share::removeProtocolFromUrl($server2));
+
+ if (rtrim($normalizedServer1, '/') === rtrim($normalizedServer2, '/')) {
+ // FIXME this should be a method in the user management instead
+ \OCP\Util::emitHook(
+ '\OCA\Files_Sharing\API\Server2Server',
+ 'preLoginNameUsedAsUserName',
+ ['uid' => &$user1]
+ );
+ \OCP\Util::emitHook(
+ '\OCA\Files_Sharing\API\Server2Server',
+ 'preLoginNameUsedAsUserName',
+ ['uid' => &$user2]
+ );
+
+ if ($user1 === $user2) {
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ public static function getTokenLength(): int {
+ $config = \OCP\Server::get(\OCP\IAppConfig::class);
+ $tokenLength = $config->getValueInt('core', 'shareapi_token_length', self::DEFAULT_TOKEN_LENGTH);
+ $tokenLength = $tokenLength ?: self::DEFAULT_TOKEN_LENGTH;
+
+ // Token length should be within the defined min and max limits
+ return max(self::MIN_TOKEN_LENGTH, min($tokenLength, self::MAX_TOKEN_LENGTH));
+ }
+}
diff --git a/lib/private/Share/Share.php b/lib/private/Share/Share.php
new file mode 100644
index 00000000000..1121d71e45f
--- /dev/null
+++ b/lib/private/Share/Share.php
@@ -0,0 +1,180 @@
+<?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 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 (\OC::$server->getConfig()->getAppValue('core', 'shareapi_enabled', 'yes') == 'yes') {
+ if (!isset(self::$backendTypes[$itemType])) {
+ self::$backendTypes[$itemType] = [
+ 'class' => $class,
+ 'collectionOf' => $collectionOf,
+ 'supportedFileExtensions' => $supportedFileExtensions
+ ];
+ return true;
+ }
+ \OC::$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 \OCP\Share_Backend
+ * @throws \Exception
+ */
+ public static function getBackend($itemType) {
+ $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'])) {
+ $class = self::$backendTypes[$itemType]['class'];
+ if (class_exists($class)) {
+ self::$backends[$itemType] = new $class;
+ if (!(self::$backends[$itemType] instanceof \OCP\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 (\OC::$server->getConfig()->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)\OC::$server->getConfig()->getAppValue('core', 'shareapi_expire_after_n_days', '7');
+ }
+}