@@ -176,7 +176,7 @@ |
||
| 176 | 176 | /** |
| 177 | 177 | * Create the circle share provider |
| 178 | 178 | * |
| 179 | - * @return FederatedShareProvider |
|
| 179 | + * @return null|\OCA\Circles\ShareByCircleProvider |
|
| 180 | 180 | * |
| 181 | 181 | * @suppress PhanUndeclaredClassMethod |
| 182 | 182 | */ |
@@ -43,233 +43,233 @@ |
||
| 43 | 43 | */ |
| 44 | 44 | class ProviderFactory implements IProviderFactory { |
| 45 | 45 | |
| 46 | - /** @var IServerContainer */ |
|
| 47 | - private $serverContainer; |
|
| 48 | - /** @var DefaultShareProvider */ |
|
| 49 | - private $defaultProvider = null; |
|
| 50 | - /** @var FederatedShareProvider */ |
|
| 51 | - private $federatedProvider = null; |
|
| 52 | - /** @var ShareByMailProvider */ |
|
| 53 | - private $shareByMailProvider; |
|
| 54 | - /** @var \OCA\Circles\ShareByCircleProvider */ |
|
| 55 | - private $shareByCircleProvider = null; |
|
| 56 | - /** @var bool */ |
|
| 57 | - private $circlesAreNotAvailable = false; |
|
| 58 | - |
|
| 59 | - /** |
|
| 60 | - * IProviderFactory constructor. |
|
| 61 | - * |
|
| 62 | - * @param IServerContainer $serverContainer |
|
| 63 | - */ |
|
| 64 | - public function __construct(IServerContainer $serverContainer) { |
|
| 65 | - $this->serverContainer = $serverContainer; |
|
| 66 | - } |
|
| 67 | - |
|
| 68 | - /** |
|
| 69 | - * Create the default share provider. |
|
| 70 | - * |
|
| 71 | - * @return DefaultShareProvider |
|
| 72 | - */ |
|
| 73 | - protected function defaultShareProvider() { |
|
| 74 | - if ($this->defaultProvider === null) { |
|
| 75 | - $this->defaultProvider = new DefaultShareProvider( |
|
| 76 | - $this->serverContainer->getDatabaseConnection(), |
|
| 77 | - $this->serverContainer->getUserManager(), |
|
| 78 | - $this->serverContainer->getGroupManager(), |
|
| 79 | - $this->serverContainer->getLazyRootFolder() |
|
| 80 | - ); |
|
| 81 | - } |
|
| 82 | - |
|
| 83 | - return $this->defaultProvider; |
|
| 84 | - } |
|
| 85 | - |
|
| 86 | - /** |
|
| 87 | - * Create the federated share provider |
|
| 88 | - * |
|
| 89 | - * @return FederatedShareProvider |
|
| 90 | - */ |
|
| 91 | - protected function federatedShareProvider() { |
|
| 92 | - if ($this->federatedProvider === null) { |
|
| 93 | - /* |
|
| 46 | + /** @var IServerContainer */ |
|
| 47 | + private $serverContainer; |
|
| 48 | + /** @var DefaultShareProvider */ |
|
| 49 | + private $defaultProvider = null; |
|
| 50 | + /** @var FederatedShareProvider */ |
|
| 51 | + private $federatedProvider = null; |
|
| 52 | + /** @var ShareByMailProvider */ |
|
| 53 | + private $shareByMailProvider; |
|
| 54 | + /** @var \OCA\Circles\ShareByCircleProvider */ |
|
| 55 | + private $shareByCircleProvider = null; |
|
| 56 | + /** @var bool */ |
|
| 57 | + private $circlesAreNotAvailable = false; |
|
| 58 | + |
|
| 59 | + /** |
|
| 60 | + * IProviderFactory constructor. |
|
| 61 | + * |
|
| 62 | + * @param IServerContainer $serverContainer |
|
| 63 | + */ |
|
| 64 | + public function __construct(IServerContainer $serverContainer) { |
|
| 65 | + $this->serverContainer = $serverContainer; |
|
| 66 | + } |
|
| 67 | + |
|
| 68 | + /** |
|
| 69 | + * Create the default share provider. |
|
| 70 | + * |
|
| 71 | + * @return DefaultShareProvider |
|
| 72 | + */ |
|
| 73 | + protected function defaultShareProvider() { |
|
| 74 | + if ($this->defaultProvider === null) { |
|
| 75 | + $this->defaultProvider = new DefaultShareProvider( |
|
| 76 | + $this->serverContainer->getDatabaseConnection(), |
|
| 77 | + $this->serverContainer->getUserManager(), |
|
| 78 | + $this->serverContainer->getGroupManager(), |
|
| 79 | + $this->serverContainer->getLazyRootFolder() |
|
| 80 | + ); |
|
| 81 | + } |
|
| 82 | + |
|
| 83 | + return $this->defaultProvider; |
|
| 84 | + } |
|
| 85 | + |
|
| 86 | + /** |
|
| 87 | + * Create the federated share provider |
|
| 88 | + * |
|
| 89 | + * @return FederatedShareProvider |
|
| 90 | + */ |
|
| 91 | + protected function federatedShareProvider() { |
|
| 92 | + if ($this->federatedProvider === null) { |
|
| 93 | + /* |
|
| 94 | 94 | * Check if the app is enabled |
| 95 | 95 | */ |
| 96 | - $appManager = $this->serverContainer->getAppManager(); |
|
| 97 | - if (!$appManager->isEnabledForUser('federatedfilesharing')) { |
|
| 98 | - return null; |
|
| 99 | - } |
|
| 96 | + $appManager = $this->serverContainer->getAppManager(); |
|
| 97 | + if (!$appManager->isEnabledForUser('federatedfilesharing')) { |
|
| 98 | + return null; |
|
| 99 | + } |
|
| 100 | 100 | |
| 101 | - /* |
|
| 101 | + /* |
|
| 102 | 102 | * TODO: add factory to federated sharing app |
| 103 | 103 | */ |
| 104 | - $l = $this->serverContainer->getL10N('federatedfilessharing'); |
|
| 105 | - $addressHandler = new AddressHandler( |
|
| 106 | - $this->serverContainer->getURLGenerator(), |
|
| 107 | - $l, |
|
| 108 | - $this->serverContainer->getCloudIdManager() |
|
| 109 | - ); |
|
| 110 | - $notifications = new Notifications( |
|
| 111 | - $addressHandler, |
|
| 112 | - $this->serverContainer->getHTTPClientService(), |
|
| 113 | - $this->serverContainer->query(\OCP\OCS\IDiscoveryService::class), |
|
| 114 | - $this->serverContainer->getJobList() |
|
| 115 | - ); |
|
| 116 | - $tokenHandler = new TokenHandler( |
|
| 117 | - $this->serverContainer->getSecureRandom() |
|
| 118 | - ); |
|
| 119 | - |
|
| 120 | - $this->federatedProvider = new FederatedShareProvider( |
|
| 121 | - $this->serverContainer->getDatabaseConnection(), |
|
| 122 | - $addressHandler, |
|
| 123 | - $notifications, |
|
| 124 | - $tokenHandler, |
|
| 125 | - $l, |
|
| 126 | - $this->serverContainer->getLogger(), |
|
| 127 | - $this->serverContainer->getLazyRootFolder(), |
|
| 128 | - $this->serverContainer->getConfig(), |
|
| 129 | - $this->serverContainer->getUserManager(), |
|
| 130 | - $this->serverContainer->getCloudIdManager(), |
|
| 131 | - $this->serverContainer->query(Config::class) |
|
| 132 | - ); |
|
| 133 | - } |
|
| 134 | - |
|
| 135 | - return $this->federatedProvider; |
|
| 136 | - } |
|
| 137 | - |
|
| 138 | - /** |
|
| 139 | - * Create the federated share provider |
|
| 140 | - * |
|
| 141 | - * @return ShareByMailProvider |
|
| 142 | - */ |
|
| 143 | - protected function getShareByMailProvider() { |
|
| 144 | - if ($this->shareByMailProvider === null) { |
|
| 145 | - /* |
|
| 104 | + $l = $this->serverContainer->getL10N('federatedfilessharing'); |
|
| 105 | + $addressHandler = new AddressHandler( |
|
| 106 | + $this->serverContainer->getURLGenerator(), |
|
| 107 | + $l, |
|
| 108 | + $this->serverContainer->getCloudIdManager() |
|
| 109 | + ); |
|
| 110 | + $notifications = new Notifications( |
|
| 111 | + $addressHandler, |
|
| 112 | + $this->serverContainer->getHTTPClientService(), |
|
| 113 | + $this->serverContainer->query(\OCP\OCS\IDiscoveryService::class), |
|
| 114 | + $this->serverContainer->getJobList() |
|
| 115 | + ); |
|
| 116 | + $tokenHandler = new TokenHandler( |
|
| 117 | + $this->serverContainer->getSecureRandom() |
|
| 118 | + ); |
|
| 119 | + |
|
| 120 | + $this->federatedProvider = new FederatedShareProvider( |
|
| 121 | + $this->serverContainer->getDatabaseConnection(), |
|
| 122 | + $addressHandler, |
|
| 123 | + $notifications, |
|
| 124 | + $tokenHandler, |
|
| 125 | + $l, |
|
| 126 | + $this->serverContainer->getLogger(), |
|
| 127 | + $this->serverContainer->getLazyRootFolder(), |
|
| 128 | + $this->serverContainer->getConfig(), |
|
| 129 | + $this->serverContainer->getUserManager(), |
|
| 130 | + $this->serverContainer->getCloudIdManager(), |
|
| 131 | + $this->serverContainer->query(Config::class) |
|
| 132 | + ); |
|
| 133 | + } |
|
| 134 | + |
|
| 135 | + return $this->federatedProvider; |
|
| 136 | + } |
|
| 137 | + |
|
| 138 | + /** |
|
| 139 | + * Create the federated share provider |
|
| 140 | + * |
|
| 141 | + * @return ShareByMailProvider |
|
| 142 | + */ |
|
| 143 | + protected function getShareByMailProvider() { |
|
| 144 | + if ($this->shareByMailProvider === null) { |
|
| 145 | + /* |
|
| 146 | 146 | * Check if the app is enabled |
| 147 | 147 | */ |
| 148 | - $appManager = $this->serverContainer->getAppManager(); |
|
| 149 | - if (!$appManager->isEnabledForUser('sharebymail')) { |
|
| 150 | - return null; |
|
| 151 | - } |
|
| 152 | - |
|
| 153 | - $settingsManager = new SettingsManager($this->serverContainer->getConfig()); |
|
| 154 | - |
|
| 155 | - $this->shareByMailProvider = new ShareByMailProvider( |
|
| 156 | - $this->serverContainer->getDatabaseConnection(), |
|
| 157 | - $this->serverContainer->getSecureRandom(), |
|
| 158 | - $this->serverContainer->getUserManager(), |
|
| 159 | - $this->serverContainer->getLazyRootFolder(), |
|
| 160 | - $this->serverContainer->getL10N('sharebymail'), |
|
| 161 | - $this->serverContainer->getLogger(), |
|
| 162 | - $this->serverContainer->getMailer(), |
|
| 163 | - $this->serverContainer->getURLGenerator(), |
|
| 164 | - $this->serverContainer->getActivityManager(), |
|
| 165 | - $settingsManager, |
|
| 166 | - $this->serverContainer->query(Defaults::class), |
|
| 167 | - $this->serverContainer->getHasher(), |
|
| 168 | - $this->serverContainer->query(CapabilitiesManager::class) |
|
| 169 | - ); |
|
| 170 | - } |
|
| 171 | - |
|
| 172 | - return $this->shareByMailProvider; |
|
| 173 | - } |
|
| 174 | - |
|
| 175 | - |
|
| 176 | - /** |
|
| 177 | - * Create the circle share provider |
|
| 178 | - * |
|
| 179 | - * @return FederatedShareProvider |
|
| 180 | - * |
|
| 181 | - * @suppress PhanUndeclaredClassMethod |
|
| 182 | - */ |
|
| 183 | - protected function getShareByCircleProvider() { |
|
| 184 | - |
|
| 185 | - if ($this->circlesAreNotAvailable) { |
|
| 186 | - return null; |
|
| 187 | - } |
|
| 188 | - |
|
| 189 | - if (!$this->serverContainer->getAppManager()->isEnabledForUser('circles') || |
|
| 190 | - !class_exists('\OCA\Circles\ShareByCircleProvider') |
|
| 191 | - ) { |
|
| 192 | - $this->circlesAreNotAvailable = true; |
|
| 193 | - return null; |
|
| 194 | - } |
|
| 195 | - |
|
| 196 | - if ($this->shareByCircleProvider === null) { |
|
| 197 | - |
|
| 198 | - $this->shareByCircleProvider = new \OCA\Circles\ShareByCircleProvider( |
|
| 199 | - $this->serverContainer->getDatabaseConnection(), |
|
| 200 | - $this->serverContainer->getSecureRandom(), |
|
| 201 | - $this->serverContainer->getUserManager(), |
|
| 202 | - $this->serverContainer->getLazyRootFolder(), |
|
| 203 | - $this->serverContainer->getL10N('circles'), |
|
| 204 | - $this->serverContainer->getLogger(), |
|
| 205 | - $this->serverContainer->getURLGenerator() |
|
| 206 | - ); |
|
| 207 | - } |
|
| 208 | - |
|
| 209 | - return $this->shareByCircleProvider; |
|
| 210 | - } |
|
| 211 | - |
|
| 212 | - |
|
| 213 | - /** |
|
| 214 | - * @inheritdoc |
|
| 215 | - */ |
|
| 216 | - public function getProvider($id) { |
|
| 217 | - $provider = null; |
|
| 218 | - if ($id === 'ocinternal') { |
|
| 219 | - $provider = $this->defaultShareProvider(); |
|
| 220 | - } else if ($id === 'ocFederatedSharing') { |
|
| 221 | - $provider = $this->federatedShareProvider(); |
|
| 222 | - } else if ($id === 'ocMailShare') { |
|
| 223 | - $provider = $this->getShareByMailProvider(); |
|
| 224 | - } else if ($id === 'ocCircleShare') { |
|
| 225 | - $provider = $this->getShareByCircleProvider(); |
|
| 226 | - } |
|
| 227 | - |
|
| 228 | - if ($provider === null) { |
|
| 229 | - throw new ProviderException('No provider with id .' . $id . ' found.'); |
|
| 230 | - } |
|
| 231 | - |
|
| 232 | - return $provider; |
|
| 233 | - } |
|
| 234 | - |
|
| 235 | - /** |
|
| 236 | - * @inheritdoc |
|
| 237 | - */ |
|
| 238 | - public function getProviderForType($shareType) { |
|
| 239 | - $provider = null; |
|
| 240 | - |
|
| 241 | - if ($shareType === \OCP\Share::SHARE_TYPE_USER || |
|
| 242 | - $shareType === \OCP\Share::SHARE_TYPE_GROUP || |
|
| 243 | - $shareType === \OCP\Share::SHARE_TYPE_LINK |
|
| 244 | - ) { |
|
| 245 | - $provider = $this->defaultShareProvider(); |
|
| 246 | - } else if ($shareType === \OCP\Share::SHARE_TYPE_REMOTE) { |
|
| 247 | - $provider = $this->federatedShareProvider(); |
|
| 248 | - } else if ($shareType === \OCP\Share::SHARE_TYPE_EMAIL) { |
|
| 249 | - $provider = $this->getShareByMailProvider(); |
|
| 250 | - } else if ($shareType === \OCP\Share::SHARE_TYPE_CIRCLE) { |
|
| 251 | - $provider = $this->getShareByCircleProvider(); |
|
| 252 | - } |
|
| 253 | - |
|
| 254 | - |
|
| 255 | - if ($provider === null) { |
|
| 256 | - throw new ProviderException('No share provider for share type ' . $shareType); |
|
| 257 | - } |
|
| 258 | - |
|
| 259 | - return $provider; |
|
| 260 | - } |
|
| 261 | - |
|
| 262 | - public function getAllProviders() { |
|
| 263 | - $shares = [$this->defaultShareProvider(), $this->federatedShareProvider()]; |
|
| 264 | - $shareByMail = $this->getShareByMailProvider(); |
|
| 265 | - if ($shareByMail !== null) { |
|
| 266 | - $shares[] = $shareByMail; |
|
| 267 | - } |
|
| 268 | - $shareByCircle = $this->getShareByCircleProvider(); |
|
| 269 | - if ($shareByCircle !== null) { |
|
| 270 | - $shares[] = $shareByCircle; |
|
| 271 | - } |
|
| 272 | - |
|
| 273 | - return $shares; |
|
| 274 | - } |
|
| 148 | + $appManager = $this->serverContainer->getAppManager(); |
|
| 149 | + if (!$appManager->isEnabledForUser('sharebymail')) { |
|
| 150 | + return null; |
|
| 151 | + } |
|
| 152 | + |
|
| 153 | + $settingsManager = new SettingsManager($this->serverContainer->getConfig()); |
|
| 154 | + |
|
| 155 | + $this->shareByMailProvider = new ShareByMailProvider( |
|
| 156 | + $this->serverContainer->getDatabaseConnection(), |
|
| 157 | + $this->serverContainer->getSecureRandom(), |
|
| 158 | + $this->serverContainer->getUserManager(), |
|
| 159 | + $this->serverContainer->getLazyRootFolder(), |
|
| 160 | + $this->serverContainer->getL10N('sharebymail'), |
|
| 161 | + $this->serverContainer->getLogger(), |
|
| 162 | + $this->serverContainer->getMailer(), |
|
| 163 | + $this->serverContainer->getURLGenerator(), |
|
| 164 | + $this->serverContainer->getActivityManager(), |
|
| 165 | + $settingsManager, |
|
| 166 | + $this->serverContainer->query(Defaults::class), |
|
| 167 | + $this->serverContainer->getHasher(), |
|
| 168 | + $this->serverContainer->query(CapabilitiesManager::class) |
|
| 169 | + ); |
|
| 170 | + } |
|
| 171 | + |
|
| 172 | + return $this->shareByMailProvider; |
|
| 173 | + } |
|
| 174 | + |
|
| 175 | + |
|
| 176 | + /** |
|
| 177 | + * Create the circle share provider |
|
| 178 | + * |
|
| 179 | + * @return FederatedShareProvider |
|
| 180 | + * |
|
| 181 | + * @suppress PhanUndeclaredClassMethod |
|
| 182 | + */ |
|
| 183 | + protected function getShareByCircleProvider() { |
|
| 184 | + |
|
| 185 | + if ($this->circlesAreNotAvailable) { |
|
| 186 | + return null; |
|
| 187 | + } |
|
| 188 | + |
|
| 189 | + if (!$this->serverContainer->getAppManager()->isEnabledForUser('circles') || |
|
| 190 | + !class_exists('\OCA\Circles\ShareByCircleProvider') |
|
| 191 | + ) { |
|
| 192 | + $this->circlesAreNotAvailable = true; |
|
| 193 | + return null; |
|
| 194 | + } |
|
| 195 | + |
|
| 196 | + if ($this->shareByCircleProvider === null) { |
|
| 197 | + |
|
| 198 | + $this->shareByCircleProvider = new \OCA\Circles\ShareByCircleProvider( |
|
| 199 | + $this->serverContainer->getDatabaseConnection(), |
|
| 200 | + $this->serverContainer->getSecureRandom(), |
|
| 201 | + $this->serverContainer->getUserManager(), |
|
| 202 | + $this->serverContainer->getLazyRootFolder(), |
|
| 203 | + $this->serverContainer->getL10N('circles'), |
|
| 204 | + $this->serverContainer->getLogger(), |
|
| 205 | + $this->serverContainer->getURLGenerator() |
|
| 206 | + ); |
|
| 207 | + } |
|
| 208 | + |
|
| 209 | + return $this->shareByCircleProvider; |
|
| 210 | + } |
|
| 211 | + |
|
| 212 | + |
|
| 213 | + /** |
|
| 214 | + * @inheritdoc |
|
| 215 | + */ |
|
| 216 | + public function getProvider($id) { |
|
| 217 | + $provider = null; |
|
| 218 | + if ($id === 'ocinternal') { |
|
| 219 | + $provider = $this->defaultShareProvider(); |
|
| 220 | + } else if ($id === 'ocFederatedSharing') { |
|
| 221 | + $provider = $this->federatedShareProvider(); |
|
| 222 | + } else if ($id === 'ocMailShare') { |
|
| 223 | + $provider = $this->getShareByMailProvider(); |
|
| 224 | + } else if ($id === 'ocCircleShare') { |
|
| 225 | + $provider = $this->getShareByCircleProvider(); |
|
| 226 | + } |
|
| 227 | + |
|
| 228 | + if ($provider === null) { |
|
| 229 | + throw new ProviderException('No provider with id .' . $id . ' found.'); |
|
| 230 | + } |
|
| 231 | + |
|
| 232 | + return $provider; |
|
| 233 | + } |
|
| 234 | + |
|
| 235 | + /** |
|
| 236 | + * @inheritdoc |
|
| 237 | + */ |
|
| 238 | + public function getProviderForType($shareType) { |
|
| 239 | + $provider = null; |
|
| 240 | + |
|
| 241 | + if ($shareType === \OCP\Share::SHARE_TYPE_USER || |
|
| 242 | + $shareType === \OCP\Share::SHARE_TYPE_GROUP || |
|
| 243 | + $shareType === \OCP\Share::SHARE_TYPE_LINK |
|
| 244 | + ) { |
|
| 245 | + $provider = $this->defaultShareProvider(); |
|
| 246 | + } else if ($shareType === \OCP\Share::SHARE_TYPE_REMOTE) { |
|
| 247 | + $provider = $this->federatedShareProvider(); |
|
| 248 | + } else if ($shareType === \OCP\Share::SHARE_TYPE_EMAIL) { |
|
| 249 | + $provider = $this->getShareByMailProvider(); |
|
| 250 | + } else if ($shareType === \OCP\Share::SHARE_TYPE_CIRCLE) { |
|
| 251 | + $provider = $this->getShareByCircleProvider(); |
|
| 252 | + } |
|
| 253 | + |
|
| 254 | + |
|
| 255 | + if ($provider === null) { |
|
| 256 | + throw new ProviderException('No share provider for share type ' . $shareType); |
|
| 257 | + } |
|
| 258 | + |
|
| 259 | + return $provider; |
|
| 260 | + } |
|
| 261 | + |
|
| 262 | + public function getAllProviders() { |
|
| 263 | + $shares = [$this->defaultShareProvider(), $this->federatedShareProvider()]; |
|
| 264 | + $shareByMail = $this->getShareByMailProvider(); |
|
| 265 | + if ($shareByMail !== null) { |
|
| 266 | + $shares[] = $shareByMail; |
|
| 267 | + } |
|
| 268 | + $shareByCircle = $this->getShareByCircleProvider(); |
|
| 269 | + if ($shareByCircle !== null) { |
|
| 270 | + $shares[] = $shareByCircle; |
|
| 271 | + } |
|
| 272 | + |
|
| 273 | + return $shares; |
|
| 274 | + } |
|
| 275 | 275 | } |
@@ -56,1392 +56,1392 @@ |
||
| 56 | 56 | */ |
| 57 | 57 | class Manager implements IManager { |
| 58 | 58 | |
| 59 | - /** @var IProviderFactory */ |
|
| 60 | - private $factory; |
|
| 61 | - /** @var ILogger */ |
|
| 62 | - private $logger; |
|
| 63 | - /** @var IConfig */ |
|
| 64 | - private $config; |
|
| 65 | - /** @var ISecureRandom */ |
|
| 66 | - private $secureRandom; |
|
| 67 | - /** @var IHasher */ |
|
| 68 | - private $hasher; |
|
| 69 | - /** @var IMountManager */ |
|
| 70 | - private $mountManager; |
|
| 71 | - /** @var IGroupManager */ |
|
| 72 | - private $groupManager; |
|
| 73 | - /** @var IL10N */ |
|
| 74 | - private $l; |
|
| 75 | - /** @var IUserManager */ |
|
| 76 | - private $userManager; |
|
| 77 | - /** @var IRootFolder */ |
|
| 78 | - private $rootFolder; |
|
| 79 | - /** @var CappedMemoryCache */ |
|
| 80 | - private $sharingDisabledForUsersCache; |
|
| 81 | - /** @var EventDispatcher */ |
|
| 82 | - private $eventDispatcher; |
|
| 83 | - /** @var LegacyHooks */ |
|
| 84 | - private $legacyHooks; |
|
| 85 | - |
|
| 86 | - |
|
| 87 | - /** |
|
| 88 | - * Manager constructor. |
|
| 89 | - * |
|
| 90 | - * @param ILogger $logger |
|
| 91 | - * @param IConfig $config |
|
| 92 | - * @param ISecureRandom $secureRandom |
|
| 93 | - * @param IHasher $hasher |
|
| 94 | - * @param IMountManager $mountManager |
|
| 95 | - * @param IGroupManager $groupManager |
|
| 96 | - * @param IL10N $l |
|
| 97 | - * @param IProviderFactory $factory |
|
| 98 | - * @param IUserManager $userManager |
|
| 99 | - * @param IRootFolder $rootFolder |
|
| 100 | - * @param EventDispatcher $eventDispatcher |
|
| 101 | - */ |
|
| 102 | - public function __construct( |
|
| 103 | - ILogger $logger, |
|
| 104 | - IConfig $config, |
|
| 105 | - ISecureRandom $secureRandom, |
|
| 106 | - IHasher $hasher, |
|
| 107 | - IMountManager $mountManager, |
|
| 108 | - IGroupManager $groupManager, |
|
| 109 | - IL10N $l, |
|
| 110 | - IProviderFactory $factory, |
|
| 111 | - IUserManager $userManager, |
|
| 112 | - IRootFolder $rootFolder, |
|
| 113 | - EventDispatcher $eventDispatcher |
|
| 114 | - ) { |
|
| 115 | - $this->logger = $logger; |
|
| 116 | - $this->config = $config; |
|
| 117 | - $this->secureRandom = $secureRandom; |
|
| 118 | - $this->hasher = $hasher; |
|
| 119 | - $this->mountManager = $mountManager; |
|
| 120 | - $this->groupManager = $groupManager; |
|
| 121 | - $this->l = $l; |
|
| 122 | - $this->factory = $factory; |
|
| 123 | - $this->userManager = $userManager; |
|
| 124 | - $this->rootFolder = $rootFolder; |
|
| 125 | - $this->eventDispatcher = $eventDispatcher; |
|
| 126 | - $this->sharingDisabledForUsersCache = new CappedMemoryCache(); |
|
| 127 | - $this->legacyHooks = new LegacyHooks($this->eventDispatcher); |
|
| 128 | - } |
|
| 129 | - |
|
| 130 | - /** |
|
| 131 | - * Convert from a full share id to a tuple (providerId, shareId) |
|
| 132 | - * |
|
| 133 | - * @param string $id |
|
| 134 | - * @return string[] |
|
| 135 | - */ |
|
| 136 | - private function splitFullId($id) { |
|
| 137 | - return explode(':', $id, 2); |
|
| 138 | - } |
|
| 139 | - |
|
| 140 | - /** |
|
| 141 | - * Verify if a password meets all requirements |
|
| 142 | - * |
|
| 143 | - * @param string $password |
|
| 144 | - * @throws \Exception |
|
| 145 | - */ |
|
| 146 | - protected function verifyPassword($password) { |
|
| 147 | - if ($password === null) { |
|
| 148 | - // No password is set, check if this is allowed. |
|
| 149 | - if ($this->shareApiLinkEnforcePassword()) { |
|
| 150 | - throw new \InvalidArgumentException('Passwords are enforced for link shares'); |
|
| 151 | - } |
|
| 152 | - |
|
| 153 | - return; |
|
| 154 | - } |
|
| 155 | - |
|
| 156 | - // Let others verify the password |
|
| 157 | - try { |
|
| 158 | - $event = new GenericEvent($password); |
|
| 159 | - $this->eventDispatcher->dispatch('OCP\PasswordPolicy::validate', $event); |
|
| 160 | - } catch (HintException $e) { |
|
| 161 | - throw new \Exception($e->getHint()); |
|
| 162 | - } |
|
| 163 | - } |
|
| 164 | - |
|
| 165 | - /** |
|
| 166 | - * Check for generic requirements before creating a share |
|
| 167 | - * |
|
| 168 | - * @param \OCP\Share\IShare $share |
|
| 169 | - * @throws \InvalidArgumentException |
|
| 170 | - * @throws GenericShareException |
|
| 171 | - * |
|
| 172 | - * @suppress PhanUndeclaredClassMethod |
|
| 173 | - */ |
|
| 174 | - protected function generalCreateChecks(\OCP\Share\IShare $share) { |
|
| 175 | - if ($share->getShareType() === \OCP\Share::SHARE_TYPE_USER) { |
|
| 176 | - // We expect a valid user as sharedWith for user shares |
|
| 177 | - if (!$this->userManager->userExists($share->getSharedWith())) { |
|
| 178 | - throw new \InvalidArgumentException('SharedWith is not a valid user'); |
|
| 179 | - } |
|
| 180 | - } else if ($share->getShareType() === \OCP\Share::SHARE_TYPE_GROUP) { |
|
| 181 | - // We expect a valid group as sharedWith for group shares |
|
| 182 | - if (!$this->groupManager->groupExists($share->getSharedWith())) { |
|
| 183 | - throw new \InvalidArgumentException('SharedWith is not a valid group'); |
|
| 184 | - } |
|
| 185 | - } else if ($share->getShareType() === \OCP\Share::SHARE_TYPE_LINK) { |
|
| 186 | - if ($share->getSharedWith() !== null) { |
|
| 187 | - throw new \InvalidArgumentException('SharedWith should be empty'); |
|
| 188 | - } |
|
| 189 | - } else if ($share->getShareType() === \OCP\Share::SHARE_TYPE_REMOTE) { |
|
| 190 | - if ($share->getSharedWith() === null) { |
|
| 191 | - throw new \InvalidArgumentException('SharedWith should not be empty'); |
|
| 192 | - } |
|
| 193 | - } else if ($share->getShareType() === \OCP\Share::SHARE_TYPE_EMAIL) { |
|
| 194 | - if ($share->getSharedWith() === null) { |
|
| 195 | - throw new \InvalidArgumentException('SharedWith should not be empty'); |
|
| 196 | - } |
|
| 197 | - } else if ($share->getShareType() === \OCP\Share::SHARE_TYPE_CIRCLE) { |
|
| 198 | - $circle = \OCA\Circles\Api\v1\Circles::detailsCircle($share->getSharedWith()); |
|
| 199 | - if ($circle === null) { |
|
| 200 | - throw new \InvalidArgumentException('SharedWith is not a valid circle'); |
|
| 201 | - } |
|
| 202 | - } else { |
|
| 203 | - // We can't handle other types yet |
|
| 204 | - throw new \InvalidArgumentException('unknown share type'); |
|
| 205 | - } |
|
| 206 | - |
|
| 207 | - // Verify the initiator of the share is set |
|
| 208 | - if ($share->getSharedBy() === null) { |
|
| 209 | - throw new \InvalidArgumentException('SharedBy should be set'); |
|
| 210 | - } |
|
| 211 | - |
|
| 212 | - // Cannot share with yourself |
|
| 213 | - if ($share->getShareType() === \OCP\Share::SHARE_TYPE_USER && |
|
| 214 | - $share->getSharedWith() === $share->getSharedBy()) { |
|
| 215 | - throw new \InvalidArgumentException('Can’t share with yourself'); |
|
| 216 | - } |
|
| 217 | - |
|
| 218 | - // The path should be set |
|
| 219 | - if ($share->getNode() === null) { |
|
| 220 | - throw new \InvalidArgumentException('Path should be set'); |
|
| 221 | - } |
|
| 222 | - |
|
| 223 | - // And it should be a file or a folder |
|
| 224 | - if (!($share->getNode() instanceof \OCP\Files\File) && |
|
| 225 | - !($share->getNode() instanceof \OCP\Files\Folder)) { |
|
| 226 | - throw new \InvalidArgumentException('Path should be either a file or a folder'); |
|
| 227 | - } |
|
| 228 | - |
|
| 229 | - // And you can't share your rootfolder |
|
| 230 | - if ($this->userManager->userExists($share->getSharedBy())) { |
|
| 231 | - $sharedPath = $this->rootFolder->getUserFolder($share->getSharedBy())->getPath(); |
|
| 232 | - } else { |
|
| 233 | - $sharedPath = $this->rootFolder->getUserFolder($share->getShareOwner())->getPath(); |
|
| 234 | - } |
|
| 235 | - if ($sharedPath === $share->getNode()->getPath()) { |
|
| 236 | - throw new \InvalidArgumentException('You can’t share your root folder'); |
|
| 237 | - } |
|
| 238 | - |
|
| 239 | - // Check if we actually have share permissions |
|
| 240 | - if (!$share->getNode()->isShareable()) { |
|
| 241 | - $message_t = $this->l->t('You are not allowed to share %s', [$share->getNode()->getPath()]); |
|
| 242 | - throw new GenericShareException($message_t, $message_t, 404); |
|
| 243 | - } |
|
| 244 | - |
|
| 245 | - // Permissions should be set |
|
| 246 | - if ($share->getPermissions() === null) { |
|
| 247 | - throw new \InvalidArgumentException('A share requires permissions'); |
|
| 248 | - } |
|
| 249 | - |
|
| 250 | - /* |
|
| 59 | + /** @var IProviderFactory */ |
|
| 60 | + private $factory; |
|
| 61 | + /** @var ILogger */ |
|
| 62 | + private $logger; |
|
| 63 | + /** @var IConfig */ |
|
| 64 | + private $config; |
|
| 65 | + /** @var ISecureRandom */ |
|
| 66 | + private $secureRandom; |
|
| 67 | + /** @var IHasher */ |
|
| 68 | + private $hasher; |
|
| 69 | + /** @var IMountManager */ |
|
| 70 | + private $mountManager; |
|
| 71 | + /** @var IGroupManager */ |
|
| 72 | + private $groupManager; |
|
| 73 | + /** @var IL10N */ |
|
| 74 | + private $l; |
|
| 75 | + /** @var IUserManager */ |
|
| 76 | + private $userManager; |
|
| 77 | + /** @var IRootFolder */ |
|
| 78 | + private $rootFolder; |
|
| 79 | + /** @var CappedMemoryCache */ |
|
| 80 | + private $sharingDisabledForUsersCache; |
|
| 81 | + /** @var EventDispatcher */ |
|
| 82 | + private $eventDispatcher; |
|
| 83 | + /** @var LegacyHooks */ |
|
| 84 | + private $legacyHooks; |
|
| 85 | + |
|
| 86 | + |
|
| 87 | + /** |
|
| 88 | + * Manager constructor. |
|
| 89 | + * |
|
| 90 | + * @param ILogger $logger |
|
| 91 | + * @param IConfig $config |
|
| 92 | + * @param ISecureRandom $secureRandom |
|
| 93 | + * @param IHasher $hasher |
|
| 94 | + * @param IMountManager $mountManager |
|
| 95 | + * @param IGroupManager $groupManager |
|
| 96 | + * @param IL10N $l |
|
| 97 | + * @param IProviderFactory $factory |
|
| 98 | + * @param IUserManager $userManager |
|
| 99 | + * @param IRootFolder $rootFolder |
|
| 100 | + * @param EventDispatcher $eventDispatcher |
|
| 101 | + */ |
|
| 102 | + public function __construct( |
|
| 103 | + ILogger $logger, |
|
| 104 | + IConfig $config, |
|
| 105 | + ISecureRandom $secureRandom, |
|
| 106 | + IHasher $hasher, |
|
| 107 | + IMountManager $mountManager, |
|
| 108 | + IGroupManager $groupManager, |
|
| 109 | + IL10N $l, |
|
| 110 | + IProviderFactory $factory, |
|
| 111 | + IUserManager $userManager, |
|
| 112 | + IRootFolder $rootFolder, |
|
| 113 | + EventDispatcher $eventDispatcher |
|
| 114 | + ) { |
|
| 115 | + $this->logger = $logger; |
|
| 116 | + $this->config = $config; |
|
| 117 | + $this->secureRandom = $secureRandom; |
|
| 118 | + $this->hasher = $hasher; |
|
| 119 | + $this->mountManager = $mountManager; |
|
| 120 | + $this->groupManager = $groupManager; |
|
| 121 | + $this->l = $l; |
|
| 122 | + $this->factory = $factory; |
|
| 123 | + $this->userManager = $userManager; |
|
| 124 | + $this->rootFolder = $rootFolder; |
|
| 125 | + $this->eventDispatcher = $eventDispatcher; |
|
| 126 | + $this->sharingDisabledForUsersCache = new CappedMemoryCache(); |
|
| 127 | + $this->legacyHooks = new LegacyHooks($this->eventDispatcher); |
|
| 128 | + } |
|
| 129 | + |
|
| 130 | + /** |
|
| 131 | + * Convert from a full share id to a tuple (providerId, shareId) |
|
| 132 | + * |
|
| 133 | + * @param string $id |
|
| 134 | + * @return string[] |
|
| 135 | + */ |
|
| 136 | + private function splitFullId($id) { |
|
| 137 | + return explode(':', $id, 2); |
|
| 138 | + } |
|
| 139 | + |
|
| 140 | + /** |
|
| 141 | + * Verify if a password meets all requirements |
|
| 142 | + * |
|
| 143 | + * @param string $password |
|
| 144 | + * @throws \Exception |
|
| 145 | + */ |
|
| 146 | + protected function verifyPassword($password) { |
|
| 147 | + if ($password === null) { |
|
| 148 | + // No password is set, check if this is allowed. |
|
| 149 | + if ($this->shareApiLinkEnforcePassword()) { |
|
| 150 | + throw new \InvalidArgumentException('Passwords are enforced for link shares'); |
|
| 151 | + } |
|
| 152 | + |
|
| 153 | + return; |
|
| 154 | + } |
|
| 155 | + |
|
| 156 | + // Let others verify the password |
|
| 157 | + try { |
|
| 158 | + $event = new GenericEvent($password); |
|
| 159 | + $this->eventDispatcher->dispatch('OCP\PasswordPolicy::validate', $event); |
|
| 160 | + } catch (HintException $e) { |
|
| 161 | + throw new \Exception($e->getHint()); |
|
| 162 | + } |
|
| 163 | + } |
|
| 164 | + |
|
| 165 | + /** |
|
| 166 | + * Check for generic requirements before creating a share |
|
| 167 | + * |
|
| 168 | + * @param \OCP\Share\IShare $share |
|
| 169 | + * @throws \InvalidArgumentException |
|
| 170 | + * @throws GenericShareException |
|
| 171 | + * |
|
| 172 | + * @suppress PhanUndeclaredClassMethod |
|
| 173 | + */ |
|
| 174 | + protected function generalCreateChecks(\OCP\Share\IShare $share) { |
|
| 175 | + if ($share->getShareType() === \OCP\Share::SHARE_TYPE_USER) { |
|
| 176 | + // We expect a valid user as sharedWith for user shares |
|
| 177 | + if (!$this->userManager->userExists($share->getSharedWith())) { |
|
| 178 | + throw new \InvalidArgumentException('SharedWith is not a valid user'); |
|
| 179 | + } |
|
| 180 | + } else if ($share->getShareType() === \OCP\Share::SHARE_TYPE_GROUP) { |
|
| 181 | + // We expect a valid group as sharedWith for group shares |
|
| 182 | + if (!$this->groupManager->groupExists($share->getSharedWith())) { |
|
| 183 | + throw new \InvalidArgumentException('SharedWith is not a valid group'); |
|
| 184 | + } |
|
| 185 | + } else if ($share->getShareType() === \OCP\Share::SHARE_TYPE_LINK) { |
|
| 186 | + if ($share->getSharedWith() !== null) { |
|
| 187 | + throw new \InvalidArgumentException('SharedWith should be empty'); |
|
| 188 | + } |
|
| 189 | + } else if ($share->getShareType() === \OCP\Share::SHARE_TYPE_REMOTE) { |
|
| 190 | + if ($share->getSharedWith() === null) { |
|
| 191 | + throw new \InvalidArgumentException('SharedWith should not be empty'); |
|
| 192 | + } |
|
| 193 | + } else if ($share->getShareType() === \OCP\Share::SHARE_TYPE_EMAIL) { |
|
| 194 | + if ($share->getSharedWith() === null) { |
|
| 195 | + throw new \InvalidArgumentException('SharedWith should not be empty'); |
|
| 196 | + } |
|
| 197 | + } else if ($share->getShareType() === \OCP\Share::SHARE_TYPE_CIRCLE) { |
|
| 198 | + $circle = \OCA\Circles\Api\v1\Circles::detailsCircle($share->getSharedWith()); |
|
| 199 | + if ($circle === null) { |
|
| 200 | + throw new \InvalidArgumentException('SharedWith is not a valid circle'); |
|
| 201 | + } |
|
| 202 | + } else { |
|
| 203 | + // We can't handle other types yet |
|
| 204 | + throw new \InvalidArgumentException('unknown share type'); |
|
| 205 | + } |
|
| 206 | + |
|
| 207 | + // Verify the initiator of the share is set |
|
| 208 | + if ($share->getSharedBy() === null) { |
|
| 209 | + throw new \InvalidArgumentException('SharedBy should be set'); |
|
| 210 | + } |
|
| 211 | + |
|
| 212 | + // Cannot share with yourself |
|
| 213 | + if ($share->getShareType() === \OCP\Share::SHARE_TYPE_USER && |
|
| 214 | + $share->getSharedWith() === $share->getSharedBy()) { |
|
| 215 | + throw new \InvalidArgumentException('Can’t share with yourself'); |
|
| 216 | + } |
|
| 217 | + |
|
| 218 | + // The path should be set |
|
| 219 | + if ($share->getNode() === null) { |
|
| 220 | + throw new \InvalidArgumentException('Path should be set'); |
|
| 221 | + } |
|
| 222 | + |
|
| 223 | + // And it should be a file or a folder |
|
| 224 | + if (!($share->getNode() instanceof \OCP\Files\File) && |
|
| 225 | + !($share->getNode() instanceof \OCP\Files\Folder)) { |
|
| 226 | + throw new \InvalidArgumentException('Path should be either a file or a folder'); |
|
| 227 | + } |
|
| 228 | + |
|
| 229 | + // And you can't share your rootfolder |
|
| 230 | + if ($this->userManager->userExists($share->getSharedBy())) { |
|
| 231 | + $sharedPath = $this->rootFolder->getUserFolder($share->getSharedBy())->getPath(); |
|
| 232 | + } else { |
|
| 233 | + $sharedPath = $this->rootFolder->getUserFolder($share->getShareOwner())->getPath(); |
|
| 234 | + } |
|
| 235 | + if ($sharedPath === $share->getNode()->getPath()) { |
|
| 236 | + throw new \InvalidArgumentException('You can’t share your root folder'); |
|
| 237 | + } |
|
| 238 | + |
|
| 239 | + // Check if we actually have share permissions |
|
| 240 | + if (!$share->getNode()->isShareable()) { |
|
| 241 | + $message_t = $this->l->t('You are not allowed to share %s', [$share->getNode()->getPath()]); |
|
| 242 | + throw new GenericShareException($message_t, $message_t, 404); |
|
| 243 | + } |
|
| 244 | + |
|
| 245 | + // Permissions should be set |
|
| 246 | + if ($share->getPermissions() === null) { |
|
| 247 | + throw new \InvalidArgumentException('A share requires permissions'); |
|
| 248 | + } |
|
| 249 | + |
|
| 250 | + /* |
|
| 251 | 251 | * Quick fix for #23536 |
| 252 | 252 | * Non moveable mount points do not have update and delete permissions |
| 253 | 253 | * while we 'most likely' do have that on the storage. |
| 254 | 254 | */ |
| 255 | - $permissions = $share->getNode()->getPermissions(); |
|
| 256 | - $mount = $share->getNode()->getMountPoint(); |
|
| 257 | - if (!($mount instanceof MoveableMount)) { |
|
| 258 | - $permissions |= \OCP\Constants::PERMISSION_DELETE | \OCP\Constants::PERMISSION_UPDATE; |
|
| 259 | - } |
|
| 260 | - |
|
| 261 | - // Check that we do not share with more permissions than we have |
|
| 262 | - if ($share->getPermissions() & ~$permissions) { |
|
| 263 | - $message_t = $this->l->t('Can’t increase permissions of %s', [$share->getNode()->getPath()]); |
|
| 264 | - throw new GenericShareException($message_t, $message_t, 404); |
|
| 265 | - } |
|
| 266 | - |
|
| 267 | - |
|
| 268 | - // Check that read permissions are always set |
|
| 269 | - // Link shares are allowed to have no read permissions to allow upload to hidden folders |
|
| 270 | - $noReadPermissionRequired = $share->getShareType() === \OCP\Share::SHARE_TYPE_LINK |
|
| 271 | - || $share->getShareType() === \OCP\Share::SHARE_TYPE_EMAIL; |
|
| 272 | - if (!$noReadPermissionRequired && |
|
| 273 | - ($share->getPermissions() & \OCP\Constants::PERMISSION_READ) === 0) { |
|
| 274 | - throw new \InvalidArgumentException('Shares need at least read permissions'); |
|
| 275 | - } |
|
| 276 | - |
|
| 277 | - if ($share->getNode() instanceof \OCP\Files\File) { |
|
| 278 | - if ($share->getPermissions() & \OCP\Constants::PERMISSION_DELETE) { |
|
| 279 | - $message_t = $this->l->t('Files can’t be shared with delete permissions'); |
|
| 280 | - throw new GenericShareException($message_t); |
|
| 281 | - } |
|
| 282 | - if ($share->getPermissions() & \OCP\Constants::PERMISSION_CREATE) { |
|
| 283 | - $message_t = $this->l->t('Files can’t be shared with create permissions'); |
|
| 284 | - throw new GenericShareException($message_t); |
|
| 285 | - } |
|
| 286 | - } |
|
| 287 | - } |
|
| 288 | - |
|
| 289 | - /** |
|
| 290 | - * Validate if the expiration date fits the system settings |
|
| 291 | - * |
|
| 292 | - * @param \OCP\Share\IShare $share The share to validate the expiration date of |
|
| 293 | - * @return \OCP\Share\IShare The modified share object |
|
| 294 | - * @throws GenericShareException |
|
| 295 | - * @throws \InvalidArgumentException |
|
| 296 | - * @throws \Exception |
|
| 297 | - */ |
|
| 298 | - protected function validateExpirationDate(\OCP\Share\IShare $share) { |
|
| 299 | - |
|
| 300 | - $expirationDate = $share->getExpirationDate(); |
|
| 301 | - |
|
| 302 | - if ($expirationDate !== null) { |
|
| 303 | - //Make sure the expiration date is a date |
|
| 304 | - $expirationDate->setTime(0, 0, 0); |
|
| 305 | - |
|
| 306 | - $date = new \DateTime(); |
|
| 307 | - $date->setTime(0, 0, 0); |
|
| 308 | - if ($date >= $expirationDate) { |
|
| 309 | - $message = $this->l->t('Expiration date is in the past'); |
|
| 310 | - throw new GenericShareException($message, $message, 404); |
|
| 311 | - } |
|
| 312 | - } |
|
| 313 | - |
|
| 314 | - // If expiredate is empty set a default one if there is a default |
|
| 315 | - $fullId = null; |
|
| 316 | - try { |
|
| 317 | - $fullId = $share->getFullId(); |
|
| 318 | - } catch (\UnexpectedValueException $e) { |
|
| 319 | - // This is a new share |
|
| 320 | - } |
|
| 321 | - |
|
| 322 | - if ($fullId === null && $expirationDate === null && $this->shareApiLinkDefaultExpireDate()) { |
|
| 323 | - $expirationDate = new \DateTime(); |
|
| 324 | - $expirationDate->setTime(0,0,0); |
|
| 325 | - $expirationDate->add(new \DateInterval('P'.$this->shareApiLinkDefaultExpireDays().'D')); |
|
| 326 | - } |
|
| 327 | - |
|
| 328 | - // If we enforce the expiration date check that is does not exceed |
|
| 329 | - if ($this->shareApiLinkDefaultExpireDateEnforced()) { |
|
| 330 | - if ($expirationDate === null) { |
|
| 331 | - throw new \InvalidArgumentException('Expiration date is enforced'); |
|
| 332 | - } |
|
| 333 | - |
|
| 334 | - $date = new \DateTime(); |
|
| 335 | - $date->setTime(0, 0, 0); |
|
| 336 | - $date->add(new \DateInterval('P' . $this->shareApiLinkDefaultExpireDays() . 'D')); |
|
| 337 | - if ($date < $expirationDate) { |
|
| 338 | - $message = $this->l->t('Can’t set expiration date more than %s days in the future', [$this->shareApiLinkDefaultExpireDays()]); |
|
| 339 | - throw new GenericShareException($message, $message, 404); |
|
| 340 | - } |
|
| 341 | - } |
|
| 342 | - |
|
| 343 | - $accepted = true; |
|
| 344 | - $message = ''; |
|
| 345 | - \OCP\Util::emitHook('\OC\Share', 'verifyExpirationDate', [ |
|
| 346 | - 'expirationDate' => &$expirationDate, |
|
| 347 | - 'accepted' => &$accepted, |
|
| 348 | - 'message' => &$message, |
|
| 349 | - 'passwordSet' => $share->getPassword() !== null, |
|
| 350 | - ]); |
|
| 351 | - |
|
| 352 | - if (!$accepted) { |
|
| 353 | - throw new \Exception($message); |
|
| 354 | - } |
|
| 355 | - |
|
| 356 | - $share->setExpirationDate($expirationDate); |
|
| 357 | - |
|
| 358 | - return $share; |
|
| 359 | - } |
|
| 360 | - |
|
| 361 | - /** |
|
| 362 | - * Check for pre share requirements for user shares |
|
| 363 | - * |
|
| 364 | - * @param \OCP\Share\IShare $share |
|
| 365 | - * @throws \Exception |
|
| 366 | - */ |
|
| 367 | - protected function userCreateChecks(\OCP\Share\IShare $share) { |
|
| 368 | - // Check if we can share with group members only |
|
| 369 | - if ($this->shareWithGroupMembersOnly()) { |
|
| 370 | - $sharedBy = $this->userManager->get($share->getSharedBy()); |
|
| 371 | - $sharedWith = $this->userManager->get($share->getSharedWith()); |
|
| 372 | - // Verify we can share with this user |
|
| 373 | - $groups = array_intersect( |
|
| 374 | - $this->groupManager->getUserGroupIds($sharedBy), |
|
| 375 | - $this->groupManager->getUserGroupIds($sharedWith) |
|
| 376 | - ); |
|
| 377 | - if (empty($groups)) { |
|
| 378 | - throw new \Exception('Sharing is only allowed with group members'); |
|
| 379 | - } |
|
| 380 | - } |
|
| 381 | - |
|
| 382 | - /* |
|
| 255 | + $permissions = $share->getNode()->getPermissions(); |
|
| 256 | + $mount = $share->getNode()->getMountPoint(); |
|
| 257 | + if (!($mount instanceof MoveableMount)) { |
|
| 258 | + $permissions |= \OCP\Constants::PERMISSION_DELETE | \OCP\Constants::PERMISSION_UPDATE; |
|
| 259 | + } |
|
| 260 | + |
|
| 261 | + // Check that we do not share with more permissions than we have |
|
| 262 | + if ($share->getPermissions() & ~$permissions) { |
|
| 263 | + $message_t = $this->l->t('Can’t increase permissions of %s', [$share->getNode()->getPath()]); |
|
| 264 | + throw new GenericShareException($message_t, $message_t, 404); |
|
| 265 | + } |
|
| 266 | + |
|
| 267 | + |
|
| 268 | + // Check that read permissions are always set |
|
| 269 | + // Link shares are allowed to have no read permissions to allow upload to hidden folders |
|
| 270 | + $noReadPermissionRequired = $share->getShareType() === \OCP\Share::SHARE_TYPE_LINK |
|
| 271 | + || $share->getShareType() === \OCP\Share::SHARE_TYPE_EMAIL; |
|
| 272 | + if (!$noReadPermissionRequired && |
|
| 273 | + ($share->getPermissions() & \OCP\Constants::PERMISSION_READ) === 0) { |
|
| 274 | + throw new \InvalidArgumentException('Shares need at least read permissions'); |
|
| 275 | + } |
|
| 276 | + |
|
| 277 | + if ($share->getNode() instanceof \OCP\Files\File) { |
|
| 278 | + if ($share->getPermissions() & \OCP\Constants::PERMISSION_DELETE) { |
|
| 279 | + $message_t = $this->l->t('Files can’t be shared with delete permissions'); |
|
| 280 | + throw new GenericShareException($message_t); |
|
| 281 | + } |
|
| 282 | + if ($share->getPermissions() & \OCP\Constants::PERMISSION_CREATE) { |
|
| 283 | + $message_t = $this->l->t('Files can’t be shared with create permissions'); |
|
| 284 | + throw new GenericShareException($message_t); |
|
| 285 | + } |
|
| 286 | + } |
|
| 287 | + } |
|
| 288 | + |
|
| 289 | + /** |
|
| 290 | + * Validate if the expiration date fits the system settings |
|
| 291 | + * |
|
| 292 | + * @param \OCP\Share\IShare $share The share to validate the expiration date of |
|
| 293 | + * @return \OCP\Share\IShare The modified share object |
|
| 294 | + * @throws GenericShareException |
|
| 295 | + * @throws \InvalidArgumentException |
|
| 296 | + * @throws \Exception |
|
| 297 | + */ |
|
| 298 | + protected function validateExpirationDate(\OCP\Share\IShare $share) { |
|
| 299 | + |
|
| 300 | + $expirationDate = $share->getExpirationDate(); |
|
| 301 | + |
|
| 302 | + if ($expirationDate !== null) { |
|
| 303 | + //Make sure the expiration date is a date |
|
| 304 | + $expirationDate->setTime(0, 0, 0); |
|
| 305 | + |
|
| 306 | + $date = new \DateTime(); |
|
| 307 | + $date->setTime(0, 0, 0); |
|
| 308 | + if ($date >= $expirationDate) { |
|
| 309 | + $message = $this->l->t('Expiration date is in the past'); |
|
| 310 | + throw new GenericShareException($message, $message, 404); |
|
| 311 | + } |
|
| 312 | + } |
|
| 313 | + |
|
| 314 | + // If expiredate is empty set a default one if there is a default |
|
| 315 | + $fullId = null; |
|
| 316 | + try { |
|
| 317 | + $fullId = $share->getFullId(); |
|
| 318 | + } catch (\UnexpectedValueException $e) { |
|
| 319 | + // This is a new share |
|
| 320 | + } |
|
| 321 | + |
|
| 322 | + if ($fullId === null && $expirationDate === null && $this->shareApiLinkDefaultExpireDate()) { |
|
| 323 | + $expirationDate = new \DateTime(); |
|
| 324 | + $expirationDate->setTime(0,0,0); |
|
| 325 | + $expirationDate->add(new \DateInterval('P'.$this->shareApiLinkDefaultExpireDays().'D')); |
|
| 326 | + } |
|
| 327 | + |
|
| 328 | + // If we enforce the expiration date check that is does not exceed |
|
| 329 | + if ($this->shareApiLinkDefaultExpireDateEnforced()) { |
|
| 330 | + if ($expirationDate === null) { |
|
| 331 | + throw new \InvalidArgumentException('Expiration date is enforced'); |
|
| 332 | + } |
|
| 333 | + |
|
| 334 | + $date = new \DateTime(); |
|
| 335 | + $date->setTime(0, 0, 0); |
|
| 336 | + $date->add(new \DateInterval('P' . $this->shareApiLinkDefaultExpireDays() . 'D')); |
|
| 337 | + if ($date < $expirationDate) { |
|
| 338 | + $message = $this->l->t('Can’t set expiration date more than %s days in the future', [$this->shareApiLinkDefaultExpireDays()]); |
|
| 339 | + throw new GenericShareException($message, $message, 404); |
|
| 340 | + } |
|
| 341 | + } |
|
| 342 | + |
|
| 343 | + $accepted = true; |
|
| 344 | + $message = ''; |
|
| 345 | + \OCP\Util::emitHook('\OC\Share', 'verifyExpirationDate', [ |
|
| 346 | + 'expirationDate' => &$expirationDate, |
|
| 347 | + 'accepted' => &$accepted, |
|
| 348 | + 'message' => &$message, |
|
| 349 | + 'passwordSet' => $share->getPassword() !== null, |
|
| 350 | + ]); |
|
| 351 | + |
|
| 352 | + if (!$accepted) { |
|
| 353 | + throw new \Exception($message); |
|
| 354 | + } |
|
| 355 | + |
|
| 356 | + $share->setExpirationDate($expirationDate); |
|
| 357 | + |
|
| 358 | + return $share; |
|
| 359 | + } |
|
| 360 | + |
|
| 361 | + /** |
|
| 362 | + * Check for pre share requirements for user shares |
|
| 363 | + * |
|
| 364 | + * @param \OCP\Share\IShare $share |
|
| 365 | + * @throws \Exception |
|
| 366 | + */ |
|
| 367 | + protected function userCreateChecks(\OCP\Share\IShare $share) { |
|
| 368 | + // Check if we can share with group members only |
|
| 369 | + if ($this->shareWithGroupMembersOnly()) { |
|
| 370 | + $sharedBy = $this->userManager->get($share->getSharedBy()); |
|
| 371 | + $sharedWith = $this->userManager->get($share->getSharedWith()); |
|
| 372 | + // Verify we can share with this user |
|
| 373 | + $groups = array_intersect( |
|
| 374 | + $this->groupManager->getUserGroupIds($sharedBy), |
|
| 375 | + $this->groupManager->getUserGroupIds($sharedWith) |
|
| 376 | + ); |
|
| 377 | + if (empty($groups)) { |
|
| 378 | + throw new \Exception('Sharing is only allowed with group members'); |
|
| 379 | + } |
|
| 380 | + } |
|
| 381 | + |
|
| 382 | + /* |
|
| 383 | 383 | * TODO: Could be costly, fix |
| 384 | 384 | * |
| 385 | 385 | * Also this is not what we want in the future.. then we want to squash identical shares. |
| 386 | 386 | */ |
| 387 | - $provider = $this->factory->getProviderForType(\OCP\Share::SHARE_TYPE_USER); |
|
| 388 | - $existingShares = $provider->getSharesByPath($share->getNode()); |
|
| 389 | - foreach($existingShares as $existingShare) { |
|
| 390 | - // Ignore if it is the same share |
|
| 391 | - try { |
|
| 392 | - if ($existingShare->getFullId() === $share->getFullId()) { |
|
| 393 | - continue; |
|
| 394 | - } |
|
| 395 | - } catch (\UnexpectedValueException $e) { |
|
| 396 | - //Shares are not identical |
|
| 397 | - } |
|
| 398 | - |
|
| 399 | - // Identical share already existst |
|
| 400 | - if ($existingShare->getSharedWith() === $share->getSharedWith()) { |
|
| 401 | - throw new \Exception('Path is already shared with this user'); |
|
| 402 | - } |
|
| 403 | - |
|
| 404 | - // The share is already shared with this user via a group share |
|
| 405 | - if ($existingShare->getShareType() === \OCP\Share::SHARE_TYPE_GROUP) { |
|
| 406 | - $group = $this->groupManager->get($existingShare->getSharedWith()); |
|
| 407 | - if (!is_null($group)) { |
|
| 408 | - $user = $this->userManager->get($share->getSharedWith()); |
|
| 409 | - |
|
| 410 | - if ($group->inGroup($user) && $existingShare->getShareOwner() !== $share->getShareOwner()) { |
|
| 411 | - throw new \Exception('Path is already shared with this user'); |
|
| 412 | - } |
|
| 413 | - } |
|
| 414 | - } |
|
| 415 | - } |
|
| 416 | - } |
|
| 417 | - |
|
| 418 | - /** |
|
| 419 | - * Check for pre share requirements for group shares |
|
| 420 | - * |
|
| 421 | - * @param \OCP\Share\IShare $share |
|
| 422 | - * @throws \Exception |
|
| 423 | - */ |
|
| 424 | - protected function groupCreateChecks(\OCP\Share\IShare $share) { |
|
| 425 | - // Verify group shares are allowed |
|
| 426 | - if (!$this->allowGroupSharing()) { |
|
| 427 | - throw new \Exception('Group sharing is now allowed'); |
|
| 428 | - } |
|
| 429 | - |
|
| 430 | - // Verify if the user can share with this group |
|
| 431 | - if ($this->shareWithGroupMembersOnly()) { |
|
| 432 | - $sharedBy = $this->userManager->get($share->getSharedBy()); |
|
| 433 | - $sharedWith = $this->groupManager->get($share->getSharedWith()); |
|
| 434 | - if (is_null($sharedWith) || !$sharedWith->inGroup($sharedBy)) { |
|
| 435 | - throw new \Exception('Sharing is only allowed within your own groups'); |
|
| 436 | - } |
|
| 437 | - } |
|
| 438 | - |
|
| 439 | - /* |
|
| 387 | + $provider = $this->factory->getProviderForType(\OCP\Share::SHARE_TYPE_USER); |
|
| 388 | + $existingShares = $provider->getSharesByPath($share->getNode()); |
|
| 389 | + foreach($existingShares as $existingShare) { |
|
| 390 | + // Ignore if it is the same share |
|
| 391 | + try { |
|
| 392 | + if ($existingShare->getFullId() === $share->getFullId()) { |
|
| 393 | + continue; |
|
| 394 | + } |
|
| 395 | + } catch (\UnexpectedValueException $e) { |
|
| 396 | + //Shares are not identical |
|
| 397 | + } |
|
| 398 | + |
|
| 399 | + // Identical share already existst |
|
| 400 | + if ($existingShare->getSharedWith() === $share->getSharedWith()) { |
|
| 401 | + throw new \Exception('Path is already shared with this user'); |
|
| 402 | + } |
|
| 403 | + |
|
| 404 | + // The share is already shared with this user via a group share |
|
| 405 | + if ($existingShare->getShareType() === \OCP\Share::SHARE_TYPE_GROUP) { |
|
| 406 | + $group = $this->groupManager->get($existingShare->getSharedWith()); |
|
| 407 | + if (!is_null($group)) { |
|
| 408 | + $user = $this->userManager->get($share->getSharedWith()); |
|
| 409 | + |
|
| 410 | + if ($group->inGroup($user) && $existingShare->getShareOwner() !== $share->getShareOwner()) { |
|
| 411 | + throw new \Exception('Path is already shared with this user'); |
|
| 412 | + } |
|
| 413 | + } |
|
| 414 | + } |
|
| 415 | + } |
|
| 416 | + } |
|
| 417 | + |
|
| 418 | + /** |
|
| 419 | + * Check for pre share requirements for group shares |
|
| 420 | + * |
|
| 421 | + * @param \OCP\Share\IShare $share |
|
| 422 | + * @throws \Exception |
|
| 423 | + */ |
|
| 424 | + protected function groupCreateChecks(\OCP\Share\IShare $share) { |
|
| 425 | + // Verify group shares are allowed |
|
| 426 | + if (!$this->allowGroupSharing()) { |
|
| 427 | + throw new \Exception('Group sharing is now allowed'); |
|
| 428 | + } |
|
| 429 | + |
|
| 430 | + // Verify if the user can share with this group |
|
| 431 | + if ($this->shareWithGroupMembersOnly()) { |
|
| 432 | + $sharedBy = $this->userManager->get($share->getSharedBy()); |
|
| 433 | + $sharedWith = $this->groupManager->get($share->getSharedWith()); |
|
| 434 | + if (is_null($sharedWith) || !$sharedWith->inGroup($sharedBy)) { |
|
| 435 | + throw new \Exception('Sharing is only allowed within your own groups'); |
|
| 436 | + } |
|
| 437 | + } |
|
| 438 | + |
|
| 439 | + /* |
|
| 440 | 440 | * TODO: Could be costly, fix |
| 441 | 441 | * |
| 442 | 442 | * Also this is not what we want in the future.. then we want to squash identical shares. |
| 443 | 443 | */ |
| 444 | - $provider = $this->factory->getProviderForType(\OCP\Share::SHARE_TYPE_GROUP); |
|
| 445 | - $existingShares = $provider->getSharesByPath($share->getNode()); |
|
| 446 | - foreach($existingShares as $existingShare) { |
|
| 447 | - try { |
|
| 448 | - if ($existingShare->getFullId() === $share->getFullId()) { |
|
| 449 | - continue; |
|
| 450 | - } |
|
| 451 | - } catch (\UnexpectedValueException $e) { |
|
| 452 | - //It is a new share so just continue |
|
| 453 | - } |
|
| 454 | - |
|
| 455 | - if ($existingShare->getSharedWith() === $share->getSharedWith()) { |
|
| 456 | - throw new \Exception('Path is already shared with this group'); |
|
| 457 | - } |
|
| 458 | - } |
|
| 459 | - } |
|
| 460 | - |
|
| 461 | - /** |
|
| 462 | - * Check for pre share requirements for link shares |
|
| 463 | - * |
|
| 464 | - * @param \OCP\Share\IShare $share |
|
| 465 | - * @throws \Exception |
|
| 466 | - */ |
|
| 467 | - protected function linkCreateChecks(\OCP\Share\IShare $share) { |
|
| 468 | - // Are link shares allowed? |
|
| 469 | - if (!$this->shareApiAllowLinks()) { |
|
| 470 | - throw new \Exception('Link sharing is not allowed'); |
|
| 471 | - } |
|
| 472 | - |
|
| 473 | - // Link shares by definition can't have share permissions |
|
| 474 | - if ($share->getPermissions() & \OCP\Constants::PERMISSION_SHARE) { |
|
| 475 | - throw new \InvalidArgumentException('Link shares can’t have reshare permissions'); |
|
| 476 | - } |
|
| 477 | - |
|
| 478 | - // Check if public upload is allowed |
|
| 479 | - if (!$this->shareApiLinkAllowPublicUpload() && |
|
| 480 | - ($share->getPermissions() & (\OCP\Constants::PERMISSION_CREATE | \OCP\Constants::PERMISSION_UPDATE | \OCP\Constants::PERMISSION_DELETE))) { |
|
| 481 | - throw new \InvalidArgumentException('Public upload is not allowed'); |
|
| 482 | - } |
|
| 483 | - } |
|
| 484 | - |
|
| 485 | - /** |
|
| 486 | - * To make sure we don't get invisible link shares we set the parent |
|
| 487 | - * of a link if it is a reshare. This is a quick word around |
|
| 488 | - * until we can properly display multiple link shares in the UI |
|
| 489 | - * |
|
| 490 | - * See: https://github.com/owncloud/core/issues/22295 |
|
| 491 | - * |
|
| 492 | - * FIXME: Remove once multiple link shares can be properly displayed |
|
| 493 | - * |
|
| 494 | - * @param \OCP\Share\IShare $share |
|
| 495 | - */ |
|
| 496 | - protected function setLinkParent(\OCP\Share\IShare $share) { |
|
| 497 | - |
|
| 498 | - // No sense in checking if the method is not there. |
|
| 499 | - if (method_exists($share, 'setParent')) { |
|
| 500 | - $storage = $share->getNode()->getStorage(); |
|
| 501 | - if ($storage->instanceOfStorage('\OCA\Files_Sharing\ISharedStorage')) { |
|
| 502 | - /** @var \OCA\Files_Sharing\SharedStorage $storage */ |
|
| 503 | - $share->setParent($storage->getShareId()); |
|
| 504 | - } |
|
| 505 | - }; |
|
| 506 | - } |
|
| 507 | - |
|
| 508 | - /** |
|
| 509 | - * @param File|Folder $path |
|
| 510 | - */ |
|
| 511 | - protected function pathCreateChecks($path) { |
|
| 512 | - // Make sure that we do not share a path that contains a shared mountpoint |
|
| 513 | - if ($path instanceof \OCP\Files\Folder) { |
|
| 514 | - $mounts = $this->mountManager->findIn($path->getPath()); |
|
| 515 | - foreach($mounts as $mount) { |
|
| 516 | - if ($mount->getStorage()->instanceOfStorage('\OCA\Files_Sharing\ISharedStorage')) { |
|
| 517 | - throw new \InvalidArgumentException('Path contains files shared with you'); |
|
| 518 | - } |
|
| 519 | - } |
|
| 520 | - } |
|
| 521 | - } |
|
| 522 | - |
|
| 523 | - /** |
|
| 524 | - * Check if the user that is sharing can actually share |
|
| 525 | - * |
|
| 526 | - * @param \OCP\Share\IShare $share |
|
| 527 | - * @throws \Exception |
|
| 528 | - */ |
|
| 529 | - protected function canShare(\OCP\Share\IShare $share) { |
|
| 530 | - if (!$this->shareApiEnabled()) { |
|
| 531 | - throw new \Exception('Sharing is disabled'); |
|
| 532 | - } |
|
| 533 | - |
|
| 534 | - if ($this->sharingDisabledForUser($share->getSharedBy())) { |
|
| 535 | - throw new \Exception('Sharing is disabled for you'); |
|
| 536 | - } |
|
| 537 | - } |
|
| 538 | - |
|
| 539 | - /** |
|
| 540 | - * Share a path |
|
| 541 | - * |
|
| 542 | - * @param \OCP\Share\IShare $share |
|
| 543 | - * @return Share The share object |
|
| 544 | - * @throws \Exception |
|
| 545 | - * |
|
| 546 | - * TODO: handle link share permissions or check them |
|
| 547 | - */ |
|
| 548 | - public function createShare(\OCP\Share\IShare $share) { |
|
| 549 | - $this->canShare($share); |
|
| 550 | - |
|
| 551 | - $this->generalCreateChecks($share); |
|
| 552 | - |
|
| 553 | - // Verify if there are any issues with the path |
|
| 554 | - $this->pathCreateChecks($share->getNode()); |
|
| 555 | - |
|
| 556 | - /* |
|
| 444 | + $provider = $this->factory->getProviderForType(\OCP\Share::SHARE_TYPE_GROUP); |
|
| 445 | + $existingShares = $provider->getSharesByPath($share->getNode()); |
|
| 446 | + foreach($existingShares as $existingShare) { |
|
| 447 | + try { |
|
| 448 | + if ($existingShare->getFullId() === $share->getFullId()) { |
|
| 449 | + continue; |
|
| 450 | + } |
|
| 451 | + } catch (\UnexpectedValueException $e) { |
|
| 452 | + //It is a new share so just continue |
|
| 453 | + } |
|
| 454 | + |
|
| 455 | + if ($existingShare->getSharedWith() === $share->getSharedWith()) { |
|
| 456 | + throw new \Exception('Path is already shared with this group'); |
|
| 457 | + } |
|
| 458 | + } |
|
| 459 | + } |
|
| 460 | + |
|
| 461 | + /** |
|
| 462 | + * Check for pre share requirements for link shares |
|
| 463 | + * |
|
| 464 | + * @param \OCP\Share\IShare $share |
|
| 465 | + * @throws \Exception |
|
| 466 | + */ |
|
| 467 | + protected function linkCreateChecks(\OCP\Share\IShare $share) { |
|
| 468 | + // Are link shares allowed? |
|
| 469 | + if (!$this->shareApiAllowLinks()) { |
|
| 470 | + throw new \Exception('Link sharing is not allowed'); |
|
| 471 | + } |
|
| 472 | + |
|
| 473 | + // Link shares by definition can't have share permissions |
|
| 474 | + if ($share->getPermissions() & \OCP\Constants::PERMISSION_SHARE) { |
|
| 475 | + throw new \InvalidArgumentException('Link shares can’t have reshare permissions'); |
|
| 476 | + } |
|
| 477 | + |
|
| 478 | + // Check if public upload is allowed |
|
| 479 | + if (!$this->shareApiLinkAllowPublicUpload() && |
|
| 480 | + ($share->getPermissions() & (\OCP\Constants::PERMISSION_CREATE | \OCP\Constants::PERMISSION_UPDATE | \OCP\Constants::PERMISSION_DELETE))) { |
|
| 481 | + throw new \InvalidArgumentException('Public upload is not allowed'); |
|
| 482 | + } |
|
| 483 | + } |
|
| 484 | + |
|
| 485 | + /** |
|
| 486 | + * To make sure we don't get invisible link shares we set the parent |
|
| 487 | + * of a link if it is a reshare. This is a quick word around |
|
| 488 | + * until we can properly display multiple link shares in the UI |
|
| 489 | + * |
|
| 490 | + * See: https://github.com/owncloud/core/issues/22295 |
|
| 491 | + * |
|
| 492 | + * FIXME: Remove once multiple link shares can be properly displayed |
|
| 493 | + * |
|
| 494 | + * @param \OCP\Share\IShare $share |
|
| 495 | + */ |
|
| 496 | + protected function setLinkParent(\OCP\Share\IShare $share) { |
|
| 497 | + |
|
| 498 | + // No sense in checking if the method is not there. |
|
| 499 | + if (method_exists($share, 'setParent')) { |
|
| 500 | + $storage = $share->getNode()->getStorage(); |
|
| 501 | + if ($storage->instanceOfStorage('\OCA\Files_Sharing\ISharedStorage')) { |
|
| 502 | + /** @var \OCA\Files_Sharing\SharedStorage $storage */ |
|
| 503 | + $share->setParent($storage->getShareId()); |
|
| 504 | + } |
|
| 505 | + }; |
|
| 506 | + } |
|
| 507 | + |
|
| 508 | + /** |
|
| 509 | + * @param File|Folder $path |
|
| 510 | + */ |
|
| 511 | + protected function pathCreateChecks($path) { |
|
| 512 | + // Make sure that we do not share a path that contains a shared mountpoint |
|
| 513 | + if ($path instanceof \OCP\Files\Folder) { |
|
| 514 | + $mounts = $this->mountManager->findIn($path->getPath()); |
|
| 515 | + foreach($mounts as $mount) { |
|
| 516 | + if ($mount->getStorage()->instanceOfStorage('\OCA\Files_Sharing\ISharedStorage')) { |
|
| 517 | + throw new \InvalidArgumentException('Path contains files shared with you'); |
|
| 518 | + } |
|
| 519 | + } |
|
| 520 | + } |
|
| 521 | + } |
|
| 522 | + |
|
| 523 | + /** |
|
| 524 | + * Check if the user that is sharing can actually share |
|
| 525 | + * |
|
| 526 | + * @param \OCP\Share\IShare $share |
|
| 527 | + * @throws \Exception |
|
| 528 | + */ |
|
| 529 | + protected function canShare(\OCP\Share\IShare $share) { |
|
| 530 | + if (!$this->shareApiEnabled()) { |
|
| 531 | + throw new \Exception('Sharing is disabled'); |
|
| 532 | + } |
|
| 533 | + |
|
| 534 | + if ($this->sharingDisabledForUser($share->getSharedBy())) { |
|
| 535 | + throw new \Exception('Sharing is disabled for you'); |
|
| 536 | + } |
|
| 537 | + } |
|
| 538 | + |
|
| 539 | + /** |
|
| 540 | + * Share a path |
|
| 541 | + * |
|
| 542 | + * @param \OCP\Share\IShare $share |
|
| 543 | + * @return Share The share object |
|
| 544 | + * @throws \Exception |
|
| 545 | + * |
|
| 546 | + * TODO: handle link share permissions or check them |
|
| 547 | + */ |
|
| 548 | + public function createShare(\OCP\Share\IShare $share) { |
|
| 549 | + $this->canShare($share); |
|
| 550 | + |
|
| 551 | + $this->generalCreateChecks($share); |
|
| 552 | + |
|
| 553 | + // Verify if there are any issues with the path |
|
| 554 | + $this->pathCreateChecks($share->getNode()); |
|
| 555 | + |
|
| 556 | + /* |
|
| 557 | 557 | * On creation of a share the owner is always the owner of the path |
| 558 | 558 | * Except for mounted federated shares. |
| 559 | 559 | */ |
| 560 | - $storage = $share->getNode()->getStorage(); |
|
| 561 | - if ($storage->instanceOfStorage('OCA\Files_Sharing\External\Storage')) { |
|
| 562 | - $parent = $share->getNode()->getParent(); |
|
| 563 | - while($parent->getStorage()->instanceOfStorage('OCA\Files_Sharing\External\Storage')) { |
|
| 564 | - $parent = $parent->getParent(); |
|
| 565 | - } |
|
| 566 | - $share->setShareOwner($parent->getOwner()->getUID()); |
|
| 567 | - } else { |
|
| 568 | - $share->setShareOwner($share->getNode()->getOwner()->getUID()); |
|
| 569 | - } |
|
| 570 | - |
|
| 571 | - //Verify share type |
|
| 572 | - if ($share->getShareType() === \OCP\Share::SHARE_TYPE_USER) { |
|
| 573 | - $this->userCreateChecks($share); |
|
| 574 | - } else if ($share->getShareType() === \OCP\Share::SHARE_TYPE_GROUP) { |
|
| 575 | - $this->groupCreateChecks($share); |
|
| 576 | - } else if ($share->getShareType() === \OCP\Share::SHARE_TYPE_LINK) { |
|
| 577 | - $this->linkCreateChecks($share); |
|
| 578 | - $this->setLinkParent($share); |
|
| 579 | - |
|
| 580 | - /* |
|
| 560 | + $storage = $share->getNode()->getStorage(); |
|
| 561 | + if ($storage->instanceOfStorage('OCA\Files_Sharing\External\Storage')) { |
|
| 562 | + $parent = $share->getNode()->getParent(); |
|
| 563 | + while($parent->getStorage()->instanceOfStorage('OCA\Files_Sharing\External\Storage')) { |
|
| 564 | + $parent = $parent->getParent(); |
|
| 565 | + } |
|
| 566 | + $share->setShareOwner($parent->getOwner()->getUID()); |
|
| 567 | + } else { |
|
| 568 | + $share->setShareOwner($share->getNode()->getOwner()->getUID()); |
|
| 569 | + } |
|
| 570 | + |
|
| 571 | + //Verify share type |
|
| 572 | + if ($share->getShareType() === \OCP\Share::SHARE_TYPE_USER) { |
|
| 573 | + $this->userCreateChecks($share); |
|
| 574 | + } else if ($share->getShareType() === \OCP\Share::SHARE_TYPE_GROUP) { |
|
| 575 | + $this->groupCreateChecks($share); |
|
| 576 | + } else if ($share->getShareType() === \OCP\Share::SHARE_TYPE_LINK) { |
|
| 577 | + $this->linkCreateChecks($share); |
|
| 578 | + $this->setLinkParent($share); |
|
| 579 | + |
|
| 580 | + /* |
|
| 581 | 581 | * For now ignore a set token. |
| 582 | 582 | */ |
| 583 | - $share->setToken( |
|
| 584 | - $this->secureRandom->generate( |
|
| 585 | - \OC\Share\Constants::TOKEN_LENGTH, |
|
| 586 | - \OCP\Security\ISecureRandom::CHAR_LOWER. |
|
| 587 | - \OCP\Security\ISecureRandom::CHAR_UPPER. |
|
| 588 | - \OCP\Security\ISecureRandom::CHAR_DIGITS |
|
| 589 | - ) |
|
| 590 | - ); |
|
| 591 | - |
|
| 592 | - //Verify the expiration date |
|
| 593 | - $this->validateExpirationDate($share); |
|
| 594 | - |
|
| 595 | - //Verify the password |
|
| 596 | - $this->verifyPassword($share->getPassword()); |
|
| 597 | - |
|
| 598 | - // If a password is set. Hash it! |
|
| 599 | - if ($share->getPassword() !== null) { |
|
| 600 | - $share->setPassword($this->hasher->hash($share->getPassword())); |
|
| 601 | - } |
|
| 602 | - } else if ($share->getShareType() === \OCP\Share::SHARE_TYPE_EMAIL) { |
|
| 603 | - $share->setToken( |
|
| 604 | - $this->secureRandom->generate( |
|
| 605 | - \OC\Share\Constants::TOKEN_LENGTH, |
|
| 606 | - \OCP\Security\ISecureRandom::CHAR_LOWER. |
|
| 607 | - \OCP\Security\ISecureRandom::CHAR_UPPER. |
|
| 608 | - \OCP\Security\ISecureRandom::CHAR_DIGITS |
|
| 609 | - ) |
|
| 610 | - ); |
|
| 611 | - } |
|
| 612 | - |
|
| 613 | - // Cannot share with the owner |
|
| 614 | - if ($share->getShareType() === \OCP\Share::SHARE_TYPE_USER && |
|
| 615 | - $share->getSharedWith() === $share->getShareOwner()) { |
|
| 616 | - throw new \InvalidArgumentException('Can’t share with the share owner'); |
|
| 617 | - } |
|
| 618 | - |
|
| 619 | - // Generate the target |
|
| 620 | - $target = $this->config->getSystemValue('share_folder', '/') .'/'. $share->getNode()->getName(); |
|
| 621 | - $target = \OC\Files\Filesystem::normalizePath($target); |
|
| 622 | - $share->setTarget($target); |
|
| 623 | - |
|
| 624 | - // Pre share hook |
|
| 625 | - $run = true; |
|
| 626 | - $error = ''; |
|
| 627 | - $preHookData = [ |
|
| 628 | - 'itemType' => $share->getNode() instanceof \OCP\Files\File ? 'file' : 'folder', |
|
| 629 | - 'itemSource' => $share->getNode()->getId(), |
|
| 630 | - 'shareType' => $share->getShareType(), |
|
| 631 | - 'uidOwner' => $share->getSharedBy(), |
|
| 632 | - 'permissions' => $share->getPermissions(), |
|
| 633 | - 'fileSource' => $share->getNode()->getId(), |
|
| 634 | - 'expiration' => $share->getExpirationDate(), |
|
| 635 | - 'token' => $share->getToken(), |
|
| 636 | - 'itemTarget' => $share->getTarget(), |
|
| 637 | - 'shareWith' => $share->getSharedWith(), |
|
| 638 | - 'run' => &$run, |
|
| 639 | - 'error' => &$error, |
|
| 640 | - ]; |
|
| 641 | - \OC_Hook::emit('OCP\Share', 'pre_shared', $preHookData); |
|
| 642 | - |
|
| 643 | - if ($run === false) { |
|
| 644 | - throw new \Exception($error); |
|
| 645 | - } |
|
| 646 | - |
|
| 647 | - $oldShare = $share; |
|
| 648 | - $provider = $this->factory->getProviderForType($share->getShareType()); |
|
| 649 | - $share = $provider->create($share); |
|
| 650 | - //reuse the node we already have |
|
| 651 | - $share->setNode($oldShare->getNode()); |
|
| 652 | - |
|
| 653 | - // Post share hook |
|
| 654 | - $postHookData = [ |
|
| 655 | - 'itemType' => $share->getNode() instanceof \OCP\Files\File ? 'file' : 'folder', |
|
| 656 | - 'itemSource' => $share->getNode()->getId(), |
|
| 657 | - 'shareType' => $share->getShareType(), |
|
| 658 | - 'uidOwner' => $share->getSharedBy(), |
|
| 659 | - 'permissions' => $share->getPermissions(), |
|
| 660 | - 'fileSource' => $share->getNode()->getId(), |
|
| 661 | - 'expiration' => $share->getExpirationDate(), |
|
| 662 | - 'token' => $share->getToken(), |
|
| 663 | - 'id' => $share->getId(), |
|
| 664 | - 'shareWith' => $share->getSharedWith(), |
|
| 665 | - 'itemTarget' => $share->getTarget(), |
|
| 666 | - 'fileTarget' => $share->getTarget(), |
|
| 667 | - ]; |
|
| 668 | - |
|
| 669 | - \OC_Hook::emit('OCP\Share', 'post_shared', $postHookData); |
|
| 670 | - |
|
| 671 | - return $share; |
|
| 672 | - } |
|
| 673 | - |
|
| 674 | - /** |
|
| 675 | - * Update a share |
|
| 676 | - * |
|
| 677 | - * @param \OCP\Share\IShare $share |
|
| 678 | - * @return \OCP\Share\IShare The share object |
|
| 679 | - * @throws \InvalidArgumentException |
|
| 680 | - */ |
|
| 681 | - public function updateShare(\OCP\Share\IShare $share) { |
|
| 682 | - $expirationDateUpdated = false; |
|
| 683 | - |
|
| 684 | - $this->canShare($share); |
|
| 685 | - |
|
| 686 | - try { |
|
| 687 | - $originalShare = $this->getShareById($share->getFullId()); |
|
| 688 | - } catch (\UnexpectedValueException $e) { |
|
| 689 | - throw new \InvalidArgumentException('Share does not have a full id'); |
|
| 690 | - } |
|
| 691 | - |
|
| 692 | - // We can't change the share type! |
|
| 693 | - if ($share->getShareType() !== $originalShare->getShareType()) { |
|
| 694 | - throw new \InvalidArgumentException('Can’t change share type'); |
|
| 695 | - } |
|
| 696 | - |
|
| 697 | - // We can only change the recipient on user shares |
|
| 698 | - if ($share->getSharedWith() !== $originalShare->getSharedWith() && |
|
| 699 | - $share->getShareType() !== \OCP\Share::SHARE_TYPE_USER) { |
|
| 700 | - throw new \InvalidArgumentException('Can only update recipient on user shares'); |
|
| 701 | - } |
|
| 702 | - |
|
| 703 | - // Cannot share with the owner |
|
| 704 | - if ($share->getShareType() === \OCP\Share::SHARE_TYPE_USER && |
|
| 705 | - $share->getSharedWith() === $share->getShareOwner()) { |
|
| 706 | - throw new \InvalidArgumentException('Can’t share with the share owner'); |
|
| 707 | - } |
|
| 708 | - |
|
| 709 | - $this->generalCreateChecks($share); |
|
| 710 | - |
|
| 711 | - if ($share->getShareType() === \OCP\Share::SHARE_TYPE_USER) { |
|
| 712 | - $this->userCreateChecks($share); |
|
| 713 | - } else if ($share->getShareType() === \OCP\Share::SHARE_TYPE_GROUP) { |
|
| 714 | - $this->groupCreateChecks($share); |
|
| 715 | - } else if ($share->getShareType() === \OCP\Share::SHARE_TYPE_LINK) { |
|
| 716 | - $this->linkCreateChecks($share); |
|
| 717 | - |
|
| 718 | - $this->updateSharePasswordIfNeeded($share, $originalShare); |
|
| 719 | - |
|
| 720 | - if ($share->getExpirationDate() != $originalShare->getExpirationDate()) { |
|
| 721 | - //Verify the expiration date |
|
| 722 | - $this->validateExpirationDate($share); |
|
| 723 | - $expirationDateUpdated = true; |
|
| 724 | - } |
|
| 725 | - } else if ($share->getShareType() === \OCP\Share::SHARE_TYPE_EMAIL) { |
|
| 726 | - $plainTextPassword = $share->getPassword(); |
|
| 727 | - if (!$this->updateSharePasswordIfNeeded($share, $originalShare)) { |
|
| 728 | - $plainTextPassword = null; |
|
| 729 | - } |
|
| 730 | - } |
|
| 731 | - |
|
| 732 | - $this->pathCreateChecks($share->getNode()); |
|
| 733 | - |
|
| 734 | - // Now update the share! |
|
| 735 | - $provider = $this->factory->getProviderForType($share->getShareType()); |
|
| 736 | - if ($share->getShareType() === \OCP\Share::SHARE_TYPE_EMAIL) { |
|
| 737 | - $share = $provider->update($share, $plainTextPassword); |
|
| 738 | - } else { |
|
| 739 | - $share = $provider->update($share); |
|
| 740 | - } |
|
| 741 | - |
|
| 742 | - if ($expirationDateUpdated === true) { |
|
| 743 | - \OC_Hook::emit('OCP\Share', 'post_set_expiration_date', [ |
|
| 744 | - 'itemType' => $share->getNode() instanceof \OCP\Files\File ? 'file' : 'folder', |
|
| 745 | - 'itemSource' => $share->getNode()->getId(), |
|
| 746 | - 'date' => $share->getExpirationDate(), |
|
| 747 | - 'uidOwner' => $share->getSharedBy(), |
|
| 748 | - ]); |
|
| 749 | - } |
|
| 750 | - |
|
| 751 | - if ($share->getPassword() !== $originalShare->getPassword()) { |
|
| 752 | - \OC_Hook::emit('OCP\Share', 'post_update_password', [ |
|
| 753 | - 'itemType' => $share->getNode() instanceof \OCP\Files\File ? 'file' : 'folder', |
|
| 754 | - 'itemSource' => $share->getNode()->getId(), |
|
| 755 | - 'uidOwner' => $share->getSharedBy(), |
|
| 756 | - 'token' => $share->getToken(), |
|
| 757 | - 'disabled' => is_null($share->getPassword()), |
|
| 758 | - ]); |
|
| 759 | - } |
|
| 760 | - |
|
| 761 | - if ($share->getPermissions() !== $originalShare->getPermissions()) { |
|
| 762 | - if ($this->userManager->userExists($share->getShareOwner())) { |
|
| 763 | - $userFolder = $this->rootFolder->getUserFolder($share->getShareOwner()); |
|
| 764 | - } else { |
|
| 765 | - $userFolder = $this->rootFolder->getUserFolder($share->getSharedBy()); |
|
| 766 | - } |
|
| 767 | - \OC_Hook::emit('OCP\Share', 'post_update_permissions', array( |
|
| 768 | - 'itemType' => $share->getNode() instanceof \OCP\Files\File ? 'file' : 'folder', |
|
| 769 | - 'itemSource' => $share->getNode()->getId(), |
|
| 770 | - 'shareType' => $share->getShareType(), |
|
| 771 | - 'shareWith' => $share->getSharedWith(), |
|
| 772 | - 'uidOwner' => $share->getSharedBy(), |
|
| 773 | - 'permissions' => $share->getPermissions(), |
|
| 774 | - 'path' => $userFolder->getRelativePath($share->getNode()->getPath()), |
|
| 775 | - )); |
|
| 776 | - } |
|
| 777 | - |
|
| 778 | - return $share; |
|
| 779 | - } |
|
| 780 | - |
|
| 781 | - /** |
|
| 782 | - * Updates the password of the given share if it is not the same as the |
|
| 783 | - * password of the original share. |
|
| 784 | - * |
|
| 785 | - * @param \OCP\Share\IShare $share the share to update its password. |
|
| 786 | - * @param \OCP\Share\IShare $originalShare the original share to compare its |
|
| 787 | - * password with. |
|
| 788 | - * @return boolean whether the password was updated or not. |
|
| 789 | - */ |
|
| 790 | - private function updateSharePasswordIfNeeded(\OCP\Share\IShare $share, \OCP\Share\IShare $originalShare) { |
|
| 791 | - // Password updated. |
|
| 792 | - if ($share->getPassword() !== $originalShare->getPassword()) { |
|
| 793 | - //Verify the password |
|
| 794 | - $this->verifyPassword($share->getPassword()); |
|
| 795 | - |
|
| 796 | - // If a password is set. Hash it! |
|
| 797 | - if ($share->getPassword() !== null) { |
|
| 798 | - $share->setPassword($this->hasher->hash($share->getPassword())); |
|
| 799 | - |
|
| 800 | - return true; |
|
| 801 | - } |
|
| 802 | - } |
|
| 803 | - |
|
| 804 | - return false; |
|
| 805 | - } |
|
| 806 | - |
|
| 807 | - /** |
|
| 808 | - * Delete all the children of this share |
|
| 809 | - * FIXME: remove once https://github.com/owncloud/core/pull/21660 is in |
|
| 810 | - * |
|
| 811 | - * @param \OCP\Share\IShare $share |
|
| 812 | - * @return \OCP\Share\IShare[] List of deleted shares |
|
| 813 | - */ |
|
| 814 | - protected function deleteChildren(\OCP\Share\IShare $share) { |
|
| 815 | - $deletedShares = []; |
|
| 816 | - |
|
| 817 | - $provider = $this->factory->getProviderForType($share->getShareType()); |
|
| 818 | - |
|
| 819 | - foreach ($provider->getChildren($share) as $child) { |
|
| 820 | - $deletedChildren = $this->deleteChildren($child); |
|
| 821 | - $deletedShares = array_merge($deletedShares, $deletedChildren); |
|
| 822 | - |
|
| 823 | - $provider->delete($child); |
|
| 824 | - $deletedShares[] = $child; |
|
| 825 | - } |
|
| 826 | - |
|
| 827 | - return $deletedShares; |
|
| 828 | - } |
|
| 829 | - |
|
| 830 | - /** |
|
| 831 | - * Delete a share |
|
| 832 | - * |
|
| 833 | - * @param \OCP\Share\IShare $share |
|
| 834 | - * @throws ShareNotFound |
|
| 835 | - * @throws \InvalidArgumentException |
|
| 836 | - */ |
|
| 837 | - public function deleteShare(\OCP\Share\IShare $share) { |
|
| 838 | - |
|
| 839 | - try { |
|
| 840 | - $share->getFullId(); |
|
| 841 | - } catch (\UnexpectedValueException $e) { |
|
| 842 | - throw new \InvalidArgumentException('Share does not have a full id'); |
|
| 843 | - } |
|
| 844 | - |
|
| 845 | - $event = new GenericEvent($share); |
|
| 846 | - $this->eventDispatcher->dispatch('OCP\Share::preUnshare', $event); |
|
| 847 | - |
|
| 848 | - // Get all children and delete them as well |
|
| 849 | - $deletedShares = $this->deleteChildren($share); |
|
| 850 | - |
|
| 851 | - // Do the actual delete |
|
| 852 | - $provider = $this->factory->getProviderForType($share->getShareType()); |
|
| 853 | - $provider->delete($share); |
|
| 854 | - |
|
| 855 | - // All the deleted shares caused by this delete |
|
| 856 | - $deletedShares[] = $share; |
|
| 857 | - |
|
| 858 | - // Emit post hook |
|
| 859 | - $event->setArgument('deletedShares', $deletedShares); |
|
| 860 | - $this->eventDispatcher->dispatch('OCP\Share::postUnshare', $event); |
|
| 861 | - } |
|
| 862 | - |
|
| 863 | - |
|
| 864 | - /** |
|
| 865 | - * Unshare a file as the recipient. |
|
| 866 | - * This can be different from a regular delete for example when one of |
|
| 867 | - * the users in a groups deletes that share. But the provider should |
|
| 868 | - * handle this. |
|
| 869 | - * |
|
| 870 | - * @param \OCP\Share\IShare $share |
|
| 871 | - * @param string $recipientId |
|
| 872 | - */ |
|
| 873 | - public function deleteFromSelf(\OCP\Share\IShare $share, $recipientId) { |
|
| 874 | - list($providerId, ) = $this->splitFullId($share->getFullId()); |
|
| 875 | - $provider = $this->factory->getProvider($providerId); |
|
| 876 | - |
|
| 877 | - $provider->deleteFromSelf($share, $recipientId); |
|
| 878 | - } |
|
| 879 | - |
|
| 880 | - /** |
|
| 881 | - * @inheritdoc |
|
| 882 | - */ |
|
| 883 | - public function moveShare(\OCP\Share\IShare $share, $recipientId) { |
|
| 884 | - if ($share->getShareType() === \OCP\Share::SHARE_TYPE_LINK) { |
|
| 885 | - throw new \InvalidArgumentException('Can’t change target of link share'); |
|
| 886 | - } |
|
| 887 | - |
|
| 888 | - if ($share->getShareType() === \OCP\Share::SHARE_TYPE_USER && $share->getSharedWith() !== $recipientId) { |
|
| 889 | - throw new \InvalidArgumentException('Invalid recipient'); |
|
| 890 | - } |
|
| 891 | - |
|
| 892 | - if ($share->getShareType() === \OCP\Share::SHARE_TYPE_GROUP) { |
|
| 893 | - $sharedWith = $this->groupManager->get($share->getSharedWith()); |
|
| 894 | - if (is_null($sharedWith)) { |
|
| 895 | - throw new \InvalidArgumentException('Group "' . $share->getSharedWith() . '" does not exist'); |
|
| 896 | - } |
|
| 897 | - $recipient = $this->userManager->get($recipientId); |
|
| 898 | - if (!$sharedWith->inGroup($recipient)) { |
|
| 899 | - throw new \InvalidArgumentException('Invalid recipient'); |
|
| 900 | - } |
|
| 901 | - } |
|
| 902 | - |
|
| 903 | - list($providerId, ) = $this->splitFullId($share->getFullId()); |
|
| 904 | - $provider = $this->factory->getProvider($providerId); |
|
| 905 | - |
|
| 906 | - $provider->move($share, $recipientId); |
|
| 907 | - } |
|
| 908 | - |
|
| 909 | - public function getSharesInFolder($userId, Folder $node, $reshares = false) { |
|
| 910 | - $providers = $this->factory->getAllProviders(); |
|
| 911 | - |
|
| 912 | - return array_reduce($providers, function($shares, IShareProvider $provider) use ($userId, $node, $reshares) { |
|
| 913 | - $newShares = $provider->getSharesInFolder($userId, $node, $reshares); |
|
| 914 | - foreach ($newShares as $fid => $data) { |
|
| 915 | - if (!isset($shares[$fid])) { |
|
| 916 | - $shares[$fid] = []; |
|
| 917 | - } |
|
| 918 | - |
|
| 919 | - $shares[$fid] = array_merge($shares[$fid], $data); |
|
| 920 | - } |
|
| 921 | - return $shares; |
|
| 922 | - }, []); |
|
| 923 | - } |
|
| 924 | - |
|
| 925 | - /** |
|
| 926 | - * @inheritdoc |
|
| 927 | - */ |
|
| 928 | - public function getSharesBy($userId, $shareType, $path = null, $reshares = false, $limit = 50, $offset = 0) { |
|
| 929 | - if ($path !== null && |
|
| 930 | - !($path instanceof \OCP\Files\File) && |
|
| 931 | - !($path instanceof \OCP\Files\Folder)) { |
|
| 932 | - throw new \InvalidArgumentException('invalid path'); |
|
| 933 | - } |
|
| 934 | - |
|
| 935 | - try { |
|
| 936 | - $provider = $this->factory->getProviderForType($shareType); |
|
| 937 | - } catch (ProviderException $e) { |
|
| 938 | - return []; |
|
| 939 | - } |
|
| 940 | - |
|
| 941 | - $shares = $provider->getSharesBy($userId, $shareType, $path, $reshares, $limit, $offset); |
|
| 942 | - |
|
| 943 | - /* |
|
| 583 | + $share->setToken( |
|
| 584 | + $this->secureRandom->generate( |
|
| 585 | + \OC\Share\Constants::TOKEN_LENGTH, |
|
| 586 | + \OCP\Security\ISecureRandom::CHAR_LOWER. |
|
| 587 | + \OCP\Security\ISecureRandom::CHAR_UPPER. |
|
| 588 | + \OCP\Security\ISecureRandom::CHAR_DIGITS |
|
| 589 | + ) |
|
| 590 | + ); |
|
| 591 | + |
|
| 592 | + //Verify the expiration date |
|
| 593 | + $this->validateExpirationDate($share); |
|
| 594 | + |
|
| 595 | + //Verify the password |
|
| 596 | + $this->verifyPassword($share->getPassword()); |
|
| 597 | + |
|
| 598 | + // If a password is set. Hash it! |
|
| 599 | + if ($share->getPassword() !== null) { |
|
| 600 | + $share->setPassword($this->hasher->hash($share->getPassword())); |
|
| 601 | + } |
|
| 602 | + } else if ($share->getShareType() === \OCP\Share::SHARE_TYPE_EMAIL) { |
|
| 603 | + $share->setToken( |
|
| 604 | + $this->secureRandom->generate( |
|
| 605 | + \OC\Share\Constants::TOKEN_LENGTH, |
|
| 606 | + \OCP\Security\ISecureRandom::CHAR_LOWER. |
|
| 607 | + \OCP\Security\ISecureRandom::CHAR_UPPER. |
|
| 608 | + \OCP\Security\ISecureRandom::CHAR_DIGITS |
|
| 609 | + ) |
|
| 610 | + ); |
|
| 611 | + } |
|
| 612 | + |
|
| 613 | + // Cannot share with the owner |
|
| 614 | + if ($share->getShareType() === \OCP\Share::SHARE_TYPE_USER && |
|
| 615 | + $share->getSharedWith() === $share->getShareOwner()) { |
|
| 616 | + throw new \InvalidArgumentException('Can’t share with the share owner'); |
|
| 617 | + } |
|
| 618 | + |
|
| 619 | + // Generate the target |
|
| 620 | + $target = $this->config->getSystemValue('share_folder', '/') .'/'. $share->getNode()->getName(); |
|
| 621 | + $target = \OC\Files\Filesystem::normalizePath($target); |
|
| 622 | + $share->setTarget($target); |
|
| 623 | + |
|
| 624 | + // Pre share hook |
|
| 625 | + $run = true; |
|
| 626 | + $error = ''; |
|
| 627 | + $preHookData = [ |
|
| 628 | + 'itemType' => $share->getNode() instanceof \OCP\Files\File ? 'file' : 'folder', |
|
| 629 | + 'itemSource' => $share->getNode()->getId(), |
|
| 630 | + 'shareType' => $share->getShareType(), |
|
| 631 | + 'uidOwner' => $share->getSharedBy(), |
|
| 632 | + 'permissions' => $share->getPermissions(), |
|
| 633 | + 'fileSource' => $share->getNode()->getId(), |
|
| 634 | + 'expiration' => $share->getExpirationDate(), |
|
| 635 | + 'token' => $share->getToken(), |
|
| 636 | + 'itemTarget' => $share->getTarget(), |
|
| 637 | + 'shareWith' => $share->getSharedWith(), |
|
| 638 | + 'run' => &$run, |
|
| 639 | + 'error' => &$error, |
|
| 640 | + ]; |
|
| 641 | + \OC_Hook::emit('OCP\Share', 'pre_shared', $preHookData); |
|
| 642 | + |
|
| 643 | + if ($run === false) { |
|
| 644 | + throw new \Exception($error); |
|
| 645 | + } |
|
| 646 | + |
|
| 647 | + $oldShare = $share; |
|
| 648 | + $provider = $this->factory->getProviderForType($share->getShareType()); |
|
| 649 | + $share = $provider->create($share); |
|
| 650 | + //reuse the node we already have |
|
| 651 | + $share->setNode($oldShare->getNode()); |
|
| 652 | + |
|
| 653 | + // Post share hook |
|
| 654 | + $postHookData = [ |
|
| 655 | + 'itemType' => $share->getNode() instanceof \OCP\Files\File ? 'file' : 'folder', |
|
| 656 | + 'itemSource' => $share->getNode()->getId(), |
|
| 657 | + 'shareType' => $share->getShareType(), |
|
| 658 | + 'uidOwner' => $share->getSharedBy(), |
|
| 659 | + 'permissions' => $share->getPermissions(), |
|
| 660 | + 'fileSource' => $share->getNode()->getId(), |
|
| 661 | + 'expiration' => $share->getExpirationDate(), |
|
| 662 | + 'token' => $share->getToken(), |
|
| 663 | + 'id' => $share->getId(), |
|
| 664 | + 'shareWith' => $share->getSharedWith(), |
|
| 665 | + 'itemTarget' => $share->getTarget(), |
|
| 666 | + 'fileTarget' => $share->getTarget(), |
|
| 667 | + ]; |
|
| 668 | + |
|
| 669 | + \OC_Hook::emit('OCP\Share', 'post_shared', $postHookData); |
|
| 670 | + |
|
| 671 | + return $share; |
|
| 672 | + } |
|
| 673 | + |
|
| 674 | + /** |
|
| 675 | + * Update a share |
|
| 676 | + * |
|
| 677 | + * @param \OCP\Share\IShare $share |
|
| 678 | + * @return \OCP\Share\IShare The share object |
|
| 679 | + * @throws \InvalidArgumentException |
|
| 680 | + */ |
|
| 681 | + public function updateShare(\OCP\Share\IShare $share) { |
|
| 682 | + $expirationDateUpdated = false; |
|
| 683 | + |
|
| 684 | + $this->canShare($share); |
|
| 685 | + |
|
| 686 | + try { |
|
| 687 | + $originalShare = $this->getShareById($share->getFullId()); |
|
| 688 | + } catch (\UnexpectedValueException $e) { |
|
| 689 | + throw new \InvalidArgumentException('Share does not have a full id'); |
|
| 690 | + } |
|
| 691 | + |
|
| 692 | + // We can't change the share type! |
|
| 693 | + if ($share->getShareType() !== $originalShare->getShareType()) { |
|
| 694 | + throw new \InvalidArgumentException('Can’t change share type'); |
|
| 695 | + } |
|
| 696 | + |
|
| 697 | + // We can only change the recipient on user shares |
|
| 698 | + if ($share->getSharedWith() !== $originalShare->getSharedWith() && |
|
| 699 | + $share->getShareType() !== \OCP\Share::SHARE_TYPE_USER) { |
|
| 700 | + throw new \InvalidArgumentException('Can only update recipient on user shares'); |
|
| 701 | + } |
|
| 702 | + |
|
| 703 | + // Cannot share with the owner |
|
| 704 | + if ($share->getShareType() === \OCP\Share::SHARE_TYPE_USER && |
|
| 705 | + $share->getSharedWith() === $share->getShareOwner()) { |
|
| 706 | + throw new \InvalidArgumentException('Can’t share with the share owner'); |
|
| 707 | + } |
|
| 708 | + |
|
| 709 | + $this->generalCreateChecks($share); |
|
| 710 | + |
|
| 711 | + if ($share->getShareType() === \OCP\Share::SHARE_TYPE_USER) { |
|
| 712 | + $this->userCreateChecks($share); |
|
| 713 | + } else if ($share->getShareType() === \OCP\Share::SHARE_TYPE_GROUP) { |
|
| 714 | + $this->groupCreateChecks($share); |
|
| 715 | + } else if ($share->getShareType() === \OCP\Share::SHARE_TYPE_LINK) { |
|
| 716 | + $this->linkCreateChecks($share); |
|
| 717 | + |
|
| 718 | + $this->updateSharePasswordIfNeeded($share, $originalShare); |
|
| 719 | + |
|
| 720 | + if ($share->getExpirationDate() != $originalShare->getExpirationDate()) { |
|
| 721 | + //Verify the expiration date |
|
| 722 | + $this->validateExpirationDate($share); |
|
| 723 | + $expirationDateUpdated = true; |
|
| 724 | + } |
|
| 725 | + } else if ($share->getShareType() === \OCP\Share::SHARE_TYPE_EMAIL) { |
|
| 726 | + $plainTextPassword = $share->getPassword(); |
|
| 727 | + if (!$this->updateSharePasswordIfNeeded($share, $originalShare)) { |
|
| 728 | + $plainTextPassword = null; |
|
| 729 | + } |
|
| 730 | + } |
|
| 731 | + |
|
| 732 | + $this->pathCreateChecks($share->getNode()); |
|
| 733 | + |
|
| 734 | + // Now update the share! |
|
| 735 | + $provider = $this->factory->getProviderForType($share->getShareType()); |
|
| 736 | + if ($share->getShareType() === \OCP\Share::SHARE_TYPE_EMAIL) { |
|
| 737 | + $share = $provider->update($share, $plainTextPassword); |
|
| 738 | + } else { |
|
| 739 | + $share = $provider->update($share); |
|
| 740 | + } |
|
| 741 | + |
|
| 742 | + if ($expirationDateUpdated === true) { |
|
| 743 | + \OC_Hook::emit('OCP\Share', 'post_set_expiration_date', [ |
|
| 744 | + 'itemType' => $share->getNode() instanceof \OCP\Files\File ? 'file' : 'folder', |
|
| 745 | + 'itemSource' => $share->getNode()->getId(), |
|
| 746 | + 'date' => $share->getExpirationDate(), |
|
| 747 | + 'uidOwner' => $share->getSharedBy(), |
|
| 748 | + ]); |
|
| 749 | + } |
|
| 750 | + |
|
| 751 | + if ($share->getPassword() !== $originalShare->getPassword()) { |
|
| 752 | + \OC_Hook::emit('OCP\Share', 'post_update_password', [ |
|
| 753 | + 'itemType' => $share->getNode() instanceof \OCP\Files\File ? 'file' : 'folder', |
|
| 754 | + 'itemSource' => $share->getNode()->getId(), |
|
| 755 | + 'uidOwner' => $share->getSharedBy(), |
|
| 756 | + 'token' => $share->getToken(), |
|
| 757 | + 'disabled' => is_null($share->getPassword()), |
|
| 758 | + ]); |
|
| 759 | + } |
|
| 760 | + |
|
| 761 | + if ($share->getPermissions() !== $originalShare->getPermissions()) { |
|
| 762 | + if ($this->userManager->userExists($share->getShareOwner())) { |
|
| 763 | + $userFolder = $this->rootFolder->getUserFolder($share->getShareOwner()); |
|
| 764 | + } else { |
|
| 765 | + $userFolder = $this->rootFolder->getUserFolder($share->getSharedBy()); |
|
| 766 | + } |
|
| 767 | + \OC_Hook::emit('OCP\Share', 'post_update_permissions', array( |
|
| 768 | + 'itemType' => $share->getNode() instanceof \OCP\Files\File ? 'file' : 'folder', |
|
| 769 | + 'itemSource' => $share->getNode()->getId(), |
|
| 770 | + 'shareType' => $share->getShareType(), |
|
| 771 | + 'shareWith' => $share->getSharedWith(), |
|
| 772 | + 'uidOwner' => $share->getSharedBy(), |
|
| 773 | + 'permissions' => $share->getPermissions(), |
|
| 774 | + 'path' => $userFolder->getRelativePath($share->getNode()->getPath()), |
|
| 775 | + )); |
|
| 776 | + } |
|
| 777 | + |
|
| 778 | + return $share; |
|
| 779 | + } |
|
| 780 | + |
|
| 781 | + /** |
|
| 782 | + * Updates the password of the given share if it is not the same as the |
|
| 783 | + * password of the original share. |
|
| 784 | + * |
|
| 785 | + * @param \OCP\Share\IShare $share the share to update its password. |
|
| 786 | + * @param \OCP\Share\IShare $originalShare the original share to compare its |
|
| 787 | + * password with. |
|
| 788 | + * @return boolean whether the password was updated or not. |
|
| 789 | + */ |
|
| 790 | + private function updateSharePasswordIfNeeded(\OCP\Share\IShare $share, \OCP\Share\IShare $originalShare) { |
|
| 791 | + // Password updated. |
|
| 792 | + if ($share->getPassword() !== $originalShare->getPassword()) { |
|
| 793 | + //Verify the password |
|
| 794 | + $this->verifyPassword($share->getPassword()); |
|
| 795 | + |
|
| 796 | + // If a password is set. Hash it! |
|
| 797 | + if ($share->getPassword() !== null) { |
|
| 798 | + $share->setPassword($this->hasher->hash($share->getPassword())); |
|
| 799 | + |
|
| 800 | + return true; |
|
| 801 | + } |
|
| 802 | + } |
|
| 803 | + |
|
| 804 | + return false; |
|
| 805 | + } |
|
| 806 | + |
|
| 807 | + /** |
|
| 808 | + * Delete all the children of this share |
|
| 809 | + * FIXME: remove once https://github.com/owncloud/core/pull/21660 is in |
|
| 810 | + * |
|
| 811 | + * @param \OCP\Share\IShare $share |
|
| 812 | + * @return \OCP\Share\IShare[] List of deleted shares |
|
| 813 | + */ |
|
| 814 | + protected function deleteChildren(\OCP\Share\IShare $share) { |
|
| 815 | + $deletedShares = []; |
|
| 816 | + |
|
| 817 | + $provider = $this->factory->getProviderForType($share->getShareType()); |
|
| 818 | + |
|
| 819 | + foreach ($provider->getChildren($share) as $child) { |
|
| 820 | + $deletedChildren = $this->deleteChildren($child); |
|
| 821 | + $deletedShares = array_merge($deletedShares, $deletedChildren); |
|
| 822 | + |
|
| 823 | + $provider->delete($child); |
|
| 824 | + $deletedShares[] = $child; |
|
| 825 | + } |
|
| 826 | + |
|
| 827 | + return $deletedShares; |
|
| 828 | + } |
|
| 829 | + |
|
| 830 | + /** |
|
| 831 | + * Delete a share |
|
| 832 | + * |
|
| 833 | + * @param \OCP\Share\IShare $share |
|
| 834 | + * @throws ShareNotFound |
|
| 835 | + * @throws \InvalidArgumentException |
|
| 836 | + */ |
|
| 837 | + public function deleteShare(\OCP\Share\IShare $share) { |
|
| 838 | + |
|
| 839 | + try { |
|
| 840 | + $share->getFullId(); |
|
| 841 | + } catch (\UnexpectedValueException $e) { |
|
| 842 | + throw new \InvalidArgumentException('Share does not have a full id'); |
|
| 843 | + } |
|
| 844 | + |
|
| 845 | + $event = new GenericEvent($share); |
|
| 846 | + $this->eventDispatcher->dispatch('OCP\Share::preUnshare', $event); |
|
| 847 | + |
|
| 848 | + // Get all children and delete them as well |
|
| 849 | + $deletedShares = $this->deleteChildren($share); |
|
| 850 | + |
|
| 851 | + // Do the actual delete |
|
| 852 | + $provider = $this->factory->getProviderForType($share->getShareType()); |
|
| 853 | + $provider->delete($share); |
|
| 854 | + |
|
| 855 | + // All the deleted shares caused by this delete |
|
| 856 | + $deletedShares[] = $share; |
|
| 857 | + |
|
| 858 | + // Emit post hook |
|
| 859 | + $event->setArgument('deletedShares', $deletedShares); |
|
| 860 | + $this->eventDispatcher->dispatch('OCP\Share::postUnshare', $event); |
|
| 861 | + } |
|
| 862 | + |
|
| 863 | + |
|
| 864 | + /** |
|
| 865 | + * Unshare a file as the recipient. |
|
| 866 | + * This can be different from a regular delete for example when one of |
|
| 867 | + * the users in a groups deletes that share. But the provider should |
|
| 868 | + * handle this. |
|
| 869 | + * |
|
| 870 | + * @param \OCP\Share\IShare $share |
|
| 871 | + * @param string $recipientId |
|
| 872 | + */ |
|
| 873 | + public function deleteFromSelf(\OCP\Share\IShare $share, $recipientId) { |
|
| 874 | + list($providerId, ) = $this->splitFullId($share->getFullId()); |
|
| 875 | + $provider = $this->factory->getProvider($providerId); |
|
| 876 | + |
|
| 877 | + $provider->deleteFromSelf($share, $recipientId); |
|
| 878 | + } |
|
| 879 | + |
|
| 880 | + /** |
|
| 881 | + * @inheritdoc |
|
| 882 | + */ |
|
| 883 | + public function moveShare(\OCP\Share\IShare $share, $recipientId) { |
|
| 884 | + if ($share->getShareType() === \OCP\Share::SHARE_TYPE_LINK) { |
|
| 885 | + throw new \InvalidArgumentException('Can’t change target of link share'); |
|
| 886 | + } |
|
| 887 | + |
|
| 888 | + if ($share->getShareType() === \OCP\Share::SHARE_TYPE_USER && $share->getSharedWith() !== $recipientId) { |
|
| 889 | + throw new \InvalidArgumentException('Invalid recipient'); |
|
| 890 | + } |
|
| 891 | + |
|
| 892 | + if ($share->getShareType() === \OCP\Share::SHARE_TYPE_GROUP) { |
|
| 893 | + $sharedWith = $this->groupManager->get($share->getSharedWith()); |
|
| 894 | + if (is_null($sharedWith)) { |
|
| 895 | + throw new \InvalidArgumentException('Group "' . $share->getSharedWith() . '" does not exist'); |
|
| 896 | + } |
|
| 897 | + $recipient = $this->userManager->get($recipientId); |
|
| 898 | + if (!$sharedWith->inGroup($recipient)) { |
|
| 899 | + throw new \InvalidArgumentException('Invalid recipient'); |
|
| 900 | + } |
|
| 901 | + } |
|
| 902 | + |
|
| 903 | + list($providerId, ) = $this->splitFullId($share->getFullId()); |
|
| 904 | + $provider = $this->factory->getProvider($providerId); |
|
| 905 | + |
|
| 906 | + $provider->move($share, $recipientId); |
|
| 907 | + } |
|
| 908 | + |
|
| 909 | + public function getSharesInFolder($userId, Folder $node, $reshares = false) { |
|
| 910 | + $providers = $this->factory->getAllProviders(); |
|
| 911 | + |
|
| 912 | + return array_reduce($providers, function($shares, IShareProvider $provider) use ($userId, $node, $reshares) { |
|
| 913 | + $newShares = $provider->getSharesInFolder($userId, $node, $reshares); |
|
| 914 | + foreach ($newShares as $fid => $data) { |
|
| 915 | + if (!isset($shares[$fid])) { |
|
| 916 | + $shares[$fid] = []; |
|
| 917 | + } |
|
| 918 | + |
|
| 919 | + $shares[$fid] = array_merge($shares[$fid], $data); |
|
| 920 | + } |
|
| 921 | + return $shares; |
|
| 922 | + }, []); |
|
| 923 | + } |
|
| 924 | + |
|
| 925 | + /** |
|
| 926 | + * @inheritdoc |
|
| 927 | + */ |
|
| 928 | + public function getSharesBy($userId, $shareType, $path = null, $reshares = false, $limit = 50, $offset = 0) { |
|
| 929 | + if ($path !== null && |
|
| 930 | + !($path instanceof \OCP\Files\File) && |
|
| 931 | + !($path instanceof \OCP\Files\Folder)) { |
|
| 932 | + throw new \InvalidArgumentException('invalid path'); |
|
| 933 | + } |
|
| 934 | + |
|
| 935 | + try { |
|
| 936 | + $provider = $this->factory->getProviderForType($shareType); |
|
| 937 | + } catch (ProviderException $e) { |
|
| 938 | + return []; |
|
| 939 | + } |
|
| 940 | + |
|
| 941 | + $shares = $provider->getSharesBy($userId, $shareType, $path, $reshares, $limit, $offset); |
|
| 942 | + |
|
| 943 | + /* |
|
| 944 | 944 | * Work around so we don't return expired shares but still follow |
| 945 | 945 | * proper pagination. |
| 946 | 946 | */ |
| 947 | 947 | |
| 948 | - $shares2 = []; |
|
| 949 | - |
|
| 950 | - while(true) { |
|
| 951 | - $added = 0; |
|
| 952 | - foreach ($shares as $share) { |
|
| 953 | - |
|
| 954 | - try { |
|
| 955 | - $this->checkExpireDate($share); |
|
| 956 | - } catch (ShareNotFound $e) { |
|
| 957 | - //Ignore since this basically means the share is deleted |
|
| 958 | - continue; |
|
| 959 | - } |
|
| 960 | - |
|
| 961 | - $added++; |
|
| 962 | - $shares2[] = $share; |
|
| 963 | - |
|
| 964 | - if (count($shares2) === $limit) { |
|
| 965 | - break; |
|
| 966 | - } |
|
| 967 | - } |
|
| 968 | - |
|
| 969 | - if (count($shares2) === $limit) { |
|
| 970 | - break; |
|
| 971 | - } |
|
| 972 | - |
|
| 973 | - // If there was no limit on the select we are done |
|
| 974 | - if ($limit === -1) { |
|
| 975 | - break; |
|
| 976 | - } |
|
| 977 | - |
|
| 978 | - $offset += $added; |
|
| 979 | - |
|
| 980 | - // Fetch again $limit shares |
|
| 981 | - $shares = $provider->getSharesBy($userId, $shareType, $path, $reshares, $limit, $offset); |
|
| 982 | - |
|
| 983 | - // No more shares means we are done |
|
| 984 | - if (empty($shares)) { |
|
| 985 | - break; |
|
| 986 | - } |
|
| 987 | - } |
|
| 988 | - |
|
| 989 | - $shares = $shares2; |
|
| 990 | - |
|
| 991 | - return $shares; |
|
| 992 | - } |
|
| 993 | - |
|
| 994 | - /** |
|
| 995 | - * @inheritdoc |
|
| 996 | - */ |
|
| 997 | - public function getSharedWith($userId, $shareType, $node = null, $limit = 50, $offset = 0) { |
|
| 998 | - try { |
|
| 999 | - $provider = $this->factory->getProviderForType($shareType); |
|
| 1000 | - } catch (ProviderException $e) { |
|
| 1001 | - return []; |
|
| 1002 | - } |
|
| 1003 | - |
|
| 1004 | - $shares = $provider->getSharedWith($userId, $shareType, $node, $limit, $offset); |
|
| 1005 | - |
|
| 1006 | - // remove all shares which are already expired |
|
| 1007 | - foreach ($shares as $key => $share) { |
|
| 1008 | - try { |
|
| 1009 | - $this->checkExpireDate($share); |
|
| 1010 | - } catch (ShareNotFound $e) { |
|
| 1011 | - unset($shares[$key]); |
|
| 1012 | - } |
|
| 1013 | - } |
|
| 1014 | - |
|
| 1015 | - return $shares; |
|
| 1016 | - } |
|
| 1017 | - |
|
| 1018 | - /** |
|
| 1019 | - * @inheritdoc |
|
| 1020 | - */ |
|
| 1021 | - public function getShareById($id, $recipient = null) { |
|
| 1022 | - if ($id === null) { |
|
| 1023 | - throw new ShareNotFound(); |
|
| 1024 | - } |
|
| 1025 | - |
|
| 1026 | - list($providerId, $id) = $this->splitFullId($id); |
|
| 1027 | - |
|
| 1028 | - try { |
|
| 1029 | - $provider = $this->factory->getProvider($providerId); |
|
| 1030 | - } catch (ProviderException $e) { |
|
| 1031 | - throw new ShareNotFound(); |
|
| 1032 | - } |
|
| 1033 | - |
|
| 1034 | - $share = $provider->getShareById($id, $recipient); |
|
| 1035 | - |
|
| 1036 | - $this->checkExpireDate($share); |
|
| 1037 | - |
|
| 1038 | - return $share; |
|
| 1039 | - } |
|
| 1040 | - |
|
| 1041 | - /** |
|
| 1042 | - * Get all the shares for a given path |
|
| 1043 | - * |
|
| 1044 | - * @param \OCP\Files\Node $path |
|
| 1045 | - * @param int $page |
|
| 1046 | - * @param int $perPage |
|
| 1047 | - * |
|
| 1048 | - * @return Share[] |
|
| 1049 | - */ |
|
| 1050 | - public function getSharesByPath(\OCP\Files\Node $path, $page=0, $perPage=50) { |
|
| 1051 | - return []; |
|
| 1052 | - } |
|
| 1053 | - |
|
| 1054 | - /** |
|
| 1055 | - * Get the share by token possible with password |
|
| 1056 | - * |
|
| 1057 | - * @param string $token |
|
| 1058 | - * @return Share |
|
| 1059 | - * |
|
| 1060 | - * @throws ShareNotFound |
|
| 1061 | - */ |
|
| 1062 | - public function getShareByToken($token) { |
|
| 1063 | - $share = null; |
|
| 1064 | - try { |
|
| 1065 | - if($this->shareApiAllowLinks()) { |
|
| 1066 | - $provider = $this->factory->getProviderForType(\OCP\Share::SHARE_TYPE_LINK); |
|
| 1067 | - $share = $provider->getShareByToken($token); |
|
| 1068 | - } |
|
| 1069 | - } catch (ProviderException $e) { |
|
| 1070 | - } catch (ShareNotFound $e) { |
|
| 1071 | - } |
|
| 1072 | - |
|
| 1073 | - |
|
| 1074 | - // If it is not a link share try to fetch a federated share by token |
|
| 1075 | - if ($share === null) { |
|
| 1076 | - try { |
|
| 1077 | - $provider = $this->factory->getProviderForType(\OCP\Share::SHARE_TYPE_REMOTE); |
|
| 1078 | - $share = $provider->getShareByToken($token); |
|
| 1079 | - } catch (ProviderException $e) { |
|
| 1080 | - } catch (ShareNotFound $e) { |
|
| 1081 | - } |
|
| 1082 | - } |
|
| 1083 | - |
|
| 1084 | - // If it is not a link share try to fetch a mail share by token |
|
| 1085 | - if ($share === null && $this->shareProviderExists(\OCP\Share::SHARE_TYPE_EMAIL)) { |
|
| 1086 | - try { |
|
| 1087 | - $provider = $this->factory->getProviderForType(\OCP\Share::SHARE_TYPE_EMAIL); |
|
| 1088 | - $share = $provider->getShareByToken($token); |
|
| 1089 | - } catch (ProviderException $e) { |
|
| 1090 | - } catch (ShareNotFound $e) { |
|
| 1091 | - } |
|
| 1092 | - } |
|
| 1093 | - |
|
| 1094 | - if ($share === null) { |
|
| 1095 | - throw new ShareNotFound($this->l->t('The requested share does not exist anymore')); |
|
| 1096 | - } |
|
| 1097 | - |
|
| 1098 | - $this->checkExpireDate($share); |
|
| 1099 | - |
|
| 1100 | - /* |
|
| 948 | + $shares2 = []; |
|
| 949 | + |
|
| 950 | + while(true) { |
|
| 951 | + $added = 0; |
|
| 952 | + foreach ($shares as $share) { |
|
| 953 | + |
|
| 954 | + try { |
|
| 955 | + $this->checkExpireDate($share); |
|
| 956 | + } catch (ShareNotFound $e) { |
|
| 957 | + //Ignore since this basically means the share is deleted |
|
| 958 | + continue; |
|
| 959 | + } |
|
| 960 | + |
|
| 961 | + $added++; |
|
| 962 | + $shares2[] = $share; |
|
| 963 | + |
|
| 964 | + if (count($shares2) === $limit) { |
|
| 965 | + break; |
|
| 966 | + } |
|
| 967 | + } |
|
| 968 | + |
|
| 969 | + if (count($shares2) === $limit) { |
|
| 970 | + break; |
|
| 971 | + } |
|
| 972 | + |
|
| 973 | + // If there was no limit on the select we are done |
|
| 974 | + if ($limit === -1) { |
|
| 975 | + break; |
|
| 976 | + } |
|
| 977 | + |
|
| 978 | + $offset += $added; |
|
| 979 | + |
|
| 980 | + // Fetch again $limit shares |
|
| 981 | + $shares = $provider->getSharesBy($userId, $shareType, $path, $reshares, $limit, $offset); |
|
| 982 | + |
|
| 983 | + // No more shares means we are done |
|
| 984 | + if (empty($shares)) { |
|
| 985 | + break; |
|
| 986 | + } |
|
| 987 | + } |
|
| 988 | + |
|
| 989 | + $shares = $shares2; |
|
| 990 | + |
|
| 991 | + return $shares; |
|
| 992 | + } |
|
| 993 | + |
|
| 994 | + /** |
|
| 995 | + * @inheritdoc |
|
| 996 | + */ |
|
| 997 | + public function getSharedWith($userId, $shareType, $node = null, $limit = 50, $offset = 0) { |
|
| 998 | + try { |
|
| 999 | + $provider = $this->factory->getProviderForType($shareType); |
|
| 1000 | + } catch (ProviderException $e) { |
|
| 1001 | + return []; |
|
| 1002 | + } |
|
| 1003 | + |
|
| 1004 | + $shares = $provider->getSharedWith($userId, $shareType, $node, $limit, $offset); |
|
| 1005 | + |
|
| 1006 | + // remove all shares which are already expired |
|
| 1007 | + foreach ($shares as $key => $share) { |
|
| 1008 | + try { |
|
| 1009 | + $this->checkExpireDate($share); |
|
| 1010 | + } catch (ShareNotFound $e) { |
|
| 1011 | + unset($shares[$key]); |
|
| 1012 | + } |
|
| 1013 | + } |
|
| 1014 | + |
|
| 1015 | + return $shares; |
|
| 1016 | + } |
|
| 1017 | + |
|
| 1018 | + /** |
|
| 1019 | + * @inheritdoc |
|
| 1020 | + */ |
|
| 1021 | + public function getShareById($id, $recipient = null) { |
|
| 1022 | + if ($id === null) { |
|
| 1023 | + throw new ShareNotFound(); |
|
| 1024 | + } |
|
| 1025 | + |
|
| 1026 | + list($providerId, $id) = $this->splitFullId($id); |
|
| 1027 | + |
|
| 1028 | + try { |
|
| 1029 | + $provider = $this->factory->getProvider($providerId); |
|
| 1030 | + } catch (ProviderException $e) { |
|
| 1031 | + throw new ShareNotFound(); |
|
| 1032 | + } |
|
| 1033 | + |
|
| 1034 | + $share = $provider->getShareById($id, $recipient); |
|
| 1035 | + |
|
| 1036 | + $this->checkExpireDate($share); |
|
| 1037 | + |
|
| 1038 | + return $share; |
|
| 1039 | + } |
|
| 1040 | + |
|
| 1041 | + /** |
|
| 1042 | + * Get all the shares for a given path |
|
| 1043 | + * |
|
| 1044 | + * @param \OCP\Files\Node $path |
|
| 1045 | + * @param int $page |
|
| 1046 | + * @param int $perPage |
|
| 1047 | + * |
|
| 1048 | + * @return Share[] |
|
| 1049 | + */ |
|
| 1050 | + public function getSharesByPath(\OCP\Files\Node $path, $page=0, $perPage=50) { |
|
| 1051 | + return []; |
|
| 1052 | + } |
|
| 1053 | + |
|
| 1054 | + /** |
|
| 1055 | + * Get the share by token possible with password |
|
| 1056 | + * |
|
| 1057 | + * @param string $token |
|
| 1058 | + * @return Share |
|
| 1059 | + * |
|
| 1060 | + * @throws ShareNotFound |
|
| 1061 | + */ |
|
| 1062 | + public function getShareByToken($token) { |
|
| 1063 | + $share = null; |
|
| 1064 | + try { |
|
| 1065 | + if($this->shareApiAllowLinks()) { |
|
| 1066 | + $provider = $this->factory->getProviderForType(\OCP\Share::SHARE_TYPE_LINK); |
|
| 1067 | + $share = $provider->getShareByToken($token); |
|
| 1068 | + } |
|
| 1069 | + } catch (ProviderException $e) { |
|
| 1070 | + } catch (ShareNotFound $e) { |
|
| 1071 | + } |
|
| 1072 | + |
|
| 1073 | + |
|
| 1074 | + // If it is not a link share try to fetch a federated share by token |
|
| 1075 | + if ($share === null) { |
|
| 1076 | + try { |
|
| 1077 | + $provider = $this->factory->getProviderForType(\OCP\Share::SHARE_TYPE_REMOTE); |
|
| 1078 | + $share = $provider->getShareByToken($token); |
|
| 1079 | + } catch (ProviderException $e) { |
|
| 1080 | + } catch (ShareNotFound $e) { |
|
| 1081 | + } |
|
| 1082 | + } |
|
| 1083 | + |
|
| 1084 | + // If it is not a link share try to fetch a mail share by token |
|
| 1085 | + if ($share === null && $this->shareProviderExists(\OCP\Share::SHARE_TYPE_EMAIL)) { |
|
| 1086 | + try { |
|
| 1087 | + $provider = $this->factory->getProviderForType(\OCP\Share::SHARE_TYPE_EMAIL); |
|
| 1088 | + $share = $provider->getShareByToken($token); |
|
| 1089 | + } catch (ProviderException $e) { |
|
| 1090 | + } catch (ShareNotFound $e) { |
|
| 1091 | + } |
|
| 1092 | + } |
|
| 1093 | + |
|
| 1094 | + if ($share === null) { |
|
| 1095 | + throw new ShareNotFound($this->l->t('The requested share does not exist anymore')); |
|
| 1096 | + } |
|
| 1097 | + |
|
| 1098 | + $this->checkExpireDate($share); |
|
| 1099 | + |
|
| 1100 | + /* |
|
| 1101 | 1101 | * Reduce the permissions for link shares if public upload is not enabled |
| 1102 | 1102 | */ |
| 1103 | - if ($share->getShareType() === \OCP\Share::SHARE_TYPE_LINK && |
|
| 1104 | - !$this->shareApiLinkAllowPublicUpload()) { |
|
| 1105 | - $share->setPermissions($share->getPermissions() & ~(\OCP\Constants::PERMISSION_CREATE | \OCP\Constants::PERMISSION_UPDATE)); |
|
| 1106 | - } |
|
| 1107 | - |
|
| 1108 | - return $share; |
|
| 1109 | - } |
|
| 1110 | - |
|
| 1111 | - protected function checkExpireDate($share) { |
|
| 1112 | - if ($share->getExpirationDate() !== null && |
|
| 1113 | - $share->getExpirationDate() <= new \DateTime()) { |
|
| 1114 | - $this->deleteShare($share); |
|
| 1115 | - throw new ShareNotFound($this->l->t('The requested share does not exist anymore')); |
|
| 1116 | - } |
|
| 1117 | - |
|
| 1118 | - } |
|
| 1119 | - |
|
| 1120 | - /** |
|
| 1121 | - * Verify the password of a public share |
|
| 1122 | - * |
|
| 1123 | - * @param \OCP\Share\IShare $share |
|
| 1124 | - * @param string $password |
|
| 1125 | - * @return bool |
|
| 1126 | - */ |
|
| 1127 | - public function checkPassword(\OCP\Share\IShare $share, $password) { |
|
| 1128 | - $passwordProtected = $share->getShareType() !== \OCP\Share::SHARE_TYPE_LINK |
|
| 1129 | - || $share->getShareType() !== \OCP\Share::SHARE_TYPE_EMAIL; |
|
| 1130 | - if (!$passwordProtected) { |
|
| 1131 | - //TODO maybe exception? |
|
| 1132 | - return false; |
|
| 1133 | - } |
|
| 1134 | - |
|
| 1135 | - if ($password === null || $share->getPassword() === null) { |
|
| 1136 | - return false; |
|
| 1137 | - } |
|
| 1138 | - |
|
| 1139 | - $newHash = ''; |
|
| 1140 | - if (!$this->hasher->verify($password, $share->getPassword(), $newHash)) { |
|
| 1141 | - return false; |
|
| 1142 | - } |
|
| 1143 | - |
|
| 1144 | - if (!empty($newHash)) { |
|
| 1145 | - $share->setPassword($newHash); |
|
| 1146 | - $provider = $this->factory->getProviderForType($share->getShareType()); |
|
| 1147 | - $provider->update($share); |
|
| 1148 | - } |
|
| 1149 | - |
|
| 1150 | - return true; |
|
| 1151 | - } |
|
| 1152 | - |
|
| 1153 | - /** |
|
| 1154 | - * @inheritdoc |
|
| 1155 | - */ |
|
| 1156 | - public function userDeleted($uid) { |
|
| 1157 | - $types = [\OCP\Share::SHARE_TYPE_USER, \OCP\Share::SHARE_TYPE_GROUP, \OCP\Share::SHARE_TYPE_LINK, \OCP\Share::SHARE_TYPE_REMOTE, \OCP\Share::SHARE_TYPE_EMAIL]; |
|
| 1158 | - |
|
| 1159 | - foreach ($types as $type) { |
|
| 1160 | - try { |
|
| 1161 | - $provider = $this->factory->getProviderForType($type); |
|
| 1162 | - } catch (ProviderException $e) { |
|
| 1163 | - continue; |
|
| 1164 | - } |
|
| 1165 | - $provider->userDeleted($uid, $type); |
|
| 1166 | - } |
|
| 1167 | - } |
|
| 1168 | - |
|
| 1169 | - /** |
|
| 1170 | - * @inheritdoc |
|
| 1171 | - */ |
|
| 1172 | - public function groupDeleted($gid) { |
|
| 1173 | - $provider = $this->factory->getProviderForType(\OCP\Share::SHARE_TYPE_GROUP); |
|
| 1174 | - $provider->groupDeleted($gid); |
|
| 1175 | - } |
|
| 1176 | - |
|
| 1177 | - /** |
|
| 1178 | - * @inheritdoc |
|
| 1179 | - */ |
|
| 1180 | - public function userDeletedFromGroup($uid, $gid) { |
|
| 1181 | - $provider = $this->factory->getProviderForType(\OCP\Share::SHARE_TYPE_GROUP); |
|
| 1182 | - $provider->userDeletedFromGroup($uid, $gid); |
|
| 1183 | - } |
|
| 1184 | - |
|
| 1185 | - /** |
|
| 1186 | - * Get access list to a path. This means |
|
| 1187 | - * all the users that can access a given path. |
|
| 1188 | - * |
|
| 1189 | - * Consider: |
|
| 1190 | - * -root |
|
| 1191 | - * |-folder1 (23) |
|
| 1192 | - * |-folder2 (32) |
|
| 1193 | - * |-fileA (42) |
|
| 1194 | - * |
|
| 1195 | - * fileA is shared with user1 and user1@server1 |
|
| 1196 | - * folder2 is shared with group2 (user4 is a member of group2) |
|
| 1197 | - * folder1 is shared with user2 (renamed to "folder (1)") and user2@server2 |
|
| 1198 | - * |
|
| 1199 | - * Then the access list to '/folder1/folder2/fileA' with $currentAccess is: |
|
| 1200 | - * [ |
|
| 1201 | - * users => [ |
|
| 1202 | - * 'user1' => ['node_id' => 42, 'node_path' => '/fileA'], |
|
| 1203 | - * 'user4' => ['node_id' => 32, 'node_path' => '/folder2'], |
|
| 1204 | - * 'user2' => ['node_id' => 23, 'node_path' => '/folder (1)'], |
|
| 1205 | - * ], |
|
| 1206 | - * remote => [ |
|
| 1207 | - * 'user1@server1' => ['node_id' => 42, 'token' => 'SeCr3t'], |
|
| 1208 | - * 'user2@server2' => ['node_id' => 23, 'token' => 'FooBaR'], |
|
| 1209 | - * ], |
|
| 1210 | - * public => bool |
|
| 1211 | - * mail => bool |
|
| 1212 | - * ] |
|
| 1213 | - * |
|
| 1214 | - * The access list to '/folder1/folder2/fileA' **without** $currentAccess is: |
|
| 1215 | - * [ |
|
| 1216 | - * users => ['user1', 'user2', 'user4'], |
|
| 1217 | - * remote => bool, |
|
| 1218 | - * public => bool |
|
| 1219 | - * mail => bool |
|
| 1220 | - * ] |
|
| 1221 | - * |
|
| 1222 | - * This is required for encryption/activity |
|
| 1223 | - * |
|
| 1224 | - * @param \OCP\Files\Node $path |
|
| 1225 | - * @param bool $recursive Should we check all parent folders as well |
|
| 1226 | - * @param bool $currentAccess Should the user have currently access to the file |
|
| 1227 | - * @return array |
|
| 1228 | - */ |
|
| 1229 | - public function getAccessList(\OCP\Files\Node $path, $recursive = true, $currentAccess = false) { |
|
| 1230 | - $owner = $path->getOwner()->getUID(); |
|
| 1231 | - |
|
| 1232 | - if ($currentAccess) { |
|
| 1233 | - $al = ['users' => [], 'remote' => [], 'public' => false]; |
|
| 1234 | - } else { |
|
| 1235 | - $al = ['users' => [], 'remote' => false, 'public' => false]; |
|
| 1236 | - } |
|
| 1237 | - if (!$this->userManager->userExists($owner)) { |
|
| 1238 | - return $al; |
|
| 1239 | - } |
|
| 1240 | - |
|
| 1241 | - //Get node for the owner |
|
| 1242 | - $userFolder = $this->rootFolder->getUserFolder($owner); |
|
| 1243 | - if ($path->getId() !== $userFolder->getId() && !$userFolder->isSubNode($path)) { |
|
| 1244 | - $path = $userFolder->getById($path->getId())[0]; |
|
| 1245 | - } |
|
| 1246 | - |
|
| 1247 | - $providers = $this->factory->getAllProviders(); |
|
| 1248 | - |
|
| 1249 | - /** @var Node[] $nodes */ |
|
| 1250 | - $nodes = []; |
|
| 1251 | - |
|
| 1252 | - |
|
| 1253 | - if ($currentAccess) { |
|
| 1254 | - $ownerPath = $path->getPath(); |
|
| 1255 | - $ownerPath = explode('/', $ownerPath, 4); |
|
| 1256 | - if (count($ownerPath) < 4) { |
|
| 1257 | - $ownerPath = ''; |
|
| 1258 | - } else { |
|
| 1259 | - $ownerPath = $ownerPath[3]; |
|
| 1260 | - } |
|
| 1261 | - $al['users'][$owner] = [ |
|
| 1262 | - 'node_id' => $path->getId(), |
|
| 1263 | - 'node_path' => '/' . $ownerPath, |
|
| 1264 | - ]; |
|
| 1265 | - } else { |
|
| 1266 | - $al['users'][] = $owner; |
|
| 1267 | - } |
|
| 1268 | - |
|
| 1269 | - // Collect all the shares |
|
| 1270 | - while ($path->getPath() !== $userFolder->getPath()) { |
|
| 1271 | - $nodes[] = $path; |
|
| 1272 | - if (!$recursive) { |
|
| 1273 | - break; |
|
| 1274 | - } |
|
| 1275 | - $path = $path->getParent(); |
|
| 1276 | - } |
|
| 1277 | - |
|
| 1278 | - foreach ($providers as $provider) { |
|
| 1279 | - $tmp = $provider->getAccessList($nodes, $currentAccess); |
|
| 1280 | - |
|
| 1281 | - foreach ($tmp as $k => $v) { |
|
| 1282 | - if (isset($al[$k])) { |
|
| 1283 | - if (is_array($al[$k])) { |
|
| 1284 | - $al[$k] = array_merge($al[$k], $v); |
|
| 1285 | - } else { |
|
| 1286 | - $al[$k] = $al[$k] || $v; |
|
| 1287 | - } |
|
| 1288 | - } else { |
|
| 1289 | - $al[$k] = $v; |
|
| 1290 | - } |
|
| 1291 | - } |
|
| 1292 | - } |
|
| 1293 | - |
|
| 1294 | - return $al; |
|
| 1295 | - } |
|
| 1296 | - |
|
| 1297 | - /** |
|
| 1298 | - * Create a new share |
|
| 1299 | - * @return \OCP\Share\IShare; |
|
| 1300 | - */ |
|
| 1301 | - public function newShare() { |
|
| 1302 | - return new \OC\Share20\Share($this->rootFolder, $this->userManager); |
|
| 1303 | - } |
|
| 1304 | - |
|
| 1305 | - /** |
|
| 1306 | - * Is the share API enabled |
|
| 1307 | - * |
|
| 1308 | - * @return bool |
|
| 1309 | - */ |
|
| 1310 | - public function shareApiEnabled() { |
|
| 1311 | - return $this->config->getAppValue('core', 'shareapi_enabled', 'yes') === 'yes'; |
|
| 1312 | - } |
|
| 1313 | - |
|
| 1314 | - /** |
|
| 1315 | - * Is public link sharing enabled |
|
| 1316 | - * |
|
| 1317 | - * @return bool |
|
| 1318 | - */ |
|
| 1319 | - public function shareApiAllowLinks() { |
|
| 1320 | - return $this->config->getAppValue('core', 'shareapi_allow_links', 'yes') === 'yes'; |
|
| 1321 | - } |
|
| 1322 | - |
|
| 1323 | - /** |
|
| 1324 | - * Is password on public link requires |
|
| 1325 | - * |
|
| 1326 | - * @return bool |
|
| 1327 | - */ |
|
| 1328 | - public function shareApiLinkEnforcePassword() { |
|
| 1329 | - return $this->config->getAppValue('core', 'shareapi_enforce_links_password', 'no') === 'yes'; |
|
| 1330 | - } |
|
| 1331 | - |
|
| 1332 | - /** |
|
| 1333 | - * Is default expire date enabled |
|
| 1334 | - * |
|
| 1335 | - * @return bool |
|
| 1336 | - */ |
|
| 1337 | - public function shareApiLinkDefaultExpireDate() { |
|
| 1338 | - return $this->config->getAppValue('core', 'shareapi_default_expire_date', 'no') === 'yes'; |
|
| 1339 | - } |
|
| 1340 | - |
|
| 1341 | - /** |
|
| 1342 | - * Is default expire date enforced |
|
| 1343 | - *` |
|
| 1344 | - * @return bool |
|
| 1345 | - */ |
|
| 1346 | - public function shareApiLinkDefaultExpireDateEnforced() { |
|
| 1347 | - return $this->shareApiLinkDefaultExpireDate() && |
|
| 1348 | - $this->config->getAppValue('core', 'shareapi_enforce_expire_date', 'no') === 'yes'; |
|
| 1349 | - } |
|
| 1350 | - |
|
| 1351 | - /** |
|
| 1352 | - * Number of default expire days |
|
| 1353 | - *shareApiLinkAllowPublicUpload |
|
| 1354 | - * @return int |
|
| 1355 | - */ |
|
| 1356 | - public function shareApiLinkDefaultExpireDays() { |
|
| 1357 | - return (int)$this->config->getAppValue('core', 'shareapi_expire_after_n_days', '7'); |
|
| 1358 | - } |
|
| 1359 | - |
|
| 1360 | - /** |
|
| 1361 | - * Allow public upload on link shares |
|
| 1362 | - * |
|
| 1363 | - * @return bool |
|
| 1364 | - */ |
|
| 1365 | - public function shareApiLinkAllowPublicUpload() { |
|
| 1366 | - return $this->config->getAppValue('core', 'shareapi_allow_public_upload', 'yes') === 'yes'; |
|
| 1367 | - } |
|
| 1368 | - |
|
| 1369 | - /** |
|
| 1370 | - * check if user can only share with group members |
|
| 1371 | - * @return bool |
|
| 1372 | - */ |
|
| 1373 | - public function shareWithGroupMembersOnly() { |
|
| 1374 | - return $this->config->getAppValue('core', 'shareapi_only_share_with_group_members', 'no') === 'yes'; |
|
| 1375 | - } |
|
| 1376 | - |
|
| 1377 | - /** |
|
| 1378 | - * Check if users can share with groups |
|
| 1379 | - * @return bool |
|
| 1380 | - */ |
|
| 1381 | - public function allowGroupSharing() { |
|
| 1382 | - return $this->config->getAppValue('core', 'shareapi_allow_group_sharing', 'yes') === 'yes'; |
|
| 1383 | - } |
|
| 1384 | - |
|
| 1385 | - /** |
|
| 1386 | - * Copied from \OC_Util::isSharingDisabledForUser |
|
| 1387 | - * |
|
| 1388 | - * TODO: Deprecate fuction from OC_Util |
|
| 1389 | - * |
|
| 1390 | - * @param string $userId |
|
| 1391 | - * @return bool |
|
| 1392 | - */ |
|
| 1393 | - public function sharingDisabledForUser($userId) { |
|
| 1394 | - if ($userId === null) { |
|
| 1395 | - return false; |
|
| 1396 | - } |
|
| 1397 | - |
|
| 1398 | - if (isset($this->sharingDisabledForUsersCache[$userId])) { |
|
| 1399 | - return $this->sharingDisabledForUsersCache[$userId]; |
|
| 1400 | - } |
|
| 1401 | - |
|
| 1402 | - if ($this->config->getAppValue('core', 'shareapi_exclude_groups', 'no') === 'yes') { |
|
| 1403 | - $groupsList = $this->config->getAppValue('core', 'shareapi_exclude_groups_list', ''); |
|
| 1404 | - $excludedGroups = json_decode($groupsList); |
|
| 1405 | - if (is_null($excludedGroups)) { |
|
| 1406 | - $excludedGroups = explode(',', $groupsList); |
|
| 1407 | - $newValue = json_encode($excludedGroups); |
|
| 1408 | - $this->config->setAppValue('core', 'shareapi_exclude_groups_list', $newValue); |
|
| 1409 | - } |
|
| 1410 | - $user = $this->userManager->get($userId); |
|
| 1411 | - $usersGroups = $this->groupManager->getUserGroupIds($user); |
|
| 1412 | - if (!empty($usersGroups)) { |
|
| 1413 | - $remainingGroups = array_diff($usersGroups, $excludedGroups); |
|
| 1414 | - // if the user is only in groups which are disabled for sharing then |
|
| 1415 | - // sharing is also disabled for the user |
|
| 1416 | - if (empty($remainingGroups)) { |
|
| 1417 | - $this->sharingDisabledForUsersCache[$userId] = true; |
|
| 1418 | - return true; |
|
| 1419 | - } |
|
| 1420 | - } |
|
| 1421 | - } |
|
| 1422 | - |
|
| 1423 | - $this->sharingDisabledForUsersCache[$userId] = false; |
|
| 1424 | - return false; |
|
| 1425 | - } |
|
| 1426 | - |
|
| 1427 | - /** |
|
| 1428 | - * @inheritdoc |
|
| 1429 | - */ |
|
| 1430 | - public function outgoingServer2ServerSharesAllowed() { |
|
| 1431 | - return $this->config->getAppValue('files_sharing', 'outgoing_server2server_share_enabled', 'yes') === 'yes'; |
|
| 1432 | - } |
|
| 1433 | - |
|
| 1434 | - /** |
|
| 1435 | - * @inheritdoc |
|
| 1436 | - */ |
|
| 1437 | - public function shareProviderExists($shareType) { |
|
| 1438 | - try { |
|
| 1439 | - $this->factory->getProviderForType($shareType); |
|
| 1440 | - } catch (ProviderException $e) { |
|
| 1441 | - return false; |
|
| 1442 | - } |
|
| 1443 | - |
|
| 1444 | - return true; |
|
| 1445 | - } |
|
| 1103 | + if ($share->getShareType() === \OCP\Share::SHARE_TYPE_LINK && |
|
| 1104 | + !$this->shareApiLinkAllowPublicUpload()) { |
|
| 1105 | + $share->setPermissions($share->getPermissions() & ~(\OCP\Constants::PERMISSION_CREATE | \OCP\Constants::PERMISSION_UPDATE)); |
|
| 1106 | + } |
|
| 1107 | + |
|
| 1108 | + return $share; |
|
| 1109 | + } |
|
| 1110 | + |
|
| 1111 | + protected function checkExpireDate($share) { |
|
| 1112 | + if ($share->getExpirationDate() !== null && |
|
| 1113 | + $share->getExpirationDate() <= new \DateTime()) { |
|
| 1114 | + $this->deleteShare($share); |
|
| 1115 | + throw new ShareNotFound($this->l->t('The requested share does not exist anymore')); |
|
| 1116 | + } |
|
| 1117 | + |
|
| 1118 | + } |
|
| 1119 | + |
|
| 1120 | + /** |
|
| 1121 | + * Verify the password of a public share |
|
| 1122 | + * |
|
| 1123 | + * @param \OCP\Share\IShare $share |
|
| 1124 | + * @param string $password |
|
| 1125 | + * @return bool |
|
| 1126 | + */ |
|
| 1127 | + public function checkPassword(\OCP\Share\IShare $share, $password) { |
|
| 1128 | + $passwordProtected = $share->getShareType() !== \OCP\Share::SHARE_TYPE_LINK |
|
| 1129 | + || $share->getShareType() !== \OCP\Share::SHARE_TYPE_EMAIL; |
|
| 1130 | + if (!$passwordProtected) { |
|
| 1131 | + //TODO maybe exception? |
|
| 1132 | + return false; |
|
| 1133 | + } |
|
| 1134 | + |
|
| 1135 | + if ($password === null || $share->getPassword() === null) { |
|
| 1136 | + return false; |
|
| 1137 | + } |
|
| 1138 | + |
|
| 1139 | + $newHash = ''; |
|
| 1140 | + if (!$this->hasher->verify($password, $share->getPassword(), $newHash)) { |
|
| 1141 | + return false; |
|
| 1142 | + } |
|
| 1143 | + |
|
| 1144 | + if (!empty($newHash)) { |
|
| 1145 | + $share->setPassword($newHash); |
|
| 1146 | + $provider = $this->factory->getProviderForType($share->getShareType()); |
|
| 1147 | + $provider->update($share); |
|
| 1148 | + } |
|
| 1149 | + |
|
| 1150 | + return true; |
|
| 1151 | + } |
|
| 1152 | + |
|
| 1153 | + /** |
|
| 1154 | + * @inheritdoc |
|
| 1155 | + */ |
|
| 1156 | + public function userDeleted($uid) { |
|
| 1157 | + $types = [\OCP\Share::SHARE_TYPE_USER, \OCP\Share::SHARE_TYPE_GROUP, \OCP\Share::SHARE_TYPE_LINK, \OCP\Share::SHARE_TYPE_REMOTE, \OCP\Share::SHARE_TYPE_EMAIL]; |
|
| 1158 | + |
|
| 1159 | + foreach ($types as $type) { |
|
| 1160 | + try { |
|
| 1161 | + $provider = $this->factory->getProviderForType($type); |
|
| 1162 | + } catch (ProviderException $e) { |
|
| 1163 | + continue; |
|
| 1164 | + } |
|
| 1165 | + $provider->userDeleted($uid, $type); |
|
| 1166 | + } |
|
| 1167 | + } |
|
| 1168 | + |
|
| 1169 | + /** |
|
| 1170 | + * @inheritdoc |
|
| 1171 | + */ |
|
| 1172 | + public function groupDeleted($gid) { |
|
| 1173 | + $provider = $this->factory->getProviderForType(\OCP\Share::SHARE_TYPE_GROUP); |
|
| 1174 | + $provider->groupDeleted($gid); |
|
| 1175 | + } |
|
| 1176 | + |
|
| 1177 | + /** |
|
| 1178 | + * @inheritdoc |
|
| 1179 | + */ |
|
| 1180 | + public function userDeletedFromGroup($uid, $gid) { |
|
| 1181 | + $provider = $this->factory->getProviderForType(\OCP\Share::SHARE_TYPE_GROUP); |
|
| 1182 | + $provider->userDeletedFromGroup($uid, $gid); |
|
| 1183 | + } |
|
| 1184 | + |
|
| 1185 | + /** |
|
| 1186 | + * Get access list to a path. This means |
|
| 1187 | + * all the users that can access a given path. |
|
| 1188 | + * |
|
| 1189 | + * Consider: |
|
| 1190 | + * -root |
|
| 1191 | + * |-folder1 (23) |
|
| 1192 | + * |-folder2 (32) |
|
| 1193 | + * |-fileA (42) |
|
| 1194 | + * |
|
| 1195 | + * fileA is shared with user1 and user1@server1 |
|
| 1196 | + * folder2 is shared with group2 (user4 is a member of group2) |
|
| 1197 | + * folder1 is shared with user2 (renamed to "folder (1)") and user2@server2 |
|
| 1198 | + * |
|
| 1199 | + * Then the access list to '/folder1/folder2/fileA' with $currentAccess is: |
|
| 1200 | + * [ |
|
| 1201 | + * users => [ |
|
| 1202 | + * 'user1' => ['node_id' => 42, 'node_path' => '/fileA'], |
|
| 1203 | + * 'user4' => ['node_id' => 32, 'node_path' => '/folder2'], |
|
| 1204 | + * 'user2' => ['node_id' => 23, 'node_path' => '/folder (1)'], |
|
| 1205 | + * ], |
|
| 1206 | + * remote => [ |
|
| 1207 | + * 'user1@server1' => ['node_id' => 42, 'token' => 'SeCr3t'], |
|
| 1208 | + * 'user2@server2' => ['node_id' => 23, 'token' => 'FooBaR'], |
|
| 1209 | + * ], |
|
| 1210 | + * public => bool |
|
| 1211 | + * mail => bool |
|
| 1212 | + * ] |
|
| 1213 | + * |
|
| 1214 | + * The access list to '/folder1/folder2/fileA' **without** $currentAccess is: |
|
| 1215 | + * [ |
|
| 1216 | + * users => ['user1', 'user2', 'user4'], |
|
| 1217 | + * remote => bool, |
|
| 1218 | + * public => bool |
|
| 1219 | + * mail => bool |
|
| 1220 | + * ] |
|
| 1221 | + * |
|
| 1222 | + * This is required for encryption/activity |
|
| 1223 | + * |
|
| 1224 | + * @param \OCP\Files\Node $path |
|
| 1225 | + * @param bool $recursive Should we check all parent folders as well |
|
| 1226 | + * @param bool $currentAccess Should the user have currently access to the file |
|
| 1227 | + * @return array |
|
| 1228 | + */ |
|
| 1229 | + public function getAccessList(\OCP\Files\Node $path, $recursive = true, $currentAccess = false) { |
|
| 1230 | + $owner = $path->getOwner()->getUID(); |
|
| 1231 | + |
|
| 1232 | + if ($currentAccess) { |
|
| 1233 | + $al = ['users' => [], 'remote' => [], 'public' => false]; |
|
| 1234 | + } else { |
|
| 1235 | + $al = ['users' => [], 'remote' => false, 'public' => false]; |
|
| 1236 | + } |
|
| 1237 | + if (!$this->userManager->userExists($owner)) { |
|
| 1238 | + return $al; |
|
| 1239 | + } |
|
| 1240 | + |
|
| 1241 | + //Get node for the owner |
|
| 1242 | + $userFolder = $this->rootFolder->getUserFolder($owner); |
|
| 1243 | + if ($path->getId() !== $userFolder->getId() && !$userFolder->isSubNode($path)) { |
|
| 1244 | + $path = $userFolder->getById($path->getId())[0]; |
|
| 1245 | + } |
|
| 1246 | + |
|
| 1247 | + $providers = $this->factory->getAllProviders(); |
|
| 1248 | + |
|
| 1249 | + /** @var Node[] $nodes */ |
|
| 1250 | + $nodes = []; |
|
| 1251 | + |
|
| 1252 | + |
|
| 1253 | + if ($currentAccess) { |
|
| 1254 | + $ownerPath = $path->getPath(); |
|
| 1255 | + $ownerPath = explode('/', $ownerPath, 4); |
|
| 1256 | + if (count($ownerPath) < 4) { |
|
| 1257 | + $ownerPath = ''; |
|
| 1258 | + } else { |
|
| 1259 | + $ownerPath = $ownerPath[3]; |
|
| 1260 | + } |
|
| 1261 | + $al['users'][$owner] = [ |
|
| 1262 | + 'node_id' => $path->getId(), |
|
| 1263 | + 'node_path' => '/' . $ownerPath, |
|
| 1264 | + ]; |
|
| 1265 | + } else { |
|
| 1266 | + $al['users'][] = $owner; |
|
| 1267 | + } |
|
| 1268 | + |
|
| 1269 | + // Collect all the shares |
|
| 1270 | + while ($path->getPath() !== $userFolder->getPath()) { |
|
| 1271 | + $nodes[] = $path; |
|
| 1272 | + if (!$recursive) { |
|
| 1273 | + break; |
|
| 1274 | + } |
|
| 1275 | + $path = $path->getParent(); |
|
| 1276 | + } |
|
| 1277 | + |
|
| 1278 | + foreach ($providers as $provider) { |
|
| 1279 | + $tmp = $provider->getAccessList($nodes, $currentAccess); |
|
| 1280 | + |
|
| 1281 | + foreach ($tmp as $k => $v) { |
|
| 1282 | + if (isset($al[$k])) { |
|
| 1283 | + if (is_array($al[$k])) { |
|
| 1284 | + $al[$k] = array_merge($al[$k], $v); |
|
| 1285 | + } else { |
|
| 1286 | + $al[$k] = $al[$k] || $v; |
|
| 1287 | + } |
|
| 1288 | + } else { |
|
| 1289 | + $al[$k] = $v; |
|
| 1290 | + } |
|
| 1291 | + } |
|
| 1292 | + } |
|
| 1293 | + |
|
| 1294 | + return $al; |
|
| 1295 | + } |
|
| 1296 | + |
|
| 1297 | + /** |
|
| 1298 | + * Create a new share |
|
| 1299 | + * @return \OCP\Share\IShare; |
|
| 1300 | + */ |
|
| 1301 | + public function newShare() { |
|
| 1302 | + return new \OC\Share20\Share($this->rootFolder, $this->userManager); |
|
| 1303 | + } |
|
| 1304 | + |
|
| 1305 | + /** |
|
| 1306 | + * Is the share API enabled |
|
| 1307 | + * |
|
| 1308 | + * @return bool |
|
| 1309 | + */ |
|
| 1310 | + public function shareApiEnabled() { |
|
| 1311 | + return $this->config->getAppValue('core', 'shareapi_enabled', 'yes') === 'yes'; |
|
| 1312 | + } |
|
| 1313 | + |
|
| 1314 | + /** |
|
| 1315 | + * Is public link sharing enabled |
|
| 1316 | + * |
|
| 1317 | + * @return bool |
|
| 1318 | + */ |
|
| 1319 | + public function shareApiAllowLinks() { |
|
| 1320 | + return $this->config->getAppValue('core', 'shareapi_allow_links', 'yes') === 'yes'; |
|
| 1321 | + } |
|
| 1322 | + |
|
| 1323 | + /** |
|
| 1324 | + * Is password on public link requires |
|
| 1325 | + * |
|
| 1326 | + * @return bool |
|
| 1327 | + */ |
|
| 1328 | + public function shareApiLinkEnforcePassword() { |
|
| 1329 | + return $this->config->getAppValue('core', 'shareapi_enforce_links_password', 'no') === 'yes'; |
|
| 1330 | + } |
|
| 1331 | + |
|
| 1332 | + /** |
|
| 1333 | + * Is default expire date enabled |
|
| 1334 | + * |
|
| 1335 | + * @return bool |
|
| 1336 | + */ |
|
| 1337 | + public function shareApiLinkDefaultExpireDate() { |
|
| 1338 | + return $this->config->getAppValue('core', 'shareapi_default_expire_date', 'no') === 'yes'; |
|
| 1339 | + } |
|
| 1340 | + |
|
| 1341 | + /** |
|
| 1342 | + * Is default expire date enforced |
|
| 1343 | + *` |
|
| 1344 | + * @return bool |
|
| 1345 | + */ |
|
| 1346 | + public function shareApiLinkDefaultExpireDateEnforced() { |
|
| 1347 | + return $this->shareApiLinkDefaultExpireDate() && |
|
| 1348 | + $this->config->getAppValue('core', 'shareapi_enforce_expire_date', 'no') === 'yes'; |
|
| 1349 | + } |
|
| 1350 | + |
|
| 1351 | + /** |
|
| 1352 | + * Number of default expire days |
|
| 1353 | + *shareApiLinkAllowPublicUpload |
|
| 1354 | + * @return int |
|
| 1355 | + */ |
|
| 1356 | + public function shareApiLinkDefaultExpireDays() { |
|
| 1357 | + return (int)$this->config->getAppValue('core', 'shareapi_expire_after_n_days', '7'); |
|
| 1358 | + } |
|
| 1359 | + |
|
| 1360 | + /** |
|
| 1361 | + * Allow public upload on link shares |
|
| 1362 | + * |
|
| 1363 | + * @return bool |
|
| 1364 | + */ |
|
| 1365 | + public function shareApiLinkAllowPublicUpload() { |
|
| 1366 | + return $this->config->getAppValue('core', 'shareapi_allow_public_upload', 'yes') === 'yes'; |
|
| 1367 | + } |
|
| 1368 | + |
|
| 1369 | + /** |
|
| 1370 | + * check if user can only share with group members |
|
| 1371 | + * @return bool |
|
| 1372 | + */ |
|
| 1373 | + public function shareWithGroupMembersOnly() { |
|
| 1374 | + return $this->config->getAppValue('core', 'shareapi_only_share_with_group_members', 'no') === 'yes'; |
|
| 1375 | + } |
|
| 1376 | + |
|
| 1377 | + /** |
|
| 1378 | + * Check if users can share with groups |
|
| 1379 | + * @return bool |
|
| 1380 | + */ |
|
| 1381 | + public function allowGroupSharing() { |
|
| 1382 | + return $this->config->getAppValue('core', 'shareapi_allow_group_sharing', 'yes') === 'yes'; |
|
| 1383 | + } |
|
| 1384 | + |
|
| 1385 | + /** |
|
| 1386 | + * Copied from \OC_Util::isSharingDisabledForUser |
|
| 1387 | + * |
|
| 1388 | + * TODO: Deprecate fuction from OC_Util |
|
| 1389 | + * |
|
| 1390 | + * @param string $userId |
|
| 1391 | + * @return bool |
|
| 1392 | + */ |
|
| 1393 | + public function sharingDisabledForUser($userId) { |
|
| 1394 | + if ($userId === null) { |
|
| 1395 | + return false; |
|
| 1396 | + } |
|
| 1397 | + |
|
| 1398 | + if (isset($this->sharingDisabledForUsersCache[$userId])) { |
|
| 1399 | + return $this->sharingDisabledForUsersCache[$userId]; |
|
| 1400 | + } |
|
| 1401 | + |
|
| 1402 | + if ($this->config->getAppValue('core', 'shareapi_exclude_groups', 'no') === 'yes') { |
|
| 1403 | + $groupsList = $this->config->getAppValue('core', 'shareapi_exclude_groups_list', ''); |
|
| 1404 | + $excludedGroups = json_decode($groupsList); |
|
| 1405 | + if (is_null($excludedGroups)) { |
|
| 1406 | + $excludedGroups = explode(',', $groupsList); |
|
| 1407 | + $newValue = json_encode($excludedGroups); |
|
| 1408 | + $this->config->setAppValue('core', 'shareapi_exclude_groups_list', $newValue); |
|
| 1409 | + } |
|
| 1410 | + $user = $this->userManager->get($userId); |
|
| 1411 | + $usersGroups = $this->groupManager->getUserGroupIds($user); |
|
| 1412 | + if (!empty($usersGroups)) { |
|
| 1413 | + $remainingGroups = array_diff($usersGroups, $excludedGroups); |
|
| 1414 | + // if the user is only in groups which are disabled for sharing then |
|
| 1415 | + // sharing is also disabled for the user |
|
| 1416 | + if (empty($remainingGroups)) { |
|
| 1417 | + $this->sharingDisabledForUsersCache[$userId] = true; |
|
| 1418 | + return true; |
|
| 1419 | + } |
|
| 1420 | + } |
|
| 1421 | + } |
|
| 1422 | + |
|
| 1423 | + $this->sharingDisabledForUsersCache[$userId] = false; |
|
| 1424 | + return false; |
|
| 1425 | + } |
|
| 1426 | + |
|
| 1427 | + /** |
|
| 1428 | + * @inheritdoc |
|
| 1429 | + */ |
|
| 1430 | + public function outgoingServer2ServerSharesAllowed() { |
|
| 1431 | + return $this->config->getAppValue('files_sharing', 'outgoing_server2server_share_enabled', 'yes') === 'yes'; |
|
| 1432 | + } |
|
| 1433 | + |
|
| 1434 | + /** |
|
| 1435 | + * @inheritdoc |
|
| 1436 | + */ |
|
| 1437 | + public function shareProviderExists($shareType) { |
|
| 1438 | + try { |
|
| 1439 | + $this->factory->getProviderForType($shareType); |
|
| 1440 | + } catch (ProviderException $e) { |
|
| 1441 | + return false; |
|
| 1442 | + } |
|
| 1443 | + |
|
| 1444 | + return true; |
|
| 1445 | + } |
|
| 1446 | 1446 | |
| 1447 | 1447 | } |
@@ -29,68 +29,68 @@ |
||
| 29 | 29 | use OCP\Diagnostics\IQueryLogger; |
| 30 | 30 | |
| 31 | 31 | class QueryLogger implements IQueryLogger { |
| 32 | - /** |
|
| 33 | - * @var \OC\Diagnostics\Query |
|
| 34 | - */ |
|
| 35 | - protected $activeQuery; |
|
| 32 | + /** |
|
| 33 | + * @var \OC\Diagnostics\Query |
|
| 34 | + */ |
|
| 35 | + protected $activeQuery; |
|
| 36 | 36 | |
| 37 | - /** |
|
| 38 | - * @var CappedMemoryCache |
|
| 39 | - */ |
|
| 40 | - protected $queries; |
|
| 37 | + /** |
|
| 38 | + * @var CappedMemoryCache |
|
| 39 | + */ |
|
| 40 | + protected $queries; |
|
| 41 | 41 | |
| 42 | - /** |
|
| 43 | - * QueryLogger constructor. |
|
| 44 | - */ |
|
| 45 | - public function __construct() { |
|
| 46 | - $this->queries = new CappedMemoryCache(1024); |
|
| 47 | - } |
|
| 42 | + /** |
|
| 43 | + * QueryLogger constructor. |
|
| 44 | + */ |
|
| 45 | + public function __construct() { |
|
| 46 | + $this->queries = new CappedMemoryCache(1024); |
|
| 47 | + } |
|
| 48 | 48 | |
| 49 | 49 | |
| 50 | - /** |
|
| 51 | - * @var bool - Module needs to be activated by some app |
|
| 52 | - */ |
|
| 53 | - private $activated = false; |
|
| 50 | + /** |
|
| 51 | + * @var bool - Module needs to be activated by some app |
|
| 52 | + */ |
|
| 53 | + private $activated = false; |
|
| 54 | 54 | |
| 55 | - /** |
|
| 56 | - * @inheritdoc |
|
| 57 | - */ |
|
| 58 | - public function startQuery($sql, array $params = null, array $types = null) { |
|
| 59 | - if ($this->activated) { |
|
| 60 | - $this->activeQuery = new Query($sql, $params, microtime(true), $this->getStack()); |
|
| 61 | - } |
|
| 62 | - } |
|
| 55 | + /** |
|
| 56 | + * @inheritdoc |
|
| 57 | + */ |
|
| 58 | + public function startQuery($sql, array $params = null, array $types = null) { |
|
| 59 | + if ($this->activated) { |
|
| 60 | + $this->activeQuery = new Query($sql, $params, microtime(true), $this->getStack()); |
|
| 61 | + } |
|
| 62 | + } |
|
| 63 | 63 | |
| 64 | - private function getStack() { |
|
| 65 | - $stack = debug_backtrace(); |
|
| 66 | - array_shift($stack); |
|
| 67 | - array_shift($stack); |
|
| 68 | - array_shift($stack); |
|
| 69 | - return $stack; |
|
| 70 | - } |
|
| 64 | + private function getStack() { |
|
| 65 | + $stack = debug_backtrace(); |
|
| 66 | + array_shift($stack); |
|
| 67 | + array_shift($stack); |
|
| 68 | + array_shift($stack); |
|
| 69 | + return $stack; |
|
| 70 | + } |
|
| 71 | 71 | |
| 72 | - /** |
|
| 73 | - * @inheritdoc |
|
| 74 | - */ |
|
| 75 | - public function stopQuery() { |
|
| 76 | - if ($this->activated && $this->activeQuery) { |
|
| 77 | - $this->activeQuery->end(microtime(true)); |
|
| 78 | - $this->queries[] = $this->activeQuery; |
|
| 79 | - $this->activeQuery = null; |
|
| 80 | - } |
|
| 81 | - } |
|
| 72 | + /** |
|
| 73 | + * @inheritdoc |
|
| 74 | + */ |
|
| 75 | + public function stopQuery() { |
|
| 76 | + if ($this->activated && $this->activeQuery) { |
|
| 77 | + $this->activeQuery->end(microtime(true)); |
|
| 78 | + $this->queries[] = $this->activeQuery; |
|
| 79 | + $this->activeQuery = null; |
|
| 80 | + } |
|
| 81 | + } |
|
| 82 | 82 | |
| 83 | - /** |
|
| 84 | - * @inheritdoc |
|
| 85 | - */ |
|
| 86 | - public function getQueries() { |
|
| 87 | - return $this->queries->getData(); |
|
| 88 | - } |
|
| 83 | + /** |
|
| 84 | + * @inheritdoc |
|
| 85 | + */ |
|
| 86 | + public function getQueries() { |
|
| 87 | + return $this->queries->getData(); |
|
| 88 | + } |
|
| 89 | 89 | |
| 90 | - /** |
|
| 91 | - * @inheritdoc |
|
| 92 | - */ |
|
| 93 | - public function activate() { |
|
| 94 | - $this->activated = true; |
|
| 95 | - } |
|
| 90 | + /** |
|
| 91 | + * @inheritdoc |
|
| 92 | + */ |
|
| 93 | + public function activate() { |
|
| 94 | + $this->activated = true; |
|
| 95 | + } |
|
| 96 | 96 | } |
@@ -48,606 +48,606 @@ |
||
| 48 | 48 | |
| 49 | 49 | class Swift extends \OC\Files\Storage\Common { |
| 50 | 50 | |
| 51 | - /** |
|
| 52 | - * @var \OpenCloud\ObjectStore\Service |
|
| 53 | - */ |
|
| 54 | - private $connection; |
|
| 55 | - /** |
|
| 56 | - * @var \OpenCloud\ObjectStore\Resource\Container |
|
| 57 | - */ |
|
| 58 | - private $container; |
|
| 59 | - /** |
|
| 60 | - * @var \OpenCloud\OpenStack |
|
| 61 | - */ |
|
| 62 | - private $anchor; |
|
| 63 | - /** |
|
| 64 | - * @var string |
|
| 65 | - */ |
|
| 66 | - private $bucket; |
|
| 67 | - /** |
|
| 68 | - * Connection parameters |
|
| 69 | - * |
|
| 70 | - * @var array |
|
| 71 | - */ |
|
| 72 | - private $params; |
|
| 73 | - |
|
| 74 | - /** @var string */ |
|
| 75 | - private $id; |
|
| 76 | - |
|
| 77 | - /** |
|
| 78 | - * @var array |
|
| 79 | - */ |
|
| 80 | - private static $tmpFiles = array(); |
|
| 81 | - |
|
| 82 | - /** |
|
| 83 | - * Key value cache mapping path to data object. Maps path to |
|
| 84 | - * \OpenCloud\OpenStack\ObjectStorage\Resource\DataObject for existing |
|
| 85 | - * paths and path to false for not existing paths. |
|
| 86 | - * @var \OCP\ICache |
|
| 87 | - */ |
|
| 88 | - private $objectCache; |
|
| 89 | - |
|
| 90 | - /** |
|
| 91 | - * @param string $path |
|
| 92 | - */ |
|
| 93 | - private function normalizePath($path) { |
|
| 94 | - $path = trim($path, '/'); |
|
| 95 | - |
|
| 96 | - if (!$path) { |
|
| 97 | - $path = '.'; |
|
| 98 | - } |
|
| 99 | - |
|
| 100 | - $path = str_replace('#', '%23', $path); |
|
| 101 | - |
|
| 102 | - return $path; |
|
| 103 | - } |
|
| 104 | - |
|
| 105 | - const SUBCONTAINER_FILE = '.subcontainers'; |
|
| 106 | - |
|
| 107 | - /** |
|
| 108 | - * translate directory path to container name |
|
| 109 | - * |
|
| 110 | - * @param string $path |
|
| 111 | - * @return string |
|
| 112 | - */ |
|
| 113 | - |
|
| 114 | - /** |
|
| 115 | - * Fetches an object from the API. |
|
| 116 | - * If the object is cached already or a |
|
| 117 | - * failed "doesn't exist" response was cached, |
|
| 118 | - * that one will be returned. |
|
| 119 | - * |
|
| 120 | - * @param string $path |
|
| 121 | - * @return \OpenCloud\ObjectStore\Resource\DataObject|bool object |
|
| 122 | - * or false if the object did not exist |
|
| 123 | - */ |
|
| 124 | - private function fetchObject($path) { |
|
| 125 | - if ($this->objectCache->hasKey($path)) { |
|
| 126 | - // might be "false" if object did not exist from last check |
|
| 127 | - return $this->objectCache->get($path); |
|
| 128 | - } |
|
| 129 | - try { |
|
| 130 | - $object = $this->getContainer()->getPartialObject($path); |
|
| 131 | - $this->objectCache->set($path, $object); |
|
| 132 | - return $object; |
|
| 133 | - } catch (ClientErrorResponseException $e) { |
|
| 134 | - // this exception happens when the object does not exist, which |
|
| 135 | - // is expected in most cases |
|
| 136 | - $this->objectCache->set($path, false); |
|
| 137 | - return false; |
|
| 138 | - } catch (ClientErrorResponseException $e) { |
|
| 139 | - // Expected response is "404 Not Found", so only log if it isn't |
|
| 140 | - if ($e->getResponse()->getStatusCode() !== 404) { |
|
| 141 | - \OCP\Util::writeLog('files_external', $e->getMessage(), \OCP\Util::ERROR); |
|
| 142 | - } |
|
| 143 | - return false; |
|
| 144 | - } |
|
| 145 | - } |
|
| 146 | - |
|
| 147 | - /** |
|
| 148 | - * Returns whether the given path exists. |
|
| 149 | - * |
|
| 150 | - * @param string $path |
|
| 151 | - * |
|
| 152 | - * @return bool true if the object exist, false otherwise |
|
| 153 | - */ |
|
| 154 | - private function doesObjectExist($path) { |
|
| 155 | - return $this->fetchObject($path) !== false; |
|
| 156 | - } |
|
| 157 | - |
|
| 158 | - public function __construct($params) { |
|
| 159 | - if ((empty($params['key']) and empty($params['password'])) |
|
| 160 | - or empty($params['user']) or empty($params['bucket']) |
|
| 161 | - or empty($params['region']) |
|
| 162 | - ) { |
|
| 163 | - throw new \Exception("API Key or password, Username, Bucket and Region have to be configured."); |
|
| 164 | - } |
|
| 165 | - |
|
| 166 | - $this->id = 'swift::' . $params['user'] . md5($params['bucket']); |
|
| 167 | - |
|
| 168 | - $bucketUrl = Url::factory($params['bucket']); |
|
| 169 | - if ($bucketUrl->isAbsolute()) { |
|
| 170 | - $this->bucket = end(($bucketUrl->getPathSegments())); |
|
| 171 | - $params['endpoint_url'] = $bucketUrl->addPath('..')->normalizePath(); |
|
| 172 | - } else { |
|
| 173 | - $this->bucket = $params['bucket']; |
|
| 174 | - } |
|
| 175 | - |
|
| 176 | - if (empty($params['url'])) { |
|
| 177 | - $params['url'] = 'https://identity.api.rackspacecloud.com/v2.0/'; |
|
| 178 | - } |
|
| 179 | - |
|
| 180 | - if (empty($params['service_name'])) { |
|
| 181 | - $params['service_name'] = 'cloudFiles'; |
|
| 182 | - } |
|
| 183 | - |
|
| 184 | - $this->params = $params; |
|
| 185 | - // FIXME: private class... |
|
| 186 | - $this->objectCache = new \OC\Cache\CappedMemoryCache(); |
|
| 187 | - } |
|
| 188 | - |
|
| 189 | - public function mkdir($path) { |
|
| 190 | - $path = $this->normalizePath($path); |
|
| 191 | - |
|
| 192 | - if ($this->is_dir($path)) { |
|
| 193 | - return false; |
|
| 194 | - } |
|
| 195 | - |
|
| 196 | - if ($path !== '.') { |
|
| 197 | - $path .= '/'; |
|
| 198 | - } |
|
| 199 | - |
|
| 200 | - try { |
|
| 201 | - $customHeaders = array('content-type' => 'httpd/unix-directory'); |
|
| 202 | - $metadataHeaders = DataObject::stockHeaders(array()); |
|
| 203 | - $allHeaders = $customHeaders + $metadataHeaders; |
|
| 204 | - $this->getContainer()->uploadObject($path, '', $allHeaders); |
|
| 205 | - // invalidate so that the next access gets the real object |
|
| 206 | - // with all properties |
|
| 207 | - $this->objectCache->remove($path); |
|
| 208 | - } catch (Exceptions\CreateUpdateError $e) { |
|
| 209 | - \OCP\Util::writeLog('files_external', $e->getMessage(), \OCP\Util::ERROR); |
|
| 210 | - return false; |
|
| 211 | - } |
|
| 212 | - |
|
| 213 | - return true; |
|
| 214 | - } |
|
| 215 | - |
|
| 216 | - public function file_exists($path) { |
|
| 217 | - $path = $this->normalizePath($path); |
|
| 218 | - |
|
| 219 | - if ($path !== '.' && $this->is_dir($path)) { |
|
| 220 | - $path .= '/'; |
|
| 221 | - } |
|
| 222 | - |
|
| 223 | - return $this->doesObjectExist($path); |
|
| 224 | - } |
|
| 225 | - |
|
| 226 | - public function rmdir($path) { |
|
| 227 | - $path = $this->normalizePath($path); |
|
| 228 | - |
|
| 229 | - if (!$this->is_dir($path) || !$this->isDeletable($path)) { |
|
| 230 | - return false; |
|
| 231 | - } |
|
| 232 | - |
|
| 233 | - $dh = $this->opendir($path); |
|
| 234 | - while ($file = readdir($dh)) { |
|
| 235 | - if (\OC\Files\Filesystem::isIgnoredDir($file)) { |
|
| 236 | - continue; |
|
| 237 | - } |
|
| 238 | - |
|
| 239 | - if ($this->is_dir($path . '/' . $file)) { |
|
| 240 | - $this->rmdir($path . '/' . $file); |
|
| 241 | - } else { |
|
| 242 | - $this->unlink($path . '/' . $file); |
|
| 243 | - } |
|
| 244 | - } |
|
| 245 | - |
|
| 246 | - try { |
|
| 247 | - $this->getContainer()->dataObject()->setName($path . '/')->delete(); |
|
| 248 | - $this->objectCache->remove($path . '/'); |
|
| 249 | - } catch (Exceptions\DeleteError $e) { |
|
| 250 | - \OCP\Util::writeLog('files_external', $e->getMessage(), \OCP\Util::ERROR); |
|
| 251 | - return false; |
|
| 252 | - } |
|
| 253 | - |
|
| 254 | - return true; |
|
| 255 | - } |
|
| 256 | - |
|
| 257 | - public function opendir($path) { |
|
| 258 | - $path = $this->normalizePath($path); |
|
| 259 | - |
|
| 260 | - if ($path === '.') { |
|
| 261 | - $path = ''; |
|
| 262 | - } else { |
|
| 263 | - $path .= '/'; |
|
| 264 | - } |
|
| 265 | - |
|
| 266 | - $path = str_replace('%23', '#', $path); // the prefix is sent as a query param, so revert the encoding of # |
|
| 267 | - |
|
| 268 | - try { |
|
| 269 | - $files = array(); |
|
| 270 | - /** @var OpenCloud\Common\Collection $objects */ |
|
| 271 | - $objects = $this->getContainer()->objectList(array( |
|
| 272 | - 'prefix' => $path, |
|
| 273 | - 'delimiter' => '/' |
|
| 274 | - )); |
|
| 275 | - |
|
| 276 | - /** @var OpenCloud\ObjectStore\Resource\DataObject $object */ |
|
| 277 | - foreach ($objects as $object) { |
|
| 278 | - $file = basename($object->getName()); |
|
| 279 | - if ($file !== basename($path)) { |
|
| 280 | - $files[] = $file; |
|
| 281 | - } |
|
| 282 | - } |
|
| 283 | - |
|
| 284 | - return IteratorDirectory::wrap($files); |
|
| 285 | - } catch (\Exception $e) { |
|
| 286 | - \OCP\Util::writeLog('files_external', $e->getMessage(), \OCP\Util::ERROR); |
|
| 287 | - return false; |
|
| 288 | - } |
|
| 289 | - |
|
| 290 | - } |
|
| 291 | - |
|
| 292 | - public function stat($path) { |
|
| 293 | - $path = $this->normalizePath($path); |
|
| 294 | - |
|
| 295 | - if ($path === '.') { |
|
| 296 | - $path = ''; |
|
| 297 | - } else if ($this->is_dir($path)) { |
|
| 298 | - $path .= '/'; |
|
| 299 | - } |
|
| 300 | - |
|
| 301 | - try { |
|
| 302 | - /** @var DataObject $object */ |
|
| 303 | - $object = $this->fetchObject($path); |
|
| 304 | - if (!$object) { |
|
| 305 | - return false; |
|
| 306 | - } |
|
| 307 | - } catch (ClientErrorResponseException $e) { |
|
| 308 | - \OCP\Util::writeLog('files_external', $e->getMessage(), \OCP\Util::ERROR); |
|
| 309 | - return false; |
|
| 310 | - } |
|
| 311 | - |
|
| 312 | - $dateTime = \DateTime::createFromFormat(\DateTime::RFC1123, $object->getLastModified()); |
|
| 313 | - if ($dateTime !== false) { |
|
| 314 | - $mtime = $dateTime->getTimestamp(); |
|
| 315 | - } else { |
|
| 316 | - $mtime = null; |
|
| 317 | - } |
|
| 318 | - $objectMetadata = $object->getMetadata(); |
|
| 319 | - $metaTimestamp = $objectMetadata->getProperty('timestamp'); |
|
| 320 | - if (isset($metaTimestamp)) { |
|
| 321 | - $mtime = $metaTimestamp; |
|
| 322 | - } |
|
| 323 | - |
|
| 324 | - if (!empty($mtime)) { |
|
| 325 | - $mtime = floor($mtime); |
|
| 326 | - } |
|
| 327 | - |
|
| 328 | - $stat = array(); |
|
| 329 | - $stat['size'] = (int)$object->getContentLength(); |
|
| 330 | - $stat['mtime'] = $mtime; |
|
| 331 | - $stat['atime'] = time(); |
|
| 332 | - return $stat; |
|
| 333 | - } |
|
| 334 | - |
|
| 335 | - public function filetype($path) { |
|
| 336 | - $path = $this->normalizePath($path); |
|
| 337 | - |
|
| 338 | - if ($path !== '.' && $this->doesObjectExist($path)) { |
|
| 339 | - return 'file'; |
|
| 340 | - } |
|
| 341 | - |
|
| 342 | - if ($path !== '.') { |
|
| 343 | - $path .= '/'; |
|
| 344 | - } |
|
| 345 | - |
|
| 346 | - if ($this->doesObjectExist($path)) { |
|
| 347 | - return 'dir'; |
|
| 348 | - } |
|
| 349 | - } |
|
| 350 | - |
|
| 351 | - public function unlink($path) { |
|
| 352 | - $path = $this->normalizePath($path); |
|
| 353 | - |
|
| 354 | - if ($this->is_dir($path)) { |
|
| 355 | - return $this->rmdir($path); |
|
| 356 | - } |
|
| 357 | - |
|
| 358 | - try { |
|
| 359 | - $this->getContainer()->dataObject()->setName($path)->delete(); |
|
| 360 | - $this->objectCache->remove($path); |
|
| 361 | - $this->objectCache->remove($path . '/'); |
|
| 362 | - } catch (ClientErrorResponseException $e) { |
|
| 363 | - if ($e->getResponse()->getStatusCode() !== 404) { |
|
| 364 | - \OCP\Util::writeLog('files_external', $e->getMessage(), \OCP\Util::ERROR); |
|
| 365 | - } |
|
| 366 | - return false; |
|
| 367 | - } |
|
| 368 | - |
|
| 369 | - return true; |
|
| 370 | - } |
|
| 371 | - |
|
| 372 | - public function fopen($path, $mode) { |
|
| 373 | - $path = $this->normalizePath($path); |
|
| 374 | - |
|
| 375 | - switch ($mode) { |
|
| 376 | - case 'a': |
|
| 377 | - case 'ab': |
|
| 378 | - case 'a+': |
|
| 379 | - return false; |
|
| 380 | - case 'r': |
|
| 381 | - case 'rb': |
|
| 382 | - try { |
|
| 383 | - $c = $this->getContainer(); |
|
| 384 | - $streamFactory = new \Guzzle\Stream\PhpStreamRequestFactory(); |
|
| 385 | - /** @var \OpenCloud\Common\Http\Client $client */ |
|
| 386 | - $client = $c->getClient(); |
|
| 387 | - $streamInterface = $streamFactory->fromRequest($client->get($c->getUrl($path))); |
|
| 388 | - $streamInterface->rewind(); |
|
| 389 | - $stream = $streamInterface->getStream(); |
|
| 390 | - stream_context_set_option($stream, 'swift','content', $streamInterface); |
|
| 391 | - if(!strrpos($streamInterface |
|
| 392 | - ->getMetaData('wrapper_data')[0], '404 Not Found')) { |
|
| 393 | - return $stream; |
|
| 394 | - } |
|
| 395 | - return false; |
|
| 396 | - } catch (\Guzzle\Http\Exception\BadResponseException $e) { |
|
| 397 | - \OCP\Util::writeLog('files_external', $e->getMessage(), \OCP\Util::ERROR); |
|
| 398 | - return false; |
|
| 399 | - } |
|
| 400 | - case 'w': |
|
| 401 | - case 'wb': |
|
| 402 | - case 'r+': |
|
| 403 | - case 'w+': |
|
| 404 | - case 'wb+': |
|
| 405 | - case 'x': |
|
| 406 | - case 'x+': |
|
| 407 | - case 'c': |
|
| 408 | - case 'c+': |
|
| 409 | - if (strrpos($path, '.') !== false) { |
|
| 410 | - $ext = substr($path, strrpos($path, '.')); |
|
| 411 | - } else { |
|
| 412 | - $ext = ''; |
|
| 413 | - } |
|
| 414 | - $tmpFile = \OCP\Files::tmpFile($ext); |
|
| 415 | - // Fetch existing file if required |
|
| 416 | - if ($mode[0] !== 'w' && $this->file_exists($path)) { |
|
| 417 | - if ($mode[0] === 'x') { |
|
| 418 | - // File cannot already exist |
|
| 419 | - return false; |
|
| 420 | - } |
|
| 421 | - $source = $this->fopen($path, 'r'); |
|
| 422 | - file_put_contents($tmpFile, $source); |
|
| 423 | - } |
|
| 424 | - $handle = fopen($tmpFile, $mode); |
|
| 425 | - return CallbackWrapper::wrap($handle, null, null, function () use ($path, $tmpFile) { |
|
| 426 | - $this->writeBack($tmpFile, $path); |
|
| 427 | - }); |
|
| 428 | - } |
|
| 429 | - } |
|
| 430 | - |
|
| 431 | - public function touch($path, $mtime = null) { |
|
| 432 | - $path = $this->normalizePath($path); |
|
| 433 | - if (is_null($mtime)) { |
|
| 434 | - $mtime = time(); |
|
| 435 | - } |
|
| 436 | - $metadata = array('timestamp' => $mtime); |
|
| 437 | - if ($this->file_exists($path)) { |
|
| 438 | - if ($this->is_dir($path) && $path != '.') { |
|
| 439 | - $path .= '/'; |
|
| 440 | - } |
|
| 441 | - |
|
| 442 | - $object = $this->fetchObject($path); |
|
| 443 | - if ($object->saveMetadata($metadata)) { |
|
| 444 | - // invalidate target object to force repopulation on fetch |
|
| 445 | - $this->objectCache->remove($path); |
|
| 446 | - } |
|
| 447 | - return true; |
|
| 448 | - } else { |
|
| 449 | - $mimeType = \OC::$server->getMimeTypeDetector()->detectPath($path); |
|
| 450 | - $customHeaders = array('content-type' => $mimeType); |
|
| 451 | - $metadataHeaders = DataObject::stockHeaders($metadata); |
|
| 452 | - $allHeaders = $customHeaders + $metadataHeaders; |
|
| 453 | - $this->getContainer()->uploadObject($path, '', $allHeaders); |
|
| 454 | - // invalidate target object to force repopulation on fetch |
|
| 455 | - $this->objectCache->remove($path); |
|
| 456 | - return true; |
|
| 457 | - } |
|
| 458 | - } |
|
| 459 | - |
|
| 460 | - public function copy($path1, $path2) { |
|
| 461 | - $path1 = $this->normalizePath($path1); |
|
| 462 | - $path2 = $this->normalizePath($path2); |
|
| 463 | - |
|
| 464 | - $fileType = $this->filetype($path1); |
|
| 465 | - if ($fileType === 'file') { |
|
| 466 | - |
|
| 467 | - // make way |
|
| 468 | - $this->unlink($path2); |
|
| 469 | - |
|
| 470 | - try { |
|
| 471 | - $source = $this->fetchObject($path1); |
|
| 472 | - $source->copy($this->bucket . '/' . $path2); |
|
| 473 | - // invalidate target object to force repopulation on fetch |
|
| 474 | - $this->objectCache->remove($path2); |
|
| 475 | - $this->objectCache->remove($path2 . '/'); |
|
| 476 | - } catch (ClientErrorResponseException $e) { |
|
| 477 | - \OCP\Util::writeLog('files_external', $e->getMessage(), \OCP\Util::ERROR); |
|
| 478 | - return false; |
|
| 479 | - } |
|
| 480 | - |
|
| 481 | - } else if ($fileType === 'dir') { |
|
| 482 | - |
|
| 483 | - // make way |
|
| 484 | - $this->unlink($path2); |
|
| 485 | - |
|
| 486 | - try { |
|
| 487 | - $source = $this->fetchObject($path1 . '/'); |
|
| 488 | - $source->copy($this->bucket . '/' . $path2 . '/'); |
|
| 489 | - // invalidate target object to force repopulation on fetch |
|
| 490 | - $this->objectCache->remove($path2); |
|
| 491 | - $this->objectCache->remove($path2 . '/'); |
|
| 492 | - } catch (ClientErrorResponseException $e) { |
|
| 493 | - \OCP\Util::writeLog('files_external', $e->getMessage(), \OCP\Util::ERROR); |
|
| 494 | - return false; |
|
| 495 | - } |
|
| 496 | - |
|
| 497 | - $dh = $this->opendir($path1); |
|
| 498 | - while ($file = readdir($dh)) { |
|
| 499 | - if (\OC\Files\Filesystem::isIgnoredDir($file)) { |
|
| 500 | - continue; |
|
| 501 | - } |
|
| 502 | - |
|
| 503 | - $source = $path1 . '/' . $file; |
|
| 504 | - $target = $path2 . '/' . $file; |
|
| 505 | - $this->copy($source, $target); |
|
| 506 | - } |
|
| 507 | - |
|
| 508 | - } else { |
|
| 509 | - //file does not exist |
|
| 510 | - return false; |
|
| 511 | - } |
|
| 512 | - |
|
| 513 | - return true; |
|
| 514 | - } |
|
| 515 | - |
|
| 516 | - public function rename($path1, $path2) { |
|
| 517 | - $path1 = $this->normalizePath($path1); |
|
| 518 | - $path2 = $this->normalizePath($path2); |
|
| 519 | - |
|
| 520 | - $fileType = $this->filetype($path1); |
|
| 521 | - |
|
| 522 | - if ($fileType === 'dir' || $fileType === 'file') { |
|
| 523 | - // copy |
|
| 524 | - if ($this->copy($path1, $path2) === false) { |
|
| 525 | - return false; |
|
| 526 | - } |
|
| 527 | - |
|
| 528 | - // cleanup |
|
| 529 | - if ($this->unlink($path1) === false) { |
|
| 530 | - $this->unlink($path2); |
|
| 531 | - return false; |
|
| 532 | - } |
|
| 533 | - |
|
| 534 | - return true; |
|
| 535 | - } |
|
| 536 | - |
|
| 537 | - return false; |
|
| 538 | - } |
|
| 539 | - |
|
| 540 | - public function getId() { |
|
| 541 | - return $this->id; |
|
| 542 | - } |
|
| 543 | - |
|
| 544 | - /** |
|
| 545 | - * Returns the connection |
|
| 546 | - * |
|
| 547 | - * @return OpenCloud\ObjectStore\Service connected client |
|
| 548 | - * @throws \Exception if connection could not be made |
|
| 549 | - */ |
|
| 550 | - public function getConnection() { |
|
| 551 | - if (!is_null($this->connection)) { |
|
| 552 | - return $this->connection; |
|
| 553 | - } |
|
| 554 | - |
|
| 555 | - $settings = array( |
|
| 556 | - 'username' => $this->params['user'], |
|
| 557 | - ); |
|
| 558 | - |
|
| 559 | - if (!empty($this->params['password'])) { |
|
| 560 | - $settings['password'] = $this->params['password']; |
|
| 561 | - } else if (!empty($this->params['key'])) { |
|
| 562 | - $settings['apiKey'] = $this->params['key']; |
|
| 563 | - } |
|
| 564 | - |
|
| 565 | - if (!empty($this->params['tenant'])) { |
|
| 566 | - $settings['tenantName'] = $this->params['tenant']; |
|
| 567 | - } |
|
| 568 | - |
|
| 569 | - if (!empty($this->params['timeout'])) { |
|
| 570 | - $settings['timeout'] = $this->params['timeout']; |
|
| 571 | - } |
|
| 572 | - |
|
| 573 | - if (isset($settings['apiKey'])) { |
|
| 574 | - $this->anchor = new Rackspace($this->params['url'], $settings); |
|
| 575 | - } else { |
|
| 576 | - $this->anchor = new OpenStack($this->params['url'], $settings); |
|
| 577 | - } |
|
| 578 | - |
|
| 579 | - $connection = $this->anchor->objectStoreService($this->params['service_name'], $this->params['region']); |
|
| 580 | - |
|
| 581 | - if (!empty($this->params['endpoint_url'])) { |
|
| 582 | - $endpoint = $connection->getEndpoint(); |
|
| 583 | - $endpoint->setPublicUrl($this->params['endpoint_url']); |
|
| 584 | - $endpoint->setPrivateUrl($this->params['endpoint_url']); |
|
| 585 | - $connection->setEndpoint($endpoint); |
|
| 586 | - } |
|
| 587 | - |
|
| 588 | - $this->connection = $connection; |
|
| 589 | - |
|
| 590 | - return $this->connection; |
|
| 591 | - } |
|
| 592 | - |
|
| 593 | - /** |
|
| 594 | - * Returns the initialized object store container. |
|
| 595 | - * |
|
| 596 | - * @return OpenCloud\ObjectStore\Resource\Container |
|
| 597 | - */ |
|
| 598 | - public function getContainer() { |
|
| 599 | - if (!is_null($this->container)) { |
|
| 600 | - return $this->container; |
|
| 601 | - } |
|
| 602 | - |
|
| 603 | - try { |
|
| 604 | - $this->container = $this->getConnection()->getContainer($this->bucket); |
|
| 605 | - } catch (ClientErrorResponseException $e) { |
|
| 606 | - $this->container = $this->getConnection()->createContainer($this->bucket); |
|
| 607 | - } |
|
| 608 | - |
|
| 609 | - if (!$this->file_exists('.')) { |
|
| 610 | - $this->mkdir('.'); |
|
| 611 | - } |
|
| 612 | - |
|
| 613 | - return $this->container; |
|
| 614 | - } |
|
| 615 | - |
|
| 616 | - public function writeBack($tmpFile, $path) { |
|
| 617 | - $fileData = fopen($tmpFile, 'r'); |
|
| 618 | - $this->getContainer()->uploadObject($path, $fileData); |
|
| 619 | - // invalidate target object to force repopulation on fetch |
|
| 620 | - $this->objectCache->remove(self::$tmpFiles[$tmpFile]); |
|
| 621 | - unlink($tmpFile); |
|
| 622 | - } |
|
| 623 | - |
|
| 624 | - public function hasUpdated($path, $time) { |
|
| 625 | - if ($this->is_file($path)) { |
|
| 626 | - return parent::hasUpdated($path, $time); |
|
| 627 | - } |
|
| 628 | - $path = $this->normalizePath($path); |
|
| 629 | - $dh = $this->opendir($path); |
|
| 630 | - $content = array(); |
|
| 631 | - while (($file = readdir($dh)) !== false) { |
|
| 632 | - $content[] = $file; |
|
| 633 | - } |
|
| 634 | - if ($path === '.') { |
|
| 635 | - $path = ''; |
|
| 636 | - } |
|
| 637 | - $cachedContent = $this->getCache()->getFolderContents($path); |
|
| 638 | - $cachedNames = array_map(function ($content) { |
|
| 639 | - return $content['name']; |
|
| 640 | - }, $cachedContent); |
|
| 641 | - sort($cachedNames); |
|
| 642 | - sort($content); |
|
| 643 | - return $cachedNames != $content; |
|
| 644 | - } |
|
| 645 | - |
|
| 646 | - /** |
|
| 647 | - * check if curl is installed |
|
| 648 | - */ |
|
| 649 | - public static function checkDependencies() { |
|
| 650 | - return true; |
|
| 651 | - } |
|
| 51 | + /** |
|
| 52 | + * @var \OpenCloud\ObjectStore\Service |
|
| 53 | + */ |
|
| 54 | + private $connection; |
|
| 55 | + /** |
|
| 56 | + * @var \OpenCloud\ObjectStore\Resource\Container |
|
| 57 | + */ |
|
| 58 | + private $container; |
|
| 59 | + /** |
|
| 60 | + * @var \OpenCloud\OpenStack |
|
| 61 | + */ |
|
| 62 | + private $anchor; |
|
| 63 | + /** |
|
| 64 | + * @var string |
|
| 65 | + */ |
|
| 66 | + private $bucket; |
|
| 67 | + /** |
|
| 68 | + * Connection parameters |
|
| 69 | + * |
|
| 70 | + * @var array |
|
| 71 | + */ |
|
| 72 | + private $params; |
|
| 73 | + |
|
| 74 | + /** @var string */ |
|
| 75 | + private $id; |
|
| 76 | + |
|
| 77 | + /** |
|
| 78 | + * @var array |
|
| 79 | + */ |
|
| 80 | + private static $tmpFiles = array(); |
|
| 81 | + |
|
| 82 | + /** |
|
| 83 | + * Key value cache mapping path to data object. Maps path to |
|
| 84 | + * \OpenCloud\OpenStack\ObjectStorage\Resource\DataObject for existing |
|
| 85 | + * paths and path to false for not existing paths. |
|
| 86 | + * @var \OCP\ICache |
|
| 87 | + */ |
|
| 88 | + private $objectCache; |
|
| 89 | + |
|
| 90 | + /** |
|
| 91 | + * @param string $path |
|
| 92 | + */ |
|
| 93 | + private function normalizePath($path) { |
|
| 94 | + $path = trim($path, '/'); |
|
| 95 | + |
|
| 96 | + if (!$path) { |
|
| 97 | + $path = '.'; |
|
| 98 | + } |
|
| 99 | + |
|
| 100 | + $path = str_replace('#', '%23', $path); |
|
| 101 | + |
|
| 102 | + return $path; |
|
| 103 | + } |
|
| 104 | + |
|
| 105 | + const SUBCONTAINER_FILE = '.subcontainers'; |
|
| 106 | + |
|
| 107 | + /** |
|
| 108 | + * translate directory path to container name |
|
| 109 | + * |
|
| 110 | + * @param string $path |
|
| 111 | + * @return string |
|
| 112 | + */ |
|
| 113 | + |
|
| 114 | + /** |
|
| 115 | + * Fetches an object from the API. |
|
| 116 | + * If the object is cached already or a |
|
| 117 | + * failed "doesn't exist" response was cached, |
|
| 118 | + * that one will be returned. |
|
| 119 | + * |
|
| 120 | + * @param string $path |
|
| 121 | + * @return \OpenCloud\ObjectStore\Resource\DataObject|bool object |
|
| 122 | + * or false if the object did not exist |
|
| 123 | + */ |
|
| 124 | + private function fetchObject($path) { |
|
| 125 | + if ($this->objectCache->hasKey($path)) { |
|
| 126 | + // might be "false" if object did not exist from last check |
|
| 127 | + return $this->objectCache->get($path); |
|
| 128 | + } |
|
| 129 | + try { |
|
| 130 | + $object = $this->getContainer()->getPartialObject($path); |
|
| 131 | + $this->objectCache->set($path, $object); |
|
| 132 | + return $object; |
|
| 133 | + } catch (ClientErrorResponseException $e) { |
|
| 134 | + // this exception happens when the object does not exist, which |
|
| 135 | + // is expected in most cases |
|
| 136 | + $this->objectCache->set($path, false); |
|
| 137 | + return false; |
|
| 138 | + } catch (ClientErrorResponseException $e) { |
|
| 139 | + // Expected response is "404 Not Found", so only log if it isn't |
|
| 140 | + if ($e->getResponse()->getStatusCode() !== 404) { |
|
| 141 | + \OCP\Util::writeLog('files_external', $e->getMessage(), \OCP\Util::ERROR); |
|
| 142 | + } |
|
| 143 | + return false; |
|
| 144 | + } |
|
| 145 | + } |
|
| 146 | + |
|
| 147 | + /** |
|
| 148 | + * Returns whether the given path exists. |
|
| 149 | + * |
|
| 150 | + * @param string $path |
|
| 151 | + * |
|
| 152 | + * @return bool true if the object exist, false otherwise |
|
| 153 | + */ |
|
| 154 | + private function doesObjectExist($path) { |
|
| 155 | + return $this->fetchObject($path) !== false; |
|
| 156 | + } |
|
| 157 | + |
|
| 158 | + public function __construct($params) { |
|
| 159 | + if ((empty($params['key']) and empty($params['password'])) |
|
| 160 | + or empty($params['user']) or empty($params['bucket']) |
|
| 161 | + or empty($params['region']) |
|
| 162 | + ) { |
|
| 163 | + throw new \Exception("API Key or password, Username, Bucket and Region have to be configured."); |
|
| 164 | + } |
|
| 165 | + |
|
| 166 | + $this->id = 'swift::' . $params['user'] . md5($params['bucket']); |
|
| 167 | + |
|
| 168 | + $bucketUrl = Url::factory($params['bucket']); |
|
| 169 | + if ($bucketUrl->isAbsolute()) { |
|
| 170 | + $this->bucket = end(($bucketUrl->getPathSegments())); |
|
| 171 | + $params['endpoint_url'] = $bucketUrl->addPath('..')->normalizePath(); |
|
| 172 | + } else { |
|
| 173 | + $this->bucket = $params['bucket']; |
|
| 174 | + } |
|
| 175 | + |
|
| 176 | + if (empty($params['url'])) { |
|
| 177 | + $params['url'] = 'https://identity.api.rackspacecloud.com/v2.0/'; |
|
| 178 | + } |
|
| 179 | + |
|
| 180 | + if (empty($params['service_name'])) { |
|
| 181 | + $params['service_name'] = 'cloudFiles'; |
|
| 182 | + } |
|
| 183 | + |
|
| 184 | + $this->params = $params; |
|
| 185 | + // FIXME: private class... |
|
| 186 | + $this->objectCache = new \OC\Cache\CappedMemoryCache(); |
|
| 187 | + } |
|
| 188 | + |
|
| 189 | + public function mkdir($path) { |
|
| 190 | + $path = $this->normalizePath($path); |
|
| 191 | + |
|
| 192 | + if ($this->is_dir($path)) { |
|
| 193 | + return false; |
|
| 194 | + } |
|
| 195 | + |
|
| 196 | + if ($path !== '.') { |
|
| 197 | + $path .= '/'; |
|
| 198 | + } |
|
| 199 | + |
|
| 200 | + try { |
|
| 201 | + $customHeaders = array('content-type' => 'httpd/unix-directory'); |
|
| 202 | + $metadataHeaders = DataObject::stockHeaders(array()); |
|
| 203 | + $allHeaders = $customHeaders + $metadataHeaders; |
|
| 204 | + $this->getContainer()->uploadObject($path, '', $allHeaders); |
|
| 205 | + // invalidate so that the next access gets the real object |
|
| 206 | + // with all properties |
|
| 207 | + $this->objectCache->remove($path); |
|
| 208 | + } catch (Exceptions\CreateUpdateError $e) { |
|
| 209 | + \OCP\Util::writeLog('files_external', $e->getMessage(), \OCP\Util::ERROR); |
|
| 210 | + return false; |
|
| 211 | + } |
|
| 212 | + |
|
| 213 | + return true; |
|
| 214 | + } |
|
| 215 | + |
|
| 216 | + public function file_exists($path) { |
|
| 217 | + $path = $this->normalizePath($path); |
|
| 218 | + |
|
| 219 | + if ($path !== '.' && $this->is_dir($path)) { |
|
| 220 | + $path .= '/'; |
|
| 221 | + } |
|
| 222 | + |
|
| 223 | + return $this->doesObjectExist($path); |
|
| 224 | + } |
|
| 225 | + |
|
| 226 | + public function rmdir($path) { |
|
| 227 | + $path = $this->normalizePath($path); |
|
| 228 | + |
|
| 229 | + if (!$this->is_dir($path) || !$this->isDeletable($path)) { |
|
| 230 | + return false; |
|
| 231 | + } |
|
| 232 | + |
|
| 233 | + $dh = $this->opendir($path); |
|
| 234 | + while ($file = readdir($dh)) { |
|
| 235 | + if (\OC\Files\Filesystem::isIgnoredDir($file)) { |
|
| 236 | + continue; |
|
| 237 | + } |
|
| 238 | + |
|
| 239 | + if ($this->is_dir($path . '/' . $file)) { |
|
| 240 | + $this->rmdir($path . '/' . $file); |
|
| 241 | + } else { |
|
| 242 | + $this->unlink($path . '/' . $file); |
|
| 243 | + } |
|
| 244 | + } |
|
| 245 | + |
|
| 246 | + try { |
|
| 247 | + $this->getContainer()->dataObject()->setName($path . '/')->delete(); |
|
| 248 | + $this->objectCache->remove($path . '/'); |
|
| 249 | + } catch (Exceptions\DeleteError $e) { |
|
| 250 | + \OCP\Util::writeLog('files_external', $e->getMessage(), \OCP\Util::ERROR); |
|
| 251 | + return false; |
|
| 252 | + } |
|
| 253 | + |
|
| 254 | + return true; |
|
| 255 | + } |
|
| 256 | + |
|
| 257 | + public function opendir($path) { |
|
| 258 | + $path = $this->normalizePath($path); |
|
| 259 | + |
|
| 260 | + if ($path === '.') { |
|
| 261 | + $path = ''; |
|
| 262 | + } else { |
|
| 263 | + $path .= '/'; |
|
| 264 | + } |
|
| 265 | + |
|
| 266 | + $path = str_replace('%23', '#', $path); // the prefix is sent as a query param, so revert the encoding of # |
|
| 267 | + |
|
| 268 | + try { |
|
| 269 | + $files = array(); |
|
| 270 | + /** @var OpenCloud\Common\Collection $objects */ |
|
| 271 | + $objects = $this->getContainer()->objectList(array( |
|
| 272 | + 'prefix' => $path, |
|
| 273 | + 'delimiter' => '/' |
|
| 274 | + )); |
|
| 275 | + |
|
| 276 | + /** @var OpenCloud\ObjectStore\Resource\DataObject $object */ |
|
| 277 | + foreach ($objects as $object) { |
|
| 278 | + $file = basename($object->getName()); |
|
| 279 | + if ($file !== basename($path)) { |
|
| 280 | + $files[] = $file; |
|
| 281 | + } |
|
| 282 | + } |
|
| 283 | + |
|
| 284 | + return IteratorDirectory::wrap($files); |
|
| 285 | + } catch (\Exception $e) { |
|
| 286 | + \OCP\Util::writeLog('files_external', $e->getMessage(), \OCP\Util::ERROR); |
|
| 287 | + return false; |
|
| 288 | + } |
|
| 289 | + |
|
| 290 | + } |
|
| 291 | + |
|
| 292 | + public function stat($path) { |
|
| 293 | + $path = $this->normalizePath($path); |
|
| 294 | + |
|
| 295 | + if ($path === '.') { |
|
| 296 | + $path = ''; |
|
| 297 | + } else if ($this->is_dir($path)) { |
|
| 298 | + $path .= '/'; |
|
| 299 | + } |
|
| 300 | + |
|
| 301 | + try { |
|
| 302 | + /** @var DataObject $object */ |
|
| 303 | + $object = $this->fetchObject($path); |
|
| 304 | + if (!$object) { |
|
| 305 | + return false; |
|
| 306 | + } |
|
| 307 | + } catch (ClientErrorResponseException $e) { |
|
| 308 | + \OCP\Util::writeLog('files_external', $e->getMessage(), \OCP\Util::ERROR); |
|
| 309 | + return false; |
|
| 310 | + } |
|
| 311 | + |
|
| 312 | + $dateTime = \DateTime::createFromFormat(\DateTime::RFC1123, $object->getLastModified()); |
|
| 313 | + if ($dateTime !== false) { |
|
| 314 | + $mtime = $dateTime->getTimestamp(); |
|
| 315 | + } else { |
|
| 316 | + $mtime = null; |
|
| 317 | + } |
|
| 318 | + $objectMetadata = $object->getMetadata(); |
|
| 319 | + $metaTimestamp = $objectMetadata->getProperty('timestamp'); |
|
| 320 | + if (isset($metaTimestamp)) { |
|
| 321 | + $mtime = $metaTimestamp; |
|
| 322 | + } |
|
| 323 | + |
|
| 324 | + if (!empty($mtime)) { |
|
| 325 | + $mtime = floor($mtime); |
|
| 326 | + } |
|
| 327 | + |
|
| 328 | + $stat = array(); |
|
| 329 | + $stat['size'] = (int)$object->getContentLength(); |
|
| 330 | + $stat['mtime'] = $mtime; |
|
| 331 | + $stat['atime'] = time(); |
|
| 332 | + return $stat; |
|
| 333 | + } |
|
| 334 | + |
|
| 335 | + public function filetype($path) { |
|
| 336 | + $path = $this->normalizePath($path); |
|
| 337 | + |
|
| 338 | + if ($path !== '.' && $this->doesObjectExist($path)) { |
|
| 339 | + return 'file'; |
|
| 340 | + } |
|
| 341 | + |
|
| 342 | + if ($path !== '.') { |
|
| 343 | + $path .= '/'; |
|
| 344 | + } |
|
| 345 | + |
|
| 346 | + if ($this->doesObjectExist($path)) { |
|
| 347 | + return 'dir'; |
|
| 348 | + } |
|
| 349 | + } |
|
| 350 | + |
|
| 351 | + public function unlink($path) { |
|
| 352 | + $path = $this->normalizePath($path); |
|
| 353 | + |
|
| 354 | + if ($this->is_dir($path)) { |
|
| 355 | + return $this->rmdir($path); |
|
| 356 | + } |
|
| 357 | + |
|
| 358 | + try { |
|
| 359 | + $this->getContainer()->dataObject()->setName($path)->delete(); |
|
| 360 | + $this->objectCache->remove($path); |
|
| 361 | + $this->objectCache->remove($path . '/'); |
|
| 362 | + } catch (ClientErrorResponseException $e) { |
|
| 363 | + if ($e->getResponse()->getStatusCode() !== 404) { |
|
| 364 | + \OCP\Util::writeLog('files_external', $e->getMessage(), \OCP\Util::ERROR); |
|
| 365 | + } |
|
| 366 | + return false; |
|
| 367 | + } |
|
| 368 | + |
|
| 369 | + return true; |
|
| 370 | + } |
|
| 371 | + |
|
| 372 | + public function fopen($path, $mode) { |
|
| 373 | + $path = $this->normalizePath($path); |
|
| 374 | + |
|
| 375 | + switch ($mode) { |
|
| 376 | + case 'a': |
|
| 377 | + case 'ab': |
|
| 378 | + case 'a+': |
|
| 379 | + return false; |
|
| 380 | + case 'r': |
|
| 381 | + case 'rb': |
|
| 382 | + try { |
|
| 383 | + $c = $this->getContainer(); |
|
| 384 | + $streamFactory = new \Guzzle\Stream\PhpStreamRequestFactory(); |
|
| 385 | + /** @var \OpenCloud\Common\Http\Client $client */ |
|
| 386 | + $client = $c->getClient(); |
|
| 387 | + $streamInterface = $streamFactory->fromRequest($client->get($c->getUrl($path))); |
|
| 388 | + $streamInterface->rewind(); |
|
| 389 | + $stream = $streamInterface->getStream(); |
|
| 390 | + stream_context_set_option($stream, 'swift','content', $streamInterface); |
|
| 391 | + if(!strrpos($streamInterface |
|
| 392 | + ->getMetaData('wrapper_data')[0], '404 Not Found')) { |
|
| 393 | + return $stream; |
|
| 394 | + } |
|
| 395 | + return false; |
|
| 396 | + } catch (\Guzzle\Http\Exception\BadResponseException $e) { |
|
| 397 | + \OCP\Util::writeLog('files_external', $e->getMessage(), \OCP\Util::ERROR); |
|
| 398 | + return false; |
|
| 399 | + } |
|
| 400 | + case 'w': |
|
| 401 | + case 'wb': |
|
| 402 | + case 'r+': |
|
| 403 | + case 'w+': |
|
| 404 | + case 'wb+': |
|
| 405 | + case 'x': |
|
| 406 | + case 'x+': |
|
| 407 | + case 'c': |
|
| 408 | + case 'c+': |
|
| 409 | + if (strrpos($path, '.') !== false) { |
|
| 410 | + $ext = substr($path, strrpos($path, '.')); |
|
| 411 | + } else { |
|
| 412 | + $ext = ''; |
|
| 413 | + } |
|
| 414 | + $tmpFile = \OCP\Files::tmpFile($ext); |
|
| 415 | + // Fetch existing file if required |
|
| 416 | + if ($mode[0] !== 'w' && $this->file_exists($path)) { |
|
| 417 | + if ($mode[0] === 'x') { |
|
| 418 | + // File cannot already exist |
|
| 419 | + return false; |
|
| 420 | + } |
|
| 421 | + $source = $this->fopen($path, 'r'); |
|
| 422 | + file_put_contents($tmpFile, $source); |
|
| 423 | + } |
|
| 424 | + $handle = fopen($tmpFile, $mode); |
|
| 425 | + return CallbackWrapper::wrap($handle, null, null, function () use ($path, $tmpFile) { |
|
| 426 | + $this->writeBack($tmpFile, $path); |
|
| 427 | + }); |
|
| 428 | + } |
|
| 429 | + } |
|
| 430 | + |
|
| 431 | + public function touch($path, $mtime = null) { |
|
| 432 | + $path = $this->normalizePath($path); |
|
| 433 | + if (is_null($mtime)) { |
|
| 434 | + $mtime = time(); |
|
| 435 | + } |
|
| 436 | + $metadata = array('timestamp' => $mtime); |
|
| 437 | + if ($this->file_exists($path)) { |
|
| 438 | + if ($this->is_dir($path) && $path != '.') { |
|
| 439 | + $path .= '/'; |
|
| 440 | + } |
|
| 441 | + |
|
| 442 | + $object = $this->fetchObject($path); |
|
| 443 | + if ($object->saveMetadata($metadata)) { |
|
| 444 | + // invalidate target object to force repopulation on fetch |
|
| 445 | + $this->objectCache->remove($path); |
|
| 446 | + } |
|
| 447 | + return true; |
|
| 448 | + } else { |
|
| 449 | + $mimeType = \OC::$server->getMimeTypeDetector()->detectPath($path); |
|
| 450 | + $customHeaders = array('content-type' => $mimeType); |
|
| 451 | + $metadataHeaders = DataObject::stockHeaders($metadata); |
|
| 452 | + $allHeaders = $customHeaders + $metadataHeaders; |
|
| 453 | + $this->getContainer()->uploadObject($path, '', $allHeaders); |
|
| 454 | + // invalidate target object to force repopulation on fetch |
|
| 455 | + $this->objectCache->remove($path); |
|
| 456 | + return true; |
|
| 457 | + } |
|
| 458 | + } |
|
| 459 | + |
|
| 460 | + public function copy($path1, $path2) { |
|
| 461 | + $path1 = $this->normalizePath($path1); |
|
| 462 | + $path2 = $this->normalizePath($path2); |
|
| 463 | + |
|
| 464 | + $fileType = $this->filetype($path1); |
|
| 465 | + if ($fileType === 'file') { |
|
| 466 | + |
|
| 467 | + // make way |
|
| 468 | + $this->unlink($path2); |
|
| 469 | + |
|
| 470 | + try { |
|
| 471 | + $source = $this->fetchObject($path1); |
|
| 472 | + $source->copy($this->bucket . '/' . $path2); |
|
| 473 | + // invalidate target object to force repopulation on fetch |
|
| 474 | + $this->objectCache->remove($path2); |
|
| 475 | + $this->objectCache->remove($path2 . '/'); |
|
| 476 | + } catch (ClientErrorResponseException $e) { |
|
| 477 | + \OCP\Util::writeLog('files_external', $e->getMessage(), \OCP\Util::ERROR); |
|
| 478 | + return false; |
|
| 479 | + } |
|
| 480 | + |
|
| 481 | + } else if ($fileType === 'dir') { |
|
| 482 | + |
|
| 483 | + // make way |
|
| 484 | + $this->unlink($path2); |
|
| 485 | + |
|
| 486 | + try { |
|
| 487 | + $source = $this->fetchObject($path1 . '/'); |
|
| 488 | + $source->copy($this->bucket . '/' . $path2 . '/'); |
|
| 489 | + // invalidate target object to force repopulation on fetch |
|
| 490 | + $this->objectCache->remove($path2); |
|
| 491 | + $this->objectCache->remove($path2 . '/'); |
|
| 492 | + } catch (ClientErrorResponseException $e) { |
|
| 493 | + \OCP\Util::writeLog('files_external', $e->getMessage(), \OCP\Util::ERROR); |
|
| 494 | + return false; |
|
| 495 | + } |
|
| 496 | + |
|
| 497 | + $dh = $this->opendir($path1); |
|
| 498 | + while ($file = readdir($dh)) { |
|
| 499 | + if (\OC\Files\Filesystem::isIgnoredDir($file)) { |
|
| 500 | + continue; |
|
| 501 | + } |
|
| 502 | + |
|
| 503 | + $source = $path1 . '/' . $file; |
|
| 504 | + $target = $path2 . '/' . $file; |
|
| 505 | + $this->copy($source, $target); |
|
| 506 | + } |
|
| 507 | + |
|
| 508 | + } else { |
|
| 509 | + //file does not exist |
|
| 510 | + return false; |
|
| 511 | + } |
|
| 512 | + |
|
| 513 | + return true; |
|
| 514 | + } |
|
| 515 | + |
|
| 516 | + public function rename($path1, $path2) { |
|
| 517 | + $path1 = $this->normalizePath($path1); |
|
| 518 | + $path2 = $this->normalizePath($path2); |
|
| 519 | + |
|
| 520 | + $fileType = $this->filetype($path1); |
|
| 521 | + |
|
| 522 | + if ($fileType === 'dir' || $fileType === 'file') { |
|
| 523 | + // copy |
|
| 524 | + if ($this->copy($path1, $path2) === false) { |
|
| 525 | + return false; |
|
| 526 | + } |
|
| 527 | + |
|
| 528 | + // cleanup |
|
| 529 | + if ($this->unlink($path1) === false) { |
|
| 530 | + $this->unlink($path2); |
|
| 531 | + return false; |
|
| 532 | + } |
|
| 533 | + |
|
| 534 | + return true; |
|
| 535 | + } |
|
| 536 | + |
|
| 537 | + return false; |
|
| 538 | + } |
|
| 539 | + |
|
| 540 | + public function getId() { |
|
| 541 | + return $this->id; |
|
| 542 | + } |
|
| 543 | + |
|
| 544 | + /** |
|
| 545 | + * Returns the connection |
|
| 546 | + * |
|
| 547 | + * @return OpenCloud\ObjectStore\Service connected client |
|
| 548 | + * @throws \Exception if connection could not be made |
|
| 549 | + */ |
|
| 550 | + public function getConnection() { |
|
| 551 | + if (!is_null($this->connection)) { |
|
| 552 | + return $this->connection; |
|
| 553 | + } |
|
| 554 | + |
|
| 555 | + $settings = array( |
|
| 556 | + 'username' => $this->params['user'], |
|
| 557 | + ); |
|
| 558 | + |
|
| 559 | + if (!empty($this->params['password'])) { |
|
| 560 | + $settings['password'] = $this->params['password']; |
|
| 561 | + } else if (!empty($this->params['key'])) { |
|
| 562 | + $settings['apiKey'] = $this->params['key']; |
|
| 563 | + } |
|
| 564 | + |
|
| 565 | + if (!empty($this->params['tenant'])) { |
|
| 566 | + $settings['tenantName'] = $this->params['tenant']; |
|
| 567 | + } |
|
| 568 | + |
|
| 569 | + if (!empty($this->params['timeout'])) { |
|
| 570 | + $settings['timeout'] = $this->params['timeout']; |
|
| 571 | + } |
|
| 572 | + |
|
| 573 | + if (isset($settings['apiKey'])) { |
|
| 574 | + $this->anchor = new Rackspace($this->params['url'], $settings); |
|
| 575 | + } else { |
|
| 576 | + $this->anchor = new OpenStack($this->params['url'], $settings); |
|
| 577 | + } |
|
| 578 | + |
|
| 579 | + $connection = $this->anchor->objectStoreService($this->params['service_name'], $this->params['region']); |
|
| 580 | + |
|
| 581 | + if (!empty($this->params['endpoint_url'])) { |
|
| 582 | + $endpoint = $connection->getEndpoint(); |
|
| 583 | + $endpoint->setPublicUrl($this->params['endpoint_url']); |
|
| 584 | + $endpoint->setPrivateUrl($this->params['endpoint_url']); |
|
| 585 | + $connection->setEndpoint($endpoint); |
|
| 586 | + } |
|
| 587 | + |
|
| 588 | + $this->connection = $connection; |
|
| 589 | + |
|
| 590 | + return $this->connection; |
|
| 591 | + } |
|
| 592 | + |
|
| 593 | + /** |
|
| 594 | + * Returns the initialized object store container. |
|
| 595 | + * |
|
| 596 | + * @return OpenCloud\ObjectStore\Resource\Container |
|
| 597 | + */ |
|
| 598 | + public function getContainer() { |
|
| 599 | + if (!is_null($this->container)) { |
|
| 600 | + return $this->container; |
|
| 601 | + } |
|
| 602 | + |
|
| 603 | + try { |
|
| 604 | + $this->container = $this->getConnection()->getContainer($this->bucket); |
|
| 605 | + } catch (ClientErrorResponseException $e) { |
|
| 606 | + $this->container = $this->getConnection()->createContainer($this->bucket); |
|
| 607 | + } |
|
| 608 | + |
|
| 609 | + if (!$this->file_exists('.')) { |
|
| 610 | + $this->mkdir('.'); |
|
| 611 | + } |
|
| 612 | + |
|
| 613 | + return $this->container; |
|
| 614 | + } |
|
| 615 | + |
|
| 616 | + public function writeBack($tmpFile, $path) { |
|
| 617 | + $fileData = fopen($tmpFile, 'r'); |
|
| 618 | + $this->getContainer()->uploadObject($path, $fileData); |
|
| 619 | + // invalidate target object to force repopulation on fetch |
|
| 620 | + $this->objectCache->remove(self::$tmpFiles[$tmpFile]); |
|
| 621 | + unlink($tmpFile); |
|
| 622 | + } |
|
| 623 | + |
|
| 624 | + public function hasUpdated($path, $time) { |
|
| 625 | + if ($this->is_file($path)) { |
|
| 626 | + return parent::hasUpdated($path, $time); |
|
| 627 | + } |
|
| 628 | + $path = $this->normalizePath($path); |
|
| 629 | + $dh = $this->opendir($path); |
|
| 630 | + $content = array(); |
|
| 631 | + while (($file = readdir($dh)) !== false) { |
|
| 632 | + $content[] = $file; |
|
| 633 | + } |
|
| 634 | + if ($path === '.') { |
|
| 635 | + $path = ''; |
|
| 636 | + } |
|
| 637 | + $cachedContent = $this->getCache()->getFolderContents($path); |
|
| 638 | + $cachedNames = array_map(function ($content) { |
|
| 639 | + return $content['name']; |
|
| 640 | + }, $cachedContent); |
|
| 641 | + sort($cachedNames); |
|
| 642 | + sort($content); |
|
| 643 | + return $cachedNames != $content; |
|
| 644 | + } |
|
| 645 | + |
|
| 646 | + /** |
|
| 647 | + * check if curl is installed |
|
| 648 | + */ |
|
| 649 | + public static function checkDependencies() { |
|
| 650 | + return true; |
|
| 651 | + } |
|
| 652 | 652 | |
| 653 | 653 | } |
@@ -52,891 +52,891 @@ |
||
| 52 | 52 | */ |
| 53 | 53 | class ShareAPIController extends OCSController { |
| 54 | 54 | |
| 55 | - /** @var IManager */ |
|
| 56 | - private $shareManager; |
|
| 57 | - /** @var IGroupManager */ |
|
| 58 | - private $groupManager; |
|
| 59 | - /** @var IUserManager */ |
|
| 60 | - private $userManager; |
|
| 61 | - /** @var IRequest */ |
|
| 62 | - protected $request; |
|
| 63 | - /** @var IRootFolder */ |
|
| 64 | - private $rootFolder; |
|
| 65 | - /** @var IURLGenerator */ |
|
| 66 | - private $urlGenerator; |
|
| 67 | - /** @var string */ |
|
| 68 | - private $currentUser; |
|
| 69 | - /** @var IL10N */ |
|
| 70 | - private $l; |
|
| 71 | - /** @var \OCP\Files\Node */ |
|
| 72 | - private $lockedNode; |
|
| 73 | - |
|
| 74 | - /** |
|
| 75 | - * Share20OCS constructor. |
|
| 76 | - * |
|
| 77 | - * @param string $appName |
|
| 78 | - * @param IRequest $request |
|
| 79 | - * @param IManager $shareManager |
|
| 80 | - * @param IGroupManager $groupManager |
|
| 81 | - * @param IUserManager $userManager |
|
| 82 | - * @param IRootFolder $rootFolder |
|
| 83 | - * @param IURLGenerator $urlGenerator |
|
| 84 | - * @param string $userId |
|
| 85 | - * @param IL10N $l10n |
|
| 86 | - */ |
|
| 87 | - public function __construct( |
|
| 88 | - $appName, |
|
| 89 | - IRequest $request, |
|
| 90 | - IManager $shareManager, |
|
| 91 | - IGroupManager $groupManager, |
|
| 92 | - IUserManager $userManager, |
|
| 93 | - IRootFolder $rootFolder, |
|
| 94 | - IURLGenerator $urlGenerator, |
|
| 95 | - $userId, |
|
| 96 | - IL10N $l10n |
|
| 97 | - ) { |
|
| 98 | - parent::__construct($appName, $request); |
|
| 99 | - |
|
| 100 | - $this->shareManager = $shareManager; |
|
| 101 | - $this->userManager = $userManager; |
|
| 102 | - $this->groupManager = $groupManager; |
|
| 103 | - $this->request = $request; |
|
| 104 | - $this->rootFolder = $rootFolder; |
|
| 105 | - $this->urlGenerator = $urlGenerator; |
|
| 106 | - $this->currentUser = $userId; |
|
| 107 | - $this->l = $l10n; |
|
| 108 | - } |
|
| 109 | - |
|
| 110 | - /** |
|
| 111 | - * Convert an IShare to an array for OCS output |
|
| 112 | - * |
|
| 113 | - * @param \OCP\Share\IShare $share |
|
| 114 | - * @param Node|null $recipientNode |
|
| 115 | - * @return array |
|
| 116 | - * @throws NotFoundException In case the node can't be resolved. |
|
| 117 | - */ |
|
| 118 | - protected function formatShare(\OCP\Share\IShare $share, Node $recipientNode = null) { |
|
| 119 | - $sharedBy = $this->userManager->get($share->getSharedBy()); |
|
| 120 | - $shareOwner = $this->userManager->get($share->getShareOwner()); |
|
| 121 | - |
|
| 122 | - $result = [ |
|
| 123 | - 'id' => $share->getId(), |
|
| 124 | - 'share_type' => $share->getShareType(), |
|
| 125 | - 'uid_owner' => $share->getSharedBy(), |
|
| 126 | - 'displayname_owner' => $sharedBy !== null ? $sharedBy->getDisplayName() : $share->getSharedBy(), |
|
| 127 | - 'permissions' => $share->getPermissions(), |
|
| 128 | - 'stime' => $share->getShareTime()->getTimestamp(), |
|
| 129 | - 'parent' => null, |
|
| 130 | - 'expiration' => null, |
|
| 131 | - 'token' => null, |
|
| 132 | - 'uid_file_owner' => $share->getShareOwner(), |
|
| 133 | - 'displayname_file_owner' => $shareOwner !== null ? $shareOwner->getDisplayName() : $share->getShareOwner(), |
|
| 134 | - ]; |
|
| 135 | - |
|
| 136 | - $userFolder = $this->rootFolder->getUserFolder($this->currentUser); |
|
| 137 | - if ($recipientNode) { |
|
| 138 | - $node = $recipientNode; |
|
| 139 | - } else { |
|
| 140 | - $nodes = $userFolder->getById($share->getNodeId()); |
|
| 141 | - |
|
| 142 | - if (empty($nodes)) { |
|
| 143 | - // fallback to guessing the path |
|
| 144 | - $node = $userFolder->get($share->getTarget()); |
|
| 145 | - if ($node === null) { |
|
| 146 | - throw new NotFoundException(); |
|
| 147 | - } |
|
| 148 | - } else { |
|
| 149 | - $node = $nodes[0]; |
|
| 150 | - } |
|
| 151 | - } |
|
| 152 | - |
|
| 153 | - $result['path'] = $userFolder->getRelativePath($node->getPath()); |
|
| 154 | - if ($node instanceOf \OCP\Files\Folder) { |
|
| 155 | - $result['item_type'] = 'folder'; |
|
| 156 | - } else { |
|
| 157 | - $result['item_type'] = 'file'; |
|
| 158 | - } |
|
| 159 | - $result['mimetype'] = $node->getMimetype(); |
|
| 160 | - $result['storage_id'] = $node->getStorage()->getId(); |
|
| 161 | - $result['storage'] = $node->getStorage()->getCache()->getNumericStorageId(); |
|
| 162 | - $result['item_source'] = $node->getId(); |
|
| 163 | - $result['file_source'] = $node->getId(); |
|
| 164 | - $result['file_parent'] = $node->getParent()->getId(); |
|
| 165 | - $result['file_target'] = $share->getTarget(); |
|
| 166 | - |
|
| 167 | - $expiration = $share->getExpirationDate(); |
|
| 168 | - if ($expiration !== null) { |
|
| 169 | - $result['expiration'] = $expiration->format('Y-m-d 00:00:00'); |
|
| 170 | - } |
|
| 171 | - |
|
| 172 | - if ($share->getShareType() === \OCP\Share::SHARE_TYPE_USER) { |
|
| 173 | - $sharedWith = $this->userManager->get($share->getSharedWith()); |
|
| 174 | - $result['share_with'] = $share->getSharedWith(); |
|
| 175 | - $result['share_with_displayname'] = $sharedWith !== null ? $sharedWith->getDisplayName() : $share->getSharedWith(); |
|
| 176 | - } else if ($share->getShareType() === \OCP\Share::SHARE_TYPE_GROUP) { |
|
| 177 | - $group = $this->groupManager->get($share->getSharedWith()); |
|
| 178 | - $result['share_with'] = $share->getSharedWith(); |
|
| 179 | - $result['share_with_displayname'] = $group !== null ? $group->getDisplayName() : $share->getSharedWith(); |
|
| 180 | - } else if ($share->getShareType() === \OCP\Share::SHARE_TYPE_LINK) { |
|
| 181 | - |
|
| 182 | - $result['share_with'] = $share->getPassword(); |
|
| 183 | - $result['share_with_displayname'] = $share->getPassword(); |
|
| 184 | - |
|
| 185 | - $result['token'] = $share->getToken(); |
|
| 186 | - $result['url'] = $this->urlGenerator->linkToRouteAbsolute('files_sharing.sharecontroller.showShare', ['token' => $share->getToken()]); |
|
| 187 | - |
|
| 188 | - } else if ($share->getShareType() === \OCP\Share::SHARE_TYPE_REMOTE) { |
|
| 189 | - $result['share_with'] = $share->getSharedWith(); |
|
| 190 | - $result['share_with_displayname'] = $this->getDisplayNameFromAddressBook($share->getSharedWith(), 'CLOUD'); |
|
| 191 | - $result['token'] = $share->getToken(); |
|
| 192 | - } else if ($share->getShareType() === \OCP\Share::SHARE_TYPE_EMAIL) { |
|
| 193 | - $result['share_with'] = $share->getSharedWith(); |
|
| 194 | - $result['password'] = $share->getPassword(); |
|
| 195 | - $result['share_with_displayname'] = $this->getDisplayNameFromAddressBook($share->getSharedWith(), 'EMAIL'); |
|
| 196 | - $result['token'] = $share->getToken(); |
|
| 197 | - } else if ($share->getShareType() === \OCP\Share::SHARE_TYPE_CIRCLE) { |
|
| 198 | - $result['share_with_displayname'] = $share->getSharedWith(); |
|
| 199 | - $result['share_with'] = explode(' ', $share->getSharedWith(), 2)[0]; |
|
| 200 | - } |
|
| 201 | - |
|
| 202 | - |
|
| 203 | - $result['mail_send'] = $share->getMailSend() ? 1 : 0; |
|
| 204 | - |
|
| 205 | - return $result; |
|
| 206 | - } |
|
| 207 | - |
|
| 208 | - /** |
|
| 209 | - * Check if one of the users address books knows the exact property, if |
|
| 210 | - * yes we return the full name. |
|
| 211 | - * |
|
| 212 | - * @param string $query |
|
| 213 | - * @param string $property |
|
| 214 | - * @return string |
|
| 215 | - */ |
|
| 216 | - private function getDisplayNameFromAddressBook($query, $property) { |
|
| 217 | - // FIXME: If we inject the contacts manager it gets initialized bofore any address books are registered |
|
| 218 | - $result = \OC::$server->getContactsManager()->search($query, [$property]); |
|
| 219 | - foreach ($result as $r) { |
|
| 220 | - foreach($r[$property] as $value) { |
|
| 221 | - if ($value === $query) { |
|
| 222 | - return $r['FN']; |
|
| 223 | - } |
|
| 224 | - } |
|
| 225 | - } |
|
| 226 | - |
|
| 227 | - return $query; |
|
| 228 | - } |
|
| 229 | - |
|
| 230 | - /** |
|
| 231 | - * Get a specific share by id |
|
| 232 | - * |
|
| 233 | - * @NoAdminRequired |
|
| 234 | - * |
|
| 235 | - * @param string $id |
|
| 236 | - * @return DataResponse |
|
| 237 | - * @throws OCSNotFoundException |
|
| 238 | - */ |
|
| 239 | - public function getShare($id) { |
|
| 240 | - try { |
|
| 241 | - $share = $this->getShareById($id); |
|
| 242 | - } catch (ShareNotFound $e) { |
|
| 243 | - throw new OCSNotFoundException($this->l->t('Wrong share ID, share doesn\'t exist')); |
|
| 244 | - } |
|
| 245 | - |
|
| 246 | - if ($this->canAccessShare($share)) { |
|
| 247 | - try { |
|
| 248 | - $share = $this->formatShare($share); |
|
| 249 | - return new DataResponse([$share]); |
|
| 250 | - } catch (NotFoundException $e) { |
|
| 251 | - //Fall trough |
|
| 252 | - } |
|
| 253 | - } |
|
| 254 | - |
|
| 255 | - throw new OCSNotFoundException($this->l->t('Wrong share ID, share doesn\'t exist')); |
|
| 256 | - } |
|
| 257 | - |
|
| 258 | - /** |
|
| 259 | - * Delete a share |
|
| 260 | - * |
|
| 261 | - * @NoAdminRequired |
|
| 262 | - * |
|
| 263 | - * @param string $id |
|
| 264 | - * @return DataResponse |
|
| 265 | - * @throws OCSNotFoundException |
|
| 266 | - */ |
|
| 267 | - public function deleteShare($id) { |
|
| 268 | - try { |
|
| 269 | - $share = $this->getShareById($id); |
|
| 270 | - } catch (ShareNotFound $e) { |
|
| 271 | - throw new OCSNotFoundException($this->l->t('Wrong share ID, share doesn\'t exist')); |
|
| 272 | - } |
|
| 273 | - |
|
| 274 | - try { |
|
| 275 | - $this->lock($share->getNode()); |
|
| 276 | - } catch (LockedException $e) { |
|
| 277 | - throw new OCSNotFoundException($this->l->t('could not delete share')); |
|
| 278 | - } |
|
| 279 | - |
|
| 280 | - if (!$this->canAccessShare($share)) { |
|
| 281 | - throw new OCSNotFoundException($this->l->t('Could not delete share')); |
|
| 282 | - } |
|
| 283 | - |
|
| 284 | - if ($share->getShareType() === \OCP\Share::SHARE_TYPE_GROUP && |
|
| 285 | - $share->getShareOwner() !== $this->currentUser && |
|
| 286 | - $share->getSharedBy() !== $this->currentUser) { |
|
| 287 | - $this->shareManager->deleteFromSelf($share, $this->currentUser); |
|
| 288 | - } else { |
|
| 289 | - $this->shareManager->deleteShare($share); |
|
| 290 | - } |
|
| 291 | - |
|
| 292 | - return new DataResponse(); |
|
| 293 | - } |
|
| 294 | - |
|
| 295 | - /** |
|
| 296 | - * @NoAdminRequired |
|
| 297 | - * |
|
| 298 | - * @param string $path |
|
| 299 | - * @param int $permissions |
|
| 300 | - * @param int $shareType |
|
| 301 | - * @param string $shareWith |
|
| 302 | - * @param string $publicUpload |
|
| 303 | - * @param string $password |
|
| 304 | - * @param string $expireDate |
|
| 305 | - * |
|
| 306 | - * @return DataResponse |
|
| 307 | - * @throws OCSNotFoundException |
|
| 308 | - * @throws OCSForbiddenException |
|
| 309 | - * @throws OCSBadRequestException |
|
| 310 | - * @throws OCSException |
|
| 311 | - * |
|
| 312 | - * @suppress PhanUndeclaredClassMethod |
|
| 313 | - */ |
|
| 314 | - public function createShare( |
|
| 315 | - $path = null, |
|
| 316 | - $permissions = \OCP\Constants::PERMISSION_ALL, |
|
| 317 | - $shareType = -1, |
|
| 318 | - $shareWith = null, |
|
| 319 | - $publicUpload = 'false', |
|
| 320 | - $password = '', |
|
| 321 | - $expireDate = '' |
|
| 322 | - ) { |
|
| 323 | - $share = $this->shareManager->newShare(); |
|
| 324 | - |
|
| 325 | - // Verify path |
|
| 326 | - if ($path === null) { |
|
| 327 | - throw new OCSNotFoundException($this->l->t('Please specify a file or folder path')); |
|
| 328 | - } |
|
| 329 | - |
|
| 330 | - $userFolder = $this->rootFolder->getUserFolder($this->currentUser); |
|
| 331 | - try { |
|
| 332 | - $path = $userFolder->get($path); |
|
| 333 | - } catch (NotFoundException $e) { |
|
| 334 | - throw new OCSNotFoundException($this->l->t('Wrong path, file/folder doesn\'t exist')); |
|
| 335 | - } |
|
| 336 | - |
|
| 337 | - $share->setNode($path); |
|
| 338 | - |
|
| 339 | - try { |
|
| 340 | - $this->lock($share->getNode()); |
|
| 341 | - } catch (LockedException $e) { |
|
| 342 | - throw new OCSNotFoundException($this->l->t('Could not create share')); |
|
| 343 | - } |
|
| 344 | - |
|
| 345 | - if ($permissions < 0 || $permissions > \OCP\Constants::PERMISSION_ALL) { |
|
| 346 | - throw new OCSNotFoundException($this->l->t('invalid permissions')); |
|
| 347 | - } |
|
| 348 | - |
|
| 349 | - // Shares always require read permissions |
|
| 350 | - $permissions |= \OCP\Constants::PERMISSION_READ; |
|
| 351 | - |
|
| 352 | - if ($path instanceof \OCP\Files\File) { |
|
| 353 | - // Single file shares should never have delete or create permissions |
|
| 354 | - $permissions &= ~\OCP\Constants::PERMISSION_DELETE; |
|
| 355 | - $permissions &= ~\OCP\Constants::PERMISSION_CREATE; |
|
| 356 | - } |
|
| 357 | - |
|
| 358 | - /* |
|
| 55 | + /** @var IManager */ |
|
| 56 | + private $shareManager; |
|
| 57 | + /** @var IGroupManager */ |
|
| 58 | + private $groupManager; |
|
| 59 | + /** @var IUserManager */ |
|
| 60 | + private $userManager; |
|
| 61 | + /** @var IRequest */ |
|
| 62 | + protected $request; |
|
| 63 | + /** @var IRootFolder */ |
|
| 64 | + private $rootFolder; |
|
| 65 | + /** @var IURLGenerator */ |
|
| 66 | + private $urlGenerator; |
|
| 67 | + /** @var string */ |
|
| 68 | + private $currentUser; |
|
| 69 | + /** @var IL10N */ |
|
| 70 | + private $l; |
|
| 71 | + /** @var \OCP\Files\Node */ |
|
| 72 | + private $lockedNode; |
|
| 73 | + |
|
| 74 | + /** |
|
| 75 | + * Share20OCS constructor. |
|
| 76 | + * |
|
| 77 | + * @param string $appName |
|
| 78 | + * @param IRequest $request |
|
| 79 | + * @param IManager $shareManager |
|
| 80 | + * @param IGroupManager $groupManager |
|
| 81 | + * @param IUserManager $userManager |
|
| 82 | + * @param IRootFolder $rootFolder |
|
| 83 | + * @param IURLGenerator $urlGenerator |
|
| 84 | + * @param string $userId |
|
| 85 | + * @param IL10N $l10n |
|
| 86 | + */ |
|
| 87 | + public function __construct( |
|
| 88 | + $appName, |
|
| 89 | + IRequest $request, |
|
| 90 | + IManager $shareManager, |
|
| 91 | + IGroupManager $groupManager, |
|
| 92 | + IUserManager $userManager, |
|
| 93 | + IRootFolder $rootFolder, |
|
| 94 | + IURLGenerator $urlGenerator, |
|
| 95 | + $userId, |
|
| 96 | + IL10N $l10n |
|
| 97 | + ) { |
|
| 98 | + parent::__construct($appName, $request); |
|
| 99 | + |
|
| 100 | + $this->shareManager = $shareManager; |
|
| 101 | + $this->userManager = $userManager; |
|
| 102 | + $this->groupManager = $groupManager; |
|
| 103 | + $this->request = $request; |
|
| 104 | + $this->rootFolder = $rootFolder; |
|
| 105 | + $this->urlGenerator = $urlGenerator; |
|
| 106 | + $this->currentUser = $userId; |
|
| 107 | + $this->l = $l10n; |
|
| 108 | + } |
|
| 109 | + |
|
| 110 | + /** |
|
| 111 | + * Convert an IShare to an array for OCS output |
|
| 112 | + * |
|
| 113 | + * @param \OCP\Share\IShare $share |
|
| 114 | + * @param Node|null $recipientNode |
|
| 115 | + * @return array |
|
| 116 | + * @throws NotFoundException In case the node can't be resolved. |
|
| 117 | + */ |
|
| 118 | + protected function formatShare(\OCP\Share\IShare $share, Node $recipientNode = null) { |
|
| 119 | + $sharedBy = $this->userManager->get($share->getSharedBy()); |
|
| 120 | + $shareOwner = $this->userManager->get($share->getShareOwner()); |
|
| 121 | + |
|
| 122 | + $result = [ |
|
| 123 | + 'id' => $share->getId(), |
|
| 124 | + 'share_type' => $share->getShareType(), |
|
| 125 | + 'uid_owner' => $share->getSharedBy(), |
|
| 126 | + 'displayname_owner' => $sharedBy !== null ? $sharedBy->getDisplayName() : $share->getSharedBy(), |
|
| 127 | + 'permissions' => $share->getPermissions(), |
|
| 128 | + 'stime' => $share->getShareTime()->getTimestamp(), |
|
| 129 | + 'parent' => null, |
|
| 130 | + 'expiration' => null, |
|
| 131 | + 'token' => null, |
|
| 132 | + 'uid_file_owner' => $share->getShareOwner(), |
|
| 133 | + 'displayname_file_owner' => $shareOwner !== null ? $shareOwner->getDisplayName() : $share->getShareOwner(), |
|
| 134 | + ]; |
|
| 135 | + |
|
| 136 | + $userFolder = $this->rootFolder->getUserFolder($this->currentUser); |
|
| 137 | + if ($recipientNode) { |
|
| 138 | + $node = $recipientNode; |
|
| 139 | + } else { |
|
| 140 | + $nodes = $userFolder->getById($share->getNodeId()); |
|
| 141 | + |
|
| 142 | + if (empty($nodes)) { |
|
| 143 | + // fallback to guessing the path |
|
| 144 | + $node = $userFolder->get($share->getTarget()); |
|
| 145 | + if ($node === null) { |
|
| 146 | + throw new NotFoundException(); |
|
| 147 | + } |
|
| 148 | + } else { |
|
| 149 | + $node = $nodes[0]; |
|
| 150 | + } |
|
| 151 | + } |
|
| 152 | + |
|
| 153 | + $result['path'] = $userFolder->getRelativePath($node->getPath()); |
|
| 154 | + if ($node instanceOf \OCP\Files\Folder) { |
|
| 155 | + $result['item_type'] = 'folder'; |
|
| 156 | + } else { |
|
| 157 | + $result['item_type'] = 'file'; |
|
| 158 | + } |
|
| 159 | + $result['mimetype'] = $node->getMimetype(); |
|
| 160 | + $result['storage_id'] = $node->getStorage()->getId(); |
|
| 161 | + $result['storage'] = $node->getStorage()->getCache()->getNumericStorageId(); |
|
| 162 | + $result['item_source'] = $node->getId(); |
|
| 163 | + $result['file_source'] = $node->getId(); |
|
| 164 | + $result['file_parent'] = $node->getParent()->getId(); |
|
| 165 | + $result['file_target'] = $share->getTarget(); |
|
| 166 | + |
|
| 167 | + $expiration = $share->getExpirationDate(); |
|
| 168 | + if ($expiration !== null) { |
|
| 169 | + $result['expiration'] = $expiration->format('Y-m-d 00:00:00'); |
|
| 170 | + } |
|
| 171 | + |
|
| 172 | + if ($share->getShareType() === \OCP\Share::SHARE_TYPE_USER) { |
|
| 173 | + $sharedWith = $this->userManager->get($share->getSharedWith()); |
|
| 174 | + $result['share_with'] = $share->getSharedWith(); |
|
| 175 | + $result['share_with_displayname'] = $sharedWith !== null ? $sharedWith->getDisplayName() : $share->getSharedWith(); |
|
| 176 | + } else if ($share->getShareType() === \OCP\Share::SHARE_TYPE_GROUP) { |
|
| 177 | + $group = $this->groupManager->get($share->getSharedWith()); |
|
| 178 | + $result['share_with'] = $share->getSharedWith(); |
|
| 179 | + $result['share_with_displayname'] = $group !== null ? $group->getDisplayName() : $share->getSharedWith(); |
|
| 180 | + } else if ($share->getShareType() === \OCP\Share::SHARE_TYPE_LINK) { |
|
| 181 | + |
|
| 182 | + $result['share_with'] = $share->getPassword(); |
|
| 183 | + $result['share_with_displayname'] = $share->getPassword(); |
|
| 184 | + |
|
| 185 | + $result['token'] = $share->getToken(); |
|
| 186 | + $result['url'] = $this->urlGenerator->linkToRouteAbsolute('files_sharing.sharecontroller.showShare', ['token' => $share->getToken()]); |
|
| 187 | + |
|
| 188 | + } else if ($share->getShareType() === \OCP\Share::SHARE_TYPE_REMOTE) { |
|
| 189 | + $result['share_with'] = $share->getSharedWith(); |
|
| 190 | + $result['share_with_displayname'] = $this->getDisplayNameFromAddressBook($share->getSharedWith(), 'CLOUD'); |
|
| 191 | + $result['token'] = $share->getToken(); |
|
| 192 | + } else if ($share->getShareType() === \OCP\Share::SHARE_TYPE_EMAIL) { |
|
| 193 | + $result['share_with'] = $share->getSharedWith(); |
|
| 194 | + $result['password'] = $share->getPassword(); |
|
| 195 | + $result['share_with_displayname'] = $this->getDisplayNameFromAddressBook($share->getSharedWith(), 'EMAIL'); |
|
| 196 | + $result['token'] = $share->getToken(); |
|
| 197 | + } else if ($share->getShareType() === \OCP\Share::SHARE_TYPE_CIRCLE) { |
|
| 198 | + $result['share_with_displayname'] = $share->getSharedWith(); |
|
| 199 | + $result['share_with'] = explode(' ', $share->getSharedWith(), 2)[0]; |
|
| 200 | + } |
|
| 201 | + |
|
| 202 | + |
|
| 203 | + $result['mail_send'] = $share->getMailSend() ? 1 : 0; |
|
| 204 | + |
|
| 205 | + return $result; |
|
| 206 | + } |
|
| 207 | + |
|
| 208 | + /** |
|
| 209 | + * Check if one of the users address books knows the exact property, if |
|
| 210 | + * yes we return the full name. |
|
| 211 | + * |
|
| 212 | + * @param string $query |
|
| 213 | + * @param string $property |
|
| 214 | + * @return string |
|
| 215 | + */ |
|
| 216 | + private function getDisplayNameFromAddressBook($query, $property) { |
|
| 217 | + // FIXME: If we inject the contacts manager it gets initialized bofore any address books are registered |
|
| 218 | + $result = \OC::$server->getContactsManager()->search($query, [$property]); |
|
| 219 | + foreach ($result as $r) { |
|
| 220 | + foreach($r[$property] as $value) { |
|
| 221 | + if ($value === $query) { |
|
| 222 | + return $r['FN']; |
|
| 223 | + } |
|
| 224 | + } |
|
| 225 | + } |
|
| 226 | + |
|
| 227 | + return $query; |
|
| 228 | + } |
|
| 229 | + |
|
| 230 | + /** |
|
| 231 | + * Get a specific share by id |
|
| 232 | + * |
|
| 233 | + * @NoAdminRequired |
|
| 234 | + * |
|
| 235 | + * @param string $id |
|
| 236 | + * @return DataResponse |
|
| 237 | + * @throws OCSNotFoundException |
|
| 238 | + */ |
|
| 239 | + public function getShare($id) { |
|
| 240 | + try { |
|
| 241 | + $share = $this->getShareById($id); |
|
| 242 | + } catch (ShareNotFound $e) { |
|
| 243 | + throw new OCSNotFoundException($this->l->t('Wrong share ID, share doesn\'t exist')); |
|
| 244 | + } |
|
| 245 | + |
|
| 246 | + if ($this->canAccessShare($share)) { |
|
| 247 | + try { |
|
| 248 | + $share = $this->formatShare($share); |
|
| 249 | + return new DataResponse([$share]); |
|
| 250 | + } catch (NotFoundException $e) { |
|
| 251 | + //Fall trough |
|
| 252 | + } |
|
| 253 | + } |
|
| 254 | + |
|
| 255 | + throw new OCSNotFoundException($this->l->t('Wrong share ID, share doesn\'t exist')); |
|
| 256 | + } |
|
| 257 | + |
|
| 258 | + /** |
|
| 259 | + * Delete a share |
|
| 260 | + * |
|
| 261 | + * @NoAdminRequired |
|
| 262 | + * |
|
| 263 | + * @param string $id |
|
| 264 | + * @return DataResponse |
|
| 265 | + * @throws OCSNotFoundException |
|
| 266 | + */ |
|
| 267 | + public function deleteShare($id) { |
|
| 268 | + try { |
|
| 269 | + $share = $this->getShareById($id); |
|
| 270 | + } catch (ShareNotFound $e) { |
|
| 271 | + throw new OCSNotFoundException($this->l->t('Wrong share ID, share doesn\'t exist')); |
|
| 272 | + } |
|
| 273 | + |
|
| 274 | + try { |
|
| 275 | + $this->lock($share->getNode()); |
|
| 276 | + } catch (LockedException $e) { |
|
| 277 | + throw new OCSNotFoundException($this->l->t('could not delete share')); |
|
| 278 | + } |
|
| 279 | + |
|
| 280 | + if (!$this->canAccessShare($share)) { |
|
| 281 | + throw new OCSNotFoundException($this->l->t('Could not delete share')); |
|
| 282 | + } |
|
| 283 | + |
|
| 284 | + if ($share->getShareType() === \OCP\Share::SHARE_TYPE_GROUP && |
|
| 285 | + $share->getShareOwner() !== $this->currentUser && |
|
| 286 | + $share->getSharedBy() !== $this->currentUser) { |
|
| 287 | + $this->shareManager->deleteFromSelf($share, $this->currentUser); |
|
| 288 | + } else { |
|
| 289 | + $this->shareManager->deleteShare($share); |
|
| 290 | + } |
|
| 291 | + |
|
| 292 | + return new DataResponse(); |
|
| 293 | + } |
|
| 294 | + |
|
| 295 | + /** |
|
| 296 | + * @NoAdminRequired |
|
| 297 | + * |
|
| 298 | + * @param string $path |
|
| 299 | + * @param int $permissions |
|
| 300 | + * @param int $shareType |
|
| 301 | + * @param string $shareWith |
|
| 302 | + * @param string $publicUpload |
|
| 303 | + * @param string $password |
|
| 304 | + * @param string $expireDate |
|
| 305 | + * |
|
| 306 | + * @return DataResponse |
|
| 307 | + * @throws OCSNotFoundException |
|
| 308 | + * @throws OCSForbiddenException |
|
| 309 | + * @throws OCSBadRequestException |
|
| 310 | + * @throws OCSException |
|
| 311 | + * |
|
| 312 | + * @suppress PhanUndeclaredClassMethod |
|
| 313 | + */ |
|
| 314 | + public function createShare( |
|
| 315 | + $path = null, |
|
| 316 | + $permissions = \OCP\Constants::PERMISSION_ALL, |
|
| 317 | + $shareType = -1, |
|
| 318 | + $shareWith = null, |
|
| 319 | + $publicUpload = 'false', |
|
| 320 | + $password = '', |
|
| 321 | + $expireDate = '' |
|
| 322 | + ) { |
|
| 323 | + $share = $this->shareManager->newShare(); |
|
| 324 | + |
|
| 325 | + // Verify path |
|
| 326 | + if ($path === null) { |
|
| 327 | + throw new OCSNotFoundException($this->l->t('Please specify a file or folder path')); |
|
| 328 | + } |
|
| 329 | + |
|
| 330 | + $userFolder = $this->rootFolder->getUserFolder($this->currentUser); |
|
| 331 | + try { |
|
| 332 | + $path = $userFolder->get($path); |
|
| 333 | + } catch (NotFoundException $e) { |
|
| 334 | + throw new OCSNotFoundException($this->l->t('Wrong path, file/folder doesn\'t exist')); |
|
| 335 | + } |
|
| 336 | + |
|
| 337 | + $share->setNode($path); |
|
| 338 | + |
|
| 339 | + try { |
|
| 340 | + $this->lock($share->getNode()); |
|
| 341 | + } catch (LockedException $e) { |
|
| 342 | + throw new OCSNotFoundException($this->l->t('Could not create share')); |
|
| 343 | + } |
|
| 344 | + |
|
| 345 | + if ($permissions < 0 || $permissions > \OCP\Constants::PERMISSION_ALL) { |
|
| 346 | + throw new OCSNotFoundException($this->l->t('invalid permissions')); |
|
| 347 | + } |
|
| 348 | + |
|
| 349 | + // Shares always require read permissions |
|
| 350 | + $permissions |= \OCP\Constants::PERMISSION_READ; |
|
| 351 | + |
|
| 352 | + if ($path instanceof \OCP\Files\File) { |
|
| 353 | + // Single file shares should never have delete or create permissions |
|
| 354 | + $permissions &= ~\OCP\Constants::PERMISSION_DELETE; |
|
| 355 | + $permissions &= ~\OCP\Constants::PERMISSION_CREATE; |
|
| 356 | + } |
|
| 357 | + |
|
| 358 | + /* |
|
| 359 | 359 | * Hack for https://github.com/owncloud/core/issues/22587 |
| 360 | 360 | * We check the permissions via webdav. But the permissions of the mount point |
| 361 | 361 | * do not equal the share permissions. Here we fix that for federated mounts. |
| 362 | 362 | */ |
| 363 | - if ($path->getStorage()->instanceOfStorage('OCA\Files_Sharing\External\Storage')) { |
|
| 364 | - $permissions &= ~($permissions & ~$path->getPermissions()); |
|
| 365 | - } |
|
| 366 | - |
|
| 367 | - if ($shareType === \OCP\Share::SHARE_TYPE_USER) { |
|
| 368 | - // Valid user is required to share |
|
| 369 | - if ($shareWith === null || !$this->userManager->userExists($shareWith)) { |
|
| 370 | - throw new OCSNotFoundException($this->l->t('Please specify a valid user')); |
|
| 371 | - } |
|
| 372 | - $share->setSharedWith($shareWith); |
|
| 373 | - $share->setPermissions($permissions); |
|
| 374 | - } else if ($shareType === \OCP\Share::SHARE_TYPE_GROUP) { |
|
| 375 | - if (!$this->shareManager->allowGroupSharing()) { |
|
| 376 | - throw new OCSNotFoundException($this->l->t('Group sharing is disabled by the administrator')); |
|
| 377 | - } |
|
| 378 | - |
|
| 379 | - // Valid group is required to share |
|
| 380 | - if ($shareWith === null || !$this->groupManager->groupExists($shareWith)) { |
|
| 381 | - throw new OCSNotFoundException($this->l->t('Please specify a valid group')); |
|
| 382 | - } |
|
| 383 | - $share->setSharedWith($shareWith); |
|
| 384 | - $share->setPermissions($permissions); |
|
| 385 | - } else if ($shareType === \OCP\Share::SHARE_TYPE_LINK) { |
|
| 386 | - //Can we even share links? |
|
| 387 | - if (!$this->shareManager->shareApiAllowLinks()) { |
|
| 388 | - throw new OCSNotFoundException($this->l->t('Public link sharing is disabled by the administrator')); |
|
| 389 | - } |
|
| 390 | - |
|
| 391 | - /* |
|
| 363 | + if ($path->getStorage()->instanceOfStorage('OCA\Files_Sharing\External\Storage')) { |
|
| 364 | + $permissions &= ~($permissions & ~$path->getPermissions()); |
|
| 365 | + } |
|
| 366 | + |
|
| 367 | + if ($shareType === \OCP\Share::SHARE_TYPE_USER) { |
|
| 368 | + // Valid user is required to share |
|
| 369 | + if ($shareWith === null || !$this->userManager->userExists($shareWith)) { |
|
| 370 | + throw new OCSNotFoundException($this->l->t('Please specify a valid user')); |
|
| 371 | + } |
|
| 372 | + $share->setSharedWith($shareWith); |
|
| 373 | + $share->setPermissions($permissions); |
|
| 374 | + } else if ($shareType === \OCP\Share::SHARE_TYPE_GROUP) { |
|
| 375 | + if (!$this->shareManager->allowGroupSharing()) { |
|
| 376 | + throw new OCSNotFoundException($this->l->t('Group sharing is disabled by the administrator')); |
|
| 377 | + } |
|
| 378 | + |
|
| 379 | + // Valid group is required to share |
|
| 380 | + if ($shareWith === null || !$this->groupManager->groupExists($shareWith)) { |
|
| 381 | + throw new OCSNotFoundException($this->l->t('Please specify a valid group')); |
|
| 382 | + } |
|
| 383 | + $share->setSharedWith($shareWith); |
|
| 384 | + $share->setPermissions($permissions); |
|
| 385 | + } else if ($shareType === \OCP\Share::SHARE_TYPE_LINK) { |
|
| 386 | + //Can we even share links? |
|
| 387 | + if (!$this->shareManager->shareApiAllowLinks()) { |
|
| 388 | + throw new OCSNotFoundException($this->l->t('Public link sharing is disabled by the administrator')); |
|
| 389 | + } |
|
| 390 | + |
|
| 391 | + /* |
|
| 392 | 392 | * For now we only allow 1 link share. |
| 393 | 393 | * Return the existing link share if this is a duplicate |
| 394 | 394 | */ |
| 395 | - $existingShares = $this->shareManager->getSharesBy($this->currentUser, \OCP\Share::SHARE_TYPE_LINK, $path, false, 1, 0); |
|
| 396 | - if (!empty($existingShares)) { |
|
| 397 | - return new DataResponse($this->formatShare($existingShares[0])); |
|
| 398 | - } |
|
| 399 | - |
|
| 400 | - if ($publicUpload === 'true') { |
|
| 401 | - // Check if public upload is allowed |
|
| 402 | - if (!$this->shareManager->shareApiLinkAllowPublicUpload()) { |
|
| 403 | - throw new OCSForbiddenException($this->l->t('Public upload disabled by the administrator')); |
|
| 404 | - } |
|
| 405 | - |
|
| 406 | - // Public upload can only be set for folders |
|
| 407 | - if ($path instanceof \OCP\Files\File) { |
|
| 408 | - throw new OCSNotFoundException($this->l->t('Public upload is only possible for publicly shared folders')); |
|
| 409 | - } |
|
| 410 | - |
|
| 411 | - $share->setPermissions( |
|
| 412 | - \OCP\Constants::PERMISSION_READ | |
|
| 413 | - \OCP\Constants::PERMISSION_CREATE | |
|
| 414 | - \OCP\Constants::PERMISSION_UPDATE | |
|
| 415 | - \OCP\Constants::PERMISSION_DELETE |
|
| 416 | - ); |
|
| 417 | - } else { |
|
| 418 | - $share->setPermissions(\OCP\Constants::PERMISSION_READ); |
|
| 419 | - } |
|
| 420 | - |
|
| 421 | - // Set password |
|
| 422 | - if ($password !== '') { |
|
| 423 | - $share->setPassword($password); |
|
| 424 | - } |
|
| 425 | - |
|
| 426 | - //Expire date |
|
| 427 | - if ($expireDate !== '') { |
|
| 428 | - try { |
|
| 429 | - $expireDate = $this->parseDate($expireDate); |
|
| 430 | - $share->setExpirationDate($expireDate); |
|
| 431 | - } catch (\Exception $e) { |
|
| 432 | - throw new OCSNotFoundException($this->l->t('Invalid date, date format must be YYYY-MM-DD')); |
|
| 433 | - } |
|
| 434 | - } |
|
| 435 | - |
|
| 436 | - } else if ($shareType === \OCP\Share::SHARE_TYPE_REMOTE) { |
|
| 437 | - if (!$this->shareManager->outgoingServer2ServerSharesAllowed()) { |
|
| 438 | - throw new OCSForbiddenException($this->l->t('Sharing %s failed because the back end does not allow shares from type %s', [$path->getPath(), $shareType])); |
|
| 439 | - } |
|
| 440 | - |
|
| 441 | - $share->setSharedWith($shareWith); |
|
| 442 | - $share->setPermissions($permissions); |
|
| 443 | - } else if ($shareType === \OCP\Share::SHARE_TYPE_EMAIL) { |
|
| 444 | - if ($share->getNodeType() === 'file') { |
|
| 445 | - $share->setPermissions(\OCP\Constants::PERMISSION_READ); |
|
| 446 | - } else { |
|
| 447 | - $share->setPermissions( |
|
| 448 | - \OCP\Constants::PERMISSION_READ | |
|
| 449 | - \OCP\Constants::PERMISSION_CREATE | |
|
| 450 | - \OCP\Constants::PERMISSION_UPDATE | |
|
| 451 | - \OCP\Constants::PERMISSION_DELETE); |
|
| 452 | - } |
|
| 453 | - $share->setSharedWith($shareWith); |
|
| 454 | - } else if ($shareType === \OCP\Share::SHARE_TYPE_CIRCLE) { |
|
| 455 | - if (!\OC::$server->getAppManager()->isEnabledForUser('circles') || !class_exists('\OCA\Circles\ShareByCircleProvider')) { |
|
| 456 | - throw new OCSNotFoundException($this->l->t('You cannot share to a Circle if the app is not enabled')); |
|
| 457 | - } |
|
| 458 | - |
|
| 459 | - $circle = \OCA\Circles\Api\v1\Circles::detailsCircle($shareWith); |
|
| 460 | - |
|
| 461 | - // Valid circle is required to share |
|
| 462 | - if ($circle === null) { |
|
| 463 | - throw new OCSNotFoundException($this->l->t('Please specify a valid circle')); |
|
| 464 | - } |
|
| 465 | - $share->setSharedWith($shareWith); |
|
| 466 | - $share->setPermissions($permissions); |
|
| 467 | - } else { |
|
| 468 | - throw new OCSBadRequestException($this->l->t('Unknown share type')); |
|
| 469 | - } |
|
| 470 | - |
|
| 471 | - $share->setShareType($shareType); |
|
| 472 | - $share->setSharedBy($this->currentUser); |
|
| 473 | - |
|
| 474 | - try { |
|
| 475 | - $share = $this->shareManager->createShare($share); |
|
| 476 | - } catch (GenericShareException $e) { |
|
| 477 | - $code = $e->getCode() === 0 ? 403 : $e->getCode(); |
|
| 478 | - throw new OCSException($e->getHint(), $code); |
|
| 479 | - } catch (\Exception $e) { |
|
| 480 | - throw new OCSForbiddenException($e->getMessage()); |
|
| 481 | - } |
|
| 482 | - |
|
| 483 | - $output = $this->formatShare($share); |
|
| 484 | - |
|
| 485 | - return new DataResponse($output); |
|
| 486 | - } |
|
| 487 | - |
|
| 488 | - /** |
|
| 489 | - * @param \OCP\Files\File|\OCP\Files\Folder $node |
|
| 490 | - * @param boolean $includeTags |
|
| 491 | - * @return DataResponse |
|
| 492 | - */ |
|
| 493 | - private function getSharedWithMe($node = null, $includeTags) { |
|
| 494 | - |
|
| 495 | - $userShares = $this->shareManager->getSharedWith($this->currentUser, \OCP\Share::SHARE_TYPE_USER, $node, -1, 0); |
|
| 496 | - $groupShares = $this->shareManager->getSharedWith($this->currentUser, \OCP\Share::SHARE_TYPE_GROUP, $node, -1, 0); |
|
| 497 | - $circleShares = $this->shareManager->getSharedWith($this->currentUser, \OCP\Share::SHARE_TYPE_CIRCLE, $node, -1, 0); |
|
| 498 | - |
|
| 499 | - $shares = array_merge($userShares, $groupShares, $circleShares); |
|
| 500 | - |
|
| 501 | - $shares = array_filter($shares, function (IShare $share) { |
|
| 502 | - return $share->getShareOwner() !== $this->currentUser; |
|
| 503 | - }); |
|
| 504 | - |
|
| 505 | - $formatted = []; |
|
| 506 | - foreach ($shares as $share) { |
|
| 507 | - if ($this->canAccessShare($share)) { |
|
| 508 | - try { |
|
| 509 | - $formatted[] = $this->formatShare($share); |
|
| 510 | - } catch (NotFoundException $e) { |
|
| 511 | - // Ignore this share |
|
| 512 | - } |
|
| 513 | - } |
|
| 514 | - } |
|
| 515 | - |
|
| 516 | - if ($includeTags) { |
|
| 517 | - $formatted = Helper::populateTags($formatted, 'file_source'); |
|
| 518 | - } |
|
| 519 | - |
|
| 520 | - return new DataResponse($formatted); |
|
| 521 | - } |
|
| 522 | - |
|
| 523 | - /** |
|
| 524 | - * @param \OCP\Files\Folder $folder |
|
| 525 | - * @return DataResponse |
|
| 526 | - * @throws OCSBadRequestException |
|
| 527 | - */ |
|
| 528 | - private function getSharesInDir($folder) { |
|
| 529 | - if (!($folder instanceof \OCP\Files\Folder)) { |
|
| 530 | - throw new OCSBadRequestException($this->l->t('Not a directory')); |
|
| 531 | - } |
|
| 532 | - |
|
| 533 | - $nodes = $folder->getDirectoryListing(); |
|
| 534 | - /** @var \OCP\Share\IShare[] $shares */ |
|
| 535 | - $shares = []; |
|
| 536 | - foreach ($nodes as $node) { |
|
| 537 | - $shares = array_merge($shares, $this->shareManager->getSharesBy($this->currentUser, \OCP\Share::SHARE_TYPE_USER, $node, false, -1, 0)); |
|
| 538 | - $shares = array_merge($shares, $this->shareManager->getSharesBy($this->currentUser, \OCP\Share::SHARE_TYPE_GROUP, $node, false, -1, 0)); |
|
| 539 | - $shares = array_merge($shares, $this->shareManager->getSharesBy($this->currentUser, \OCP\Share::SHARE_TYPE_LINK, $node, false, -1, 0)); |
|
| 540 | - if($this->shareManager->shareProviderExists(\OCP\Share::SHARE_TYPE_EMAIL)) { |
|
| 541 | - $shares = array_merge($shares, $this->shareManager->getSharesBy($this->currentUser, \OCP\Share::SHARE_TYPE_EMAIL, $node, false, -1, 0)); |
|
| 542 | - } |
|
| 543 | - if ($this->shareManager->outgoingServer2ServerSharesAllowed()) { |
|
| 544 | - $shares = array_merge($shares, $this->shareManager->getSharesBy($this->currentUser, \OCP\Share::SHARE_TYPE_REMOTE, $node, false, -1, 0)); |
|
| 545 | - } |
|
| 546 | - } |
|
| 547 | - |
|
| 548 | - $formatted = []; |
|
| 549 | - foreach ($shares as $share) { |
|
| 550 | - try { |
|
| 551 | - $formatted[] = $this->formatShare($share); |
|
| 552 | - } catch (NotFoundException $e) { |
|
| 553 | - //Ignore this share |
|
| 554 | - } |
|
| 555 | - } |
|
| 556 | - |
|
| 557 | - return new DataResponse($formatted); |
|
| 558 | - } |
|
| 559 | - |
|
| 560 | - /** |
|
| 561 | - * The getShares function. |
|
| 562 | - * |
|
| 563 | - * @NoAdminRequired |
|
| 564 | - * |
|
| 565 | - * @param string $shared_with_me |
|
| 566 | - * @param string $reshares |
|
| 567 | - * @param string $subfiles |
|
| 568 | - * @param string $path |
|
| 569 | - * |
|
| 570 | - * - Get shares by the current user |
|
| 571 | - * - Get shares by the current user and reshares (?reshares=true) |
|
| 572 | - * - Get shares with the current user (?shared_with_me=true) |
|
| 573 | - * - Get shares for a specific path (?path=...) |
|
| 574 | - * - Get all shares in a folder (?subfiles=true&path=..) |
|
| 575 | - * |
|
| 576 | - * @return DataResponse |
|
| 577 | - * @throws OCSNotFoundException |
|
| 578 | - */ |
|
| 579 | - public function getShares( |
|
| 580 | - $shared_with_me = 'false', |
|
| 581 | - $reshares = 'false', |
|
| 582 | - $subfiles = 'false', |
|
| 583 | - $path = null, |
|
| 584 | - $include_tags = 'false' |
|
| 585 | - ) { |
|
| 586 | - |
|
| 587 | - if ($path !== null) { |
|
| 588 | - $userFolder = $this->rootFolder->getUserFolder($this->currentUser); |
|
| 589 | - try { |
|
| 590 | - $path = $userFolder->get($path); |
|
| 591 | - $this->lock($path); |
|
| 592 | - } catch (\OCP\Files\NotFoundException $e) { |
|
| 593 | - throw new OCSNotFoundException($this->l->t('Wrong path, file/folder doesn\'t exist')); |
|
| 594 | - } catch (LockedException $e) { |
|
| 595 | - throw new OCSNotFoundException($this->l->t('Could not lock path')); |
|
| 596 | - } |
|
| 597 | - } |
|
| 598 | - |
|
| 599 | - if ($shared_with_me === 'true') { |
|
| 600 | - $result = $this->getSharedWithMe($path, $include_tags); |
|
| 601 | - return $result; |
|
| 602 | - } |
|
| 603 | - |
|
| 604 | - if ($subfiles === 'true') { |
|
| 605 | - $result = $this->getSharesInDir($path); |
|
| 606 | - return $result; |
|
| 607 | - } |
|
| 608 | - |
|
| 609 | - if ($reshares === 'true') { |
|
| 610 | - $reshares = true; |
|
| 611 | - } else { |
|
| 612 | - $reshares = false; |
|
| 613 | - } |
|
| 614 | - |
|
| 615 | - // Get all shares |
|
| 616 | - $userShares = $this->shareManager->getSharesBy($this->currentUser, \OCP\Share::SHARE_TYPE_USER, $path, $reshares, -1, 0); |
|
| 617 | - $groupShares = $this->shareManager->getSharesBy($this->currentUser, \OCP\Share::SHARE_TYPE_GROUP, $path, $reshares, -1, 0); |
|
| 618 | - $linkShares = $this->shareManager->getSharesBy($this->currentUser, \OCP\Share::SHARE_TYPE_LINK, $path, $reshares, -1, 0); |
|
| 619 | - if ($this->shareManager->shareProviderExists(\OCP\Share::SHARE_TYPE_EMAIL)) { |
|
| 620 | - $mailShares = $this->shareManager->getSharesBy($this->currentUser, \OCP\Share::SHARE_TYPE_EMAIL, $path, $reshares, -1, 0); |
|
| 621 | - } else { |
|
| 622 | - $mailShares = []; |
|
| 623 | - } |
|
| 624 | - if ($this->shareManager->shareProviderExists(\OCP\Share::SHARE_TYPE_CIRCLE)) { |
|
| 625 | - $circleShares = $this->shareManager->getSharesBy($this->currentUser, \OCP\Share::SHARE_TYPE_CIRCLE, $path, $reshares, -1, 0); |
|
| 626 | - } else { |
|
| 627 | - $circleShares = []; |
|
| 628 | - } |
|
| 629 | - |
|
| 630 | - $shares = array_merge($userShares, $groupShares, $linkShares, $mailShares, $circleShares); |
|
| 631 | - |
|
| 632 | - if ($this->shareManager->outgoingServer2ServerSharesAllowed()) { |
|
| 633 | - $federatedShares = $this->shareManager->getSharesBy($this->currentUser, \OCP\Share::SHARE_TYPE_REMOTE, $path, $reshares, -1, 0); |
|
| 634 | - $shares = array_merge($shares, $federatedShares); |
|
| 635 | - } |
|
| 636 | - |
|
| 637 | - $formatted = []; |
|
| 638 | - foreach ($shares as $share) { |
|
| 639 | - try { |
|
| 640 | - $formatted[] = $this->formatShare($share, $path); |
|
| 641 | - } catch (NotFoundException $e) { |
|
| 642 | - //Ignore share |
|
| 643 | - } |
|
| 644 | - } |
|
| 645 | - |
|
| 646 | - if ($include_tags) { |
|
| 647 | - $formatted = Helper::populateTags($formatted, 'file_source'); |
|
| 648 | - } |
|
| 649 | - |
|
| 650 | - return new DataResponse($formatted); |
|
| 651 | - } |
|
| 652 | - |
|
| 653 | - /** |
|
| 654 | - * @NoAdminRequired |
|
| 655 | - * |
|
| 656 | - * @param int $id |
|
| 657 | - * @param int $permissions |
|
| 658 | - * @param string $password |
|
| 659 | - * @param string $publicUpload |
|
| 660 | - * @param string $expireDate |
|
| 661 | - * @return DataResponse |
|
| 662 | - * @throws OCSNotFoundException |
|
| 663 | - * @throws OCSBadRequestException |
|
| 664 | - * @throws OCSForbiddenException |
|
| 665 | - */ |
|
| 666 | - public function updateShare( |
|
| 667 | - $id, |
|
| 668 | - $permissions = null, |
|
| 669 | - $password = null, |
|
| 670 | - $publicUpload = null, |
|
| 671 | - $expireDate = null |
|
| 672 | - ) { |
|
| 673 | - try { |
|
| 674 | - $share = $this->getShareById($id); |
|
| 675 | - } catch (ShareNotFound $e) { |
|
| 676 | - throw new OCSNotFoundException($this->l->t('Wrong share ID, share doesn\'t exist')); |
|
| 677 | - } |
|
| 678 | - |
|
| 679 | - $this->lock($share->getNode()); |
|
| 680 | - |
|
| 681 | - if (!$this->canAccessShare($share, false)) { |
|
| 682 | - throw new OCSNotFoundException($this->l->t('Wrong share ID, share doesn\'t exist')); |
|
| 683 | - } |
|
| 684 | - |
|
| 685 | - if ($permissions === null && $password === null && $publicUpload === null && $expireDate === null) { |
|
| 686 | - throw new OCSBadRequestException($this->l->t('Wrong or no update parameter given')); |
|
| 687 | - } |
|
| 688 | - |
|
| 689 | - /* |
|
| 395 | + $existingShares = $this->shareManager->getSharesBy($this->currentUser, \OCP\Share::SHARE_TYPE_LINK, $path, false, 1, 0); |
|
| 396 | + if (!empty($existingShares)) { |
|
| 397 | + return new DataResponse($this->formatShare($existingShares[0])); |
|
| 398 | + } |
|
| 399 | + |
|
| 400 | + if ($publicUpload === 'true') { |
|
| 401 | + // Check if public upload is allowed |
|
| 402 | + if (!$this->shareManager->shareApiLinkAllowPublicUpload()) { |
|
| 403 | + throw new OCSForbiddenException($this->l->t('Public upload disabled by the administrator')); |
|
| 404 | + } |
|
| 405 | + |
|
| 406 | + // Public upload can only be set for folders |
|
| 407 | + if ($path instanceof \OCP\Files\File) { |
|
| 408 | + throw new OCSNotFoundException($this->l->t('Public upload is only possible for publicly shared folders')); |
|
| 409 | + } |
|
| 410 | + |
|
| 411 | + $share->setPermissions( |
|
| 412 | + \OCP\Constants::PERMISSION_READ | |
|
| 413 | + \OCP\Constants::PERMISSION_CREATE | |
|
| 414 | + \OCP\Constants::PERMISSION_UPDATE | |
|
| 415 | + \OCP\Constants::PERMISSION_DELETE |
|
| 416 | + ); |
|
| 417 | + } else { |
|
| 418 | + $share->setPermissions(\OCP\Constants::PERMISSION_READ); |
|
| 419 | + } |
|
| 420 | + |
|
| 421 | + // Set password |
|
| 422 | + if ($password !== '') { |
|
| 423 | + $share->setPassword($password); |
|
| 424 | + } |
|
| 425 | + |
|
| 426 | + //Expire date |
|
| 427 | + if ($expireDate !== '') { |
|
| 428 | + try { |
|
| 429 | + $expireDate = $this->parseDate($expireDate); |
|
| 430 | + $share->setExpirationDate($expireDate); |
|
| 431 | + } catch (\Exception $e) { |
|
| 432 | + throw new OCSNotFoundException($this->l->t('Invalid date, date format must be YYYY-MM-DD')); |
|
| 433 | + } |
|
| 434 | + } |
|
| 435 | + |
|
| 436 | + } else if ($shareType === \OCP\Share::SHARE_TYPE_REMOTE) { |
|
| 437 | + if (!$this->shareManager->outgoingServer2ServerSharesAllowed()) { |
|
| 438 | + throw new OCSForbiddenException($this->l->t('Sharing %s failed because the back end does not allow shares from type %s', [$path->getPath(), $shareType])); |
|
| 439 | + } |
|
| 440 | + |
|
| 441 | + $share->setSharedWith($shareWith); |
|
| 442 | + $share->setPermissions($permissions); |
|
| 443 | + } else if ($shareType === \OCP\Share::SHARE_TYPE_EMAIL) { |
|
| 444 | + if ($share->getNodeType() === 'file') { |
|
| 445 | + $share->setPermissions(\OCP\Constants::PERMISSION_READ); |
|
| 446 | + } else { |
|
| 447 | + $share->setPermissions( |
|
| 448 | + \OCP\Constants::PERMISSION_READ | |
|
| 449 | + \OCP\Constants::PERMISSION_CREATE | |
|
| 450 | + \OCP\Constants::PERMISSION_UPDATE | |
|
| 451 | + \OCP\Constants::PERMISSION_DELETE); |
|
| 452 | + } |
|
| 453 | + $share->setSharedWith($shareWith); |
|
| 454 | + } else if ($shareType === \OCP\Share::SHARE_TYPE_CIRCLE) { |
|
| 455 | + if (!\OC::$server->getAppManager()->isEnabledForUser('circles') || !class_exists('\OCA\Circles\ShareByCircleProvider')) { |
|
| 456 | + throw new OCSNotFoundException($this->l->t('You cannot share to a Circle if the app is not enabled')); |
|
| 457 | + } |
|
| 458 | + |
|
| 459 | + $circle = \OCA\Circles\Api\v1\Circles::detailsCircle($shareWith); |
|
| 460 | + |
|
| 461 | + // Valid circle is required to share |
|
| 462 | + if ($circle === null) { |
|
| 463 | + throw new OCSNotFoundException($this->l->t('Please specify a valid circle')); |
|
| 464 | + } |
|
| 465 | + $share->setSharedWith($shareWith); |
|
| 466 | + $share->setPermissions($permissions); |
|
| 467 | + } else { |
|
| 468 | + throw new OCSBadRequestException($this->l->t('Unknown share type')); |
|
| 469 | + } |
|
| 470 | + |
|
| 471 | + $share->setShareType($shareType); |
|
| 472 | + $share->setSharedBy($this->currentUser); |
|
| 473 | + |
|
| 474 | + try { |
|
| 475 | + $share = $this->shareManager->createShare($share); |
|
| 476 | + } catch (GenericShareException $e) { |
|
| 477 | + $code = $e->getCode() === 0 ? 403 : $e->getCode(); |
|
| 478 | + throw new OCSException($e->getHint(), $code); |
|
| 479 | + } catch (\Exception $e) { |
|
| 480 | + throw new OCSForbiddenException($e->getMessage()); |
|
| 481 | + } |
|
| 482 | + |
|
| 483 | + $output = $this->formatShare($share); |
|
| 484 | + |
|
| 485 | + return new DataResponse($output); |
|
| 486 | + } |
|
| 487 | + |
|
| 488 | + /** |
|
| 489 | + * @param \OCP\Files\File|\OCP\Files\Folder $node |
|
| 490 | + * @param boolean $includeTags |
|
| 491 | + * @return DataResponse |
|
| 492 | + */ |
|
| 493 | + private function getSharedWithMe($node = null, $includeTags) { |
|
| 494 | + |
|
| 495 | + $userShares = $this->shareManager->getSharedWith($this->currentUser, \OCP\Share::SHARE_TYPE_USER, $node, -1, 0); |
|
| 496 | + $groupShares = $this->shareManager->getSharedWith($this->currentUser, \OCP\Share::SHARE_TYPE_GROUP, $node, -1, 0); |
|
| 497 | + $circleShares = $this->shareManager->getSharedWith($this->currentUser, \OCP\Share::SHARE_TYPE_CIRCLE, $node, -1, 0); |
|
| 498 | + |
|
| 499 | + $shares = array_merge($userShares, $groupShares, $circleShares); |
|
| 500 | + |
|
| 501 | + $shares = array_filter($shares, function (IShare $share) { |
|
| 502 | + return $share->getShareOwner() !== $this->currentUser; |
|
| 503 | + }); |
|
| 504 | + |
|
| 505 | + $formatted = []; |
|
| 506 | + foreach ($shares as $share) { |
|
| 507 | + if ($this->canAccessShare($share)) { |
|
| 508 | + try { |
|
| 509 | + $formatted[] = $this->formatShare($share); |
|
| 510 | + } catch (NotFoundException $e) { |
|
| 511 | + // Ignore this share |
|
| 512 | + } |
|
| 513 | + } |
|
| 514 | + } |
|
| 515 | + |
|
| 516 | + if ($includeTags) { |
|
| 517 | + $formatted = Helper::populateTags($formatted, 'file_source'); |
|
| 518 | + } |
|
| 519 | + |
|
| 520 | + return new DataResponse($formatted); |
|
| 521 | + } |
|
| 522 | + |
|
| 523 | + /** |
|
| 524 | + * @param \OCP\Files\Folder $folder |
|
| 525 | + * @return DataResponse |
|
| 526 | + * @throws OCSBadRequestException |
|
| 527 | + */ |
|
| 528 | + private function getSharesInDir($folder) { |
|
| 529 | + if (!($folder instanceof \OCP\Files\Folder)) { |
|
| 530 | + throw new OCSBadRequestException($this->l->t('Not a directory')); |
|
| 531 | + } |
|
| 532 | + |
|
| 533 | + $nodes = $folder->getDirectoryListing(); |
|
| 534 | + /** @var \OCP\Share\IShare[] $shares */ |
|
| 535 | + $shares = []; |
|
| 536 | + foreach ($nodes as $node) { |
|
| 537 | + $shares = array_merge($shares, $this->shareManager->getSharesBy($this->currentUser, \OCP\Share::SHARE_TYPE_USER, $node, false, -1, 0)); |
|
| 538 | + $shares = array_merge($shares, $this->shareManager->getSharesBy($this->currentUser, \OCP\Share::SHARE_TYPE_GROUP, $node, false, -1, 0)); |
|
| 539 | + $shares = array_merge($shares, $this->shareManager->getSharesBy($this->currentUser, \OCP\Share::SHARE_TYPE_LINK, $node, false, -1, 0)); |
|
| 540 | + if($this->shareManager->shareProviderExists(\OCP\Share::SHARE_TYPE_EMAIL)) { |
|
| 541 | + $shares = array_merge($shares, $this->shareManager->getSharesBy($this->currentUser, \OCP\Share::SHARE_TYPE_EMAIL, $node, false, -1, 0)); |
|
| 542 | + } |
|
| 543 | + if ($this->shareManager->outgoingServer2ServerSharesAllowed()) { |
|
| 544 | + $shares = array_merge($shares, $this->shareManager->getSharesBy($this->currentUser, \OCP\Share::SHARE_TYPE_REMOTE, $node, false, -1, 0)); |
|
| 545 | + } |
|
| 546 | + } |
|
| 547 | + |
|
| 548 | + $formatted = []; |
|
| 549 | + foreach ($shares as $share) { |
|
| 550 | + try { |
|
| 551 | + $formatted[] = $this->formatShare($share); |
|
| 552 | + } catch (NotFoundException $e) { |
|
| 553 | + //Ignore this share |
|
| 554 | + } |
|
| 555 | + } |
|
| 556 | + |
|
| 557 | + return new DataResponse($formatted); |
|
| 558 | + } |
|
| 559 | + |
|
| 560 | + /** |
|
| 561 | + * The getShares function. |
|
| 562 | + * |
|
| 563 | + * @NoAdminRequired |
|
| 564 | + * |
|
| 565 | + * @param string $shared_with_me |
|
| 566 | + * @param string $reshares |
|
| 567 | + * @param string $subfiles |
|
| 568 | + * @param string $path |
|
| 569 | + * |
|
| 570 | + * - Get shares by the current user |
|
| 571 | + * - Get shares by the current user and reshares (?reshares=true) |
|
| 572 | + * - Get shares with the current user (?shared_with_me=true) |
|
| 573 | + * - Get shares for a specific path (?path=...) |
|
| 574 | + * - Get all shares in a folder (?subfiles=true&path=..) |
|
| 575 | + * |
|
| 576 | + * @return DataResponse |
|
| 577 | + * @throws OCSNotFoundException |
|
| 578 | + */ |
|
| 579 | + public function getShares( |
|
| 580 | + $shared_with_me = 'false', |
|
| 581 | + $reshares = 'false', |
|
| 582 | + $subfiles = 'false', |
|
| 583 | + $path = null, |
|
| 584 | + $include_tags = 'false' |
|
| 585 | + ) { |
|
| 586 | + |
|
| 587 | + if ($path !== null) { |
|
| 588 | + $userFolder = $this->rootFolder->getUserFolder($this->currentUser); |
|
| 589 | + try { |
|
| 590 | + $path = $userFolder->get($path); |
|
| 591 | + $this->lock($path); |
|
| 592 | + } catch (\OCP\Files\NotFoundException $e) { |
|
| 593 | + throw new OCSNotFoundException($this->l->t('Wrong path, file/folder doesn\'t exist')); |
|
| 594 | + } catch (LockedException $e) { |
|
| 595 | + throw new OCSNotFoundException($this->l->t('Could not lock path')); |
|
| 596 | + } |
|
| 597 | + } |
|
| 598 | + |
|
| 599 | + if ($shared_with_me === 'true') { |
|
| 600 | + $result = $this->getSharedWithMe($path, $include_tags); |
|
| 601 | + return $result; |
|
| 602 | + } |
|
| 603 | + |
|
| 604 | + if ($subfiles === 'true') { |
|
| 605 | + $result = $this->getSharesInDir($path); |
|
| 606 | + return $result; |
|
| 607 | + } |
|
| 608 | + |
|
| 609 | + if ($reshares === 'true') { |
|
| 610 | + $reshares = true; |
|
| 611 | + } else { |
|
| 612 | + $reshares = false; |
|
| 613 | + } |
|
| 614 | + |
|
| 615 | + // Get all shares |
|
| 616 | + $userShares = $this->shareManager->getSharesBy($this->currentUser, \OCP\Share::SHARE_TYPE_USER, $path, $reshares, -1, 0); |
|
| 617 | + $groupShares = $this->shareManager->getSharesBy($this->currentUser, \OCP\Share::SHARE_TYPE_GROUP, $path, $reshares, -1, 0); |
|
| 618 | + $linkShares = $this->shareManager->getSharesBy($this->currentUser, \OCP\Share::SHARE_TYPE_LINK, $path, $reshares, -1, 0); |
|
| 619 | + if ($this->shareManager->shareProviderExists(\OCP\Share::SHARE_TYPE_EMAIL)) { |
|
| 620 | + $mailShares = $this->shareManager->getSharesBy($this->currentUser, \OCP\Share::SHARE_TYPE_EMAIL, $path, $reshares, -1, 0); |
|
| 621 | + } else { |
|
| 622 | + $mailShares = []; |
|
| 623 | + } |
|
| 624 | + if ($this->shareManager->shareProviderExists(\OCP\Share::SHARE_TYPE_CIRCLE)) { |
|
| 625 | + $circleShares = $this->shareManager->getSharesBy($this->currentUser, \OCP\Share::SHARE_TYPE_CIRCLE, $path, $reshares, -1, 0); |
|
| 626 | + } else { |
|
| 627 | + $circleShares = []; |
|
| 628 | + } |
|
| 629 | + |
|
| 630 | + $shares = array_merge($userShares, $groupShares, $linkShares, $mailShares, $circleShares); |
|
| 631 | + |
|
| 632 | + if ($this->shareManager->outgoingServer2ServerSharesAllowed()) { |
|
| 633 | + $federatedShares = $this->shareManager->getSharesBy($this->currentUser, \OCP\Share::SHARE_TYPE_REMOTE, $path, $reshares, -1, 0); |
|
| 634 | + $shares = array_merge($shares, $federatedShares); |
|
| 635 | + } |
|
| 636 | + |
|
| 637 | + $formatted = []; |
|
| 638 | + foreach ($shares as $share) { |
|
| 639 | + try { |
|
| 640 | + $formatted[] = $this->formatShare($share, $path); |
|
| 641 | + } catch (NotFoundException $e) { |
|
| 642 | + //Ignore share |
|
| 643 | + } |
|
| 644 | + } |
|
| 645 | + |
|
| 646 | + if ($include_tags) { |
|
| 647 | + $formatted = Helper::populateTags($formatted, 'file_source'); |
|
| 648 | + } |
|
| 649 | + |
|
| 650 | + return new DataResponse($formatted); |
|
| 651 | + } |
|
| 652 | + |
|
| 653 | + /** |
|
| 654 | + * @NoAdminRequired |
|
| 655 | + * |
|
| 656 | + * @param int $id |
|
| 657 | + * @param int $permissions |
|
| 658 | + * @param string $password |
|
| 659 | + * @param string $publicUpload |
|
| 660 | + * @param string $expireDate |
|
| 661 | + * @return DataResponse |
|
| 662 | + * @throws OCSNotFoundException |
|
| 663 | + * @throws OCSBadRequestException |
|
| 664 | + * @throws OCSForbiddenException |
|
| 665 | + */ |
|
| 666 | + public function updateShare( |
|
| 667 | + $id, |
|
| 668 | + $permissions = null, |
|
| 669 | + $password = null, |
|
| 670 | + $publicUpload = null, |
|
| 671 | + $expireDate = null |
|
| 672 | + ) { |
|
| 673 | + try { |
|
| 674 | + $share = $this->getShareById($id); |
|
| 675 | + } catch (ShareNotFound $e) { |
|
| 676 | + throw new OCSNotFoundException($this->l->t('Wrong share ID, share doesn\'t exist')); |
|
| 677 | + } |
|
| 678 | + |
|
| 679 | + $this->lock($share->getNode()); |
|
| 680 | + |
|
| 681 | + if (!$this->canAccessShare($share, false)) { |
|
| 682 | + throw new OCSNotFoundException($this->l->t('Wrong share ID, share doesn\'t exist')); |
|
| 683 | + } |
|
| 684 | + |
|
| 685 | + if ($permissions === null && $password === null && $publicUpload === null && $expireDate === null) { |
|
| 686 | + throw new OCSBadRequestException($this->l->t('Wrong or no update parameter given')); |
|
| 687 | + } |
|
| 688 | + |
|
| 689 | + /* |
|
| 690 | 690 | * expirationdate, password and publicUpload only make sense for link shares |
| 691 | 691 | */ |
| 692 | - if ($share->getShareType() === \OCP\Share::SHARE_TYPE_LINK) { |
|
| 693 | - |
|
| 694 | - $newPermissions = null; |
|
| 695 | - if ($publicUpload === 'true') { |
|
| 696 | - $newPermissions = \OCP\Constants::PERMISSION_READ | \OCP\Constants::PERMISSION_CREATE | \OCP\Constants::PERMISSION_UPDATE | \OCP\Constants::PERMISSION_DELETE; |
|
| 697 | - } else if ($publicUpload === 'false') { |
|
| 698 | - $newPermissions = \OCP\Constants::PERMISSION_READ; |
|
| 699 | - } |
|
| 700 | - |
|
| 701 | - if ($permissions !== null) { |
|
| 702 | - $newPermissions = (int)$permissions; |
|
| 703 | - } |
|
| 704 | - |
|
| 705 | - if ($newPermissions !== null && |
|
| 706 | - !in_array($newPermissions, [ |
|
| 707 | - \OCP\Constants::PERMISSION_READ, |
|
| 708 | - \OCP\Constants::PERMISSION_READ | \OCP\Constants::PERMISSION_CREATE | \OCP\Constants::PERMISSION_UPDATE, // legacy |
|
| 709 | - \OCP\Constants::PERMISSION_READ | \OCP\Constants::PERMISSION_CREATE | \OCP\Constants::PERMISSION_UPDATE | \OCP\Constants::PERMISSION_DELETE, // correct |
|
| 710 | - \OCP\Constants::PERMISSION_CREATE, // hidden file list |
|
| 711 | - \OCP\Constants::PERMISSION_READ | \OCP\Constants::PERMISSION_UPDATE, // allow to edit single files |
|
| 712 | - ]) |
|
| 713 | - ) { |
|
| 714 | - throw new OCSBadRequestException($this->l->t('Can\'t change permissions for public share links')); |
|
| 715 | - } |
|
| 716 | - |
|
| 717 | - if ( |
|
| 718 | - // legacy |
|
| 719 | - $newPermissions === (\OCP\Constants::PERMISSION_READ | \OCP\Constants::PERMISSION_CREATE | \OCP\Constants::PERMISSION_UPDATE) || |
|
| 720 | - // correct |
|
| 721 | - $newPermissions === (\OCP\Constants::PERMISSION_READ | \OCP\Constants::PERMISSION_CREATE | \OCP\Constants::PERMISSION_UPDATE | \OCP\Constants::PERMISSION_DELETE) |
|
| 722 | - ) { |
|
| 723 | - if (!$this->shareManager->shareApiLinkAllowPublicUpload()) { |
|
| 724 | - throw new OCSForbiddenException($this->l->t('Public upload disabled by the administrator')); |
|
| 725 | - } |
|
| 726 | - |
|
| 727 | - if (!($share->getNode() instanceof \OCP\Files\Folder)) { |
|
| 728 | - throw new OCSBadRequestException($this->l->t('Public upload is only possible for publicly shared folders')); |
|
| 729 | - } |
|
| 730 | - |
|
| 731 | - // normalize to correct public upload permissions |
|
| 732 | - $newPermissions = \OCP\Constants::PERMISSION_READ | \OCP\Constants::PERMISSION_CREATE | \OCP\Constants::PERMISSION_UPDATE | \OCP\Constants::PERMISSION_DELETE; |
|
| 733 | - } |
|
| 734 | - |
|
| 735 | - if ($newPermissions !== null) { |
|
| 736 | - $share->setPermissions($newPermissions); |
|
| 737 | - $permissions = $newPermissions; |
|
| 738 | - } |
|
| 739 | - |
|
| 740 | - if ($expireDate === '') { |
|
| 741 | - $share->setExpirationDate(null); |
|
| 742 | - } else if ($expireDate !== null) { |
|
| 743 | - try { |
|
| 744 | - $expireDate = $this->parseDate($expireDate); |
|
| 745 | - } catch (\Exception $e) { |
|
| 746 | - throw new OCSBadRequestException($e->getMessage()); |
|
| 747 | - } |
|
| 748 | - $share->setExpirationDate($expireDate); |
|
| 749 | - } |
|
| 750 | - |
|
| 751 | - if ($password === '') { |
|
| 752 | - $share->setPassword(null); |
|
| 753 | - } else if ($password !== null) { |
|
| 754 | - $share->setPassword($password); |
|
| 755 | - } |
|
| 756 | - |
|
| 757 | - } else { |
|
| 758 | - if ($permissions !== null) { |
|
| 759 | - $permissions = (int)$permissions; |
|
| 760 | - $share->setPermissions($permissions); |
|
| 761 | - } |
|
| 762 | - |
|
| 763 | - if ($share->getShareType() === \OCP\Share::SHARE_TYPE_EMAIL) { |
|
| 764 | - if ($password === '') { |
|
| 765 | - $share->setPassword(null); |
|
| 766 | - } else if ($password !== null) { |
|
| 767 | - $share->setPassword($password); |
|
| 768 | - } |
|
| 769 | - } |
|
| 770 | - |
|
| 771 | - if ($expireDate === '') { |
|
| 772 | - $share->setExpirationDate(null); |
|
| 773 | - } else if ($expireDate !== null) { |
|
| 774 | - try { |
|
| 775 | - $expireDate = $this->parseDate($expireDate); |
|
| 776 | - } catch (\Exception $e) { |
|
| 777 | - throw new OCSBadRequestException($e->getMessage()); |
|
| 778 | - } |
|
| 779 | - $share->setExpirationDate($expireDate); |
|
| 780 | - } |
|
| 781 | - |
|
| 782 | - } |
|
| 783 | - |
|
| 784 | - if ($permissions !== null && $share->getShareOwner() !== $this->currentUser) { |
|
| 785 | - /* Check if this is an incomming share */ |
|
| 786 | - $incomingShares = $this->shareManager->getSharedWith($this->currentUser, \OCP\Share::SHARE_TYPE_USER, $share->getNode(), -1, 0); |
|
| 787 | - $incomingShares = array_merge($incomingShares, $this->shareManager->getSharedWith($this->currentUser, \OCP\Share::SHARE_TYPE_GROUP, $share->getNode(), -1, 0)); |
|
| 788 | - |
|
| 789 | - /** @var \OCP\Share\IShare[] $incomingShares */ |
|
| 790 | - if (!empty($incomingShares)) { |
|
| 791 | - $maxPermissions = 0; |
|
| 792 | - foreach ($incomingShares as $incomingShare) { |
|
| 793 | - $maxPermissions |= $incomingShare->getPermissions(); |
|
| 794 | - } |
|
| 795 | - |
|
| 796 | - if ($share->getPermissions() & ~$maxPermissions) { |
|
| 797 | - throw new OCSNotFoundException($this->l->t('Cannot increase permissions')); |
|
| 798 | - } |
|
| 799 | - } |
|
| 800 | - } |
|
| 801 | - |
|
| 802 | - |
|
| 803 | - try { |
|
| 804 | - $share = $this->shareManager->updateShare($share); |
|
| 805 | - } catch (\Exception $e) { |
|
| 806 | - throw new OCSBadRequestException($e->getMessage()); |
|
| 807 | - } |
|
| 808 | - |
|
| 809 | - return new DataResponse($this->formatShare($share)); |
|
| 810 | - } |
|
| 811 | - |
|
| 812 | - /** |
|
| 813 | - * @param \OCP\Share\IShare $share |
|
| 814 | - * @return bool |
|
| 815 | - */ |
|
| 816 | - protected function canAccessShare(\OCP\Share\IShare $share, $checkGroups = true) { |
|
| 817 | - // A file with permissions 0 can't be accessed by us. So Don't show it |
|
| 818 | - if ($share->getPermissions() === 0) { |
|
| 819 | - return false; |
|
| 820 | - } |
|
| 821 | - |
|
| 822 | - // Owner of the file and the sharer of the file can always get share |
|
| 823 | - if ($share->getShareOwner() === $this->currentUser || |
|
| 824 | - $share->getSharedBy() === $this->currentUser |
|
| 825 | - ) { |
|
| 826 | - return true; |
|
| 827 | - } |
|
| 828 | - |
|
| 829 | - // If the share is shared with you (or a group you are a member of) |
|
| 830 | - if ($share->getShareType() === \OCP\Share::SHARE_TYPE_USER && |
|
| 831 | - $share->getSharedWith() === $this->currentUser |
|
| 832 | - ) { |
|
| 833 | - return true; |
|
| 834 | - } |
|
| 835 | - |
|
| 836 | - if ($checkGroups && $share->getShareType() === \OCP\Share::SHARE_TYPE_GROUP) { |
|
| 837 | - $sharedWith = $this->groupManager->get($share->getSharedWith()); |
|
| 838 | - $user = $this->userManager->get($this->currentUser); |
|
| 839 | - if ($user !== null && $sharedWith !== null && $sharedWith->inGroup($user)) { |
|
| 840 | - return true; |
|
| 841 | - } |
|
| 842 | - } |
|
| 843 | - |
|
| 844 | - if ($share->getShareType() === \OCP\Share::SHARE_TYPE_CIRCLE) { |
|
| 845 | - // TODO: have a sanity check like above? |
|
| 846 | - return true; |
|
| 847 | - } |
|
| 848 | - |
|
| 849 | - return false; |
|
| 850 | - } |
|
| 851 | - |
|
| 852 | - /** |
|
| 853 | - * Make sure that the passed date is valid ISO 8601 |
|
| 854 | - * So YYYY-MM-DD |
|
| 855 | - * If not throw an exception |
|
| 856 | - * |
|
| 857 | - * @param string $expireDate |
|
| 858 | - * |
|
| 859 | - * @throws \Exception |
|
| 860 | - * @return \DateTime |
|
| 861 | - */ |
|
| 862 | - private function parseDate($expireDate) { |
|
| 863 | - try { |
|
| 864 | - $date = new \DateTime($expireDate); |
|
| 865 | - } catch (\Exception $e) { |
|
| 866 | - throw new \Exception('Invalid date. Format must be YYYY-MM-DD'); |
|
| 867 | - } |
|
| 868 | - |
|
| 869 | - if ($date === false) { |
|
| 870 | - throw new \Exception('Invalid date. Format must be YYYY-MM-DD'); |
|
| 871 | - } |
|
| 872 | - |
|
| 873 | - $date->setTime(0, 0, 0); |
|
| 874 | - |
|
| 875 | - return $date; |
|
| 876 | - } |
|
| 877 | - |
|
| 878 | - /** |
|
| 879 | - * Since we have multiple providers but the OCS Share API v1 does |
|
| 880 | - * not support this we need to check all backends. |
|
| 881 | - * |
|
| 882 | - * @param string $id |
|
| 883 | - * @return \OCP\Share\IShare |
|
| 884 | - * @throws ShareNotFound |
|
| 885 | - */ |
|
| 886 | - private function getShareById($id) { |
|
| 887 | - $share = null; |
|
| 888 | - |
|
| 889 | - // First check if it is an internal share. |
|
| 890 | - try { |
|
| 891 | - $share = $this->shareManager->getShareById('ocinternal:' . $id); |
|
| 892 | - return $share; |
|
| 893 | - } catch (ShareNotFound $e) { |
|
| 894 | - // Do nothing, just try the other share type |
|
| 895 | - } |
|
| 896 | - |
|
| 897 | - |
|
| 898 | - try { |
|
| 899 | - if ($this->shareManager->shareProviderExists(\OCP\Share::SHARE_TYPE_CIRCLE)) { |
|
| 900 | - $share = $this->shareManager->getShareById('ocCircleShare:' . $id); |
|
| 901 | - return $share; |
|
| 902 | - } |
|
| 903 | - } catch (ShareNotFound $e) { |
|
| 904 | - // Do nothing, just try the other share type |
|
| 905 | - } |
|
| 906 | - |
|
| 907 | - try { |
|
| 908 | - if ($this->shareManager->shareProviderExists(\OCP\Share::SHARE_TYPE_EMAIL)) { |
|
| 909 | - $share = $this->shareManager->getShareById('ocMailShare:' . $id); |
|
| 910 | - return $share; |
|
| 911 | - } |
|
| 912 | - } catch (ShareNotFound $e) { |
|
| 913 | - // Do nothing, just try the other share type |
|
| 914 | - } |
|
| 915 | - |
|
| 916 | - if (!$this->shareManager->outgoingServer2ServerSharesAllowed()) { |
|
| 917 | - throw new ShareNotFound(); |
|
| 918 | - } |
|
| 919 | - $share = $this->shareManager->getShareById('ocFederatedSharing:' . $id); |
|
| 920 | - |
|
| 921 | - return $share; |
|
| 922 | - } |
|
| 923 | - |
|
| 924 | - /** |
|
| 925 | - * Lock a Node |
|
| 926 | - * |
|
| 927 | - * @param \OCP\Files\Node $node |
|
| 928 | - */ |
|
| 929 | - private function lock(\OCP\Files\Node $node) { |
|
| 930 | - $node->lock(ILockingProvider::LOCK_SHARED); |
|
| 931 | - $this->lockedNode = $node; |
|
| 932 | - } |
|
| 933 | - |
|
| 934 | - /** |
|
| 935 | - * Cleanup the remaining locks |
|
| 936 | - */ |
|
| 937 | - public function cleanup() { |
|
| 938 | - if ($this->lockedNode !== null) { |
|
| 939 | - $this->lockedNode->unlock(ILockingProvider::LOCK_SHARED); |
|
| 940 | - } |
|
| 941 | - } |
|
| 692 | + if ($share->getShareType() === \OCP\Share::SHARE_TYPE_LINK) { |
|
| 693 | + |
|
| 694 | + $newPermissions = null; |
|
| 695 | + if ($publicUpload === 'true') { |
|
| 696 | + $newPermissions = \OCP\Constants::PERMISSION_READ | \OCP\Constants::PERMISSION_CREATE | \OCP\Constants::PERMISSION_UPDATE | \OCP\Constants::PERMISSION_DELETE; |
|
| 697 | + } else if ($publicUpload === 'false') { |
|
| 698 | + $newPermissions = \OCP\Constants::PERMISSION_READ; |
|
| 699 | + } |
|
| 700 | + |
|
| 701 | + if ($permissions !== null) { |
|
| 702 | + $newPermissions = (int)$permissions; |
|
| 703 | + } |
|
| 704 | + |
|
| 705 | + if ($newPermissions !== null && |
|
| 706 | + !in_array($newPermissions, [ |
|
| 707 | + \OCP\Constants::PERMISSION_READ, |
|
| 708 | + \OCP\Constants::PERMISSION_READ | \OCP\Constants::PERMISSION_CREATE | \OCP\Constants::PERMISSION_UPDATE, // legacy |
|
| 709 | + \OCP\Constants::PERMISSION_READ | \OCP\Constants::PERMISSION_CREATE | \OCP\Constants::PERMISSION_UPDATE | \OCP\Constants::PERMISSION_DELETE, // correct |
|
| 710 | + \OCP\Constants::PERMISSION_CREATE, // hidden file list |
|
| 711 | + \OCP\Constants::PERMISSION_READ | \OCP\Constants::PERMISSION_UPDATE, // allow to edit single files |
|
| 712 | + ]) |
|
| 713 | + ) { |
|
| 714 | + throw new OCSBadRequestException($this->l->t('Can\'t change permissions for public share links')); |
|
| 715 | + } |
|
| 716 | + |
|
| 717 | + if ( |
|
| 718 | + // legacy |
|
| 719 | + $newPermissions === (\OCP\Constants::PERMISSION_READ | \OCP\Constants::PERMISSION_CREATE | \OCP\Constants::PERMISSION_UPDATE) || |
|
| 720 | + // correct |
|
| 721 | + $newPermissions === (\OCP\Constants::PERMISSION_READ | \OCP\Constants::PERMISSION_CREATE | \OCP\Constants::PERMISSION_UPDATE | \OCP\Constants::PERMISSION_DELETE) |
|
| 722 | + ) { |
|
| 723 | + if (!$this->shareManager->shareApiLinkAllowPublicUpload()) { |
|
| 724 | + throw new OCSForbiddenException($this->l->t('Public upload disabled by the administrator')); |
|
| 725 | + } |
|
| 726 | + |
|
| 727 | + if (!($share->getNode() instanceof \OCP\Files\Folder)) { |
|
| 728 | + throw new OCSBadRequestException($this->l->t('Public upload is only possible for publicly shared folders')); |
|
| 729 | + } |
|
| 730 | + |
|
| 731 | + // normalize to correct public upload permissions |
|
| 732 | + $newPermissions = \OCP\Constants::PERMISSION_READ | \OCP\Constants::PERMISSION_CREATE | \OCP\Constants::PERMISSION_UPDATE | \OCP\Constants::PERMISSION_DELETE; |
|
| 733 | + } |
|
| 734 | + |
|
| 735 | + if ($newPermissions !== null) { |
|
| 736 | + $share->setPermissions($newPermissions); |
|
| 737 | + $permissions = $newPermissions; |
|
| 738 | + } |
|
| 739 | + |
|
| 740 | + if ($expireDate === '') { |
|
| 741 | + $share->setExpirationDate(null); |
|
| 742 | + } else if ($expireDate !== null) { |
|
| 743 | + try { |
|
| 744 | + $expireDate = $this->parseDate($expireDate); |
|
| 745 | + } catch (\Exception $e) { |
|
| 746 | + throw new OCSBadRequestException($e->getMessage()); |
|
| 747 | + } |
|
| 748 | + $share->setExpirationDate($expireDate); |
|
| 749 | + } |
|
| 750 | + |
|
| 751 | + if ($password === '') { |
|
| 752 | + $share->setPassword(null); |
|
| 753 | + } else if ($password !== null) { |
|
| 754 | + $share->setPassword($password); |
|
| 755 | + } |
|
| 756 | + |
|
| 757 | + } else { |
|
| 758 | + if ($permissions !== null) { |
|
| 759 | + $permissions = (int)$permissions; |
|
| 760 | + $share->setPermissions($permissions); |
|
| 761 | + } |
|
| 762 | + |
|
| 763 | + if ($share->getShareType() === \OCP\Share::SHARE_TYPE_EMAIL) { |
|
| 764 | + if ($password === '') { |
|
| 765 | + $share->setPassword(null); |
|
| 766 | + } else if ($password !== null) { |
|
| 767 | + $share->setPassword($password); |
|
| 768 | + } |
|
| 769 | + } |
|
| 770 | + |
|
| 771 | + if ($expireDate === '') { |
|
| 772 | + $share->setExpirationDate(null); |
|
| 773 | + } else if ($expireDate !== null) { |
|
| 774 | + try { |
|
| 775 | + $expireDate = $this->parseDate($expireDate); |
|
| 776 | + } catch (\Exception $e) { |
|
| 777 | + throw new OCSBadRequestException($e->getMessage()); |
|
| 778 | + } |
|
| 779 | + $share->setExpirationDate($expireDate); |
|
| 780 | + } |
|
| 781 | + |
|
| 782 | + } |
|
| 783 | + |
|
| 784 | + if ($permissions !== null && $share->getShareOwner() !== $this->currentUser) { |
|
| 785 | + /* Check if this is an incomming share */ |
|
| 786 | + $incomingShares = $this->shareManager->getSharedWith($this->currentUser, \OCP\Share::SHARE_TYPE_USER, $share->getNode(), -1, 0); |
|
| 787 | + $incomingShares = array_merge($incomingShares, $this->shareManager->getSharedWith($this->currentUser, \OCP\Share::SHARE_TYPE_GROUP, $share->getNode(), -1, 0)); |
|
| 788 | + |
|
| 789 | + /** @var \OCP\Share\IShare[] $incomingShares */ |
|
| 790 | + if (!empty($incomingShares)) { |
|
| 791 | + $maxPermissions = 0; |
|
| 792 | + foreach ($incomingShares as $incomingShare) { |
|
| 793 | + $maxPermissions |= $incomingShare->getPermissions(); |
|
| 794 | + } |
|
| 795 | + |
|
| 796 | + if ($share->getPermissions() & ~$maxPermissions) { |
|
| 797 | + throw new OCSNotFoundException($this->l->t('Cannot increase permissions')); |
|
| 798 | + } |
|
| 799 | + } |
|
| 800 | + } |
|
| 801 | + |
|
| 802 | + |
|
| 803 | + try { |
|
| 804 | + $share = $this->shareManager->updateShare($share); |
|
| 805 | + } catch (\Exception $e) { |
|
| 806 | + throw new OCSBadRequestException($e->getMessage()); |
|
| 807 | + } |
|
| 808 | + |
|
| 809 | + return new DataResponse($this->formatShare($share)); |
|
| 810 | + } |
|
| 811 | + |
|
| 812 | + /** |
|
| 813 | + * @param \OCP\Share\IShare $share |
|
| 814 | + * @return bool |
|
| 815 | + */ |
|
| 816 | + protected function canAccessShare(\OCP\Share\IShare $share, $checkGroups = true) { |
|
| 817 | + // A file with permissions 0 can't be accessed by us. So Don't show it |
|
| 818 | + if ($share->getPermissions() === 0) { |
|
| 819 | + return false; |
|
| 820 | + } |
|
| 821 | + |
|
| 822 | + // Owner of the file and the sharer of the file can always get share |
|
| 823 | + if ($share->getShareOwner() === $this->currentUser || |
|
| 824 | + $share->getSharedBy() === $this->currentUser |
|
| 825 | + ) { |
|
| 826 | + return true; |
|
| 827 | + } |
|
| 828 | + |
|
| 829 | + // If the share is shared with you (or a group you are a member of) |
|
| 830 | + if ($share->getShareType() === \OCP\Share::SHARE_TYPE_USER && |
|
| 831 | + $share->getSharedWith() === $this->currentUser |
|
| 832 | + ) { |
|
| 833 | + return true; |
|
| 834 | + } |
|
| 835 | + |
|
| 836 | + if ($checkGroups && $share->getShareType() === \OCP\Share::SHARE_TYPE_GROUP) { |
|
| 837 | + $sharedWith = $this->groupManager->get($share->getSharedWith()); |
|
| 838 | + $user = $this->userManager->get($this->currentUser); |
|
| 839 | + if ($user !== null && $sharedWith !== null && $sharedWith->inGroup($user)) { |
|
| 840 | + return true; |
|
| 841 | + } |
|
| 842 | + } |
|
| 843 | + |
|
| 844 | + if ($share->getShareType() === \OCP\Share::SHARE_TYPE_CIRCLE) { |
|
| 845 | + // TODO: have a sanity check like above? |
|
| 846 | + return true; |
|
| 847 | + } |
|
| 848 | + |
|
| 849 | + return false; |
|
| 850 | + } |
|
| 851 | + |
|
| 852 | + /** |
|
| 853 | + * Make sure that the passed date is valid ISO 8601 |
|
| 854 | + * So YYYY-MM-DD |
|
| 855 | + * If not throw an exception |
|
| 856 | + * |
|
| 857 | + * @param string $expireDate |
|
| 858 | + * |
|
| 859 | + * @throws \Exception |
|
| 860 | + * @return \DateTime |
|
| 861 | + */ |
|
| 862 | + private function parseDate($expireDate) { |
|
| 863 | + try { |
|
| 864 | + $date = new \DateTime($expireDate); |
|
| 865 | + } catch (\Exception $e) { |
|
| 866 | + throw new \Exception('Invalid date. Format must be YYYY-MM-DD'); |
|
| 867 | + } |
|
| 868 | + |
|
| 869 | + if ($date === false) { |
|
| 870 | + throw new \Exception('Invalid date. Format must be YYYY-MM-DD'); |
|
| 871 | + } |
|
| 872 | + |
|
| 873 | + $date->setTime(0, 0, 0); |
|
| 874 | + |
|
| 875 | + return $date; |
|
| 876 | + } |
|
| 877 | + |
|
| 878 | + /** |
|
| 879 | + * Since we have multiple providers but the OCS Share API v1 does |
|
| 880 | + * not support this we need to check all backends. |
|
| 881 | + * |
|
| 882 | + * @param string $id |
|
| 883 | + * @return \OCP\Share\IShare |
|
| 884 | + * @throws ShareNotFound |
|
| 885 | + */ |
|
| 886 | + private function getShareById($id) { |
|
| 887 | + $share = null; |
|
| 888 | + |
|
| 889 | + // First check if it is an internal share. |
|
| 890 | + try { |
|
| 891 | + $share = $this->shareManager->getShareById('ocinternal:' . $id); |
|
| 892 | + return $share; |
|
| 893 | + } catch (ShareNotFound $e) { |
|
| 894 | + // Do nothing, just try the other share type |
|
| 895 | + } |
|
| 896 | + |
|
| 897 | + |
|
| 898 | + try { |
|
| 899 | + if ($this->shareManager->shareProviderExists(\OCP\Share::SHARE_TYPE_CIRCLE)) { |
|
| 900 | + $share = $this->shareManager->getShareById('ocCircleShare:' . $id); |
|
| 901 | + return $share; |
|
| 902 | + } |
|
| 903 | + } catch (ShareNotFound $e) { |
|
| 904 | + // Do nothing, just try the other share type |
|
| 905 | + } |
|
| 906 | + |
|
| 907 | + try { |
|
| 908 | + if ($this->shareManager->shareProviderExists(\OCP\Share::SHARE_TYPE_EMAIL)) { |
|
| 909 | + $share = $this->shareManager->getShareById('ocMailShare:' . $id); |
|
| 910 | + return $share; |
|
| 911 | + } |
|
| 912 | + } catch (ShareNotFound $e) { |
|
| 913 | + // Do nothing, just try the other share type |
|
| 914 | + } |
|
| 915 | + |
|
| 916 | + if (!$this->shareManager->outgoingServer2ServerSharesAllowed()) { |
|
| 917 | + throw new ShareNotFound(); |
|
| 918 | + } |
|
| 919 | + $share = $this->shareManager->getShareById('ocFederatedSharing:' . $id); |
|
| 920 | + |
|
| 921 | + return $share; |
|
| 922 | + } |
|
| 923 | + |
|
| 924 | + /** |
|
| 925 | + * Lock a Node |
|
| 926 | + * |
|
| 927 | + * @param \OCP\Files\Node $node |
|
| 928 | + */ |
|
| 929 | + private function lock(\OCP\Files\Node $node) { |
|
| 930 | + $node->lock(ILockingProvider::LOCK_SHARED); |
|
| 931 | + $this->lockedNode = $node; |
|
| 932 | + } |
|
| 933 | + |
|
| 934 | + /** |
|
| 935 | + * Cleanup the remaining locks |
|
| 936 | + */ |
|
| 937 | + public function cleanup() { |
|
| 938 | + if ($this->lockedNode !== null) { |
|
| 939 | + $this->lockedNode->unlock(ILockingProvider::LOCK_SHARED); |
|
| 940 | + } |
|
| 941 | + } |
|
| 942 | 942 | } |
@@ -43,725 +43,725 @@ |
||
| 43 | 43 | |
| 44 | 44 | class ShareesAPIController extends OCSController { |
| 45 | 45 | |
| 46 | - /** @var IGroupManager */ |
|
| 47 | - protected $groupManager; |
|
| 48 | - |
|
| 49 | - /** @var IUserManager */ |
|
| 50 | - protected $userManager; |
|
| 51 | - |
|
| 52 | - /** @var IManager */ |
|
| 53 | - protected $contactsManager; |
|
| 54 | - |
|
| 55 | - /** @var IConfig */ |
|
| 56 | - protected $config; |
|
| 57 | - |
|
| 58 | - /** @var IUserSession */ |
|
| 59 | - protected $userSession; |
|
| 60 | - |
|
| 61 | - /** @var IURLGenerator */ |
|
| 62 | - protected $urlGenerator; |
|
| 63 | - |
|
| 64 | - /** @var ILogger */ |
|
| 65 | - protected $logger; |
|
| 66 | - |
|
| 67 | - /** @var \OCP\Share\IManager */ |
|
| 68 | - protected $shareManager; |
|
| 69 | - |
|
| 70 | - /** @var IClientService */ |
|
| 71 | - protected $clientService; |
|
| 72 | - |
|
| 73 | - /** @var ICloudIdManager */ |
|
| 74 | - protected $cloudIdManager; |
|
| 75 | - |
|
| 76 | - /** @var bool */ |
|
| 77 | - protected $shareWithGroupOnly = false; |
|
| 78 | - |
|
| 79 | - /** @var bool */ |
|
| 80 | - protected $shareeEnumeration = true; |
|
| 81 | - |
|
| 82 | - /** @var int */ |
|
| 83 | - protected $offset = 0; |
|
| 84 | - |
|
| 85 | - /** @var int */ |
|
| 86 | - protected $limit = 10; |
|
| 87 | - |
|
| 88 | - /** @var array */ |
|
| 89 | - protected $result = [ |
|
| 90 | - 'exact' => [ |
|
| 91 | - 'users' => [], |
|
| 92 | - 'groups' => [], |
|
| 93 | - 'remotes' => [], |
|
| 94 | - 'emails' => [], |
|
| 95 | - 'circles' => [], |
|
| 96 | - ], |
|
| 97 | - 'users' => [], |
|
| 98 | - 'groups' => [], |
|
| 99 | - 'remotes' => [], |
|
| 100 | - 'emails' => [], |
|
| 101 | - 'lookup' => [], |
|
| 102 | - 'circles' => [], |
|
| 103 | - ]; |
|
| 104 | - |
|
| 105 | - protected $reachedEndFor = []; |
|
| 106 | - |
|
| 107 | - /** |
|
| 108 | - * @param string $appName |
|
| 109 | - * @param IRequest $request |
|
| 110 | - * @param IGroupManager $groupManager |
|
| 111 | - * @param IUserManager $userManager |
|
| 112 | - * @param IManager $contactsManager |
|
| 113 | - * @param IConfig $config |
|
| 114 | - * @param IUserSession $userSession |
|
| 115 | - * @param IURLGenerator $urlGenerator |
|
| 116 | - * @param ILogger $logger |
|
| 117 | - * @param \OCP\Share\IManager $shareManager |
|
| 118 | - * @param IClientService $clientService |
|
| 119 | - * @param ICloudIdManager $cloudIdManager |
|
| 120 | - */ |
|
| 121 | - public function __construct($appName, |
|
| 122 | - IRequest $request, |
|
| 123 | - IGroupManager $groupManager, |
|
| 124 | - IUserManager $userManager, |
|
| 125 | - IManager $contactsManager, |
|
| 126 | - IConfig $config, |
|
| 127 | - IUserSession $userSession, |
|
| 128 | - IURLGenerator $urlGenerator, |
|
| 129 | - ILogger $logger, |
|
| 130 | - \OCP\Share\IManager $shareManager, |
|
| 131 | - IClientService $clientService, |
|
| 132 | - ICloudIdManager $cloudIdManager |
|
| 133 | - ) { |
|
| 134 | - parent::__construct($appName, $request); |
|
| 135 | - |
|
| 136 | - $this->groupManager = $groupManager; |
|
| 137 | - $this->userManager = $userManager; |
|
| 138 | - $this->contactsManager = $contactsManager; |
|
| 139 | - $this->config = $config; |
|
| 140 | - $this->userSession = $userSession; |
|
| 141 | - $this->urlGenerator = $urlGenerator; |
|
| 142 | - $this->logger = $logger; |
|
| 143 | - $this->shareManager = $shareManager; |
|
| 144 | - $this->clientService = $clientService; |
|
| 145 | - $this->cloudIdManager = $cloudIdManager; |
|
| 146 | - } |
|
| 147 | - |
|
| 148 | - /** |
|
| 149 | - * @param string $search |
|
| 150 | - */ |
|
| 151 | - protected function getUsers($search) { |
|
| 152 | - $this->result['users'] = $this->result['exact']['users'] = $users = []; |
|
| 153 | - |
|
| 154 | - $userGroups = []; |
|
| 155 | - if ($this->shareWithGroupOnly) { |
|
| 156 | - // Search in all the groups this user is part of |
|
| 157 | - $userGroups = $this->groupManager->getUserGroupIds($this->userSession->getUser()); |
|
| 158 | - foreach ($userGroups as $userGroup) { |
|
| 159 | - $usersTmp = $this->groupManager->displayNamesInGroup($userGroup, $search, $this->limit, $this->offset); |
|
| 160 | - foreach ($usersTmp as $uid => $userDisplayName) { |
|
| 161 | - $users[$uid] = $userDisplayName; |
|
| 162 | - } |
|
| 163 | - } |
|
| 164 | - } else { |
|
| 165 | - // Search in all users |
|
| 166 | - $usersTmp = $this->userManager->searchDisplayName($search, $this->limit, $this->offset); |
|
| 167 | - |
|
| 168 | - foreach ($usersTmp as $user) { |
|
| 169 | - $users[$user->getUID()] = $user->getDisplayName(); |
|
| 170 | - } |
|
| 171 | - } |
|
| 172 | - |
|
| 173 | - if (!$this->shareeEnumeration || sizeof($users) < $this->limit) { |
|
| 174 | - $this->reachedEndFor[] = 'users'; |
|
| 175 | - } |
|
| 176 | - |
|
| 177 | - $foundUserById = false; |
|
| 178 | - $lowerSearch = strtolower($search); |
|
| 179 | - foreach ($users as $uid => $userDisplayName) { |
|
| 180 | - if (strtolower($uid) === $lowerSearch || strtolower($userDisplayName) === $lowerSearch) { |
|
| 181 | - if (strtolower($uid) === $lowerSearch) { |
|
| 182 | - $foundUserById = true; |
|
| 183 | - } |
|
| 184 | - $this->result['exact']['users'][] = [ |
|
| 185 | - 'label' => $userDisplayName, |
|
| 186 | - 'value' => [ |
|
| 187 | - 'shareType' => Share::SHARE_TYPE_USER, |
|
| 188 | - 'shareWith' => $uid, |
|
| 189 | - ], |
|
| 190 | - ]; |
|
| 191 | - } else { |
|
| 192 | - $this->result['users'][] = [ |
|
| 193 | - 'label' => $userDisplayName, |
|
| 194 | - 'value' => [ |
|
| 195 | - 'shareType' => Share::SHARE_TYPE_USER, |
|
| 196 | - 'shareWith' => $uid, |
|
| 197 | - ], |
|
| 198 | - ]; |
|
| 199 | - } |
|
| 200 | - } |
|
| 201 | - |
|
| 202 | - if ($this->offset === 0 && !$foundUserById) { |
|
| 203 | - // On page one we try if the search result has a direct hit on the |
|
| 204 | - // user id and if so, we add that to the exact match list |
|
| 205 | - $user = $this->userManager->get($search); |
|
| 206 | - if ($user instanceof IUser) { |
|
| 207 | - $addUser = true; |
|
| 208 | - |
|
| 209 | - if ($this->shareWithGroupOnly) { |
|
| 210 | - // Only add, if we have a common group |
|
| 211 | - $commonGroups = array_intersect($userGroups, $this->groupManager->getUserGroupIds($user)); |
|
| 212 | - $addUser = !empty($commonGroups); |
|
| 213 | - } |
|
| 214 | - |
|
| 215 | - if ($addUser) { |
|
| 216 | - array_push($this->result['exact']['users'], [ |
|
| 217 | - 'label' => $user->getDisplayName(), |
|
| 218 | - 'value' => [ |
|
| 219 | - 'shareType' => Share::SHARE_TYPE_USER, |
|
| 220 | - 'shareWith' => $user->getUID(), |
|
| 221 | - ], |
|
| 222 | - ]); |
|
| 223 | - } |
|
| 224 | - } |
|
| 225 | - } |
|
| 226 | - |
|
| 227 | - if (!$this->shareeEnumeration) { |
|
| 228 | - $this->result['users'] = []; |
|
| 229 | - } |
|
| 230 | - } |
|
| 231 | - |
|
| 232 | - /** |
|
| 233 | - * @param string $search |
|
| 234 | - */ |
|
| 235 | - protected function getGroups($search) { |
|
| 236 | - $this->result['groups'] = $this->result['exact']['groups'] = []; |
|
| 237 | - |
|
| 238 | - $groups = $this->groupManager->search($search, $this->limit, $this->offset); |
|
| 239 | - $groupIds = array_map(function (IGroup $group) { return $group->getGID(); }, $groups); |
|
| 240 | - |
|
| 241 | - if (!$this->shareeEnumeration || sizeof($groups) < $this->limit) { |
|
| 242 | - $this->reachedEndFor[] = 'groups'; |
|
| 243 | - } |
|
| 244 | - |
|
| 245 | - $userGroups = []; |
|
| 246 | - if (!empty($groups) && $this->shareWithGroupOnly) { |
|
| 247 | - // Intersect all the groups that match with the groups this user is a member of |
|
| 248 | - $userGroups = $this->groupManager->getUserGroups($this->userSession->getUser()); |
|
| 249 | - $userGroups = array_map(function (IGroup $group) { return $group->getGID(); }, $userGroups); |
|
| 250 | - $groupIds = array_intersect($groupIds, $userGroups); |
|
| 251 | - } |
|
| 252 | - |
|
| 253 | - $lowerSearch = strtolower($search); |
|
| 254 | - foreach ($groups as $group) { |
|
| 255 | - // FIXME: use a more efficient approach |
|
| 256 | - $gid = $group->getGID(); |
|
| 257 | - if (!in_array($gid, $groupIds)) { |
|
| 258 | - continue; |
|
| 259 | - } |
|
| 260 | - if (strtolower($gid) === $lowerSearch || strtolower($group->getDisplayName()) === $lowerSearch) { |
|
| 261 | - $this->result['exact']['groups'][] = [ |
|
| 262 | - 'label' => $group->getDisplayName(), |
|
| 263 | - 'value' => [ |
|
| 264 | - 'shareType' => Share::SHARE_TYPE_GROUP, |
|
| 265 | - 'shareWith' => $gid, |
|
| 266 | - ], |
|
| 267 | - ]; |
|
| 268 | - } else { |
|
| 269 | - $this->result['groups'][] = [ |
|
| 270 | - 'label' => $group->getDisplayName(), |
|
| 271 | - 'value' => [ |
|
| 272 | - 'shareType' => Share::SHARE_TYPE_GROUP, |
|
| 273 | - 'shareWith' => $gid, |
|
| 274 | - ], |
|
| 275 | - ]; |
|
| 276 | - } |
|
| 277 | - } |
|
| 278 | - |
|
| 279 | - if ($this->offset === 0 && empty($this->result['exact']['groups'])) { |
|
| 280 | - // On page one we try if the search result has a direct hit on the |
|
| 281 | - // user id and if so, we add that to the exact match list |
|
| 282 | - $group = $this->groupManager->get($search); |
|
| 283 | - if ($group instanceof IGroup && (!$this->shareWithGroupOnly || in_array($group->getGID(), $userGroups))) { |
|
| 284 | - array_push($this->result['exact']['groups'], [ |
|
| 285 | - 'label' => $group->getDisplayName(), |
|
| 286 | - 'value' => [ |
|
| 287 | - 'shareType' => Share::SHARE_TYPE_GROUP, |
|
| 288 | - 'shareWith' => $group->getGID(), |
|
| 289 | - ], |
|
| 290 | - ]); |
|
| 291 | - } |
|
| 292 | - } |
|
| 293 | - |
|
| 294 | - if (!$this->shareeEnumeration) { |
|
| 295 | - $this->result['groups'] = []; |
|
| 296 | - } |
|
| 297 | - } |
|
| 298 | - |
|
| 299 | - |
|
| 300 | - /** |
|
| 301 | - * @param string $search |
|
| 302 | - * @suppress PhanUndeclaredClassMethod |
|
| 303 | - */ |
|
| 304 | - protected function getCircles($search) { |
|
| 305 | - $this->result['circles'] = $this->result['exact']['circles'] = []; |
|
| 306 | - |
|
| 307 | - $result = \OCA\Circles\Api\Sharees::search($search, $this->limit, $this->offset); |
|
| 308 | - if (array_key_exists('circles', $result['exact'])) { |
|
| 309 | - $this->result['exact']['circles'] = $result['exact']['circles']; |
|
| 310 | - } |
|
| 311 | - if (array_key_exists('circles', $result)) { |
|
| 312 | - $this->result['circles'] = $result['circles']; |
|
| 313 | - } |
|
| 314 | - } |
|
| 315 | - |
|
| 316 | - |
|
| 317 | - /** |
|
| 318 | - * @param string $search |
|
| 319 | - * @return array |
|
| 320 | - */ |
|
| 321 | - protected function getRemote($search) { |
|
| 322 | - $result = ['results' => [], 'exact' => []]; |
|
| 323 | - |
|
| 324 | - // Search in contacts |
|
| 325 | - //@todo Pagination missing |
|
| 326 | - $addressBookContacts = $this->contactsManager->search($search, ['CLOUD', 'FN']); |
|
| 327 | - $result['exactIdMatch'] = false; |
|
| 328 | - foreach ($addressBookContacts as $contact) { |
|
| 329 | - if (isset($contact['isLocalSystemBook'])) { |
|
| 330 | - continue; |
|
| 331 | - } |
|
| 332 | - if (isset($contact['CLOUD'])) { |
|
| 333 | - $cloudIds = $contact['CLOUD']; |
|
| 334 | - if (!is_array($cloudIds)) { |
|
| 335 | - $cloudIds = [$cloudIds]; |
|
| 336 | - } |
|
| 337 | - $lowerSearch = strtolower($search); |
|
| 338 | - foreach ($cloudIds as $cloudId) { |
|
| 339 | - list(, $serverUrl) = $this->splitUserRemote($cloudId); |
|
| 340 | - if (strtolower($contact['FN']) === $lowerSearch || strtolower($cloudId) === $lowerSearch) { |
|
| 341 | - if (strtolower($cloudId) === $lowerSearch) { |
|
| 342 | - $result['exactIdMatch'] = true; |
|
| 343 | - } |
|
| 344 | - $result['exact'][] = [ |
|
| 345 | - 'label' => $contact['FN'] . " ($cloudId)", |
|
| 346 | - 'value' => [ |
|
| 347 | - 'shareType' => Share::SHARE_TYPE_REMOTE, |
|
| 348 | - 'shareWith' => $cloudId, |
|
| 349 | - 'server' => $serverUrl, |
|
| 350 | - ], |
|
| 351 | - ]; |
|
| 352 | - } else { |
|
| 353 | - $result['results'][] = [ |
|
| 354 | - 'label' => $contact['FN'] . " ($cloudId)", |
|
| 355 | - 'value' => [ |
|
| 356 | - 'shareType' => Share::SHARE_TYPE_REMOTE, |
|
| 357 | - 'shareWith' => $cloudId, |
|
| 358 | - 'server' => $serverUrl, |
|
| 359 | - ], |
|
| 360 | - ]; |
|
| 361 | - } |
|
| 362 | - } |
|
| 363 | - } |
|
| 364 | - } |
|
| 365 | - |
|
| 366 | - if (!$this->shareeEnumeration) { |
|
| 367 | - $result['results'] = []; |
|
| 368 | - } |
|
| 369 | - |
|
| 370 | - if (!$result['exactIdMatch'] && $this->cloudIdManager->isValidCloudId($search) && $this->offset === 0) { |
|
| 371 | - $result['exact'][] = [ |
|
| 372 | - 'label' => $search, |
|
| 373 | - 'value' => [ |
|
| 374 | - 'shareType' => Share::SHARE_TYPE_REMOTE, |
|
| 375 | - 'shareWith' => $search, |
|
| 376 | - ], |
|
| 377 | - ]; |
|
| 378 | - } |
|
| 379 | - |
|
| 380 | - $this->reachedEndFor[] = 'remotes'; |
|
| 381 | - |
|
| 382 | - return $result; |
|
| 383 | - } |
|
| 384 | - |
|
| 385 | - /** |
|
| 386 | - * split user and remote from federated cloud id |
|
| 387 | - * |
|
| 388 | - * @param string $address federated share address |
|
| 389 | - * @return array [user, remoteURL] |
|
| 390 | - * @throws \Exception |
|
| 391 | - */ |
|
| 392 | - public function splitUserRemote($address) { |
|
| 393 | - try { |
|
| 394 | - $cloudId = $this->cloudIdManager->resolveCloudId($address); |
|
| 395 | - return [$cloudId->getUser(), $cloudId->getRemote()]; |
|
| 396 | - } catch (\InvalidArgumentException $e) { |
|
| 397 | - throw new \Exception('Invalid Federated Cloud ID', 0, $e); |
|
| 398 | - } |
|
| 399 | - } |
|
| 400 | - |
|
| 401 | - /** |
|
| 402 | - * Strips away a potential file names and trailing slashes: |
|
| 403 | - * - http://localhost |
|
| 404 | - * - http://localhost/ |
|
| 405 | - * - http://localhost/index.php |
|
| 406 | - * - http://localhost/index.php/s/{shareToken} |
|
| 407 | - * |
|
| 408 | - * all return: http://localhost |
|
| 409 | - * |
|
| 410 | - * @param string $remote |
|
| 411 | - * @return string |
|
| 412 | - */ |
|
| 413 | - protected function fixRemoteURL($remote) { |
|
| 414 | - $remote = str_replace('\\', '/', $remote); |
|
| 415 | - if ($fileNamePosition = strpos($remote, '/index.php')) { |
|
| 416 | - $remote = substr($remote, 0, $fileNamePosition); |
|
| 417 | - } |
|
| 418 | - $remote = rtrim($remote, '/'); |
|
| 419 | - |
|
| 420 | - return $remote; |
|
| 421 | - } |
|
| 422 | - |
|
| 423 | - /** |
|
| 424 | - * @NoAdminRequired |
|
| 425 | - * |
|
| 426 | - * @param string $search |
|
| 427 | - * @param string $itemType |
|
| 428 | - * @param int $page |
|
| 429 | - * @param int $perPage |
|
| 430 | - * @param int|int[] $shareType |
|
| 431 | - * @param bool $lookup |
|
| 432 | - * @return DataResponse |
|
| 433 | - * @throws OCSBadRequestException |
|
| 434 | - */ |
|
| 435 | - public function search($search = '', $itemType = null, $page = 1, $perPage = 200, $shareType = null, $lookup = true) { |
|
| 436 | - |
|
| 437 | - // only search for string larger than a given threshold |
|
| 438 | - $threshold = (int)$this->config->getSystemValue('sharing.minSearchStringLength', 0); |
|
| 439 | - if (strlen($search) < $threshold) { |
|
| 440 | - return new DataResponse($this->result); |
|
| 441 | - } |
|
| 442 | - |
|
| 443 | - // never return more than the max. number of results configured in the config.php |
|
| 444 | - $maxResults = (int)$this->config->getSystemValue('sharing.maxAutocompleteResults', 0); |
|
| 445 | - if ($maxResults > 0) { |
|
| 446 | - $perPage = min($perPage, $maxResults); |
|
| 447 | - } |
|
| 448 | - if ($perPage <= 0) { |
|
| 449 | - throw new OCSBadRequestException('Invalid perPage argument'); |
|
| 450 | - } |
|
| 451 | - if ($page <= 0) { |
|
| 452 | - throw new OCSBadRequestException('Invalid page'); |
|
| 453 | - } |
|
| 454 | - |
|
| 455 | - $shareTypes = [ |
|
| 456 | - Share::SHARE_TYPE_USER, |
|
| 457 | - ]; |
|
| 458 | - |
|
| 459 | - if ($itemType === 'file' || $itemType === 'folder') { |
|
| 460 | - if ($this->shareManager->allowGroupSharing()) { |
|
| 461 | - $shareTypes[] = Share::SHARE_TYPE_GROUP; |
|
| 462 | - } |
|
| 463 | - |
|
| 464 | - if ($this->isRemoteSharingAllowed($itemType)) { |
|
| 465 | - $shareTypes[] = Share::SHARE_TYPE_REMOTE; |
|
| 466 | - } |
|
| 467 | - |
|
| 468 | - if ($this->shareManager->shareProviderExists(Share::SHARE_TYPE_EMAIL)) { |
|
| 469 | - $shareTypes[] = Share::SHARE_TYPE_EMAIL; |
|
| 470 | - } |
|
| 471 | - } else { |
|
| 472 | - $shareTypes[] = Share::SHARE_TYPE_GROUP; |
|
| 473 | - $shareTypes[] = Share::SHARE_TYPE_EMAIL; |
|
| 474 | - } |
|
| 475 | - |
|
| 476 | - if (\OC::$server->getAppManager()->isEnabledForUser('circles') && class_exists('\OCA\Circles\ShareByCircleProvider')) { |
|
| 477 | - $shareTypes[] = Share::SHARE_TYPE_CIRCLE; |
|
| 478 | - } |
|
| 479 | - |
|
| 480 | - if (isset($_GET['shareType']) && is_array($_GET['shareType'])) { |
|
| 481 | - $shareTypes = array_intersect($shareTypes, $_GET['shareType']); |
|
| 482 | - sort($shareTypes); |
|
| 483 | - } else if (is_numeric($shareType)) { |
|
| 484 | - $shareTypes = array_intersect($shareTypes, [(int) $shareType]); |
|
| 485 | - sort($shareTypes); |
|
| 486 | - } |
|
| 487 | - |
|
| 488 | - $this->shareWithGroupOnly = $this->config->getAppValue('core', 'shareapi_only_share_with_group_members', 'no') === 'yes'; |
|
| 489 | - $this->shareeEnumeration = $this->config->getAppValue('core', 'shareapi_allow_share_dialog_user_enumeration', 'yes') === 'yes'; |
|
| 490 | - $this->limit = (int) $perPage; |
|
| 491 | - $this->offset = $perPage * ($page - 1); |
|
| 492 | - |
|
| 493 | - return $this->searchSharees($search, $itemType, $shareTypes, $page, $perPage, $lookup); |
|
| 494 | - } |
|
| 495 | - |
|
| 496 | - /** |
|
| 497 | - * Method to get out the static call for better testing |
|
| 498 | - * |
|
| 499 | - * @param string $itemType |
|
| 500 | - * @return bool |
|
| 501 | - */ |
|
| 502 | - protected function isRemoteSharingAllowed($itemType) { |
|
| 503 | - try { |
|
| 504 | - $backend = Share::getBackend($itemType); |
|
| 505 | - return $backend->isShareTypeAllowed(Share::SHARE_TYPE_REMOTE); |
|
| 506 | - } catch (\Exception $e) { |
|
| 507 | - return false; |
|
| 508 | - } |
|
| 509 | - } |
|
| 510 | - |
|
| 511 | - /** |
|
| 512 | - * Testable search function that does not need globals |
|
| 513 | - * |
|
| 514 | - * @param string $search |
|
| 515 | - * @param string $itemType |
|
| 516 | - * @param array $shareTypes |
|
| 517 | - * @param int $page |
|
| 518 | - * @param int $perPage |
|
| 519 | - * @param bool $lookup |
|
| 520 | - * @return DataResponse |
|
| 521 | - * @throws OCSBadRequestException |
|
| 522 | - */ |
|
| 523 | - protected function searchSharees($search, $itemType, array $shareTypes, $page, $perPage, $lookup) { |
|
| 524 | - // Verify arguments |
|
| 525 | - if ($itemType === null) { |
|
| 526 | - throw new OCSBadRequestException('Missing itemType'); |
|
| 527 | - } |
|
| 528 | - |
|
| 529 | - // Get users |
|
| 530 | - if (in_array(Share::SHARE_TYPE_USER, $shareTypes)) { |
|
| 531 | - $this->getUsers($search); |
|
| 532 | - } |
|
| 533 | - |
|
| 534 | - // Get groups |
|
| 535 | - if (in_array(Share::SHARE_TYPE_GROUP, $shareTypes)) { |
|
| 536 | - $this->getGroups($search); |
|
| 537 | - } |
|
| 538 | - |
|
| 539 | - // Get circles |
|
| 540 | - if (in_array(Share::SHARE_TYPE_CIRCLE, $shareTypes)) { |
|
| 541 | - $this->getCircles($search); |
|
| 542 | - } |
|
| 543 | - |
|
| 544 | - |
|
| 545 | - // Get remote |
|
| 546 | - $remoteResults = ['results' => [], 'exact' => [], 'exactIdMatch' => false]; |
|
| 547 | - if (in_array(Share::SHARE_TYPE_REMOTE, $shareTypes)) { |
|
| 548 | - $remoteResults = $this->getRemote($search); |
|
| 549 | - } |
|
| 550 | - |
|
| 551 | - // Get emails |
|
| 552 | - $mailResults = ['results' => [], 'exact' => [], 'exactIdMatch' => false]; |
|
| 553 | - if (in_array(Share::SHARE_TYPE_EMAIL, $shareTypes)) { |
|
| 554 | - $mailResults = $this->getEmail($search); |
|
| 555 | - } |
|
| 556 | - |
|
| 557 | - // Get from lookup server |
|
| 558 | - if ($lookup) { |
|
| 559 | - $this->getLookup($search); |
|
| 560 | - } |
|
| 561 | - |
|
| 562 | - // if we have a exact match, either for the federated cloud id or for the |
|
| 563 | - // email address we only return the exact match. It is highly unlikely |
|
| 564 | - // that the exact same email address and federated cloud id exists |
|
| 565 | - if ($mailResults['exactIdMatch'] && !$remoteResults['exactIdMatch']) { |
|
| 566 | - $this->result['emails'] = $mailResults['results']; |
|
| 567 | - $this->result['exact']['emails'] = $mailResults['exact']; |
|
| 568 | - } else if (!$mailResults['exactIdMatch'] && $remoteResults['exactIdMatch']) { |
|
| 569 | - $this->result['remotes'] = $remoteResults['results']; |
|
| 570 | - $this->result['exact']['remotes'] = $remoteResults['exact']; |
|
| 571 | - } else { |
|
| 572 | - $this->result['remotes'] = $remoteResults['results']; |
|
| 573 | - $this->result['exact']['remotes'] = $remoteResults['exact']; |
|
| 574 | - $this->result['emails'] = $mailResults['results']; |
|
| 575 | - $this->result['exact']['emails'] = $mailResults['exact']; |
|
| 576 | - } |
|
| 577 | - |
|
| 578 | - $response = new DataResponse($this->result); |
|
| 579 | - |
|
| 580 | - if (sizeof($this->reachedEndFor) < 3) { |
|
| 581 | - $response->addHeader('Link', $this->getPaginationLink($page, [ |
|
| 582 | - 'search' => $search, |
|
| 583 | - 'itemType' => $itemType, |
|
| 584 | - 'shareType' => $shareTypes, |
|
| 585 | - 'perPage' => $perPage, |
|
| 586 | - ])); |
|
| 587 | - } |
|
| 588 | - |
|
| 589 | - return $response; |
|
| 590 | - } |
|
| 591 | - |
|
| 592 | - /** |
|
| 593 | - * @param string $search |
|
| 594 | - * @return array |
|
| 595 | - */ |
|
| 596 | - protected function getEmail($search) { |
|
| 597 | - $result = ['results' => [], 'exact' => [], 'exactIdMatch' => false]; |
|
| 598 | - |
|
| 599 | - // Search in contacts |
|
| 600 | - //@todo Pagination missing |
|
| 601 | - $addressBookContacts = $this->contactsManager->search($search, ['EMAIL', 'FN']); |
|
| 602 | - $lowerSearch = strtolower($search); |
|
| 603 | - foreach ($addressBookContacts as $contact) { |
|
| 604 | - if (isset($contact['EMAIL'])) { |
|
| 605 | - $emailAddresses = $contact['EMAIL']; |
|
| 606 | - if (!is_array($emailAddresses)) { |
|
| 607 | - $emailAddresses = [$emailAddresses]; |
|
| 608 | - } |
|
| 609 | - foreach ($emailAddresses as $emailAddress) { |
|
| 610 | - $exactEmailMatch = strtolower($emailAddress) === $lowerSearch; |
|
| 611 | - |
|
| 612 | - if (isset($contact['isLocalSystemBook'])) { |
|
| 613 | - if ($exactEmailMatch) { |
|
| 614 | - $cloud = $this->cloudIdManager->resolveCloudId($contact['CLOUD'][0]); |
|
| 615 | - if (!$this->hasUserInResult($cloud->getUser())) { |
|
| 616 | - $this->result['exact']['users'][] = [ |
|
| 617 | - 'label' => $contact['FN'] . " ($emailAddress)", |
|
| 618 | - 'value' => [ |
|
| 619 | - 'shareType' => Share::SHARE_TYPE_USER, |
|
| 620 | - 'shareWith' => $cloud->getUser(), |
|
| 621 | - ], |
|
| 622 | - ]; |
|
| 623 | - } |
|
| 624 | - return ['results' => [], 'exact' => [], 'exactIdMatch' => true]; |
|
| 625 | - } |
|
| 626 | - if ($this->shareeEnumeration) { |
|
| 627 | - $cloud = $this->cloudIdManager->resolveCloudId($contact['CLOUD'][0]); |
|
| 628 | - if (!$this->hasUserInResult($cloud->getUser())) { |
|
| 629 | - $this->result['users'][] = [ |
|
| 630 | - 'label' => $contact['FN'] . " ($emailAddress)", |
|
| 631 | - 'value' => [ |
|
| 632 | - 'shareType' => Share::SHARE_TYPE_USER, |
|
| 633 | - 'shareWith' => $cloud->getUser(), |
|
| 634 | - ], |
|
| 635 | - ]; |
|
| 636 | - } |
|
| 637 | - } |
|
| 638 | - continue; |
|
| 639 | - } |
|
| 640 | - |
|
| 641 | - if ($exactEmailMatch || strtolower($contact['FN']) === $lowerSearch) { |
|
| 642 | - if ($exactEmailMatch) { |
|
| 643 | - $result['exactIdMatch'] = true; |
|
| 644 | - } |
|
| 645 | - $result['exact'][] = [ |
|
| 646 | - 'label' => $contact['FN'] . " ($emailAddress)", |
|
| 647 | - 'value' => [ |
|
| 648 | - 'shareType' => Share::SHARE_TYPE_EMAIL, |
|
| 649 | - 'shareWith' => $emailAddress, |
|
| 650 | - ], |
|
| 651 | - ]; |
|
| 652 | - } else { |
|
| 653 | - $result['results'][] = [ |
|
| 654 | - 'label' => $contact['FN'] . " ($emailAddress)", |
|
| 655 | - 'value' => [ |
|
| 656 | - 'shareType' => Share::SHARE_TYPE_EMAIL, |
|
| 657 | - 'shareWith' => $emailAddress, |
|
| 658 | - ], |
|
| 659 | - ]; |
|
| 660 | - } |
|
| 661 | - } |
|
| 662 | - } |
|
| 663 | - } |
|
| 664 | - |
|
| 665 | - if (!$this->shareeEnumeration) { |
|
| 666 | - $result['results'] = []; |
|
| 667 | - } |
|
| 668 | - |
|
| 669 | - if (!$result['exactIdMatch'] && filter_var($search, FILTER_VALIDATE_EMAIL)) { |
|
| 670 | - $result['exact'][] = [ |
|
| 671 | - 'label' => $search, |
|
| 672 | - 'value' => [ |
|
| 673 | - 'shareType' => Share::SHARE_TYPE_EMAIL, |
|
| 674 | - 'shareWith' => $search, |
|
| 675 | - ], |
|
| 676 | - ]; |
|
| 677 | - } |
|
| 678 | - |
|
| 679 | - $this->reachedEndFor[] = 'emails'; |
|
| 680 | - |
|
| 681 | - return $result; |
|
| 682 | - } |
|
| 683 | - |
|
| 684 | - protected function getLookup($search) { |
|
| 685 | - $isEnabled = $this->config->getAppValue('files_sharing', 'lookupServerEnabled', 'no'); |
|
| 686 | - $lookupServerUrl = $this->config->getSystemValue('lookup_server', 'https://lookup.nextcloud.com'); |
|
| 687 | - $lookupServerUrl = rtrim($lookupServerUrl, '/'); |
|
| 688 | - $result = []; |
|
| 689 | - |
|
| 690 | - if($isEnabled === 'yes') { |
|
| 691 | - try { |
|
| 692 | - $client = $this->clientService->newClient(); |
|
| 693 | - $response = $client->get( |
|
| 694 | - $lookupServerUrl . '/users?search=' . urlencode($search), |
|
| 695 | - [ |
|
| 696 | - 'timeout' => 10, |
|
| 697 | - 'connect_timeout' => 3, |
|
| 698 | - ] |
|
| 699 | - ); |
|
| 700 | - |
|
| 701 | - $body = json_decode($response->getBody(), true); |
|
| 702 | - |
|
| 703 | - $result = []; |
|
| 704 | - foreach ($body as $lookup) { |
|
| 705 | - $result[] = [ |
|
| 706 | - 'label' => $lookup['federationId'], |
|
| 707 | - 'value' => [ |
|
| 708 | - 'shareType' => Share::SHARE_TYPE_REMOTE, |
|
| 709 | - 'shareWith' => $lookup['federationId'], |
|
| 710 | - ], |
|
| 711 | - 'extra' => $lookup, |
|
| 712 | - ]; |
|
| 713 | - } |
|
| 714 | - } catch (\Exception $e) {} |
|
| 715 | - } |
|
| 716 | - |
|
| 717 | - $this->result['lookup'] = $result; |
|
| 718 | - } |
|
| 719 | - |
|
| 720 | - /** |
|
| 721 | - * Check if a given user is already part of the result |
|
| 722 | - * |
|
| 723 | - * @param string $userId |
|
| 724 | - * @return bool |
|
| 725 | - */ |
|
| 726 | - protected function hasUserInResult($userId) { |
|
| 727 | - foreach ($this->result['exact']['users'] as $result) { |
|
| 728 | - if ($result['value']['shareWith'] === $userId) { |
|
| 729 | - return true; |
|
| 730 | - } |
|
| 731 | - } |
|
| 732 | - |
|
| 733 | - foreach ($this->result['users'] as $result) { |
|
| 734 | - if ($result['value']['shareWith'] === $userId) { |
|
| 735 | - return true; |
|
| 736 | - } |
|
| 737 | - } |
|
| 738 | - |
|
| 739 | - return false; |
|
| 740 | - } |
|
| 741 | - |
|
| 742 | - /** |
|
| 743 | - * Generates a bunch of pagination links for the current page |
|
| 744 | - * |
|
| 745 | - * @param int $page Current page |
|
| 746 | - * @param array $params Parameters for the URL |
|
| 747 | - * @return string |
|
| 748 | - */ |
|
| 749 | - protected function getPaginationLink($page, array $params) { |
|
| 750 | - if ($this->isV2()) { |
|
| 751 | - $url = $this->urlGenerator->getAbsoluteURL('/ocs/v2.php/apps/files_sharing/api/v1/sharees') . '?'; |
|
| 752 | - } else { |
|
| 753 | - $url = $this->urlGenerator->getAbsoluteURL('/ocs/v1.php/apps/files_sharing/api/v1/sharees') . '?'; |
|
| 754 | - } |
|
| 755 | - $params['page'] = $page + 1; |
|
| 756 | - $link = '<' . $url . http_build_query($params) . '>; rel="next"'; |
|
| 757 | - |
|
| 758 | - return $link; |
|
| 759 | - } |
|
| 760 | - |
|
| 761 | - /** |
|
| 762 | - * @return bool |
|
| 763 | - */ |
|
| 764 | - protected function isV2() { |
|
| 765 | - return $this->request->getScriptName() === '/ocs/v2.php'; |
|
| 766 | - } |
|
| 46 | + /** @var IGroupManager */ |
|
| 47 | + protected $groupManager; |
|
| 48 | + |
|
| 49 | + /** @var IUserManager */ |
|
| 50 | + protected $userManager; |
|
| 51 | + |
|
| 52 | + /** @var IManager */ |
|
| 53 | + protected $contactsManager; |
|
| 54 | + |
|
| 55 | + /** @var IConfig */ |
|
| 56 | + protected $config; |
|
| 57 | + |
|
| 58 | + /** @var IUserSession */ |
|
| 59 | + protected $userSession; |
|
| 60 | + |
|
| 61 | + /** @var IURLGenerator */ |
|
| 62 | + protected $urlGenerator; |
|
| 63 | + |
|
| 64 | + /** @var ILogger */ |
|
| 65 | + protected $logger; |
|
| 66 | + |
|
| 67 | + /** @var \OCP\Share\IManager */ |
|
| 68 | + protected $shareManager; |
|
| 69 | + |
|
| 70 | + /** @var IClientService */ |
|
| 71 | + protected $clientService; |
|
| 72 | + |
|
| 73 | + /** @var ICloudIdManager */ |
|
| 74 | + protected $cloudIdManager; |
|
| 75 | + |
|
| 76 | + /** @var bool */ |
|
| 77 | + protected $shareWithGroupOnly = false; |
|
| 78 | + |
|
| 79 | + /** @var bool */ |
|
| 80 | + protected $shareeEnumeration = true; |
|
| 81 | + |
|
| 82 | + /** @var int */ |
|
| 83 | + protected $offset = 0; |
|
| 84 | + |
|
| 85 | + /** @var int */ |
|
| 86 | + protected $limit = 10; |
|
| 87 | + |
|
| 88 | + /** @var array */ |
|
| 89 | + protected $result = [ |
|
| 90 | + 'exact' => [ |
|
| 91 | + 'users' => [], |
|
| 92 | + 'groups' => [], |
|
| 93 | + 'remotes' => [], |
|
| 94 | + 'emails' => [], |
|
| 95 | + 'circles' => [], |
|
| 96 | + ], |
|
| 97 | + 'users' => [], |
|
| 98 | + 'groups' => [], |
|
| 99 | + 'remotes' => [], |
|
| 100 | + 'emails' => [], |
|
| 101 | + 'lookup' => [], |
|
| 102 | + 'circles' => [], |
|
| 103 | + ]; |
|
| 104 | + |
|
| 105 | + protected $reachedEndFor = []; |
|
| 106 | + |
|
| 107 | + /** |
|
| 108 | + * @param string $appName |
|
| 109 | + * @param IRequest $request |
|
| 110 | + * @param IGroupManager $groupManager |
|
| 111 | + * @param IUserManager $userManager |
|
| 112 | + * @param IManager $contactsManager |
|
| 113 | + * @param IConfig $config |
|
| 114 | + * @param IUserSession $userSession |
|
| 115 | + * @param IURLGenerator $urlGenerator |
|
| 116 | + * @param ILogger $logger |
|
| 117 | + * @param \OCP\Share\IManager $shareManager |
|
| 118 | + * @param IClientService $clientService |
|
| 119 | + * @param ICloudIdManager $cloudIdManager |
|
| 120 | + */ |
|
| 121 | + public function __construct($appName, |
|
| 122 | + IRequest $request, |
|
| 123 | + IGroupManager $groupManager, |
|
| 124 | + IUserManager $userManager, |
|
| 125 | + IManager $contactsManager, |
|
| 126 | + IConfig $config, |
|
| 127 | + IUserSession $userSession, |
|
| 128 | + IURLGenerator $urlGenerator, |
|
| 129 | + ILogger $logger, |
|
| 130 | + \OCP\Share\IManager $shareManager, |
|
| 131 | + IClientService $clientService, |
|
| 132 | + ICloudIdManager $cloudIdManager |
|
| 133 | + ) { |
|
| 134 | + parent::__construct($appName, $request); |
|
| 135 | + |
|
| 136 | + $this->groupManager = $groupManager; |
|
| 137 | + $this->userManager = $userManager; |
|
| 138 | + $this->contactsManager = $contactsManager; |
|
| 139 | + $this->config = $config; |
|
| 140 | + $this->userSession = $userSession; |
|
| 141 | + $this->urlGenerator = $urlGenerator; |
|
| 142 | + $this->logger = $logger; |
|
| 143 | + $this->shareManager = $shareManager; |
|
| 144 | + $this->clientService = $clientService; |
|
| 145 | + $this->cloudIdManager = $cloudIdManager; |
|
| 146 | + } |
|
| 147 | + |
|
| 148 | + /** |
|
| 149 | + * @param string $search |
|
| 150 | + */ |
|
| 151 | + protected function getUsers($search) { |
|
| 152 | + $this->result['users'] = $this->result['exact']['users'] = $users = []; |
|
| 153 | + |
|
| 154 | + $userGroups = []; |
|
| 155 | + if ($this->shareWithGroupOnly) { |
|
| 156 | + // Search in all the groups this user is part of |
|
| 157 | + $userGroups = $this->groupManager->getUserGroupIds($this->userSession->getUser()); |
|
| 158 | + foreach ($userGroups as $userGroup) { |
|
| 159 | + $usersTmp = $this->groupManager->displayNamesInGroup($userGroup, $search, $this->limit, $this->offset); |
|
| 160 | + foreach ($usersTmp as $uid => $userDisplayName) { |
|
| 161 | + $users[$uid] = $userDisplayName; |
|
| 162 | + } |
|
| 163 | + } |
|
| 164 | + } else { |
|
| 165 | + // Search in all users |
|
| 166 | + $usersTmp = $this->userManager->searchDisplayName($search, $this->limit, $this->offset); |
|
| 167 | + |
|
| 168 | + foreach ($usersTmp as $user) { |
|
| 169 | + $users[$user->getUID()] = $user->getDisplayName(); |
|
| 170 | + } |
|
| 171 | + } |
|
| 172 | + |
|
| 173 | + if (!$this->shareeEnumeration || sizeof($users) < $this->limit) { |
|
| 174 | + $this->reachedEndFor[] = 'users'; |
|
| 175 | + } |
|
| 176 | + |
|
| 177 | + $foundUserById = false; |
|
| 178 | + $lowerSearch = strtolower($search); |
|
| 179 | + foreach ($users as $uid => $userDisplayName) { |
|
| 180 | + if (strtolower($uid) === $lowerSearch || strtolower($userDisplayName) === $lowerSearch) { |
|
| 181 | + if (strtolower($uid) === $lowerSearch) { |
|
| 182 | + $foundUserById = true; |
|
| 183 | + } |
|
| 184 | + $this->result['exact']['users'][] = [ |
|
| 185 | + 'label' => $userDisplayName, |
|
| 186 | + 'value' => [ |
|
| 187 | + 'shareType' => Share::SHARE_TYPE_USER, |
|
| 188 | + 'shareWith' => $uid, |
|
| 189 | + ], |
|
| 190 | + ]; |
|
| 191 | + } else { |
|
| 192 | + $this->result['users'][] = [ |
|
| 193 | + 'label' => $userDisplayName, |
|
| 194 | + 'value' => [ |
|
| 195 | + 'shareType' => Share::SHARE_TYPE_USER, |
|
| 196 | + 'shareWith' => $uid, |
|
| 197 | + ], |
|
| 198 | + ]; |
|
| 199 | + } |
|
| 200 | + } |
|
| 201 | + |
|
| 202 | + if ($this->offset === 0 && !$foundUserById) { |
|
| 203 | + // On page one we try if the search result has a direct hit on the |
|
| 204 | + // user id and if so, we add that to the exact match list |
|
| 205 | + $user = $this->userManager->get($search); |
|
| 206 | + if ($user instanceof IUser) { |
|
| 207 | + $addUser = true; |
|
| 208 | + |
|
| 209 | + if ($this->shareWithGroupOnly) { |
|
| 210 | + // Only add, if we have a common group |
|
| 211 | + $commonGroups = array_intersect($userGroups, $this->groupManager->getUserGroupIds($user)); |
|
| 212 | + $addUser = !empty($commonGroups); |
|
| 213 | + } |
|
| 214 | + |
|
| 215 | + if ($addUser) { |
|
| 216 | + array_push($this->result['exact']['users'], [ |
|
| 217 | + 'label' => $user->getDisplayName(), |
|
| 218 | + 'value' => [ |
|
| 219 | + 'shareType' => Share::SHARE_TYPE_USER, |
|
| 220 | + 'shareWith' => $user->getUID(), |
|
| 221 | + ], |
|
| 222 | + ]); |
|
| 223 | + } |
|
| 224 | + } |
|
| 225 | + } |
|
| 226 | + |
|
| 227 | + if (!$this->shareeEnumeration) { |
|
| 228 | + $this->result['users'] = []; |
|
| 229 | + } |
|
| 230 | + } |
|
| 231 | + |
|
| 232 | + /** |
|
| 233 | + * @param string $search |
|
| 234 | + */ |
|
| 235 | + protected function getGroups($search) { |
|
| 236 | + $this->result['groups'] = $this->result['exact']['groups'] = []; |
|
| 237 | + |
|
| 238 | + $groups = $this->groupManager->search($search, $this->limit, $this->offset); |
|
| 239 | + $groupIds = array_map(function (IGroup $group) { return $group->getGID(); }, $groups); |
|
| 240 | + |
|
| 241 | + if (!$this->shareeEnumeration || sizeof($groups) < $this->limit) { |
|
| 242 | + $this->reachedEndFor[] = 'groups'; |
|
| 243 | + } |
|
| 244 | + |
|
| 245 | + $userGroups = []; |
|
| 246 | + if (!empty($groups) && $this->shareWithGroupOnly) { |
|
| 247 | + // Intersect all the groups that match with the groups this user is a member of |
|
| 248 | + $userGroups = $this->groupManager->getUserGroups($this->userSession->getUser()); |
|
| 249 | + $userGroups = array_map(function (IGroup $group) { return $group->getGID(); }, $userGroups); |
|
| 250 | + $groupIds = array_intersect($groupIds, $userGroups); |
|
| 251 | + } |
|
| 252 | + |
|
| 253 | + $lowerSearch = strtolower($search); |
|
| 254 | + foreach ($groups as $group) { |
|
| 255 | + // FIXME: use a more efficient approach |
|
| 256 | + $gid = $group->getGID(); |
|
| 257 | + if (!in_array($gid, $groupIds)) { |
|
| 258 | + continue; |
|
| 259 | + } |
|
| 260 | + if (strtolower($gid) === $lowerSearch || strtolower($group->getDisplayName()) === $lowerSearch) { |
|
| 261 | + $this->result['exact']['groups'][] = [ |
|
| 262 | + 'label' => $group->getDisplayName(), |
|
| 263 | + 'value' => [ |
|
| 264 | + 'shareType' => Share::SHARE_TYPE_GROUP, |
|
| 265 | + 'shareWith' => $gid, |
|
| 266 | + ], |
|
| 267 | + ]; |
|
| 268 | + } else { |
|
| 269 | + $this->result['groups'][] = [ |
|
| 270 | + 'label' => $group->getDisplayName(), |
|
| 271 | + 'value' => [ |
|
| 272 | + 'shareType' => Share::SHARE_TYPE_GROUP, |
|
| 273 | + 'shareWith' => $gid, |
|
| 274 | + ], |
|
| 275 | + ]; |
|
| 276 | + } |
|
| 277 | + } |
|
| 278 | + |
|
| 279 | + if ($this->offset === 0 && empty($this->result['exact']['groups'])) { |
|
| 280 | + // On page one we try if the search result has a direct hit on the |
|
| 281 | + // user id and if so, we add that to the exact match list |
|
| 282 | + $group = $this->groupManager->get($search); |
|
| 283 | + if ($group instanceof IGroup && (!$this->shareWithGroupOnly || in_array($group->getGID(), $userGroups))) { |
|
| 284 | + array_push($this->result['exact']['groups'], [ |
|
| 285 | + 'label' => $group->getDisplayName(), |
|
| 286 | + 'value' => [ |
|
| 287 | + 'shareType' => Share::SHARE_TYPE_GROUP, |
|
| 288 | + 'shareWith' => $group->getGID(), |
|
| 289 | + ], |
|
| 290 | + ]); |
|
| 291 | + } |
|
| 292 | + } |
|
| 293 | + |
|
| 294 | + if (!$this->shareeEnumeration) { |
|
| 295 | + $this->result['groups'] = []; |
|
| 296 | + } |
|
| 297 | + } |
|
| 298 | + |
|
| 299 | + |
|
| 300 | + /** |
|
| 301 | + * @param string $search |
|
| 302 | + * @suppress PhanUndeclaredClassMethod |
|
| 303 | + */ |
|
| 304 | + protected function getCircles($search) { |
|
| 305 | + $this->result['circles'] = $this->result['exact']['circles'] = []; |
|
| 306 | + |
|
| 307 | + $result = \OCA\Circles\Api\Sharees::search($search, $this->limit, $this->offset); |
|
| 308 | + if (array_key_exists('circles', $result['exact'])) { |
|
| 309 | + $this->result['exact']['circles'] = $result['exact']['circles']; |
|
| 310 | + } |
|
| 311 | + if (array_key_exists('circles', $result)) { |
|
| 312 | + $this->result['circles'] = $result['circles']; |
|
| 313 | + } |
|
| 314 | + } |
|
| 315 | + |
|
| 316 | + |
|
| 317 | + /** |
|
| 318 | + * @param string $search |
|
| 319 | + * @return array |
|
| 320 | + */ |
|
| 321 | + protected function getRemote($search) { |
|
| 322 | + $result = ['results' => [], 'exact' => []]; |
|
| 323 | + |
|
| 324 | + // Search in contacts |
|
| 325 | + //@todo Pagination missing |
|
| 326 | + $addressBookContacts = $this->contactsManager->search($search, ['CLOUD', 'FN']); |
|
| 327 | + $result['exactIdMatch'] = false; |
|
| 328 | + foreach ($addressBookContacts as $contact) { |
|
| 329 | + if (isset($contact['isLocalSystemBook'])) { |
|
| 330 | + continue; |
|
| 331 | + } |
|
| 332 | + if (isset($contact['CLOUD'])) { |
|
| 333 | + $cloudIds = $contact['CLOUD']; |
|
| 334 | + if (!is_array($cloudIds)) { |
|
| 335 | + $cloudIds = [$cloudIds]; |
|
| 336 | + } |
|
| 337 | + $lowerSearch = strtolower($search); |
|
| 338 | + foreach ($cloudIds as $cloudId) { |
|
| 339 | + list(, $serverUrl) = $this->splitUserRemote($cloudId); |
|
| 340 | + if (strtolower($contact['FN']) === $lowerSearch || strtolower($cloudId) === $lowerSearch) { |
|
| 341 | + if (strtolower($cloudId) === $lowerSearch) { |
|
| 342 | + $result['exactIdMatch'] = true; |
|
| 343 | + } |
|
| 344 | + $result['exact'][] = [ |
|
| 345 | + 'label' => $contact['FN'] . " ($cloudId)", |
|
| 346 | + 'value' => [ |
|
| 347 | + 'shareType' => Share::SHARE_TYPE_REMOTE, |
|
| 348 | + 'shareWith' => $cloudId, |
|
| 349 | + 'server' => $serverUrl, |
|
| 350 | + ], |
|
| 351 | + ]; |
|
| 352 | + } else { |
|
| 353 | + $result['results'][] = [ |
|
| 354 | + 'label' => $contact['FN'] . " ($cloudId)", |
|
| 355 | + 'value' => [ |
|
| 356 | + 'shareType' => Share::SHARE_TYPE_REMOTE, |
|
| 357 | + 'shareWith' => $cloudId, |
|
| 358 | + 'server' => $serverUrl, |
|
| 359 | + ], |
|
| 360 | + ]; |
|
| 361 | + } |
|
| 362 | + } |
|
| 363 | + } |
|
| 364 | + } |
|
| 365 | + |
|
| 366 | + if (!$this->shareeEnumeration) { |
|
| 367 | + $result['results'] = []; |
|
| 368 | + } |
|
| 369 | + |
|
| 370 | + if (!$result['exactIdMatch'] && $this->cloudIdManager->isValidCloudId($search) && $this->offset === 0) { |
|
| 371 | + $result['exact'][] = [ |
|
| 372 | + 'label' => $search, |
|
| 373 | + 'value' => [ |
|
| 374 | + 'shareType' => Share::SHARE_TYPE_REMOTE, |
|
| 375 | + 'shareWith' => $search, |
|
| 376 | + ], |
|
| 377 | + ]; |
|
| 378 | + } |
|
| 379 | + |
|
| 380 | + $this->reachedEndFor[] = 'remotes'; |
|
| 381 | + |
|
| 382 | + return $result; |
|
| 383 | + } |
|
| 384 | + |
|
| 385 | + /** |
|
| 386 | + * split user and remote from federated cloud id |
|
| 387 | + * |
|
| 388 | + * @param string $address federated share address |
|
| 389 | + * @return array [user, remoteURL] |
|
| 390 | + * @throws \Exception |
|
| 391 | + */ |
|
| 392 | + public function splitUserRemote($address) { |
|
| 393 | + try { |
|
| 394 | + $cloudId = $this->cloudIdManager->resolveCloudId($address); |
|
| 395 | + return [$cloudId->getUser(), $cloudId->getRemote()]; |
|
| 396 | + } catch (\InvalidArgumentException $e) { |
|
| 397 | + throw new \Exception('Invalid Federated Cloud ID', 0, $e); |
|
| 398 | + } |
|
| 399 | + } |
|
| 400 | + |
|
| 401 | + /** |
|
| 402 | + * Strips away a potential file names and trailing slashes: |
|
| 403 | + * - http://localhost |
|
| 404 | + * - http://localhost/ |
|
| 405 | + * - http://localhost/index.php |
|
| 406 | + * - http://localhost/index.php/s/{shareToken} |
|
| 407 | + * |
|
| 408 | + * all return: http://localhost |
|
| 409 | + * |
|
| 410 | + * @param string $remote |
|
| 411 | + * @return string |
|
| 412 | + */ |
|
| 413 | + protected function fixRemoteURL($remote) { |
|
| 414 | + $remote = str_replace('\\', '/', $remote); |
|
| 415 | + if ($fileNamePosition = strpos($remote, '/index.php')) { |
|
| 416 | + $remote = substr($remote, 0, $fileNamePosition); |
|
| 417 | + } |
|
| 418 | + $remote = rtrim($remote, '/'); |
|
| 419 | + |
|
| 420 | + return $remote; |
|
| 421 | + } |
|
| 422 | + |
|
| 423 | + /** |
|
| 424 | + * @NoAdminRequired |
|
| 425 | + * |
|
| 426 | + * @param string $search |
|
| 427 | + * @param string $itemType |
|
| 428 | + * @param int $page |
|
| 429 | + * @param int $perPage |
|
| 430 | + * @param int|int[] $shareType |
|
| 431 | + * @param bool $lookup |
|
| 432 | + * @return DataResponse |
|
| 433 | + * @throws OCSBadRequestException |
|
| 434 | + */ |
|
| 435 | + public function search($search = '', $itemType = null, $page = 1, $perPage = 200, $shareType = null, $lookup = true) { |
|
| 436 | + |
|
| 437 | + // only search for string larger than a given threshold |
|
| 438 | + $threshold = (int)$this->config->getSystemValue('sharing.minSearchStringLength', 0); |
|
| 439 | + if (strlen($search) < $threshold) { |
|
| 440 | + return new DataResponse($this->result); |
|
| 441 | + } |
|
| 442 | + |
|
| 443 | + // never return more than the max. number of results configured in the config.php |
|
| 444 | + $maxResults = (int)$this->config->getSystemValue('sharing.maxAutocompleteResults', 0); |
|
| 445 | + if ($maxResults > 0) { |
|
| 446 | + $perPage = min($perPage, $maxResults); |
|
| 447 | + } |
|
| 448 | + if ($perPage <= 0) { |
|
| 449 | + throw new OCSBadRequestException('Invalid perPage argument'); |
|
| 450 | + } |
|
| 451 | + if ($page <= 0) { |
|
| 452 | + throw new OCSBadRequestException('Invalid page'); |
|
| 453 | + } |
|
| 454 | + |
|
| 455 | + $shareTypes = [ |
|
| 456 | + Share::SHARE_TYPE_USER, |
|
| 457 | + ]; |
|
| 458 | + |
|
| 459 | + if ($itemType === 'file' || $itemType === 'folder') { |
|
| 460 | + if ($this->shareManager->allowGroupSharing()) { |
|
| 461 | + $shareTypes[] = Share::SHARE_TYPE_GROUP; |
|
| 462 | + } |
|
| 463 | + |
|
| 464 | + if ($this->isRemoteSharingAllowed($itemType)) { |
|
| 465 | + $shareTypes[] = Share::SHARE_TYPE_REMOTE; |
|
| 466 | + } |
|
| 467 | + |
|
| 468 | + if ($this->shareManager->shareProviderExists(Share::SHARE_TYPE_EMAIL)) { |
|
| 469 | + $shareTypes[] = Share::SHARE_TYPE_EMAIL; |
|
| 470 | + } |
|
| 471 | + } else { |
|
| 472 | + $shareTypes[] = Share::SHARE_TYPE_GROUP; |
|
| 473 | + $shareTypes[] = Share::SHARE_TYPE_EMAIL; |
|
| 474 | + } |
|
| 475 | + |
|
| 476 | + if (\OC::$server->getAppManager()->isEnabledForUser('circles') && class_exists('\OCA\Circles\ShareByCircleProvider')) { |
|
| 477 | + $shareTypes[] = Share::SHARE_TYPE_CIRCLE; |
|
| 478 | + } |
|
| 479 | + |
|
| 480 | + if (isset($_GET['shareType']) && is_array($_GET['shareType'])) { |
|
| 481 | + $shareTypes = array_intersect($shareTypes, $_GET['shareType']); |
|
| 482 | + sort($shareTypes); |
|
| 483 | + } else if (is_numeric($shareType)) { |
|
| 484 | + $shareTypes = array_intersect($shareTypes, [(int) $shareType]); |
|
| 485 | + sort($shareTypes); |
|
| 486 | + } |
|
| 487 | + |
|
| 488 | + $this->shareWithGroupOnly = $this->config->getAppValue('core', 'shareapi_only_share_with_group_members', 'no') === 'yes'; |
|
| 489 | + $this->shareeEnumeration = $this->config->getAppValue('core', 'shareapi_allow_share_dialog_user_enumeration', 'yes') === 'yes'; |
|
| 490 | + $this->limit = (int) $perPage; |
|
| 491 | + $this->offset = $perPage * ($page - 1); |
|
| 492 | + |
|
| 493 | + return $this->searchSharees($search, $itemType, $shareTypes, $page, $perPage, $lookup); |
|
| 494 | + } |
|
| 495 | + |
|
| 496 | + /** |
|
| 497 | + * Method to get out the static call for better testing |
|
| 498 | + * |
|
| 499 | + * @param string $itemType |
|
| 500 | + * @return bool |
|
| 501 | + */ |
|
| 502 | + protected function isRemoteSharingAllowed($itemType) { |
|
| 503 | + try { |
|
| 504 | + $backend = Share::getBackend($itemType); |
|
| 505 | + return $backend->isShareTypeAllowed(Share::SHARE_TYPE_REMOTE); |
|
| 506 | + } catch (\Exception $e) { |
|
| 507 | + return false; |
|
| 508 | + } |
|
| 509 | + } |
|
| 510 | + |
|
| 511 | + /** |
|
| 512 | + * Testable search function that does not need globals |
|
| 513 | + * |
|
| 514 | + * @param string $search |
|
| 515 | + * @param string $itemType |
|
| 516 | + * @param array $shareTypes |
|
| 517 | + * @param int $page |
|
| 518 | + * @param int $perPage |
|
| 519 | + * @param bool $lookup |
|
| 520 | + * @return DataResponse |
|
| 521 | + * @throws OCSBadRequestException |
|
| 522 | + */ |
|
| 523 | + protected function searchSharees($search, $itemType, array $shareTypes, $page, $perPage, $lookup) { |
|
| 524 | + // Verify arguments |
|
| 525 | + if ($itemType === null) { |
|
| 526 | + throw new OCSBadRequestException('Missing itemType'); |
|
| 527 | + } |
|
| 528 | + |
|
| 529 | + // Get users |
|
| 530 | + if (in_array(Share::SHARE_TYPE_USER, $shareTypes)) { |
|
| 531 | + $this->getUsers($search); |
|
| 532 | + } |
|
| 533 | + |
|
| 534 | + // Get groups |
|
| 535 | + if (in_array(Share::SHARE_TYPE_GROUP, $shareTypes)) { |
|
| 536 | + $this->getGroups($search); |
|
| 537 | + } |
|
| 538 | + |
|
| 539 | + // Get circles |
|
| 540 | + if (in_array(Share::SHARE_TYPE_CIRCLE, $shareTypes)) { |
|
| 541 | + $this->getCircles($search); |
|
| 542 | + } |
|
| 543 | + |
|
| 544 | + |
|
| 545 | + // Get remote |
|
| 546 | + $remoteResults = ['results' => [], 'exact' => [], 'exactIdMatch' => false]; |
|
| 547 | + if (in_array(Share::SHARE_TYPE_REMOTE, $shareTypes)) { |
|
| 548 | + $remoteResults = $this->getRemote($search); |
|
| 549 | + } |
|
| 550 | + |
|
| 551 | + // Get emails |
|
| 552 | + $mailResults = ['results' => [], 'exact' => [], 'exactIdMatch' => false]; |
|
| 553 | + if (in_array(Share::SHARE_TYPE_EMAIL, $shareTypes)) { |
|
| 554 | + $mailResults = $this->getEmail($search); |
|
| 555 | + } |
|
| 556 | + |
|
| 557 | + // Get from lookup server |
|
| 558 | + if ($lookup) { |
|
| 559 | + $this->getLookup($search); |
|
| 560 | + } |
|
| 561 | + |
|
| 562 | + // if we have a exact match, either for the federated cloud id or for the |
|
| 563 | + // email address we only return the exact match. It is highly unlikely |
|
| 564 | + // that the exact same email address and federated cloud id exists |
|
| 565 | + if ($mailResults['exactIdMatch'] && !$remoteResults['exactIdMatch']) { |
|
| 566 | + $this->result['emails'] = $mailResults['results']; |
|
| 567 | + $this->result['exact']['emails'] = $mailResults['exact']; |
|
| 568 | + } else if (!$mailResults['exactIdMatch'] && $remoteResults['exactIdMatch']) { |
|
| 569 | + $this->result['remotes'] = $remoteResults['results']; |
|
| 570 | + $this->result['exact']['remotes'] = $remoteResults['exact']; |
|
| 571 | + } else { |
|
| 572 | + $this->result['remotes'] = $remoteResults['results']; |
|
| 573 | + $this->result['exact']['remotes'] = $remoteResults['exact']; |
|
| 574 | + $this->result['emails'] = $mailResults['results']; |
|
| 575 | + $this->result['exact']['emails'] = $mailResults['exact']; |
|
| 576 | + } |
|
| 577 | + |
|
| 578 | + $response = new DataResponse($this->result); |
|
| 579 | + |
|
| 580 | + if (sizeof($this->reachedEndFor) < 3) { |
|
| 581 | + $response->addHeader('Link', $this->getPaginationLink($page, [ |
|
| 582 | + 'search' => $search, |
|
| 583 | + 'itemType' => $itemType, |
|
| 584 | + 'shareType' => $shareTypes, |
|
| 585 | + 'perPage' => $perPage, |
|
| 586 | + ])); |
|
| 587 | + } |
|
| 588 | + |
|
| 589 | + return $response; |
|
| 590 | + } |
|
| 591 | + |
|
| 592 | + /** |
|
| 593 | + * @param string $search |
|
| 594 | + * @return array |
|
| 595 | + */ |
|
| 596 | + protected function getEmail($search) { |
|
| 597 | + $result = ['results' => [], 'exact' => [], 'exactIdMatch' => false]; |
|
| 598 | + |
|
| 599 | + // Search in contacts |
|
| 600 | + //@todo Pagination missing |
|
| 601 | + $addressBookContacts = $this->contactsManager->search($search, ['EMAIL', 'FN']); |
|
| 602 | + $lowerSearch = strtolower($search); |
|
| 603 | + foreach ($addressBookContacts as $contact) { |
|
| 604 | + if (isset($contact['EMAIL'])) { |
|
| 605 | + $emailAddresses = $contact['EMAIL']; |
|
| 606 | + if (!is_array($emailAddresses)) { |
|
| 607 | + $emailAddresses = [$emailAddresses]; |
|
| 608 | + } |
|
| 609 | + foreach ($emailAddresses as $emailAddress) { |
|
| 610 | + $exactEmailMatch = strtolower($emailAddress) === $lowerSearch; |
|
| 611 | + |
|
| 612 | + if (isset($contact['isLocalSystemBook'])) { |
|
| 613 | + if ($exactEmailMatch) { |
|
| 614 | + $cloud = $this->cloudIdManager->resolveCloudId($contact['CLOUD'][0]); |
|
| 615 | + if (!$this->hasUserInResult($cloud->getUser())) { |
|
| 616 | + $this->result['exact']['users'][] = [ |
|
| 617 | + 'label' => $contact['FN'] . " ($emailAddress)", |
|
| 618 | + 'value' => [ |
|
| 619 | + 'shareType' => Share::SHARE_TYPE_USER, |
|
| 620 | + 'shareWith' => $cloud->getUser(), |
|
| 621 | + ], |
|
| 622 | + ]; |
|
| 623 | + } |
|
| 624 | + return ['results' => [], 'exact' => [], 'exactIdMatch' => true]; |
|
| 625 | + } |
|
| 626 | + if ($this->shareeEnumeration) { |
|
| 627 | + $cloud = $this->cloudIdManager->resolveCloudId($contact['CLOUD'][0]); |
|
| 628 | + if (!$this->hasUserInResult($cloud->getUser())) { |
|
| 629 | + $this->result['users'][] = [ |
|
| 630 | + 'label' => $contact['FN'] . " ($emailAddress)", |
|
| 631 | + 'value' => [ |
|
| 632 | + 'shareType' => Share::SHARE_TYPE_USER, |
|
| 633 | + 'shareWith' => $cloud->getUser(), |
|
| 634 | + ], |
|
| 635 | + ]; |
|
| 636 | + } |
|
| 637 | + } |
|
| 638 | + continue; |
|
| 639 | + } |
|
| 640 | + |
|
| 641 | + if ($exactEmailMatch || strtolower($contact['FN']) === $lowerSearch) { |
|
| 642 | + if ($exactEmailMatch) { |
|
| 643 | + $result['exactIdMatch'] = true; |
|
| 644 | + } |
|
| 645 | + $result['exact'][] = [ |
|
| 646 | + 'label' => $contact['FN'] . " ($emailAddress)", |
|
| 647 | + 'value' => [ |
|
| 648 | + 'shareType' => Share::SHARE_TYPE_EMAIL, |
|
| 649 | + 'shareWith' => $emailAddress, |
|
| 650 | + ], |
|
| 651 | + ]; |
|
| 652 | + } else { |
|
| 653 | + $result['results'][] = [ |
|
| 654 | + 'label' => $contact['FN'] . " ($emailAddress)", |
|
| 655 | + 'value' => [ |
|
| 656 | + 'shareType' => Share::SHARE_TYPE_EMAIL, |
|
| 657 | + 'shareWith' => $emailAddress, |
|
| 658 | + ], |
|
| 659 | + ]; |
|
| 660 | + } |
|
| 661 | + } |
|
| 662 | + } |
|
| 663 | + } |
|
| 664 | + |
|
| 665 | + if (!$this->shareeEnumeration) { |
|
| 666 | + $result['results'] = []; |
|
| 667 | + } |
|
| 668 | + |
|
| 669 | + if (!$result['exactIdMatch'] && filter_var($search, FILTER_VALIDATE_EMAIL)) { |
|
| 670 | + $result['exact'][] = [ |
|
| 671 | + 'label' => $search, |
|
| 672 | + 'value' => [ |
|
| 673 | + 'shareType' => Share::SHARE_TYPE_EMAIL, |
|
| 674 | + 'shareWith' => $search, |
|
| 675 | + ], |
|
| 676 | + ]; |
|
| 677 | + } |
|
| 678 | + |
|
| 679 | + $this->reachedEndFor[] = 'emails'; |
|
| 680 | + |
|
| 681 | + return $result; |
|
| 682 | + } |
|
| 683 | + |
|
| 684 | + protected function getLookup($search) { |
|
| 685 | + $isEnabled = $this->config->getAppValue('files_sharing', 'lookupServerEnabled', 'no'); |
|
| 686 | + $lookupServerUrl = $this->config->getSystemValue('lookup_server', 'https://lookup.nextcloud.com'); |
|
| 687 | + $lookupServerUrl = rtrim($lookupServerUrl, '/'); |
|
| 688 | + $result = []; |
|
| 689 | + |
|
| 690 | + if($isEnabled === 'yes') { |
|
| 691 | + try { |
|
| 692 | + $client = $this->clientService->newClient(); |
|
| 693 | + $response = $client->get( |
|
| 694 | + $lookupServerUrl . '/users?search=' . urlencode($search), |
|
| 695 | + [ |
|
| 696 | + 'timeout' => 10, |
|
| 697 | + 'connect_timeout' => 3, |
|
| 698 | + ] |
|
| 699 | + ); |
|
| 700 | + |
|
| 701 | + $body = json_decode($response->getBody(), true); |
|
| 702 | + |
|
| 703 | + $result = []; |
|
| 704 | + foreach ($body as $lookup) { |
|
| 705 | + $result[] = [ |
|
| 706 | + 'label' => $lookup['federationId'], |
|
| 707 | + 'value' => [ |
|
| 708 | + 'shareType' => Share::SHARE_TYPE_REMOTE, |
|
| 709 | + 'shareWith' => $lookup['federationId'], |
|
| 710 | + ], |
|
| 711 | + 'extra' => $lookup, |
|
| 712 | + ]; |
|
| 713 | + } |
|
| 714 | + } catch (\Exception $e) {} |
|
| 715 | + } |
|
| 716 | + |
|
| 717 | + $this->result['lookup'] = $result; |
|
| 718 | + } |
|
| 719 | + |
|
| 720 | + /** |
|
| 721 | + * Check if a given user is already part of the result |
|
| 722 | + * |
|
| 723 | + * @param string $userId |
|
| 724 | + * @return bool |
|
| 725 | + */ |
|
| 726 | + protected function hasUserInResult($userId) { |
|
| 727 | + foreach ($this->result['exact']['users'] as $result) { |
|
| 728 | + if ($result['value']['shareWith'] === $userId) { |
|
| 729 | + return true; |
|
| 730 | + } |
|
| 731 | + } |
|
| 732 | + |
|
| 733 | + foreach ($this->result['users'] as $result) { |
|
| 734 | + if ($result['value']['shareWith'] === $userId) { |
|
| 735 | + return true; |
|
| 736 | + } |
|
| 737 | + } |
|
| 738 | + |
|
| 739 | + return false; |
|
| 740 | + } |
|
| 741 | + |
|
| 742 | + /** |
|
| 743 | + * Generates a bunch of pagination links for the current page |
|
| 744 | + * |
|
| 745 | + * @param int $page Current page |
|
| 746 | + * @param array $params Parameters for the URL |
|
| 747 | + * @return string |
|
| 748 | + */ |
|
| 749 | + protected function getPaginationLink($page, array $params) { |
|
| 750 | + if ($this->isV2()) { |
|
| 751 | + $url = $this->urlGenerator->getAbsoluteURL('/ocs/v2.php/apps/files_sharing/api/v1/sharees') . '?'; |
|
| 752 | + } else { |
|
| 753 | + $url = $this->urlGenerator->getAbsoluteURL('/ocs/v1.php/apps/files_sharing/api/v1/sharees') . '?'; |
|
| 754 | + } |
|
| 755 | + $params['page'] = $page + 1; |
|
| 756 | + $link = '<' . $url . http_build_query($params) . '>; rel="next"'; |
|
| 757 | + |
|
| 758 | + return $link; |
|
| 759 | + } |
|
| 760 | + |
|
| 761 | + /** |
|
| 762 | + * @return bool |
|
| 763 | + */ |
|
| 764 | + protected function isV2() { |
|
| 765 | + return $this->request->getScriptName() === '/ocs/v2.php'; |
|
| 766 | + } |
|
| 767 | 767 | } |
@@ -53,1017 +53,1017 @@ |
||
| 53 | 53 | */ |
| 54 | 54 | class ShareByMailProvider implements IShareProvider { |
| 55 | 55 | |
| 56 | - /** @var IDBConnection */ |
|
| 57 | - private $dbConnection; |
|
| 58 | - |
|
| 59 | - /** @var ILogger */ |
|
| 60 | - private $logger; |
|
| 61 | - |
|
| 62 | - /** @var ISecureRandom */ |
|
| 63 | - private $secureRandom; |
|
| 64 | - |
|
| 65 | - /** @var IUserManager */ |
|
| 66 | - private $userManager; |
|
| 67 | - |
|
| 68 | - /** @var IRootFolder */ |
|
| 69 | - private $rootFolder; |
|
| 70 | - |
|
| 71 | - /** @var IL10N */ |
|
| 72 | - private $l; |
|
| 73 | - |
|
| 74 | - /** @var IMailer */ |
|
| 75 | - private $mailer; |
|
| 76 | - |
|
| 77 | - /** @var IURLGenerator */ |
|
| 78 | - private $urlGenerator; |
|
| 79 | - |
|
| 80 | - /** @var IManager */ |
|
| 81 | - private $activityManager; |
|
| 82 | - |
|
| 83 | - /** @var SettingsManager */ |
|
| 84 | - private $settingsManager; |
|
| 85 | - |
|
| 86 | - /** @var Defaults */ |
|
| 87 | - private $defaults; |
|
| 88 | - |
|
| 89 | - /** @var IHasher */ |
|
| 90 | - private $hasher; |
|
| 91 | - |
|
| 92 | - /** @var CapabilitiesManager */ |
|
| 93 | - private $capabilitiesManager; |
|
| 94 | - |
|
| 95 | - /** |
|
| 96 | - * Return the identifier of this provider. |
|
| 97 | - * |
|
| 98 | - * @return string Containing only [a-zA-Z0-9] |
|
| 99 | - */ |
|
| 100 | - public function identifier() { |
|
| 101 | - return 'ocMailShare'; |
|
| 102 | - } |
|
| 103 | - |
|
| 104 | - /** |
|
| 105 | - * DefaultShareProvider constructor. |
|
| 106 | - * |
|
| 107 | - * @param IDBConnection $connection |
|
| 108 | - * @param ISecureRandom $secureRandom |
|
| 109 | - * @param IUserManager $userManager |
|
| 110 | - * @param IRootFolder $rootFolder |
|
| 111 | - * @param IL10N $l |
|
| 112 | - * @param ILogger $logger |
|
| 113 | - * @param IMailer $mailer |
|
| 114 | - * @param IURLGenerator $urlGenerator |
|
| 115 | - * @param IManager $activityManager |
|
| 116 | - * @param SettingsManager $settingsManager |
|
| 117 | - * @param Defaults $defaults |
|
| 118 | - * @param IHasher $hasher |
|
| 119 | - * @param CapabilitiesManager $capabilitiesManager |
|
| 120 | - */ |
|
| 121 | - public function __construct( |
|
| 122 | - IDBConnection $connection, |
|
| 123 | - ISecureRandom $secureRandom, |
|
| 124 | - IUserManager $userManager, |
|
| 125 | - IRootFolder $rootFolder, |
|
| 126 | - IL10N $l, |
|
| 127 | - ILogger $logger, |
|
| 128 | - IMailer $mailer, |
|
| 129 | - IURLGenerator $urlGenerator, |
|
| 130 | - IManager $activityManager, |
|
| 131 | - SettingsManager $settingsManager, |
|
| 132 | - Defaults $defaults, |
|
| 133 | - IHasher $hasher, |
|
| 134 | - CapabilitiesManager $capabilitiesManager |
|
| 135 | - ) { |
|
| 136 | - $this->dbConnection = $connection; |
|
| 137 | - $this->secureRandom = $secureRandom; |
|
| 138 | - $this->userManager = $userManager; |
|
| 139 | - $this->rootFolder = $rootFolder; |
|
| 140 | - $this->l = $l; |
|
| 141 | - $this->logger = $logger; |
|
| 142 | - $this->mailer = $mailer; |
|
| 143 | - $this->urlGenerator = $urlGenerator; |
|
| 144 | - $this->activityManager = $activityManager; |
|
| 145 | - $this->settingsManager = $settingsManager; |
|
| 146 | - $this->defaults = $defaults; |
|
| 147 | - $this->hasher = $hasher; |
|
| 148 | - $this->capabilitiesManager = $capabilitiesManager; |
|
| 149 | - } |
|
| 150 | - |
|
| 151 | - /** |
|
| 152 | - * Share a path |
|
| 153 | - * |
|
| 154 | - * @param IShare $share |
|
| 155 | - * @return IShare The share object |
|
| 156 | - * @throws ShareNotFound |
|
| 157 | - * @throws \Exception |
|
| 158 | - */ |
|
| 159 | - public function create(IShare $share) { |
|
| 160 | - |
|
| 161 | - $shareWith = $share->getSharedWith(); |
|
| 162 | - /* |
|
| 56 | + /** @var IDBConnection */ |
|
| 57 | + private $dbConnection; |
|
| 58 | + |
|
| 59 | + /** @var ILogger */ |
|
| 60 | + private $logger; |
|
| 61 | + |
|
| 62 | + /** @var ISecureRandom */ |
|
| 63 | + private $secureRandom; |
|
| 64 | + |
|
| 65 | + /** @var IUserManager */ |
|
| 66 | + private $userManager; |
|
| 67 | + |
|
| 68 | + /** @var IRootFolder */ |
|
| 69 | + private $rootFolder; |
|
| 70 | + |
|
| 71 | + /** @var IL10N */ |
|
| 72 | + private $l; |
|
| 73 | + |
|
| 74 | + /** @var IMailer */ |
|
| 75 | + private $mailer; |
|
| 76 | + |
|
| 77 | + /** @var IURLGenerator */ |
|
| 78 | + private $urlGenerator; |
|
| 79 | + |
|
| 80 | + /** @var IManager */ |
|
| 81 | + private $activityManager; |
|
| 82 | + |
|
| 83 | + /** @var SettingsManager */ |
|
| 84 | + private $settingsManager; |
|
| 85 | + |
|
| 86 | + /** @var Defaults */ |
|
| 87 | + private $defaults; |
|
| 88 | + |
|
| 89 | + /** @var IHasher */ |
|
| 90 | + private $hasher; |
|
| 91 | + |
|
| 92 | + /** @var CapabilitiesManager */ |
|
| 93 | + private $capabilitiesManager; |
|
| 94 | + |
|
| 95 | + /** |
|
| 96 | + * Return the identifier of this provider. |
|
| 97 | + * |
|
| 98 | + * @return string Containing only [a-zA-Z0-9] |
|
| 99 | + */ |
|
| 100 | + public function identifier() { |
|
| 101 | + return 'ocMailShare'; |
|
| 102 | + } |
|
| 103 | + |
|
| 104 | + /** |
|
| 105 | + * DefaultShareProvider constructor. |
|
| 106 | + * |
|
| 107 | + * @param IDBConnection $connection |
|
| 108 | + * @param ISecureRandom $secureRandom |
|
| 109 | + * @param IUserManager $userManager |
|
| 110 | + * @param IRootFolder $rootFolder |
|
| 111 | + * @param IL10N $l |
|
| 112 | + * @param ILogger $logger |
|
| 113 | + * @param IMailer $mailer |
|
| 114 | + * @param IURLGenerator $urlGenerator |
|
| 115 | + * @param IManager $activityManager |
|
| 116 | + * @param SettingsManager $settingsManager |
|
| 117 | + * @param Defaults $defaults |
|
| 118 | + * @param IHasher $hasher |
|
| 119 | + * @param CapabilitiesManager $capabilitiesManager |
|
| 120 | + */ |
|
| 121 | + public function __construct( |
|
| 122 | + IDBConnection $connection, |
|
| 123 | + ISecureRandom $secureRandom, |
|
| 124 | + IUserManager $userManager, |
|
| 125 | + IRootFolder $rootFolder, |
|
| 126 | + IL10N $l, |
|
| 127 | + ILogger $logger, |
|
| 128 | + IMailer $mailer, |
|
| 129 | + IURLGenerator $urlGenerator, |
|
| 130 | + IManager $activityManager, |
|
| 131 | + SettingsManager $settingsManager, |
|
| 132 | + Defaults $defaults, |
|
| 133 | + IHasher $hasher, |
|
| 134 | + CapabilitiesManager $capabilitiesManager |
|
| 135 | + ) { |
|
| 136 | + $this->dbConnection = $connection; |
|
| 137 | + $this->secureRandom = $secureRandom; |
|
| 138 | + $this->userManager = $userManager; |
|
| 139 | + $this->rootFolder = $rootFolder; |
|
| 140 | + $this->l = $l; |
|
| 141 | + $this->logger = $logger; |
|
| 142 | + $this->mailer = $mailer; |
|
| 143 | + $this->urlGenerator = $urlGenerator; |
|
| 144 | + $this->activityManager = $activityManager; |
|
| 145 | + $this->settingsManager = $settingsManager; |
|
| 146 | + $this->defaults = $defaults; |
|
| 147 | + $this->hasher = $hasher; |
|
| 148 | + $this->capabilitiesManager = $capabilitiesManager; |
|
| 149 | + } |
|
| 150 | + |
|
| 151 | + /** |
|
| 152 | + * Share a path |
|
| 153 | + * |
|
| 154 | + * @param IShare $share |
|
| 155 | + * @return IShare The share object |
|
| 156 | + * @throws ShareNotFound |
|
| 157 | + * @throws \Exception |
|
| 158 | + */ |
|
| 159 | + public function create(IShare $share) { |
|
| 160 | + |
|
| 161 | + $shareWith = $share->getSharedWith(); |
|
| 162 | + /* |
|
| 163 | 163 | * Check if file is not already shared with the remote user |
| 164 | 164 | */ |
| 165 | - $alreadyShared = $this->getSharedWith($shareWith, \OCP\Share::SHARE_TYPE_EMAIL, $share->getNode(), 1, 0); |
|
| 166 | - if (!empty($alreadyShared)) { |
|
| 167 | - $message = 'Sharing %s failed, this item is already shared with %s'; |
|
| 168 | - $message_t = $this->l->t('Sharing %s failed, this item is already shared with %s', array($share->getNode()->getName(), $shareWith)); |
|
| 169 | - $this->logger->debug(sprintf($message, $share->getNode()->getName(), $shareWith), ['app' => 'Federated File Sharing']); |
|
| 170 | - throw new \Exception($message_t); |
|
| 171 | - } |
|
| 172 | - |
|
| 173 | - // if the admin enforces a password for all mail shares we create a |
|
| 174 | - // random password and send it to the recipient |
|
| 175 | - $password = ''; |
|
| 176 | - $passwordEnforced = $this->settingsManager->enforcePasswordProtection(); |
|
| 177 | - if ($passwordEnforced) { |
|
| 178 | - $password = $this->autoGeneratePassword($share); |
|
| 179 | - } |
|
| 180 | - |
|
| 181 | - $shareId = $this->createMailShare($share); |
|
| 182 | - $send = $this->sendPassword($share, $password); |
|
| 183 | - if ($passwordEnforced && $send === false) { |
|
| 184 | - $this->sendPasswordToOwner($share, $password); |
|
| 185 | - } |
|
| 186 | - |
|
| 187 | - $this->createShareActivity($share); |
|
| 188 | - $data = $this->getRawShare($shareId); |
|
| 189 | - |
|
| 190 | - return $this->createShareObject($data); |
|
| 191 | - |
|
| 192 | - } |
|
| 193 | - |
|
| 194 | - /** |
|
| 195 | - * auto generate password in case of password enforcement on mail shares |
|
| 196 | - * |
|
| 197 | - * @param IShare $share |
|
| 198 | - * @return string |
|
| 199 | - * @throws \Exception |
|
| 200 | - */ |
|
| 201 | - protected function autoGeneratePassword($share) { |
|
| 202 | - $initiatorUser = $this->userManager->get($share->getSharedBy()); |
|
| 203 | - $initiatorEMailAddress = ($initiatorUser instanceof IUser) ? $initiatorUser->getEMailAddress() : null; |
|
| 204 | - $allowPasswordByMail = $this->settingsManager->sendPasswordByMail(); |
|
| 205 | - |
|
| 206 | - if ($initiatorEMailAddress === null && !$allowPasswordByMail) { |
|
| 207 | - throw new \Exception( |
|
| 208 | - $this->l->t("We can't send you the auto-generated password. Please set a valid email address in your personal settings and try again.") |
|
| 209 | - ); |
|
| 210 | - } |
|
| 211 | - |
|
| 212 | - $passwordPolicy = $this->getPasswordPolicy(); |
|
| 213 | - $passwordCharset = ISecureRandom::CHAR_LOWER . ISecureRandom::CHAR_UPPER . ISecureRandom::CHAR_DIGITS; |
|
| 214 | - $passwordLength = 8; |
|
| 215 | - if (!empty($passwordPolicy)) { |
|
| 216 | - $passwordLength = (int)$passwordPolicy['minLength'] > 0 ? (int)$passwordPolicy['minLength'] : $passwordLength; |
|
| 217 | - $passwordCharset .= $passwordPolicy['enforceSpecialCharacters'] ? ISecureRandom::CHAR_SYMBOLS : ''; |
|
| 218 | - } |
|
| 219 | - |
|
| 220 | - $password = $this->secureRandom->generate($passwordLength, $passwordCharset); |
|
| 221 | - |
|
| 222 | - $share->setPassword($this->hasher->hash($password)); |
|
| 223 | - |
|
| 224 | - return $password; |
|
| 225 | - } |
|
| 226 | - |
|
| 227 | - /** |
|
| 228 | - * get password policy |
|
| 229 | - * |
|
| 230 | - * @return array |
|
| 231 | - */ |
|
| 232 | - protected function getPasswordPolicy() { |
|
| 233 | - $capabilities = $this->capabilitiesManager->getCapabilities(); |
|
| 234 | - if (isset($capabilities['password_policy'])) { |
|
| 235 | - return $capabilities['password_policy']; |
|
| 236 | - } |
|
| 237 | - |
|
| 238 | - return []; |
|
| 239 | - } |
|
| 240 | - |
|
| 241 | - /** |
|
| 242 | - * create activity if a file/folder was shared by mail |
|
| 243 | - * |
|
| 244 | - * @param IShare $share |
|
| 245 | - */ |
|
| 246 | - protected function createShareActivity(IShare $share) { |
|
| 247 | - |
|
| 248 | - $userFolder = $this->rootFolder->getUserFolder($share->getSharedBy()); |
|
| 249 | - |
|
| 250 | - $this->publishActivity( |
|
| 251 | - Activity::SUBJECT_SHARED_EMAIL_SELF, |
|
| 252 | - [$userFolder->getRelativePath($share->getNode()->getPath()), $share->getSharedWith()], |
|
| 253 | - $share->getSharedBy(), |
|
| 254 | - $share->getNode()->getId(), |
|
| 255 | - $userFolder->getRelativePath($share->getNode()->getPath()) |
|
| 256 | - ); |
|
| 257 | - |
|
| 258 | - if ($share->getShareOwner() !== $share->getSharedBy()) { |
|
| 259 | - $ownerFolder = $this->rootFolder->getUserFolder($share->getShareOwner()); |
|
| 260 | - $fileId = $share->getNode()->getId(); |
|
| 261 | - $nodes = $ownerFolder->getById($fileId); |
|
| 262 | - $ownerPath = $nodes[0]->getPath(); |
|
| 263 | - $this->publishActivity( |
|
| 264 | - Activity::SUBJECT_SHARED_EMAIL_BY, |
|
| 265 | - [$ownerFolder->getRelativePath($ownerPath), $share->getSharedWith(), $share->getSharedBy()], |
|
| 266 | - $share->getShareOwner(), |
|
| 267 | - $fileId, |
|
| 268 | - $ownerFolder->getRelativePath($ownerPath) |
|
| 269 | - ); |
|
| 270 | - } |
|
| 271 | - |
|
| 272 | - } |
|
| 273 | - |
|
| 274 | - /** |
|
| 275 | - * create activity if a file/folder was shared by mail |
|
| 276 | - * |
|
| 277 | - * @param IShare $share |
|
| 278 | - * @param string $sharedWith |
|
| 279 | - * @param bool $sendToSelf |
|
| 280 | - */ |
|
| 281 | - protected function createPasswordSendActivity(IShare $share, $sharedWith, $sendToSelf) { |
|
| 282 | - |
|
| 283 | - $userFolder = $this->rootFolder->getUserFolder($share->getSharedBy()); |
|
| 284 | - |
|
| 285 | - if ($sendToSelf) { |
|
| 286 | - $this->publishActivity( |
|
| 287 | - Activity::SUBJECT_SHARED_EMAIL_PASSWORD_SEND_SELF, |
|
| 288 | - [$userFolder->getRelativePath($share->getNode()->getPath())], |
|
| 289 | - $share->getSharedBy(), |
|
| 290 | - $share->getNode()->getId(), |
|
| 291 | - $userFolder->getRelativePath($share->getNode()->getPath()) |
|
| 292 | - ); |
|
| 293 | - } else { |
|
| 294 | - $this->publishActivity( |
|
| 295 | - Activity::SUBJECT_SHARED_EMAIL_PASSWORD_SEND, |
|
| 296 | - [$userFolder->getRelativePath($share->getNode()->getPath()), $sharedWith], |
|
| 297 | - $share->getSharedBy(), |
|
| 298 | - $share->getNode()->getId(), |
|
| 299 | - $userFolder->getRelativePath($share->getNode()->getPath()) |
|
| 300 | - ); |
|
| 301 | - } |
|
| 302 | - } |
|
| 303 | - |
|
| 304 | - |
|
| 305 | - /** |
|
| 306 | - * publish activity if a file/folder was shared by mail |
|
| 307 | - * |
|
| 308 | - * @param $subject |
|
| 309 | - * @param $parameters |
|
| 310 | - * @param $affectedUser |
|
| 311 | - * @param $fileId |
|
| 312 | - * @param $filePath |
|
| 313 | - */ |
|
| 314 | - protected function publishActivity($subject, $parameters, $affectedUser, $fileId, $filePath) { |
|
| 315 | - $event = $this->activityManager->generateEvent(); |
|
| 316 | - $event->setApp('sharebymail') |
|
| 317 | - ->setType('shared') |
|
| 318 | - ->setSubject($subject, $parameters) |
|
| 319 | - ->setAffectedUser($affectedUser) |
|
| 320 | - ->setObject('files', $fileId, $filePath); |
|
| 321 | - $this->activityManager->publish($event); |
|
| 322 | - |
|
| 323 | - } |
|
| 324 | - |
|
| 325 | - /** |
|
| 326 | - * @param IShare $share |
|
| 327 | - * @return int |
|
| 328 | - * @throws \Exception |
|
| 329 | - */ |
|
| 330 | - protected function createMailShare(IShare $share) { |
|
| 331 | - $share->setToken($this->generateToken()); |
|
| 332 | - $shareId = $this->addShareToDB( |
|
| 333 | - $share->getNodeId(), |
|
| 334 | - $share->getNodeType(), |
|
| 335 | - $share->getSharedWith(), |
|
| 336 | - $share->getSharedBy(), |
|
| 337 | - $share->getShareOwner(), |
|
| 338 | - $share->getPermissions(), |
|
| 339 | - $share->getToken(), |
|
| 340 | - $share->getPassword() |
|
| 341 | - ); |
|
| 342 | - |
|
| 343 | - try { |
|
| 344 | - $link = $this->urlGenerator->linkToRouteAbsolute('files_sharing.sharecontroller.showShare', |
|
| 345 | - ['token' => $share->getToken()]); |
|
| 346 | - $this->sendMailNotification( |
|
| 347 | - $share->getNode()->getName(), |
|
| 348 | - $link, |
|
| 349 | - $share->getSharedBy(), |
|
| 350 | - $share->getSharedWith() |
|
| 351 | - ); |
|
| 352 | - } catch (HintException $hintException) { |
|
| 353 | - $this->logger->error('Failed to send share by mail: ' . $hintException->getMessage()); |
|
| 354 | - $this->removeShareFromTable($shareId); |
|
| 355 | - throw $hintException; |
|
| 356 | - } catch (\Exception $e) { |
|
| 357 | - $this->logger->error('Failed to send share by email: ' . $e->getMessage()); |
|
| 358 | - $this->removeShareFromTable($shareId); |
|
| 359 | - throw new HintException('Failed to send share by mail', |
|
| 360 | - $this->l->t('Failed to send share by email')); |
|
| 361 | - } |
|
| 362 | - |
|
| 363 | - return $shareId; |
|
| 364 | - |
|
| 365 | - } |
|
| 366 | - |
|
| 367 | - /** |
|
| 368 | - * @param string $filename |
|
| 369 | - * @param string $link |
|
| 370 | - * @param string $initiator |
|
| 371 | - * @param string $shareWith |
|
| 372 | - * @throws \Exception If mail couldn't be sent |
|
| 373 | - */ |
|
| 374 | - protected function sendMailNotification($filename, |
|
| 375 | - $link, |
|
| 376 | - $initiator, |
|
| 377 | - $shareWith) { |
|
| 378 | - $initiatorUser = $this->userManager->get($initiator); |
|
| 379 | - $initiatorDisplayName = ($initiatorUser instanceof IUser) ? $initiatorUser->getDisplayName() : $initiator; |
|
| 380 | - $subject = (string)$this->l->t('%s shared »%s« with you', array($initiatorDisplayName, $filename)); |
|
| 381 | - |
|
| 382 | - $message = $this->mailer->createMessage(); |
|
| 383 | - |
|
| 384 | - $emailTemplate = $this->mailer->createEMailTemplate(); |
|
| 385 | - |
|
| 386 | - $emailTemplate->addHeader(); |
|
| 387 | - $emailTemplate->addHeading($this->l->t('%s shared »%s« with you', [$initiatorDisplayName, $filename]), false); |
|
| 388 | - $text = $this->l->t('%s shared »%s« with you.', [$initiatorDisplayName, $filename]); |
|
| 389 | - |
|
| 390 | - $emailTemplate->addBodyText( |
|
| 391 | - $text . ' ' . $this->l->t('Click the button below to open it.'), |
|
| 392 | - $text |
|
| 393 | - ); |
|
| 394 | - $emailTemplate->addBodyButton( |
|
| 395 | - $this->l->t('Open »%s«', [$filename]), |
|
| 396 | - $link |
|
| 397 | - ); |
|
| 398 | - |
|
| 399 | - $message->setTo([$shareWith]); |
|
| 400 | - |
|
| 401 | - // The "From" contains the sharers name |
|
| 402 | - $instanceName = $this->defaults->getName(); |
|
| 403 | - $senderName = $this->l->t( |
|
| 404 | - '%s via %s', |
|
| 405 | - [ |
|
| 406 | - $initiatorDisplayName, |
|
| 407 | - $instanceName |
|
| 408 | - ] |
|
| 409 | - ); |
|
| 410 | - $message->setFrom([\OCP\Util::getDefaultEmailAddress($instanceName) => $senderName]); |
|
| 411 | - |
|
| 412 | - // The "Reply-To" is set to the sharer if an mail address is configured |
|
| 413 | - // also the default footer contains a "Do not reply" which needs to be adjusted. |
|
| 414 | - $initiatorEmail = $initiatorUser->getEMailAddress(); |
|
| 415 | - if($initiatorEmail !== null) { |
|
| 416 | - $message->setReplyTo([$initiatorEmail => $initiatorDisplayName]); |
|
| 417 | - $emailTemplate->addFooter($instanceName . ' - ' . $this->defaults->getSlogan()); |
|
| 418 | - } else { |
|
| 419 | - $emailTemplate->addFooter(); |
|
| 420 | - } |
|
| 421 | - |
|
| 422 | - $message->setSubject($subject); |
|
| 423 | - $message->setPlainBody($emailTemplate->renderText()); |
|
| 424 | - $message->setHtmlBody($emailTemplate->renderHtml()); |
|
| 425 | - $this->mailer->send($message); |
|
| 426 | - } |
|
| 427 | - |
|
| 428 | - /** |
|
| 429 | - * send password to recipient of a mail share |
|
| 430 | - * |
|
| 431 | - * @param IShare $share |
|
| 432 | - * @param string $password |
|
| 433 | - * @return bool |
|
| 434 | - */ |
|
| 435 | - protected function sendPassword(IShare $share, $password) { |
|
| 436 | - |
|
| 437 | - $filename = $share->getNode()->getName(); |
|
| 438 | - $initiator = $share->getSharedBy(); |
|
| 439 | - $shareWith = $share->getSharedWith(); |
|
| 440 | - |
|
| 441 | - if ($password === '' || $this->settingsManager->sendPasswordByMail() === false) { |
|
| 442 | - return false; |
|
| 443 | - } |
|
| 444 | - |
|
| 445 | - $initiatorUser = $this->userManager->get($initiator); |
|
| 446 | - $initiatorDisplayName = ($initiatorUser instanceof IUser) ? $initiatorUser->getDisplayName() : $initiator; |
|
| 447 | - $initiatorEmailAddress = ($initiatorUser instanceof IUser) ? $initiatorUser->getEMailAddress() : null; |
|
| 448 | - |
|
| 449 | - $subject = (string)$this->l->t('Password to access »%s« shared to you by %s', [$filename, $initiatorDisplayName]); |
|
| 450 | - $plainBodyPart = $this->l->t("%s shared »%s« with you.\nYou should have already received a separate mail with a link to access it.\n", [$initiatorDisplayName, $filename]); |
|
| 451 | - $htmlBodyPart = $this->l->t('%s shared »%s« with you. You should have already received a separate mail with a link to access it.', [$initiatorDisplayName, $filename]); |
|
| 452 | - |
|
| 453 | - $message = $this->mailer->createMessage(); |
|
| 454 | - |
|
| 455 | - $emailTemplate = $this->mailer->createEMailTemplate(); |
|
| 456 | - $emailTemplate->addHeader(); |
|
| 457 | - $emailTemplate->addHeading($this->l->t('Password to access »%s«', [$filename]), false); |
|
| 458 | - $emailTemplate->addBodyText($htmlBodyPart, $plainBodyPart); |
|
| 459 | - $emailTemplate->addBodyText($this->l->t('It is protected with the following password: %s', [$password])); |
|
| 460 | - |
|
| 461 | - // The "From" contains the sharers name |
|
| 462 | - $instanceName = $this->defaults->getName(); |
|
| 463 | - $senderName = $this->l->t( |
|
| 464 | - '%s via %s', |
|
| 465 | - [ |
|
| 466 | - $initiatorDisplayName, |
|
| 467 | - $instanceName |
|
| 468 | - ] |
|
| 469 | - ); |
|
| 470 | - $message->setFrom([\OCP\Util::getDefaultEmailAddress($instanceName) => $senderName]); |
|
| 471 | - if ($initiatorEmailAddress !== null) { |
|
| 472 | - $message->setReplyTo([$initiatorEmailAddress => $initiatorDisplayName]); |
|
| 473 | - $emailTemplate->addFooter($instanceName . ' - ' . $this->defaults->getSlogan()); |
|
| 474 | - } else { |
|
| 475 | - $emailTemplate->addFooter(); |
|
| 476 | - } |
|
| 477 | - |
|
| 478 | - $message->setTo([$shareWith]); |
|
| 479 | - $message->setSubject($subject); |
|
| 480 | - $message->setBody($emailTemplate->renderText(), 'text/plain'); |
|
| 481 | - $message->setHtmlBody($emailTemplate->renderHtml()); |
|
| 482 | - $this->mailer->send($message); |
|
| 483 | - |
|
| 484 | - $this->createPasswordSendActivity($share, $shareWith, false); |
|
| 485 | - |
|
| 486 | - return true; |
|
| 487 | - } |
|
| 488 | - |
|
| 489 | - /** |
|
| 490 | - * send auto generated password to the owner. This happens if the admin enforces |
|
| 491 | - * a password for mail shares and forbid to send the password by mail to the recipient |
|
| 492 | - * |
|
| 493 | - * @param IShare $share |
|
| 494 | - * @param string $password |
|
| 495 | - * @return bool |
|
| 496 | - * @throws \Exception |
|
| 497 | - */ |
|
| 498 | - protected function sendPasswordToOwner(IShare $share, $password) { |
|
| 499 | - |
|
| 500 | - $filename = $share->getNode()->getName(); |
|
| 501 | - $initiator = $this->userManager->get($share->getSharedBy()); |
|
| 502 | - $initiatorEMailAddress = ($initiator instanceof IUser) ? $initiator->getEMailAddress() : null; |
|
| 503 | - $initiatorDisplayName = ($initiator instanceof IUser) ? $initiator->getDisplayName() : $share->getSharedBy(); |
|
| 504 | - $shareWith = $share->getSharedWith(); |
|
| 505 | - |
|
| 506 | - if ($initiatorEMailAddress === null) { |
|
| 507 | - throw new \Exception( |
|
| 508 | - $this->l->t("We can't send you the auto-generated password. Please set a valid email address in your personal settings and try again.") |
|
| 509 | - ); |
|
| 510 | - } |
|
| 511 | - |
|
| 512 | - $subject = (string)$this->l->t('Password to access »%s« shared with %s', [$filename, $shareWith]); |
|
| 513 | - $bodyPart = $this->l->t("You just shared »%s« with %s. The share was already send to the recipient. Due to the security policies defined by the administrator of %s each share needs to be protected by password and it is not allowed to send the password directly to the recipient. Therefore you need to forward the password manually to the recipient.", [$filename, $shareWith, $this->defaults->getName()]); |
|
| 514 | - |
|
| 515 | - $message = $this->mailer->createMessage(); |
|
| 516 | - $emailTemplate = $this->mailer->createEMailTemplate(); |
|
| 517 | - |
|
| 518 | - $emailTemplate->addHeader(); |
|
| 519 | - $emailTemplate->addHeading($this->l->t('Password to access »%s«', [$filename]), false); |
|
| 520 | - $emailTemplate->addBodyText($bodyPart); |
|
| 521 | - $emailTemplate->addBodyText($this->l->t('This is the password: %s', [$password])); |
|
| 522 | - $emailTemplate->addBodyText($this->l->t('You can choose a different password at any time in the share dialog.')); |
|
| 523 | - $emailTemplate->addFooter(); |
|
| 524 | - |
|
| 525 | - if ($initiatorEMailAddress) { |
|
| 526 | - $message->setFrom([$initiatorEMailAddress => $initiatorDisplayName]); |
|
| 527 | - } |
|
| 528 | - $message->setTo([$initiatorEMailAddress => $initiatorDisplayName]); |
|
| 529 | - $message->setSubject($subject); |
|
| 530 | - $message->setBody($emailTemplate->renderText(), 'text/plain'); |
|
| 531 | - $message->setHtmlBody($emailTemplate->renderHtml()); |
|
| 532 | - $this->mailer->send($message); |
|
| 533 | - |
|
| 534 | - $this->createPasswordSendActivity($share, $shareWith, true); |
|
| 535 | - |
|
| 536 | - return true; |
|
| 537 | - } |
|
| 538 | - |
|
| 539 | - /** |
|
| 540 | - * generate share token |
|
| 541 | - * |
|
| 542 | - * @return string |
|
| 543 | - */ |
|
| 544 | - protected function generateToken($size = 15) { |
|
| 545 | - $token = $this->secureRandom->generate( |
|
| 546 | - $size, ISecureRandom::CHAR_LOWER . ISecureRandom::CHAR_UPPER . ISecureRandom::CHAR_DIGITS); |
|
| 547 | - return $token; |
|
| 548 | - } |
|
| 549 | - |
|
| 550 | - /** |
|
| 551 | - * Get all children of this share |
|
| 552 | - * |
|
| 553 | - * @param IShare $parent |
|
| 554 | - * @return IShare[] |
|
| 555 | - */ |
|
| 556 | - public function getChildren(IShare $parent) { |
|
| 557 | - $children = []; |
|
| 558 | - |
|
| 559 | - $qb = $this->dbConnection->getQueryBuilder(); |
|
| 560 | - $qb->select('*') |
|
| 561 | - ->from('share') |
|
| 562 | - ->where($qb->expr()->eq('parent', $qb->createNamedParameter($parent->getId()))) |
|
| 563 | - ->andWhere($qb->expr()->eq('share_type', $qb->createNamedParameter(\OCP\Share::SHARE_TYPE_EMAIL))) |
|
| 564 | - ->orderBy('id'); |
|
| 565 | - |
|
| 566 | - $cursor = $qb->execute(); |
|
| 567 | - while($data = $cursor->fetch()) { |
|
| 568 | - $children[] = $this->createShareObject($data); |
|
| 569 | - } |
|
| 570 | - $cursor->closeCursor(); |
|
| 571 | - |
|
| 572 | - return $children; |
|
| 573 | - } |
|
| 574 | - |
|
| 575 | - /** |
|
| 576 | - * add share to the database and return the ID |
|
| 577 | - * |
|
| 578 | - * @param int $itemSource |
|
| 579 | - * @param string $itemType |
|
| 580 | - * @param string $shareWith |
|
| 581 | - * @param string $sharedBy |
|
| 582 | - * @param string $uidOwner |
|
| 583 | - * @param int $permissions |
|
| 584 | - * @param string $token |
|
| 585 | - * @return int |
|
| 586 | - */ |
|
| 587 | - protected function addShareToDB($itemSource, $itemType, $shareWith, $sharedBy, $uidOwner, $permissions, $token, $password) { |
|
| 588 | - $qb = $this->dbConnection->getQueryBuilder(); |
|
| 589 | - $qb->insert('share') |
|
| 590 | - ->setValue('share_type', $qb->createNamedParameter(\OCP\Share::SHARE_TYPE_EMAIL)) |
|
| 591 | - ->setValue('item_type', $qb->createNamedParameter($itemType)) |
|
| 592 | - ->setValue('item_source', $qb->createNamedParameter($itemSource)) |
|
| 593 | - ->setValue('file_source', $qb->createNamedParameter($itemSource)) |
|
| 594 | - ->setValue('share_with', $qb->createNamedParameter($shareWith)) |
|
| 595 | - ->setValue('uid_owner', $qb->createNamedParameter($uidOwner)) |
|
| 596 | - ->setValue('uid_initiator', $qb->createNamedParameter($sharedBy)) |
|
| 597 | - ->setValue('permissions', $qb->createNamedParameter($permissions)) |
|
| 598 | - ->setValue('token', $qb->createNamedParameter($token)) |
|
| 599 | - ->setValue('password', $qb->createNamedParameter($password)) |
|
| 600 | - ->setValue('stime', $qb->createNamedParameter(time())); |
|
| 601 | - |
|
| 602 | - /* |
|
| 165 | + $alreadyShared = $this->getSharedWith($shareWith, \OCP\Share::SHARE_TYPE_EMAIL, $share->getNode(), 1, 0); |
|
| 166 | + if (!empty($alreadyShared)) { |
|
| 167 | + $message = 'Sharing %s failed, this item is already shared with %s'; |
|
| 168 | + $message_t = $this->l->t('Sharing %s failed, this item is already shared with %s', array($share->getNode()->getName(), $shareWith)); |
|
| 169 | + $this->logger->debug(sprintf($message, $share->getNode()->getName(), $shareWith), ['app' => 'Federated File Sharing']); |
|
| 170 | + throw new \Exception($message_t); |
|
| 171 | + } |
|
| 172 | + |
|
| 173 | + // if the admin enforces a password for all mail shares we create a |
|
| 174 | + // random password and send it to the recipient |
|
| 175 | + $password = ''; |
|
| 176 | + $passwordEnforced = $this->settingsManager->enforcePasswordProtection(); |
|
| 177 | + if ($passwordEnforced) { |
|
| 178 | + $password = $this->autoGeneratePassword($share); |
|
| 179 | + } |
|
| 180 | + |
|
| 181 | + $shareId = $this->createMailShare($share); |
|
| 182 | + $send = $this->sendPassword($share, $password); |
|
| 183 | + if ($passwordEnforced && $send === false) { |
|
| 184 | + $this->sendPasswordToOwner($share, $password); |
|
| 185 | + } |
|
| 186 | + |
|
| 187 | + $this->createShareActivity($share); |
|
| 188 | + $data = $this->getRawShare($shareId); |
|
| 189 | + |
|
| 190 | + return $this->createShareObject($data); |
|
| 191 | + |
|
| 192 | + } |
|
| 193 | + |
|
| 194 | + /** |
|
| 195 | + * auto generate password in case of password enforcement on mail shares |
|
| 196 | + * |
|
| 197 | + * @param IShare $share |
|
| 198 | + * @return string |
|
| 199 | + * @throws \Exception |
|
| 200 | + */ |
|
| 201 | + protected function autoGeneratePassword($share) { |
|
| 202 | + $initiatorUser = $this->userManager->get($share->getSharedBy()); |
|
| 203 | + $initiatorEMailAddress = ($initiatorUser instanceof IUser) ? $initiatorUser->getEMailAddress() : null; |
|
| 204 | + $allowPasswordByMail = $this->settingsManager->sendPasswordByMail(); |
|
| 205 | + |
|
| 206 | + if ($initiatorEMailAddress === null && !$allowPasswordByMail) { |
|
| 207 | + throw new \Exception( |
|
| 208 | + $this->l->t("We can't send you the auto-generated password. Please set a valid email address in your personal settings and try again.") |
|
| 209 | + ); |
|
| 210 | + } |
|
| 211 | + |
|
| 212 | + $passwordPolicy = $this->getPasswordPolicy(); |
|
| 213 | + $passwordCharset = ISecureRandom::CHAR_LOWER . ISecureRandom::CHAR_UPPER . ISecureRandom::CHAR_DIGITS; |
|
| 214 | + $passwordLength = 8; |
|
| 215 | + if (!empty($passwordPolicy)) { |
|
| 216 | + $passwordLength = (int)$passwordPolicy['minLength'] > 0 ? (int)$passwordPolicy['minLength'] : $passwordLength; |
|
| 217 | + $passwordCharset .= $passwordPolicy['enforceSpecialCharacters'] ? ISecureRandom::CHAR_SYMBOLS : ''; |
|
| 218 | + } |
|
| 219 | + |
|
| 220 | + $password = $this->secureRandom->generate($passwordLength, $passwordCharset); |
|
| 221 | + |
|
| 222 | + $share->setPassword($this->hasher->hash($password)); |
|
| 223 | + |
|
| 224 | + return $password; |
|
| 225 | + } |
|
| 226 | + |
|
| 227 | + /** |
|
| 228 | + * get password policy |
|
| 229 | + * |
|
| 230 | + * @return array |
|
| 231 | + */ |
|
| 232 | + protected function getPasswordPolicy() { |
|
| 233 | + $capabilities = $this->capabilitiesManager->getCapabilities(); |
|
| 234 | + if (isset($capabilities['password_policy'])) { |
|
| 235 | + return $capabilities['password_policy']; |
|
| 236 | + } |
|
| 237 | + |
|
| 238 | + return []; |
|
| 239 | + } |
|
| 240 | + |
|
| 241 | + /** |
|
| 242 | + * create activity if a file/folder was shared by mail |
|
| 243 | + * |
|
| 244 | + * @param IShare $share |
|
| 245 | + */ |
|
| 246 | + protected function createShareActivity(IShare $share) { |
|
| 247 | + |
|
| 248 | + $userFolder = $this->rootFolder->getUserFolder($share->getSharedBy()); |
|
| 249 | + |
|
| 250 | + $this->publishActivity( |
|
| 251 | + Activity::SUBJECT_SHARED_EMAIL_SELF, |
|
| 252 | + [$userFolder->getRelativePath($share->getNode()->getPath()), $share->getSharedWith()], |
|
| 253 | + $share->getSharedBy(), |
|
| 254 | + $share->getNode()->getId(), |
|
| 255 | + $userFolder->getRelativePath($share->getNode()->getPath()) |
|
| 256 | + ); |
|
| 257 | + |
|
| 258 | + if ($share->getShareOwner() !== $share->getSharedBy()) { |
|
| 259 | + $ownerFolder = $this->rootFolder->getUserFolder($share->getShareOwner()); |
|
| 260 | + $fileId = $share->getNode()->getId(); |
|
| 261 | + $nodes = $ownerFolder->getById($fileId); |
|
| 262 | + $ownerPath = $nodes[0]->getPath(); |
|
| 263 | + $this->publishActivity( |
|
| 264 | + Activity::SUBJECT_SHARED_EMAIL_BY, |
|
| 265 | + [$ownerFolder->getRelativePath($ownerPath), $share->getSharedWith(), $share->getSharedBy()], |
|
| 266 | + $share->getShareOwner(), |
|
| 267 | + $fileId, |
|
| 268 | + $ownerFolder->getRelativePath($ownerPath) |
|
| 269 | + ); |
|
| 270 | + } |
|
| 271 | + |
|
| 272 | + } |
|
| 273 | + |
|
| 274 | + /** |
|
| 275 | + * create activity if a file/folder was shared by mail |
|
| 276 | + * |
|
| 277 | + * @param IShare $share |
|
| 278 | + * @param string $sharedWith |
|
| 279 | + * @param bool $sendToSelf |
|
| 280 | + */ |
|
| 281 | + protected function createPasswordSendActivity(IShare $share, $sharedWith, $sendToSelf) { |
|
| 282 | + |
|
| 283 | + $userFolder = $this->rootFolder->getUserFolder($share->getSharedBy()); |
|
| 284 | + |
|
| 285 | + if ($sendToSelf) { |
|
| 286 | + $this->publishActivity( |
|
| 287 | + Activity::SUBJECT_SHARED_EMAIL_PASSWORD_SEND_SELF, |
|
| 288 | + [$userFolder->getRelativePath($share->getNode()->getPath())], |
|
| 289 | + $share->getSharedBy(), |
|
| 290 | + $share->getNode()->getId(), |
|
| 291 | + $userFolder->getRelativePath($share->getNode()->getPath()) |
|
| 292 | + ); |
|
| 293 | + } else { |
|
| 294 | + $this->publishActivity( |
|
| 295 | + Activity::SUBJECT_SHARED_EMAIL_PASSWORD_SEND, |
|
| 296 | + [$userFolder->getRelativePath($share->getNode()->getPath()), $sharedWith], |
|
| 297 | + $share->getSharedBy(), |
|
| 298 | + $share->getNode()->getId(), |
|
| 299 | + $userFolder->getRelativePath($share->getNode()->getPath()) |
|
| 300 | + ); |
|
| 301 | + } |
|
| 302 | + } |
|
| 303 | + |
|
| 304 | + |
|
| 305 | + /** |
|
| 306 | + * publish activity if a file/folder was shared by mail |
|
| 307 | + * |
|
| 308 | + * @param $subject |
|
| 309 | + * @param $parameters |
|
| 310 | + * @param $affectedUser |
|
| 311 | + * @param $fileId |
|
| 312 | + * @param $filePath |
|
| 313 | + */ |
|
| 314 | + protected function publishActivity($subject, $parameters, $affectedUser, $fileId, $filePath) { |
|
| 315 | + $event = $this->activityManager->generateEvent(); |
|
| 316 | + $event->setApp('sharebymail') |
|
| 317 | + ->setType('shared') |
|
| 318 | + ->setSubject($subject, $parameters) |
|
| 319 | + ->setAffectedUser($affectedUser) |
|
| 320 | + ->setObject('files', $fileId, $filePath); |
|
| 321 | + $this->activityManager->publish($event); |
|
| 322 | + |
|
| 323 | + } |
|
| 324 | + |
|
| 325 | + /** |
|
| 326 | + * @param IShare $share |
|
| 327 | + * @return int |
|
| 328 | + * @throws \Exception |
|
| 329 | + */ |
|
| 330 | + protected function createMailShare(IShare $share) { |
|
| 331 | + $share->setToken($this->generateToken()); |
|
| 332 | + $shareId = $this->addShareToDB( |
|
| 333 | + $share->getNodeId(), |
|
| 334 | + $share->getNodeType(), |
|
| 335 | + $share->getSharedWith(), |
|
| 336 | + $share->getSharedBy(), |
|
| 337 | + $share->getShareOwner(), |
|
| 338 | + $share->getPermissions(), |
|
| 339 | + $share->getToken(), |
|
| 340 | + $share->getPassword() |
|
| 341 | + ); |
|
| 342 | + |
|
| 343 | + try { |
|
| 344 | + $link = $this->urlGenerator->linkToRouteAbsolute('files_sharing.sharecontroller.showShare', |
|
| 345 | + ['token' => $share->getToken()]); |
|
| 346 | + $this->sendMailNotification( |
|
| 347 | + $share->getNode()->getName(), |
|
| 348 | + $link, |
|
| 349 | + $share->getSharedBy(), |
|
| 350 | + $share->getSharedWith() |
|
| 351 | + ); |
|
| 352 | + } catch (HintException $hintException) { |
|
| 353 | + $this->logger->error('Failed to send share by mail: ' . $hintException->getMessage()); |
|
| 354 | + $this->removeShareFromTable($shareId); |
|
| 355 | + throw $hintException; |
|
| 356 | + } catch (\Exception $e) { |
|
| 357 | + $this->logger->error('Failed to send share by email: ' . $e->getMessage()); |
|
| 358 | + $this->removeShareFromTable($shareId); |
|
| 359 | + throw new HintException('Failed to send share by mail', |
|
| 360 | + $this->l->t('Failed to send share by email')); |
|
| 361 | + } |
|
| 362 | + |
|
| 363 | + return $shareId; |
|
| 364 | + |
|
| 365 | + } |
|
| 366 | + |
|
| 367 | + /** |
|
| 368 | + * @param string $filename |
|
| 369 | + * @param string $link |
|
| 370 | + * @param string $initiator |
|
| 371 | + * @param string $shareWith |
|
| 372 | + * @throws \Exception If mail couldn't be sent |
|
| 373 | + */ |
|
| 374 | + protected function sendMailNotification($filename, |
|
| 375 | + $link, |
|
| 376 | + $initiator, |
|
| 377 | + $shareWith) { |
|
| 378 | + $initiatorUser = $this->userManager->get($initiator); |
|
| 379 | + $initiatorDisplayName = ($initiatorUser instanceof IUser) ? $initiatorUser->getDisplayName() : $initiator; |
|
| 380 | + $subject = (string)$this->l->t('%s shared »%s« with you', array($initiatorDisplayName, $filename)); |
|
| 381 | + |
|
| 382 | + $message = $this->mailer->createMessage(); |
|
| 383 | + |
|
| 384 | + $emailTemplate = $this->mailer->createEMailTemplate(); |
|
| 385 | + |
|
| 386 | + $emailTemplate->addHeader(); |
|
| 387 | + $emailTemplate->addHeading($this->l->t('%s shared »%s« with you', [$initiatorDisplayName, $filename]), false); |
|
| 388 | + $text = $this->l->t('%s shared »%s« with you.', [$initiatorDisplayName, $filename]); |
|
| 389 | + |
|
| 390 | + $emailTemplate->addBodyText( |
|
| 391 | + $text . ' ' . $this->l->t('Click the button below to open it.'), |
|
| 392 | + $text |
|
| 393 | + ); |
|
| 394 | + $emailTemplate->addBodyButton( |
|
| 395 | + $this->l->t('Open »%s«', [$filename]), |
|
| 396 | + $link |
|
| 397 | + ); |
|
| 398 | + |
|
| 399 | + $message->setTo([$shareWith]); |
|
| 400 | + |
|
| 401 | + // The "From" contains the sharers name |
|
| 402 | + $instanceName = $this->defaults->getName(); |
|
| 403 | + $senderName = $this->l->t( |
|
| 404 | + '%s via %s', |
|
| 405 | + [ |
|
| 406 | + $initiatorDisplayName, |
|
| 407 | + $instanceName |
|
| 408 | + ] |
|
| 409 | + ); |
|
| 410 | + $message->setFrom([\OCP\Util::getDefaultEmailAddress($instanceName) => $senderName]); |
|
| 411 | + |
|
| 412 | + // The "Reply-To" is set to the sharer if an mail address is configured |
|
| 413 | + // also the default footer contains a "Do not reply" which needs to be adjusted. |
|
| 414 | + $initiatorEmail = $initiatorUser->getEMailAddress(); |
|
| 415 | + if($initiatorEmail !== null) { |
|
| 416 | + $message->setReplyTo([$initiatorEmail => $initiatorDisplayName]); |
|
| 417 | + $emailTemplate->addFooter($instanceName . ' - ' . $this->defaults->getSlogan()); |
|
| 418 | + } else { |
|
| 419 | + $emailTemplate->addFooter(); |
|
| 420 | + } |
|
| 421 | + |
|
| 422 | + $message->setSubject($subject); |
|
| 423 | + $message->setPlainBody($emailTemplate->renderText()); |
|
| 424 | + $message->setHtmlBody($emailTemplate->renderHtml()); |
|
| 425 | + $this->mailer->send($message); |
|
| 426 | + } |
|
| 427 | + |
|
| 428 | + /** |
|
| 429 | + * send password to recipient of a mail share |
|
| 430 | + * |
|
| 431 | + * @param IShare $share |
|
| 432 | + * @param string $password |
|
| 433 | + * @return bool |
|
| 434 | + */ |
|
| 435 | + protected function sendPassword(IShare $share, $password) { |
|
| 436 | + |
|
| 437 | + $filename = $share->getNode()->getName(); |
|
| 438 | + $initiator = $share->getSharedBy(); |
|
| 439 | + $shareWith = $share->getSharedWith(); |
|
| 440 | + |
|
| 441 | + if ($password === '' || $this->settingsManager->sendPasswordByMail() === false) { |
|
| 442 | + return false; |
|
| 443 | + } |
|
| 444 | + |
|
| 445 | + $initiatorUser = $this->userManager->get($initiator); |
|
| 446 | + $initiatorDisplayName = ($initiatorUser instanceof IUser) ? $initiatorUser->getDisplayName() : $initiator; |
|
| 447 | + $initiatorEmailAddress = ($initiatorUser instanceof IUser) ? $initiatorUser->getEMailAddress() : null; |
|
| 448 | + |
|
| 449 | + $subject = (string)$this->l->t('Password to access »%s« shared to you by %s', [$filename, $initiatorDisplayName]); |
|
| 450 | + $plainBodyPart = $this->l->t("%s shared »%s« with you.\nYou should have already received a separate mail with a link to access it.\n", [$initiatorDisplayName, $filename]); |
|
| 451 | + $htmlBodyPart = $this->l->t('%s shared »%s« with you. You should have already received a separate mail with a link to access it.', [$initiatorDisplayName, $filename]); |
|
| 452 | + |
|
| 453 | + $message = $this->mailer->createMessage(); |
|
| 454 | + |
|
| 455 | + $emailTemplate = $this->mailer->createEMailTemplate(); |
|
| 456 | + $emailTemplate->addHeader(); |
|
| 457 | + $emailTemplate->addHeading($this->l->t('Password to access »%s«', [$filename]), false); |
|
| 458 | + $emailTemplate->addBodyText($htmlBodyPart, $plainBodyPart); |
|
| 459 | + $emailTemplate->addBodyText($this->l->t('It is protected with the following password: %s', [$password])); |
|
| 460 | + |
|
| 461 | + // The "From" contains the sharers name |
|
| 462 | + $instanceName = $this->defaults->getName(); |
|
| 463 | + $senderName = $this->l->t( |
|
| 464 | + '%s via %s', |
|
| 465 | + [ |
|
| 466 | + $initiatorDisplayName, |
|
| 467 | + $instanceName |
|
| 468 | + ] |
|
| 469 | + ); |
|
| 470 | + $message->setFrom([\OCP\Util::getDefaultEmailAddress($instanceName) => $senderName]); |
|
| 471 | + if ($initiatorEmailAddress !== null) { |
|
| 472 | + $message->setReplyTo([$initiatorEmailAddress => $initiatorDisplayName]); |
|
| 473 | + $emailTemplate->addFooter($instanceName . ' - ' . $this->defaults->getSlogan()); |
|
| 474 | + } else { |
|
| 475 | + $emailTemplate->addFooter(); |
|
| 476 | + } |
|
| 477 | + |
|
| 478 | + $message->setTo([$shareWith]); |
|
| 479 | + $message->setSubject($subject); |
|
| 480 | + $message->setBody($emailTemplate->renderText(), 'text/plain'); |
|
| 481 | + $message->setHtmlBody($emailTemplate->renderHtml()); |
|
| 482 | + $this->mailer->send($message); |
|
| 483 | + |
|
| 484 | + $this->createPasswordSendActivity($share, $shareWith, false); |
|
| 485 | + |
|
| 486 | + return true; |
|
| 487 | + } |
|
| 488 | + |
|
| 489 | + /** |
|
| 490 | + * send auto generated password to the owner. This happens if the admin enforces |
|
| 491 | + * a password for mail shares and forbid to send the password by mail to the recipient |
|
| 492 | + * |
|
| 493 | + * @param IShare $share |
|
| 494 | + * @param string $password |
|
| 495 | + * @return bool |
|
| 496 | + * @throws \Exception |
|
| 497 | + */ |
|
| 498 | + protected function sendPasswordToOwner(IShare $share, $password) { |
|
| 499 | + |
|
| 500 | + $filename = $share->getNode()->getName(); |
|
| 501 | + $initiator = $this->userManager->get($share->getSharedBy()); |
|
| 502 | + $initiatorEMailAddress = ($initiator instanceof IUser) ? $initiator->getEMailAddress() : null; |
|
| 503 | + $initiatorDisplayName = ($initiator instanceof IUser) ? $initiator->getDisplayName() : $share->getSharedBy(); |
|
| 504 | + $shareWith = $share->getSharedWith(); |
|
| 505 | + |
|
| 506 | + if ($initiatorEMailAddress === null) { |
|
| 507 | + throw new \Exception( |
|
| 508 | + $this->l->t("We can't send you the auto-generated password. Please set a valid email address in your personal settings and try again.") |
|
| 509 | + ); |
|
| 510 | + } |
|
| 511 | + |
|
| 512 | + $subject = (string)$this->l->t('Password to access »%s« shared with %s', [$filename, $shareWith]); |
|
| 513 | + $bodyPart = $this->l->t("You just shared »%s« with %s. The share was already send to the recipient. Due to the security policies defined by the administrator of %s each share needs to be protected by password and it is not allowed to send the password directly to the recipient. Therefore you need to forward the password manually to the recipient.", [$filename, $shareWith, $this->defaults->getName()]); |
|
| 514 | + |
|
| 515 | + $message = $this->mailer->createMessage(); |
|
| 516 | + $emailTemplate = $this->mailer->createEMailTemplate(); |
|
| 517 | + |
|
| 518 | + $emailTemplate->addHeader(); |
|
| 519 | + $emailTemplate->addHeading($this->l->t('Password to access »%s«', [$filename]), false); |
|
| 520 | + $emailTemplate->addBodyText($bodyPart); |
|
| 521 | + $emailTemplate->addBodyText($this->l->t('This is the password: %s', [$password])); |
|
| 522 | + $emailTemplate->addBodyText($this->l->t('You can choose a different password at any time in the share dialog.')); |
|
| 523 | + $emailTemplate->addFooter(); |
|
| 524 | + |
|
| 525 | + if ($initiatorEMailAddress) { |
|
| 526 | + $message->setFrom([$initiatorEMailAddress => $initiatorDisplayName]); |
|
| 527 | + } |
|
| 528 | + $message->setTo([$initiatorEMailAddress => $initiatorDisplayName]); |
|
| 529 | + $message->setSubject($subject); |
|
| 530 | + $message->setBody($emailTemplate->renderText(), 'text/plain'); |
|
| 531 | + $message->setHtmlBody($emailTemplate->renderHtml()); |
|
| 532 | + $this->mailer->send($message); |
|
| 533 | + |
|
| 534 | + $this->createPasswordSendActivity($share, $shareWith, true); |
|
| 535 | + |
|
| 536 | + return true; |
|
| 537 | + } |
|
| 538 | + |
|
| 539 | + /** |
|
| 540 | + * generate share token |
|
| 541 | + * |
|
| 542 | + * @return string |
|
| 543 | + */ |
|
| 544 | + protected function generateToken($size = 15) { |
|
| 545 | + $token = $this->secureRandom->generate( |
|
| 546 | + $size, ISecureRandom::CHAR_LOWER . ISecureRandom::CHAR_UPPER . ISecureRandom::CHAR_DIGITS); |
|
| 547 | + return $token; |
|
| 548 | + } |
|
| 549 | + |
|
| 550 | + /** |
|
| 551 | + * Get all children of this share |
|
| 552 | + * |
|
| 553 | + * @param IShare $parent |
|
| 554 | + * @return IShare[] |
|
| 555 | + */ |
|
| 556 | + public function getChildren(IShare $parent) { |
|
| 557 | + $children = []; |
|
| 558 | + |
|
| 559 | + $qb = $this->dbConnection->getQueryBuilder(); |
|
| 560 | + $qb->select('*') |
|
| 561 | + ->from('share') |
|
| 562 | + ->where($qb->expr()->eq('parent', $qb->createNamedParameter($parent->getId()))) |
|
| 563 | + ->andWhere($qb->expr()->eq('share_type', $qb->createNamedParameter(\OCP\Share::SHARE_TYPE_EMAIL))) |
|
| 564 | + ->orderBy('id'); |
|
| 565 | + |
|
| 566 | + $cursor = $qb->execute(); |
|
| 567 | + while($data = $cursor->fetch()) { |
|
| 568 | + $children[] = $this->createShareObject($data); |
|
| 569 | + } |
|
| 570 | + $cursor->closeCursor(); |
|
| 571 | + |
|
| 572 | + return $children; |
|
| 573 | + } |
|
| 574 | + |
|
| 575 | + /** |
|
| 576 | + * add share to the database and return the ID |
|
| 577 | + * |
|
| 578 | + * @param int $itemSource |
|
| 579 | + * @param string $itemType |
|
| 580 | + * @param string $shareWith |
|
| 581 | + * @param string $sharedBy |
|
| 582 | + * @param string $uidOwner |
|
| 583 | + * @param int $permissions |
|
| 584 | + * @param string $token |
|
| 585 | + * @return int |
|
| 586 | + */ |
|
| 587 | + protected function addShareToDB($itemSource, $itemType, $shareWith, $sharedBy, $uidOwner, $permissions, $token, $password) { |
|
| 588 | + $qb = $this->dbConnection->getQueryBuilder(); |
|
| 589 | + $qb->insert('share') |
|
| 590 | + ->setValue('share_type', $qb->createNamedParameter(\OCP\Share::SHARE_TYPE_EMAIL)) |
|
| 591 | + ->setValue('item_type', $qb->createNamedParameter($itemType)) |
|
| 592 | + ->setValue('item_source', $qb->createNamedParameter($itemSource)) |
|
| 593 | + ->setValue('file_source', $qb->createNamedParameter($itemSource)) |
|
| 594 | + ->setValue('share_with', $qb->createNamedParameter($shareWith)) |
|
| 595 | + ->setValue('uid_owner', $qb->createNamedParameter($uidOwner)) |
|
| 596 | + ->setValue('uid_initiator', $qb->createNamedParameter($sharedBy)) |
|
| 597 | + ->setValue('permissions', $qb->createNamedParameter($permissions)) |
|
| 598 | + ->setValue('token', $qb->createNamedParameter($token)) |
|
| 599 | + ->setValue('password', $qb->createNamedParameter($password)) |
|
| 600 | + ->setValue('stime', $qb->createNamedParameter(time())); |
|
| 601 | + |
|
| 602 | + /* |
|
| 603 | 603 | * Added to fix https://github.com/owncloud/core/issues/22215 |
| 604 | 604 | * Can be removed once we get rid of ajax/share.php |
| 605 | 605 | */ |
| 606 | - $qb->setValue('file_target', $qb->createNamedParameter('')); |
|
| 606 | + $qb->setValue('file_target', $qb->createNamedParameter('')); |
|
| 607 | 607 | |
| 608 | - $qb->execute(); |
|
| 609 | - $id = $qb->getLastInsertId(); |
|
| 608 | + $qb->execute(); |
|
| 609 | + $id = $qb->getLastInsertId(); |
|
| 610 | 610 | |
| 611 | - return (int)$id; |
|
| 612 | - } |
|
| 611 | + return (int)$id; |
|
| 612 | + } |
|
| 613 | 613 | |
| 614 | - /** |
|
| 615 | - * Update a share |
|
| 616 | - * |
|
| 617 | - * @param IShare $share |
|
| 618 | - * @param string|null $plainTextPassword |
|
| 619 | - * @return IShare The share object |
|
| 620 | - */ |
|
| 621 | - public function update(IShare $share, $plainTextPassword = null) { |
|
| 614 | + /** |
|
| 615 | + * Update a share |
|
| 616 | + * |
|
| 617 | + * @param IShare $share |
|
| 618 | + * @param string|null $plainTextPassword |
|
| 619 | + * @return IShare The share object |
|
| 620 | + */ |
|
| 621 | + public function update(IShare $share, $plainTextPassword = null) { |
|
| 622 | 622 | |
| 623 | - $originalShare = $this->getShareById($share->getId()); |
|
| 623 | + $originalShare = $this->getShareById($share->getId()); |
|
| 624 | 624 | |
| 625 | - // a real password was given |
|
| 626 | - $validPassword = $plainTextPassword !== null && $plainTextPassword !== ''; |
|
| 625 | + // a real password was given |
|
| 626 | + $validPassword = $plainTextPassword !== null && $plainTextPassword !== ''; |
|
| 627 | 627 | |
| 628 | - if($validPassword && $originalShare->getPassword() !== $share->getPassword()) { |
|
| 629 | - $this->sendPassword($share, $plainTextPassword); |
|
| 630 | - } |
|
| 631 | - /* |
|
| 628 | + if($validPassword && $originalShare->getPassword() !== $share->getPassword()) { |
|
| 629 | + $this->sendPassword($share, $plainTextPassword); |
|
| 630 | + } |
|
| 631 | + /* |
|
| 632 | 632 | * We allow updating the permissions and password of mail shares |
| 633 | 633 | */ |
| 634 | - $qb = $this->dbConnection->getQueryBuilder(); |
|
| 635 | - $qb->update('share') |
|
| 636 | - ->where($qb->expr()->eq('id', $qb->createNamedParameter($share->getId()))) |
|
| 637 | - ->set('permissions', $qb->createNamedParameter($share->getPermissions())) |
|
| 638 | - ->set('uid_owner', $qb->createNamedParameter($share->getShareOwner())) |
|
| 639 | - ->set('uid_initiator', $qb->createNamedParameter($share->getSharedBy())) |
|
| 640 | - ->set('password', $qb->createNamedParameter($share->getPassword())) |
|
| 641 | - ->set('expiration', $qb->createNamedParameter($share->getExpirationDate(), IQueryBuilder::PARAM_DATE)) |
|
| 642 | - ->execute(); |
|
| 643 | - |
|
| 644 | - return $share; |
|
| 645 | - } |
|
| 646 | - |
|
| 647 | - /** |
|
| 648 | - * @inheritdoc |
|
| 649 | - */ |
|
| 650 | - public function move(IShare $share, $recipient) { |
|
| 651 | - /** |
|
| 652 | - * nothing to do here, mail shares are only outgoing shares |
|
| 653 | - */ |
|
| 654 | - return $share; |
|
| 655 | - } |
|
| 656 | - |
|
| 657 | - /** |
|
| 658 | - * Delete a share (owner unShares the file) |
|
| 659 | - * |
|
| 660 | - * @param IShare $share |
|
| 661 | - */ |
|
| 662 | - public function delete(IShare $share) { |
|
| 663 | - $this->removeShareFromTable($share->getId()); |
|
| 664 | - } |
|
| 665 | - |
|
| 666 | - /** |
|
| 667 | - * @inheritdoc |
|
| 668 | - */ |
|
| 669 | - public function deleteFromSelf(IShare $share, $recipient) { |
|
| 670 | - // nothing to do here, mail shares are only outgoing shares |
|
| 671 | - return; |
|
| 672 | - } |
|
| 673 | - |
|
| 674 | - /** |
|
| 675 | - * @inheritdoc |
|
| 676 | - */ |
|
| 677 | - public function getSharesBy($userId, $shareType, $node, $reshares, $limit, $offset) { |
|
| 678 | - $qb = $this->dbConnection->getQueryBuilder(); |
|
| 679 | - $qb->select('*') |
|
| 680 | - ->from('share'); |
|
| 681 | - |
|
| 682 | - $qb->andWhere($qb->expr()->eq('share_type', $qb->createNamedParameter(\OCP\Share::SHARE_TYPE_EMAIL))); |
|
| 683 | - |
|
| 684 | - /** |
|
| 685 | - * Reshares for this user are shares where they are the owner. |
|
| 686 | - */ |
|
| 687 | - if ($reshares === false) { |
|
| 688 | - //Special case for old shares created via the web UI |
|
| 689 | - $or1 = $qb->expr()->andX( |
|
| 690 | - $qb->expr()->eq('uid_owner', $qb->createNamedParameter($userId)), |
|
| 691 | - $qb->expr()->isNull('uid_initiator') |
|
| 692 | - ); |
|
| 693 | - |
|
| 694 | - $qb->andWhere( |
|
| 695 | - $qb->expr()->orX( |
|
| 696 | - $qb->expr()->eq('uid_initiator', $qb->createNamedParameter($userId)), |
|
| 697 | - $or1 |
|
| 698 | - ) |
|
| 699 | - ); |
|
| 700 | - } else { |
|
| 701 | - $qb->andWhere( |
|
| 702 | - $qb->expr()->orX( |
|
| 703 | - $qb->expr()->eq('uid_owner', $qb->createNamedParameter($userId)), |
|
| 704 | - $qb->expr()->eq('uid_initiator', $qb->createNamedParameter($userId)) |
|
| 705 | - ) |
|
| 706 | - ); |
|
| 707 | - } |
|
| 708 | - |
|
| 709 | - if ($node !== null) { |
|
| 710 | - $qb->andWhere($qb->expr()->eq('file_source', $qb->createNamedParameter($node->getId()))); |
|
| 711 | - } |
|
| 712 | - |
|
| 713 | - if ($limit !== -1) { |
|
| 714 | - $qb->setMaxResults($limit); |
|
| 715 | - } |
|
| 716 | - |
|
| 717 | - $qb->setFirstResult($offset); |
|
| 718 | - $qb->orderBy('id'); |
|
| 719 | - |
|
| 720 | - $cursor = $qb->execute(); |
|
| 721 | - $shares = []; |
|
| 722 | - while($data = $cursor->fetch()) { |
|
| 723 | - $shares[] = $this->createShareObject($data); |
|
| 724 | - } |
|
| 725 | - $cursor->closeCursor(); |
|
| 726 | - |
|
| 727 | - return $shares; |
|
| 728 | - } |
|
| 729 | - |
|
| 730 | - /** |
|
| 731 | - * @inheritdoc |
|
| 732 | - */ |
|
| 733 | - public function getShareById($id, $recipientId = null) { |
|
| 734 | - $qb = $this->dbConnection->getQueryBuilder(); |
|
| 735 | - |
|
| 736 | - $qb->select('*') |
|
| 737 | - ->from('share') |
|
| 738 | - ->where($qb->expr()->eq('id', $qb->createNamedParameter($id))) |
|
| 739 | - ->andWhere($qb->expr()->eq('share_type', $qb->createNamedParameter(\OCP\Share::SHARE_TYPE_EMAIL))); |
|
| 740 | - |
|
| 741 | - $cursor = $qb->execute(); |
|
| 742 | - $data = $cursor->fetch(); |
|
| 743 | - $cursor->closeCursor(); |
|
| 744 | - |
|
| 745 | - if ($data === false) { |
|
| 746 | - throw new ShareNotFound(); |
|
| 747 | - } |
|
| 748 | - |
|
| 749 | - try { |
|
| 750 | - $share = $this->createShareObject($data); |
|
| 751 | - } catch (InvalidShare $e) { |
|
| 752 | - throw new ShareNotFound(); |
|
| 753 | - } |
|
| 754 | - |
|
| 755 | - return $share; |
|
| 756 | - } |
|
| 757 | - |
|
| 758 | - /** |
|
| 759 | - * Get shares for a given path |
|
| 760 | - * |
|
| 761 | - * @param \OCP\Files\Node $path |
|
| 762 | - * @return IShare[] |
|
| 763 | - */ |
|
| 764 | - public function getSharesByPath(Node $path) { |
|
| 765 | - $qb = $this->dbConnection->getQueryBuilder(); |
|
| 766 | - |
|
| 767 | - $cursor = $qb->select('*') |
|
| 768 | - ->from('share') |
|
| 769 | - ->andWhere($qb->expr()->eq('file_source', $qb->createNamedParameter($path->getId()))) |
|
| 770 | - ->andWhere($qb->expr()->eq('share_type', $qb->createNamedParameter(\OCP\Share::SHARE_TYPE_EMAIL))) |
|
| 771 | - ->execute(); |
|
| 772 | - |
|
| 773 | - $shares = []; |
|
| 774 | - while($data = $cursor->fetch()) { |
|
| 775 | - $shares[] = $this->createShareObject($data); |
|
| 776 | - } |
|
| 777 | - $cursor->closeCursor(); |
|
| 778 | - |
|
| 779 | - return $shares; |
|
| 780 | - } |
|
| 781 | - |
|
| 782 | - /** |
|
| 783 | - * @inheritdoc |
|
| 784 | - */ |
|
| 785 | - public function getSharedWith($userId, $shareType, $node, $limit, $offset) { |
|
| 786 | - /** @var IShare[] $shares */ |
|
| 787 | - $shares = []; |
|
| 788 | - |
|
| 789 | - //Get shares directly with this user |
|
| 790 | - $qb = $this->dbConnection->getQueryBuilder(); |
|
| 791 | - $qb->select('*') |
|
| 792 | - ->from('share'); |
|
| 793 | - |
|
| 794 | - // Order by id |
|
| 795 | - $qb->orderBy('id'); |
|
| 796 | - |
|
| 797 | - // Set limit and offset |
|
| 798 | - if ($limit !== -1) { |
|
| 799 | - $qb->setMaxResults($limit); |
|
| 800 | - } |
|
| 801 | - $qb->setFirstResult($offset); |
|
| 802 | - |
|
| 803 | - $qb->where($qb->expr()->eq('share_type', $qb->createNamedParameter(\OCP\Share::SHARE_TYPE_EMAIL))); |
|
| 804 | - $qb->andWhere($qb->expr()->eq('share_with', $qb->createNamedParameter($userId))); |
|
| 805 | - |
|
| 806 | - // Filter by node if provided |
|
| 807 | - if ($node !== null) { |
|
| 808 | - $qb->andWhere($qb->expr()->eq('file_source', $qb->createNamedParameter($node->getId()))); |
|
| 809 | - } |
|
| 810 | - |
|
| 811 | - $cursor = $qb->execute(); |
|
| 812 | - |
|
| 813 | - while($data = $cursor->fetch()) { |
|
| 814 | - $shares[] = $this->createShareObject($data); |
|
| 815 | - } |
|
| 816 | - $cursor->closeCursor(); |
|
| 817 | - |
|
| 818 | - |
|
| 819 | - return $shares; |
|
| 820 | - } |
|
| 821 | - |
|
| 822 | - /** |
|
| 823 | - * Get a share by token |
|
| 824 | - * |
|
| 825 | - * @param string $token |
|
| 826 | - * @return IShare |
|
| 827 | - * @throws ShareNotFound |
|
| 828 | - */ |
|
| 829 | - public function getShareByToken($token) { |
|
| 830 | - $qb = $this->dbConnection->getQueryBuilder(); |
|
| 831 | - |
|
| 832 | - $cursor = $qb->select('*') |
|
| 833 | - ->from('share') |
|
| 834 | - ->where($qb->expr()->eq('share_type', $qb->createNamedParameter(\OCP\Share::SHARE_TYPE_EMAIL))) |
|
| 835 | - ->andWhere($qb->expr()->eq('token', $qb->createNamedParameter($token))) |
|
| 836 | - ->execute(); |
|
| 837 | - |
|
| 838 | - $data = $cursor->fetch(); |
|
| 839 | - |
|
| 840 | - if ($data === false) { |
|
| 841 | - throw new ShareNotFound('Share not found', $this->l->t('Could not find share')); |
|
| 842 | - } |
|
| 843 | - |
|
| 844 | - try { |
|
| 845 | - $share = $this->createShareObject($data); |
|
| 846 | - } catch (InvalidShare $e) { |
|
| 847 | - throw new ShareNotFound('Share not found', $this->l->t('Could not find share')); |
|
| 848 | - } |
|
| 849 | - |
|
| 850 | - return $share; |
|
| 851 | - } |
|
| 852 | - |
|
| 853 | - /** |
|
| 854 | - * remove share from table |
|
| 855 | - * |
|
| 856 | - * @param string $shareId |
|
| 857 | - */ |
|
| 858 | - protected function removeShareFromTable($shareId) { |
|
| 859 | - $qb = $this->dbConnection->getQueryBuilder(); |
|
| 860 | - $qb->delete('share') |
|
| 861 | - ->where($qb->expr()->eq('id', $qb->createNamedParameter($shareId))); |
|
| 862 | - $qb->execute(); |
|
| 863 | - } |
|
| 864 | - |
|
| 865 | - /** |
|
| 866 | - * Create a share object from an database row |
|
| 867 | - * |
|
| 868 | - * @param array $data |
|
| 869 | - * @return IShare |
|
| 870 | - * @throws InvalidShare |
|
| 871 | - * @throws ShareNotFound |
|
| 872 | - */ |
|
| 873 | - protected function createShareObject($data) { |
|
| 874 | - |
|
| 875 | - $share = new Share($this->rootFolder, $this->userManager); |
|
| 876 | - $share->setId((int)$data['id']) |
|
| 877 | - ->setShareType((int)$data['share_type']) |
|
| 878 | - ->setPermissions((int)$data['permissions']) |
|
| 879 | - ->setTarget($data['file_target']) |
|
| 880 | - ->setMailSend((bool)$data['mail_send']) |
|
| 881 | - ->setToken($data['token']); |
|
| 882 | - |
|
| 883 | - $shareTime = new \DateTime(); |
|
| 884 | - $shareTime->setTimestamp((int)$data['stime']); |
|
| 885 | - $share->setShareTime($shareTime); |
|
| 886 | - $share->setSharedWith($data['share_with']); |
|
| 887 | - $share->setPassword($data['password']); |
|
| 888 | - |
|
| 889 | - if ($data['uid_initiator'] !== null) { |
|
| 890 | - $share->setShareOwner($data['uid_owner']); |
|
| 891 | - $share->setSharedBy($data['uid_initiator']); |
|
| 892 | - } else { |
|
| 893 | - //OLD SHARE |
|
| 894 | - $share->setSharedBy($data['uid_owner']); |
|
| 895 | - $path = $this->getNode($share->getSharedBy(), (int)$data['file_source']); |
|
| 896 | - |
|
| 897 | - $owner = $path->getOwner(); |
|
| 898 | - $share->setShareOwner($owner->getUID()); |
|
| 899 | - } |
|
| 900 | - |
|
| 901 | - if ($data['expiration'] !== null) { |
|
| 902 | - $expiration = \DateTime::createFromFormat('Y-m-d H:i:s', $data['expiration']); |
|
| 903 | - if ($expiration !== false) { |
|
| 904 | - $share->setExpirationDate($expiration); |
|
| 905 | - } |
|
| 906 | - } |
|
| 907 | - |
|
| 908 | - $share->setNodeId((int)$data['file_source']); |
|
| 909 | - $share->setNodeType($data['item_type']); |
|
| 910 | - |
|
| 911 | - $share->setProviderId($this->identifier()); |
|
| 912 | - |
|
| 913 | - return $share; |
|
| 914 | - } |
|
| 915 | - |
|
| 916 | - /** |
|
| 917 | - * Get the node with file $id for $user |
|
| 918 | - * |
|
| 919 | - * @param string $userId |
|
| 920 | - * @param int $id |
|
| 921 | - * @return \OCP\Files\File|\OCP\Files\Folder |
|
| 922 | - * @throws InvalidShare |
|
| 923 | - */ |
|
| 924 | - private function getNode($userId, $id) { |
|
| 925 | - try { |
|
| 926 | - $userFolder = $this->rootFolder->getUserFolder($userId); |
|
| 927 | - } catch (NoUserException $e) { |
|
| 928 | - throw new InvalidShare(); |
|
| 929 | - } |
|
| 930 | - |
|
| 931 | - $nodes = $userFolder->getById($id); |
|
| 932 | - |
|
| 933 | - if (empty($nodes)) { |
|
| 934 | - throw new InvalidShare(); |
|
| 935 | - } |
|
| 936 | - |
|
| 937 | - return $nodes[0]; |
|
| 938 | - } |
|
| 939 | - |
|
| 940 | - /** |
|
| 941 | - * A user is deleted from the system |
|
| 942 | - * So clean up the relevant shares. |
|
| 943 | - * |
|
| 944 | - * @param string $uid |
|
| 945 | - * @param int $shareType |
|
| 946 | - */ |
|
| 947 | - public function userDeleted($uid, $shareType) { |
|
| 948 | - $qb = $this->dbConnection->getQueryBuilder(); |
|
| 949 | - |
|
| 950 | - $qb->delete('share') |
|
| 951 | - ->where($qb->expr()->eq('share_type', $qb->createNamedParameter(\OCP\Share::SHARE_TYPE_EMAIL))) |
|
| 952 | - ->andWhere($qb->expr()->eq('uid_owner', $qb->createNamedParameter($uid))) |
|
| 953 | - ->execute(); |
|
| 954 | - } |
|
| 955 | - |
|
| 956 | - /** |
|
| 957 | - * This provider does not support group shares |
|
| 958 | - * |
|
| 959 | - * @param string $gid |
|
| 960 | - */ |
|
| 961 | - public function groupDeleted($gid) { |
|
| 962 | - return; |
|
| 963 | - } |
|
| 964 | - |
|
| 965 | - /** |
|
| 966 | - * This provider does not support group shares |
|
| 967 | - * |
|
| 968 | - * @param string $uid |
|
| 969 | - * @param string $gid |
|
| 970 | - */ |
|
| 971 | - public function userDeletedFromGroup($uid, $gid) { |
|
| 972 | - return; |
|
| 973 | - } |
|
| 974 | - |
|
| 975 | - /** |
|
| 976 | - * get database row of a give share |
|
| 977 | - * |
|
| 978 | - * @param $id |
|
| 979 | - * @return array |
|
| 980 | - * @throws ShareNotFound |
|
| 981 | - */ |
|
| 982 | - protected function getRawShare($id) { |
|
| 983 | - |
|
| 984 | - // Now fetch the inserted share and create a complete share object |
|
| 985 | - $qb = $this->dbConnection->getQueryBuilder(); |
|
| 986 | - $qb->select('*') |
|
| 987 | - ->from('share') |
|
| 988 | - ->where($qb->expr()->eq('id', $qb->createNamedParameter($id))); |
|
| 989 | - |
|
| 990 | - $cursor = $qb->execute(); |
|
| 991 | - $data = $cursor->fetch(); |
|
| 992 | - $cursor->closeCursor(); |
|
| 993 | - |
|
| 994 | - if ($data === false) { |
|
| 995 | - throw new ShareNotFound; |
|
| 996 | - } |
|
| 997 | - |
|
| 998 | - return $data; |
|
| 999 | - } |
|
| 1000 | - |
|
| 1001 | - public function getSharesInFolder($userId, Folder $node, $reshares) { |
|
| 1002 | - $qb = $this->dbConnection->getQueryBuilder(); |
|
| 1003 | - $qb->select('*') |
|
| 1004 | - ->from('share', 's') |
|
| 1005 | - ->andWhere($qb->expr()->orX( |
|
| 1006 | - $qb->expr()->eq('item_type', $qb->createNamedParameter('file')), |
|
| 1007 | - $qb->expr()->eq('item_type', $qb->createNamedParameter('folder')) |
|
| 1008 | - )) |
|
| 1009 | - ->andWhere( |
|
| 1010 | - $qb->expr()->eq('share_type', $qb->createNamedParameter(\OCP\Share::SHARE_TYPE_EMAIL)) |
|
| 1011 | - ); |
|
| 1012 | - |
|
| 1013 | - /** |
|
| 1014 | - * Reshares for this user are shares where they are the owner. |
|
| 1015 | - */ |
|
| 1016 | - if ($reshares === false) { |
|
| 1017 | - $qb->andWhere($qb->expr()->eq('uid_initiator', $qb->createNamedParameter($userId))); |
|
| 1018 | - } else { |
|
| 1019 | - $qb->andWhere( |
|
| 1020 | - $qb->expr()->orX( |
|
| 1021 | - $qb->expr()->eq('uid_owner', $qb->createNamedParameter($userId)), |
|
| 1022 | - $qb->expr()->eq('uid_initiator', $qb->createNamedParameter($userId)) |
|
| 1023 | - ) |
|
| 1024 | - ); |
|
| 1025 | - } |
|
| 1026 | - |
|
| 1027 | - $qb->innerJoin('s', 'filecache' ,'f', $qb->expr()->eq('s.file_source', 'f.fileid')); |
|
| 1028 | - $qb->andWhere($qb->expr()->eq('f.parent', $qb->createNamedParameter($node->getId()))); |
|
| 1029 | - |
|
| 1030 | - $qb->orderBy('id'); |
|
| 1031 | - |
|
| 1032 | - $cursor = $qb->execute(); |
|
| 1033 | - $shares = []; |
|
| 1034 | - while ($data = $cursor->fetch()) { |
|
| 1035 | - $shares[$data['fileid']][] = $this->createShareObject($data); |
|
| 1036 | - } |
|
| 1037 | - $cursor->closeCursor(); |
|
| 1038 | - |
|
| 1039 | - return $shares; |
|
| 1040 | - } |
|
| 1041 | - |
|
| 1042 | - /** |
|
| 1043 | - * @inheritdoc |
|
| 1044 | - */ |
|
| 1045 | - public function getAccessList($nodes, $currentAccess) { |
|
| 1046 | - $ids = []; |
|
| 1047 | - foreach ($nodes as $node) { |
|
| 1048 | - $ids[] = $node->getId(); |
|
| 1049 | - } |
|
| 1050 | - |
|
| 1051 | - $qb = $this->dbConnection->getQueryBuilder(); |
|
| 1052 | - $qb->select('share_with') |
|
| 1053 | - ->from('share') |
|
| 1054 | - ->where($qb->expr()->eq('share_type', $qb->createNamedParameter(\OCP\Share::SHARE_TYPE_EMAIL))) |
|
| 1055 | - ->andWhere($qb->expr()->in('file_source', $qb->createNamedParameter($ids, IQueryBuilder::PARAM_INT_ARRAY))) |
|
| 1056 | - ->andWhere($qb->expr()->orX( |
|
| 1057 | - $qb->expr()->eq('item_type', $qb->createNamedParameter('file')), |
|
| 1058 | - $qb->expr()->eq('item_type', $qb->createNamedParameter('folder')) |
|
| 1059 | - )) |
|
| 1060 | - ->setMaxResults(1); |
|
| 1061 | - $cursor = $qb->execute(); |
|
| 1062 | - |
|
| 1063 | - $mail = $cursor->fetch() !== false; |
|
| 1064 | - $cursor->closeCursor(); |
|
| 1065 | - |
|
| 1066 | - return ['public' => $mail]; |
|
| 1067 | - } |
|
| 634 | + $qb = $this->dbConnection->getQueryBuilder(); |
|
| 635 | + $qb->update('share') |
|
| 636 | + ->where($qb->expr()->eq('id', $qb->createNamedParameter($share->getId()))) |
|
| 637 | + ->set('permissions', $qb->createNamedParameter($share->getPermissions())) |
|
| 638 | + ->set('uid_owner', $qb->createNamedParameter($share->getShareOwner())) |
|
| 639 | + ->set('uid_initiator', $qb->createNamedParameter($share->getSharedBy())) |
|
| 640 | + ->set('password', $qb->createNamedParameter($share->getPassword())) |
|
| 641 | + ->set('expiration', $qb->createNamedParameter($share->getExpirationDate(), IQueryBuilder::PARAM_DATE)) |
|
| 642 | + ->execute(); |
|
| 643 | + |
|
| 644 | + return $share; |
|
| 645 | + } |
|
| 646 | + |
|
| 647 | + /** |
|
| 648 | + * @inheritdoc |
|
| 649 | + */ |
|
| 650 | + public function move(IShare $share, $recipient) { |
|
| 651 | + /** |
|
| 652 | + * nothing to do here, mail shares are only outgoing shares |
|
| 653 | + */ |
|
| 654 | + return $share; |
|
| 655 | + } |
|
| 656 | + |
|
| 657 | + /** |
|
| 658 | + * Delete a share (owner unShares the file) |
|
| 659 | + * |
|
| 660 | + * @param IShare $share |
|
| 661 | + */ |
|
| 662 | + public function delete(IShare $share) { |
|
| 663 | + $this->removeShareFromTable($share->getId()); |
|
| 664 | + } |
|
| 665 | + |
|
| 666 | + /** |
|
| 667 | + * @inheritdoc |
|
| 668 | + */ |
|
| 669 | + public function deleteFromSelf(IShare $share, $recipient) { |
|
| 670 | + // nothing to do here, mail shares are only outgoing shares |
|
| 671 | + return; |
|
| 672 | + } |
|
| 673 | + |
|
| 674 | + /** |
|
| 675 | + * @inheritdoc |
|
| 676 | + */ |
|
| 677 | + public function getSharesBy($userId, $shareType, $node, $reshares, $limit, $offset) { |
|
| 678 | + $qb = $this->dbConnection->getQueryBuilder(); |
|
| 679 | + $qb->select('*') |
|
| 680 | + ->from('share'); |
|
| 681 | + |
|
| 682 | + $qb->andWhere($qb->expr()->eq('share_type', $qb->createNamedParameter(\OCP\Share::SHARE_TYPE_EMAIL))); |
|
| 683 | + |
|
| 684 | + /** |
|
| 685 | + * Reshares for this user are shares where they are the owner. |
|
| 686 | + */ |
|
| 687 | + if ($reshares === false) { |
|
| 688 | + //Special case for old shares created via the web UI |
|
| 689 | + $or1 = $qb->expr()->andX( |
|
| 690 | + $qb->expr()->eq('uid_owner', $qb->createNamedParameter($userId)), |
|
| 691 | + $qb->expr()->isNull('uid_initiator') |
|
| 692 | + ); |
|
| 693 | + |
|
| 694 | + $qb->andWhere( |
|
| 695 | + $qb->expr()->orX( |
|
| 696 | + $qb->expr()->eq('uid_initiator', $qb->createNamedParameter($userId)), |
|
| 697 | + $or1 |
|
| 698 | + ) |
|
| 699 | + ); |
|
| 700 | + } else { |
|
| 701 | + $qb->andWhere( |
|
| 702 | + $qb->expr()->orX( |
|
| 703 | + $qb->expr()->eq('uid_owner', $qb->createNamedParameter($userId)), |
|
| 704 | + $qb->expr()->eq('uid_initiator', $qb->createNamedParameter($userId)) |
|
| 705 | + ) |
|
| 706 | + ); |
|
| 707 | + } |
|
| 708 | + |
|
| 709 | + if ($node !== null) { |
|
| 710 | + $qb->andWhere($qb->expr()->eq('file_source', $qb->createNamedParameter($node->getId()))); |
|
| 711 | + } |
|
| 712 | + |
|
| 713 | + if ($limit !== -1) { |
|
| 714 | + $qb->setMaxResults($limit); |
|
| 715 | + } |
|
| 716 | + |
|
| 717 | + $qb->setFirstResult($offset); |
|
| 718 | + $qb->orderBy('id'); |
|
| 719 | + |
|
| 720 | + $cursor = $qb->execute(); |
|
| 721 | + $shares = []; |
|
| 722 | + while($data = $cursor->fetch()) { |
|
| 723 | + $shares[] = $this->createShareObject($data); |
|
| 724 | + } |
|
| 725 | + $cursor->closeCursor(); |
|
| 726 | + |
|
| 727 | + return $shares; |
|
| 728 | + } |
|
| 729 | + |
|
| 730 | + /** |
|
| 731 | + * @inheritdoc |
|
| 732 | + */ |
|
| 733 | + public function getShareById($id, $recipientId = null) { |
|
| 734 | + $qb = $this->dbConnection->getQueryBuilder(); |
|
| 735 | + |
|
| 736 | + $qb->select('*') |
|
| 737 | + ->from('share') |
|
| 738 | + ->where($qb->expr()->eq('id', $qb->createNamedParameter($id))) |
|
| 739 | + ->andWhere($qb->expr()->eq('share_type', $qb->createNamedParameter(\OCP\Share::SHARE_TYPE_EMAIL))); |
|
| 740 | + |
|
| 741 | + $cursor = $qb->execute(); |
|
| 742 | + $data = $cursor->fetch(); |
|
| 743 | + $cursor->closeCursor(); |
|
| 744 | + |
|
| 745 | + if ($data === false) { |
|
| 746 | + throw new ShareNotFound(); |
|
| 747 | + } |
|
| 748 | + |
|
| 749 | + try { |
|
| 750 | + $share = $this->createShareObject($data); |
|
| 751 | + } catch (InvalidShare $e) { |
|
| 752 | + throw new ShareNotFound(); |
|
| 753 | + } |
|
| 754 | + |
|
| 755 | + return $share; |
|
| 756 | + } |
|
| 757 | + |
|
| 758 | + /** |
|
| 759 | + * Get shares for a given path |
|
| 760 | + * |
|
| 761 | + * @param \OCP\Files\Node $path |
|
| 762 | + * @return IShare[] |
|
| 763 | + */ |
|
| 764 | + public function getSharesByPath(Node $path) { |
|
| 765 | + $qb = $this->dbConnection->getQueryBuilder(); |
|
| 766 | + |
|
| 767 | + $cursor = $qb->select('*') |
|
| 768 | + ->from('share') |
|
| 769 | + ->andWhere($qb->expr()->eq('file_source', $qb->createNamedParameter($path->getId()))) |
|
| 770 | + ->andWhere($qb->expr()->eq('share_type', $qb->createNamedParameter(\OCP\Share::SHARE_TYPE_EMAIL))) |
|
| 771 | + ->execute(); |
|
| 772 | + |
|
| 773 | + $shares = []; |
|
| 774 | + while($data = $cursor->fetch()) { |
|
| 775 | + $shares[] = $this->createShareObject($data); |
|
| 776 | + } |
|
| 777 | + $cursor->closeCursor(); |
|
| 778 | + |
|
| 779 | + return $shares; |
|
| 780 | + } |
|
| 781 | + |
|
| 782 | + /** |
|
| 783 | + * @inheritdoc |
|
| 784 | + */ |
|
| 785 | + public function getSharedWith($userId, $shareType, $node, $limit, $offset) { |
|
| 786 | + /** @var IShare[] $shares */ |
|
| 787 | + $shares = []; |
|
| 788 | + |
|
| 789 | + //Get shares directly with this user |
|
| 790 | + $qb = $this->dbConnection->getQueryBuilder(); |
|
| 791 | + $qb->select('*') |
|
| 792 | + ->from('share'); |
|
| 793 | + |
|
| 794 | + // Order by id |
|
| 795 | + $qb->orderBy('id'); |
|
| 796 | + |
|
| 797 | + // Set limit and offset |
|
| 798 | + if ($limit !== -1) { |
|
| 799 | + $qb->setMaxResults($limit); |
|
| 800 | + } |
|
| 801 | + $qb->setFirstResult($offset); |
|
| 802 | + |
|
| 803 | + $qb->where($qb->expr()->eq('share_type', $qb->createNamedParameter(\OCP\Share::SHARE_TYPE_EMAIL))); |
|
| 804 | + $qb->andWhere($qb->expr()->eq('share_with', $qb->createNamedParameter($userId))); |
|
| 805 | + |
|
| 806 | + // Filter by node if provided |
|
| 807 | + if ($node !== null) { |
|
| 808 | + $qb->andWhere($qb->expr()->eq('file_source', $qb->createNamedParameter($node->getId()))); |
|
| 809 | + } |
|
| 810 | + |
|
| 811 | + $cursor = $qb->execute(); |
|
| 812 | + |
|
| 813 | + while($data = $cursor->fetch()) { |
|
| 814 | + $shares[] = $this->createShareObject($data); |
|
| 815 | + } |
|
| 816 | + $cursor->closeCursor(); |
|
| 817 | + |
|
| 818 | + |
|
| 819 | + return $shares; |
|
| 820 | + } |
|
| 821 | + |
|
| 822 | + /** |
|
| 823 | + * Get a share by token |
|
| 824 | + * |
|
| 825 | + * @param string $token |
|
| 826 | + * @return IShare |
|
| 827 | + * @throws ShareNotFound |
|
| 828 | + */ |
|
| 829 | + public function getShareByToken($token) { |
|
| 830 | + $qb = $this->dbConnection->getQueryBuilder(); |
|
| 831 | + |
|
| 832 | + $cursor = $qb->select('*') |
|
| 833 | + ->from('share') |
|
| 834 | + ->where($qb->expr()->eq('share_type', $qb->createNamedParameter(\OCP\Share::SHARE_TYPE_EMAIL))) |
|
| 835 | + ->andWhere($qb->expr()->eq('token', $qb->createNamedParameter($token))) |
|
| 836 | + ->execute(); |
|
| 837 | + |
|
| 838 | + $data = $cursor->fetch(); |
|
| 839 | + |
|
| 840 | + if ($data === false) { |
|
| 841 | + throw new ShareNotFound('Share not found', $this->l->t('Could not find share')); |
|
| 842 | + } |
|
| 843 | + |
|
| 844 | + try { |
|
| 845 | + $share = $this->createShareObject($data); |
|
| 846 | + } catch (InvalidShare $e) { |
|
| 847 | + throw new ShareNotFound('Share not found', $this->l->t('Could not find share')); |
|
| 848 | + } |
|
| 849 | + |
|
| 850 | + return $share; |
|
| 851 | + } |
|
| 852 | + |
|
| 853 | + /** |
|
| 854 | + * remove share from table |
|
| 855 | + * |
|
| 856 | + * @param string $shareId |
|
| 857 | + */ |
|
| 858 | + protected function removeShareFromTable($shareId) { |
|
| 859 | + $qb = $this->dbConnection->getQueryBuilder(); |
|
| 860 | + $qb->delete('share') |
|
| 861 | + ->where($qb->expr()->eq('id', $qb->createNamedParameter($shareId))); |
|
| 862 | + $qb->execute(); |
|
| 863 | + } |
|
| 864 | + |
|
| 865 | + /** |
|
| 866 | + * Create a share object from an database row |
|
| 867 | + * |
|
| 868 | + * @param array $data |
|
| 869 | + * @return IShare |
|
| 870 | + * @throws InvalidShare |
|
| 871 | + * @throws ShareNotFound |
|
| 872 | + */ |
|
| 873 | + protected function createShareObject($data) { |
|
| 874 | + |
|
| 875 | + $share = new Share($this->rootFolder, $this->userManager); |
|
| 876 | + $share->setId((int)$data['id']) |
|
| 877 | + ->setShareType((int)$data['share_type']) |
|
| 878 | + ->setPermissions((int)$data['permissions']) |
|
| 879 | + ->setTarget($data['file_target']) |
|
| 880 | + ->setMailSend((bool)$data['mail_send']) |
|
| 881 | + ->setToken($data['token']); |
|
| 882 | + |
|
| 883 | + $shareTime = new \DateTime(); |
|
| 884 | + $shareTime->setTimestamp((int)$data['stime']); |
|
| 885 | + $share->setShareTime($shareTime); |
|
| 886 | + $share->setSharedWith($data['share_with']); |
|
| 887 | + $share->setPassword($data['password']); |
|
| 888 | + |
|
| 889 | + if ($data['uid_initiator'] !== null) { |
|
| 890 | + $share->setShareOwner($data['uid_owner']); |
|
| 891 | + $share->setSharedBy($data['uid_initiator']); |
|
| 892 | + } else { |
|
| 893 | + //OLD SHARE |
|
| 894 | + $share->setSharedBy($data['uid_owner']); |
|
| 895 | + $path = $this->getNode($share->getSharedBy(), (int)$data['file_source']); |
|
| 896 | + |
|
| 897 | + $owner = $path->getOwner(); |
|
| 898 | + $share->setShareOwner($owner->getUID()); |
|
| 899 | + } |
|
| 900 | + |
|
| 901 | + if ($data['expiration'] !== null) { |
|
| 902 | + $expiration = \DateTime::createFromFormat('Y-m-d H:i:s', $data['expiration']); |
|
| 903 | + if ($expiration !== false) { |
|
| 904 | + $share->setExpirationDate($expiration); |
|
| 905 | + } |
|
| 906 | + } |
|
| 907 | + |
|
| 908 | + $share->setNodeId((int)$data['file_source']); |
|
| 909 | + $share->setNodeType($data['item_type']); |
|
| 910 | + |
|
| 911 | + $share->setProviderId($this->identifier()); |
|
| 912 | + |
|
| 913 | + return $share; |
|
| 914 | + } |
|
| 915 | + |
|
| 916 | + /** |
|
| 917 | + * Get the node with file $id for $user |
|
| 918 | + * |
|
| 919 | + * @param string $userId |
|
| 920 | + * @param int $id |
|
| 921 | + * @return \OCP\Files\File|\OCP\Files\Folder |
|
| 922 | + * @throws InvalidShare |
|
| 923 | + */ |
|
| 924 | + private function getNode($userId, $id) { |
|
| 925 | + try { |
|
| 926 | + $userFolder = $this->rootFolder->getUserFolder($userId); |
|
| 927 | + } catch (NoUserException $e) { |
|
| 928 | + throw new InvalidShare(); |
|
| 929 | + } |
|
| 930 | + |
|
| 931 | + $nodes = $userFolder->getById($id); |
|
| 932 | + |
|
| 933 | + if (empty($nodes)) { |
|
| 934 | + throw new InvalidShare(); |
|
| 935 | + } |
|
| 936 | + |
|
| 937 | + return $nodes[0]; |
|
| 938 | + } |
|
| 939 | + |
|
| 940 | + /** |
|
| 941 | + * A user is deleted from the system |
|
| 942 | + * So clean up the relevant shares. |
|
| 943 | + * |
|
| 944 | + * @param string $uid |
|
| 945 | + * @param int $shareType |
|
| 946 | + */ |
|
| 947 | + public function userDeleted($uid, $shareType) { |
|
| 948 | + $qb = $this->dbConnection->getQueryBuilder(); |
|
| 949 | + |
|
| 950 | + $qb->delete('share') |
|
| 951 | + ->where($qb->expr()->eq('share_type', $qb->createNamedParameter(\OCP\Share::SHARE_TYPE_EMAIL))) |
|
| 952 | + ->andWhere($qb->expr()->eq('uid_owner', $qb->createNamedParameter($uid))) |
|
| 953 | + ->execute(); |
|
| 954 | + } |
|
| 955 | + |
|
| 956 | + /** |
|
| 957 | + * This provider does not support group shares |
|
| 958 | + * |
|
| 959 | + * @param string $gid |
|
| 960 | + */ |
|
| 961 | + public function groupDeleted($gid) { |
|
| 962 | + return; |
|
| 963 | + } |
|
| 964 | + |
|
| 965 | + /** |
|
| 966 | + * This provider does not support group shares |
|
| 967 | + * |
|
| 968 | + * @param string $uid |
|
| 969 | + * @param string $gid |
|
| 970 | + */ |
|
| 971 | + public function userDeletedFromGroup($uid, $gid) { |
|
| 972 | + return; |
|
| 973 | + } |
|
| 974 | + |
|
| 975 | + /** |
|
| 976 | + * get database row of a give share |
|
| 977 | + * |
|
| 978 | + * @param $id |
|
| 979 | + * @return array |
|
| 980 | + * @throws ShareNotFound |
|
| 981 | + */ |
|
| 982 | + protected function getRawShare($id) { |
|
| 983 | + |
|
| 984 | + // Now fetch the inserted share and create a complete share object |
|
| 985 | + $qb = $this->dbConnection->getQueryBuilder(); |
|
| 986 | + $qb->select('*') |
|
| 987 | + ->from('share') |
|
| 988 | + ->where($qb->expr()->eq('id', $qb->createNamedParameter($id))); |
|
| 989 | + |
|
| 990 | + $cursor = $qb->execute(); |
|
| 991 | + $data = $cursor->fetch(); |
|
| 992 | + $cursor->closeCursor(); |
|
| 993 | + |
|
| 994 | + if ($data === false) { |
|
| 995 | + throw new ShareNotFound; |
|
| 996 | + } |
|
| 997 | + |
|
| 998 | + return $data; |
|
| 999 | + } |
|
| 1000 | + |
|
| 1001 | + public function getSharesInFolder($userId, Folder $node, $reshares) { |
|
| 1002 | + $qb = $this->dbConnection->getQueryBuilder(); |
|
| 1003 | + $qb->select('*') |
|
| 1004 | + ->from('share', 's') |
|
| 1005 | + ->andWhere($qb->expr()->orX( |
|
| 1006 | + $qb->expr()->eq('item_type', $qb->createNamedParameter('file')), |
|
| 1007 | + $qb->expr()->eq('item_type', $qb->createNamedParameter('folder')) |
|
| 1008 | + )) |
|
| 1009 | + ->andWhere( |
|
| 1010 | + $qb->expr()->eq('share_type', $qb->createNamedParameter(\OCP\Share::SHARE_TYPE_EMAIL)) |
|
| 1011 | + ); |
|
| 1012 | + |
|
| 1013 | + /** |
|
| 1014 | + * Reshares for this user are shares where they are the owner. |
|
| 1015 | + */ |
|
| 1016 | + if ($reshares === false) { |
|
| 1017 | + $qb->andWhere($qb->expr()->eq('uid_initiator', $qb->createNamedParameter($userId))); |
|
| 1018 | + } else { |
|
| 1019 | + $qb->andWhere( |
|
| 1020 | + $qb->expr()->orX( |
|
| 1021 | + $qb->expr()->eq('uid_owner', $qb->createNamedParameter($userId)), |
|
| 1022 | + $qb->expr()->eq('uid_initiator', $qb->createNamedParameter($userId)) |
|
| 1023 | + ) |
|
| 1024 | + ); |
|
| 1025 | + } |
|
| 1026 | + |
|
| 1027 | + $qb->innerJoin('s', 'filecache' ,'f', $qb->expr()->eq('s.file_source', 'f.fileid')); |
|
| 1028 | + $qb->andWhere($qb->expr()->eq('f.parent', $qb->createNamedParameter($node->getId()))); |
|
| 1029 | + |
|
| 1030 | + $qb->orderBy('id'); |
|
| 1031 | + |
|
| 1032 | + $cursor = $qb->execute(); |
|
| 1033 | + $shares = []; |
|
| 1034 | + while ($data = $cursor->fetch()) { |
|
| 1035 | + $shares[$data['fileid']][] = $this->createShareObject($data); |
|
| 1036 | + } |
|
| 1037 | + $cursor->closeCursor(); |
|
| 1038 | + |
|
| 1039 | + return $shares; |
|
| 1040 | + } |
|
| 1041 | + |
|
| 1042 | + /** |
|
| 1043 | + * @inheritdoc |
|
| 1044 | + */ |
|
| 1045 | + public function getAccessList($nodes, $currentAccess) { |
|
| 1046 | + $ids = []; |
|
| 1047 | + foreach ($nodes as $node) { |
|
| 1048 | + $ids[] = $node->getId(); |
|
| 1049 | + } |
|
| 1050 | + |
|
| 1051 | + $qb = $this->dbConnection->getQueryBuilder(); |
|
| 1052 | + $qb->select('share_with') |
|
| 1053 | + ->from('share') |
|
| 1054 | + ->where($qb->expr()->eq('share_type', $qb->createNamedParameter(\OCP\Share::SHARE_TYPE_EMAIL))) |
|
| 1055 | + ->andWhere($qb->expr()->in('file_source', $qb->createNamedParameter($ids, IQueryBuilder::PARAM_INT_ARRAY))) |
|
| 1056 | + ->andWhere($qb->expr()->orX( |
|
| 1057 | + $qb->expr()->eq('item_type', $qb->createNamedParameter('file')), |
|
| 1058 | + $qb->expr()->eq('item_type', $qb->createNamedParameter('folder')) |
|
| 1059 | + )) |
|
| 1060 | + ->setMaxResults(1); |
|
| 1061 | + $cursor = $qb->execute(); |
|
| 1062 | + |
|
| 1063 | + $mail = $cursor->fetch() !== false; |
|
| 1064 | + $cursor->closeCursor(); |
|
| 1065 | + |
|
| 1066 | + return ['public' => $mail]; |
|
| 1067 | + } |
|
| 1068 | 1068 | |
| 1069 | 1069 | } |
@@ -35,40 +35,40 @@ discard block |
||
| 35 | 35 | |
| 36 | 36 | $application = new Application(); |
| 37 | 37 | $application->registerRoutes($this, [ |
| 38 | - 'routes' => [ |
|
| 39 | - ['name' => 'lost#email', 'url' => '/lostpassword/email', 'verb' => 'POST'], |
|
| 40 | - ['name' => 'lost#resetform', 'url' => '/lostpassword/reset/form/{token}/{userId}', 'verb' => 'GET'], |
|
| 41 | - ['name' => 'lost#setPassword', 'url' => '/lostpassword/set/{token}/{userId}', 'verb' => 'POST'], |
|
| 42 | - ['name' => 'user#getDisplayNames', 'url' => '/displaynames', 'verb' => 'POST'], |
|
| 43 | - ['name' => 'avatar#getAvatar', 'url' => '/avatar/{userId}/{size}', 'verb' => 'GET'], |
|
| 44 | - ['name' => 'avatar#deleteAvatar', 'url' => '/avatar/', 'verb' => 'DELETE'], |
|
| 45 | - ['name' => 'avatar#postCroppedAvatar', 'url' => '/avatar/cropped', 'verb' => 'POST'], |
|
| 46 | - ['name' => 'avatar#getTmpAvatar', 'url' => '/avatar/tmp', 'verb' => 'GET'], |
|
| 47 | - ['name' => 'avatar#postAvatar', 'url' => '/avatar/', 'verb' => 'POST'], |
|
| 48 | - ['name' => 'login#tryLogin', 'url' => '/login', 'verb' => 'POST'], |
|
| 49 | - ['name' => 'login#confirmPassword', 'url' => '/login/confirm', 'verb' => 'POST'], |
|
| 50 | - ['name' => 'login#showLoginForm', 'url' => '/login', 'verb' => 'GET'], |
|
| 51 | - ['name' => 'login#logout', 'url' => '/logout', 'verb' => 'GET'], |
|
| 52 | - ['name' => 'ClientFlowLogin#showAuthPickerPage', 'url' => '/login/flow', 'verb' => 'GET'], |
|
| 53 | - ['name' => 'ClientFlowLogin#redirectPage', 'url' => '/login/flow/redirect', 'verb' => 'GET'], |
|
| 54 | - ['name' => 'ClientFlowLogin#generateAppPassword', 'url' => '/login/flow', 'verb' => 'POST'], |
|
| 55 | - ['name' => 'TwoFactorChallenge#selectChallenge', 'url' => '/login/selectchallenge', 'verb' => 'GET'], |
|
| 56 | - ['name' => 'TwoFactorChallenge#showChallenge', 'url' => '/login/challenge/{challengeProviderId}', 'verb' => 'GET'], |
|
| 57 | - ['name' => 'TwoFactorChallenge#solveChallenge', 'url' => '/login/challenge/{challengeProviderId}', 'verb' => 'POST'], |
|
| 58 | - ['name' => 'OCJS#getConfig', 'url' => '/core/js/oc.js', 'verb' => 'GET'], |
|
| 59 | - ['name' => 'Preview#getPreview', 'url' => '/core/preview', 'verb' => 'GET'], |
|
| 60 | - ['name' => 'Preview#getPreview', 'url' => '/core/preview.png', 'verb' => 'GET'], |
|
| 61 | - ['name' => 'Css#getCss', 'url' => '/css/{appName}/{fileName}', 'verb' => 'GET'], |
|
| 62 | - ['name' => 'Js#getJs', 'url' => '/js/{appName}/{fileName}', 'verb' => 'GET'], |
|
| 63 | - ['name' => 'contactsMenu#index', 'url' => '/contactsmenu/contacts', 'verb' => 'POST'], |
|
| 64 | - ['name' => 'contactsMenu#findOne', 'url' => '/contactsmenu/findOne', 'verb' => 'POST'], |
|
| 65 | - ], |
|
| 66 | - 'ocs' => [ |
|
| 67 | - ['root' => '/cloud', 'name' => 'OCS#getCapabilities', 'url' => '/capabilities', 'verb' => 'GET'], |
|
| 68 | - ['root' => '', 'name' => 'OCS#getConfig', 'url' => '/config', 'verb' => 'GET'], |
|
| 69 | - ['root' => '/person', 'name' => 'OCS#personCheck', 'url' => '/check', 'verb' => 'POST'], |
|
| 70 | - ['root' => '/identityproof', 'name' => 'OCS#getIdentityProof', 'url' => '/key/{cloudId}', 'verb' => 'GET'], |
|
| 71 | - ], |
|
| 38 | + 'routes' => [ |
|
| 39 | + ['name' => 'lost#email', 'url' => '/lostpassword/email', 'verb' => 'POST'], |
|
| 40 | + ['name' => 'lost#resetform', 'url' => '/lostpassword/reset/form/{token}/{userId}', 'verb' => 'GET'], |
|
| 41 | + ['name' => 'lost#setPassword', 'url' => '/lostpassword/set/{token}/{userId}', 'verb' => 'POST'], |
|
| 42 | + ['name' => 'user#getDisplayNames', 'url' => '/displaynames', 'verb' => 'POST'], |
|
| 43 | + ['name' => 'avatar#getAvatar', 'url' => '/avatar/{userId}/{size}', 'verb' => 'GET'], |
|
| 44 | + ['name' => 'avatar#deleteAvatar', 'url' => '/avatar/', 'verb' => 'DELETE'], |
|
| 45 | + ['name' => 'avatar#postCroppedAvatar', 'url' => '/avatar/cropped', 'verb' => 'POST'], |
|
| 46 | + ['name' => 'avatar#getTmpAvatar', 'url' => '/avatar/tmp', 'verb' => 'GET'], |
|
| 47 | + ['name' => 'avatar#postAvatar', 'url' => '/avatar/', 'verb' => 'POST'], |
|
| 48 | + ['name' => 'login#tryLogin', 'url' => '/login', 'verb' => 'POST'], |
|
| 49 | + ['name' => 'login#confirmPassword', 'url' => '/login/confirm', 'verb' => 'POST'], |
|
| 50 | + ['name' => 'login#showLoginForm', 'url' => '/login', 'verb' => 'GET'], |
|
| 51 | + ['name' => 'login#logout', 'url' => '/logout', 'verb' => 'GET'], |
|
| 52 | + ['name' => 'ClientFlowLogin#showAuthPickerPage', 'url' => '/login/flow', 'verb' => 'GET'], |
|
| 53 | + ['name' => 'ClientFlowLogin#redirectPage', 'url' => '/login/flow/redirect', 'verb' => 'GET'], |
|
| 54 | + ['name' => 'ClientFlowLogin#generateAppPassword', 'url' => '/login/flow', 'verb' => 'POST'], |
|
| 55 | + ['name' => 'TwoFactorChallenge#selectChallenge', 'url' => '/login/selectchallenge', 'verb' => 'GET'], |
|
| 56 | + ['name' => 'TwoFactorChallenge#showChallenge', 'url' => '/login/challenge/{challengeProviderId}', 'verb' => 'GET'], |
|
| 57 | + ['name' => 'TwoFactorChallenge#solveChallenge', 'url' => '/login/challenge/{challengeProviderId}', 'verb' => 'POST'], |
|
| 58 | + ['name' => 'OCJS#getConfig', 'url' => '/core/js/oc.js', 'verb' => 'GET'], |
|
| 59 | + ['name' => 'Preview#getPreview', 'url' => '/core/preview', 'verb' => 'GET'], |
|
| 60 | + ['name' => 'Preview#getPreview', 'url' => '/core/preview.png', 'verb' => 'GET'], |
|
| 61 | + ['name' => 'Css#getCss', 'url' => '/css/{appName}/{fileName}', 'verb' => 'GET'], |
|
| 62 | + ['name' => 'Js#getJs', 'url' => '/js/{appName}/{fileName}', 'verb' => 'GET'], |
|
| 63 | + ['name' => 'contactsMenu#index', 'url' => '/contactsmenu/contacts', 'verb' => 'POST'], |
|
| 64 | + ['name' => 'contactsMenu#findOne', 'url' => '/contactsmenu/findOne', 'verb' => 'POST'], |
|
| 65 | + ], |
|
| 66 | + 'ocs' => [ |
|
| 67 | + ['root' => '/cloud', 'name' => 'OCS#getCapabilities', 'url' => '/capabilities', 'verb' => 'GET'], |
|
| 68 | + ['root' => '', 'name' => 'OCS#getConfig', 'url' => '/config', 'verb' => 'GET'], |
|
| 69 | + ['root' => '/person', 'name' => 'OCS#personCheck', 'url' => '/check', 'verb' => 'POST'], |
|
| 70 | + ['root' => '/identityproof', 'name' => 'OCS#getIdentityProof', 'url' => '/key/{cloudId}', 'verb' => 'GET'], |
|
| 71 | + ], |
|
| 72 | 72 | ]); |
| 73 | 73 | |
| 74 | 74 | // Post installation check |
@@ -77,15 +77,15 @@ discard block |
||
| 77 | 77 | // Core ajax actions |
| 78 | 78 | // Search |
| 79 | 79 | $this->create('search_ajax_search', '/core/search') |
| 80 | - ->actionInclude('core/search/ajax/search.php'); |
|
| 80 | + ->actionInclude('core/search/ajax/search.php'); |
|
| 81 | 81 | // Routing |
| 82 | 82 | $this->create('core_ajax_update', '/core/ajax/update.php') |
| 83 | - ->actionInclude('core/ajax/update.php'); |
|
| 83 | + ->actionInclude('core/ajax/update.php'); |
|
| 84 | 84 | |
| 85 | 85 | // File routes |
| 86 | 86 | $this->create('files.viewcontroller.showFile', '/f/{fileid}')->action(function($urlParams) { |
| 87 | - $app = new \OCA\Files\AppInfo\Application($urlParams); |
|
| 88 | - $app->dispatch('ViewController', 'index'); |
|
| 87 | + $app = new \OCA\Files\AppInfo\Application($urlParams); |
|
| 88 | + $app->dispatch('ViewController', 'index'); |
|
| 89 | 89 | }); |
| 90 | 90 | |
| 91 | 91 | // Call routes |
@@ -94,49 +94,49 @@ discard block |
||
| 94 | 94 | * @suppress PhanUndeclaredClassMethod |
| 95 | 95 | */ |
| 96 | 96 | $this->create('spreed.pagecontroller.showCall', '/call/{token}')->action(function($urlParams) { |
| 97 | - if (class_exists(\OCA\Spreed\AppInfo\Application::class, false)) { |
|
| 98 | - $app = new \OCA\Spreed\AppInfo\Application($urlParams); |
|
| 99 | - $app->dispatch('PageController', 'index'); |
|
| 100 | - } else { |
|
| 101 | - throw new \OC\HintException('App spreed is not enabled'); |
|
| 102 | - } |
|
| 97 | + if (class_exists(\OCA\Spreed\AppInfo\Application::class, false)) { |
|
| 98 | + $app = new \OCA\Spreed\AppInfo\Application($urlParams); |
|
| 99 | + $app->dispatch('PageController', 'index'); |
|
| 100 | + } else { |
|
| 101 | + throw new \OC\HintException('App spreed is not enabled'); |
|
| 102 | + } |
|
| 103 | 103 | }); |
| 104 | 104 | |
| 105 | 105 | // Sharing routes |
| 106 | 106 | $this->create('files_sharing.sharecontroller.showShare', '/s/{token}')->action(function($urlParams) { |
| 107 | - if (class_exists(\OCA\Files_Sharing\AppInfo\Application::class, false)) { |
|
| 108 | - $app = new \OCA\Files_Sharing\AppInfo\Application($urlParams); |
|
| 109 | - $app->dispatch('ShareController', 'showShare'); |
|
| 110 | - } else { |
|
| 111 | - throw new \OC\HintException('App file sharing is not enabled'); |
|
| 112 | - } |
|
| 107 | + if (class_exists(\OCA\Files_Sharing\AppInfo\Application::class, false)) { |
|
| 108 | + $app = new \OCA\Files_Sharing\AppInfo\Application($urlParams); |
|
| 109 | + $app->dispatch('ShareController', 'showShare'); |
|
| 110 | + } else { |
|
| 111 | + throw new \OC\HintException('App file sharing is not enabled'); |
|
| 112 | + } |
|
| 113 | 113 | }); |
| 114 | 114 | $this->create('files_sharing.sharecontroller.authenticate', '/s/{token}/authenticate')->post()->action(function($urlParams) { |
| 115 | - if (class_exists(\OCA\Files_Sharing\AppInfo\Application::class, false)) { |
|
| 116 | - $app = new \OCA\Files_Sharing\AppInfo\Application($urlParams); |
|
| 117 | - $app->dispatch('ShareController', 'authenticate'); |
|
| 118 | - } else { |
|
| 119 | - throw new \OC\HintException('App file sharing is not enabled'); |
|
| 120 | - } |
|
| 115 | + if (class_exists(\OCA\Files_Sharing\AppInfo\Application::class, false)) { |
|
| 116 | + $app = new \OCA\Files_Sharing\AppInfo\Application($urlParams); |
|
| 117 | + $app->dispatch('ShareController', 'authenticate'); |
|
| 118 | + } else { |
|
| 119 | + throw new \OC\HintException('App file sharing is not enabled'); |
|
| 120 | + } |
|
| 121 | 121 | }); |
| 122 | 122 | $this->create('files_sharing.sharecontroller.showAuthenticate', '/s/{token}/authenticate')->get()->action(function($urlParams) { |
| 123 | - if (class_exists(\OCA\Files_Sharing\AppInfo\Application::class, false)) { |
|
| 124 | - $app = new \OCA\Files_Sharing\AppInfo\Application($urlParams); |
|
| 125 | - $app->dispatch('ShareController', 'showAuthenticate'); |
|
| 126 | - } else { |
|
| 127 | - throw new \OC\HintException('App file sharing is not enabled'); |
|
| 128 | - } |
|
| 123 | + if (class_exists(\OCA\Files_Sharing\AppInfo\Application::class, false)) { |
|
| 124 | + $app = new \OCA\Files_Sharing\AppInfo\Application($urlParams); |
|
| 125 | + $app->dispatch('ShareController', 'showAuthenticate'); |
|
| 126 | + } else { |
|
| 127 | + throw new \OC\HintException('App file sharing is not enabled'); |
|
| 128 | + } |
|
| 129 | 129 | }); |
| 130 | 130 | $this->create('files_sharing.sharecontroller.downloadShare', '/s/{token}/download')->get()->action(function($urlParams) { |
| 131 | - if (class_exists(\OCA\Files_Sharing\AppInfo\Application::class, false)) { |
|
| 132 | - $app = new \OCA\Files_Sharing\AppInfo\Application($urlParams); |
|
| 133 | - $app->dispatch('ShareController', 'downloadShare'); |
|
| 134 | - } else { |
|
| 135 | - throw new \OC\HintException('App file sharing is not enabled'); |
|
| 136 | - } |
|
| 131 | + if (class_exists(\OCA\Files_Sharing\AppInfo\Application::class, false)) { |
|
| 132 | + $app = new \OCA\Files_Sharing\AppInfo\Application($urlParams); |
|
| 133 | + $app->dispatch('ShareController', 'downloadShare'); |
|
| 134 | + } else { |
|
| 135 | + throw new \OC\HintException('App file sharing is not enabled'); |
|
| 136 | + } |
|
| 137 | 137 | }); |
| 138 | 138 | |
| 139 | 139 | // used for heartbeat |
| 140 | 140 | $this->create('heartbeat', '/heartbeat')->action(function(){ |
| 141 | - // do nothing |
|
| 141 | + // do nothing |
|
| 142 | 142 | }); |
@@ -69,857 +69,857 @@ |
||
| 69 | 69 | |
| 70 | 70 | class Filesystem { |
| 71 | 71 | |
| 72 | - /** |
|
| 73 | - * @var Mount\Manager $mounts |
|
| 74 | - */ |
|
| 75 | - private static $mounts; |
|
| 76 | - |
|
| 77 | - public static $loaded = false; |
|
| 78 | - /** |
|
| 79 | - * @var \OC\Files\View $defaultInstance |
|
| 80 | - */ |
|
| 81 | - static private $defaultInstance; |
|
| 82 | - |
|
| 83 | - static private $usersSetup = array(); |
|
| 84 | - |
|
| 85 | - static private $normalizedPathCache = null; |
|
| 86 | - |
|
| 87 | - static private $listeningForProviders = false; |
|
| 88 | - |
|
| 89 | - /** |
|
| 90 | - * classname which used for hooks handling |
|
| 91 | - * used as signalclass in OC_Hooks::emit() |
|
| 92 | - */ |
|
| 93 | - const CLASSNAME = 'OC_Filesystem'; |
|
| 94 | - |
|
| 95 | - /** |
|
| 96 | - * signalname emitted before file renaming |
|
| 97 | - * |
|
| 98 | - * @param string $oldpath |
|
| 99 | - * @param string $newpath |
|
| 100 | - */ |
|
| 101 | - const signal_rename = 'rename'; |
|
| 102 | - |
|
| 103 | - /** |
|
| 104 | - * signal emitted after file renaming |
|
| 105 | - * |
|
| 106 | - * @param string $oldpath |
|
| 107 | - * @param string $newpath |
|
| 108 | - */ |
|
| 109 | - const signal_post_rename = 'post_rename'; |
|
| 110 | - |
|
| 111 | - /** |
|
| 112 | - * signal emitted before file/dir creation |
|
| 113 | - * |
|
| 114 | - * @param string $path |
|
| 115 | - * @param bool $run changing this flag to false in hook handler will cancel event |
|
| 116 | - */ |
|
| 117 | - const signal_create = 'create'; |
|
| 118 | - |
|
| 119 | - /** |
|
| 120 | - * signal emitted after file/dir creation |
|
| 121 | - * |
|
| 122 | - * @param string $path |
|
| 123 | - * @param bool $run changing this flag to false in hook handler will cancel event |
|
| 124 | - */ |
|
| 125 | - const signal_post_create = 'post_create'; |
|
| 126 | - |
|
| 127 | - /** |
|
| 128 | - * signal emits before file/dir copy |
|
| 129 | - * |
|
| 130 | - * @param string $oldpath |
|
| 131 | - * @param string $newpath |
|
| 132 | - * @param bool $run changing this flag to false in hook handler will cancel event |
|
| 133 | - */ |
|
| 134 | - const signal_copy = 'copy'; |
|
| 135 | - |
|
| 136 | - /** |
|
| 137 | - * signal emits after file/dir copy |
|
| 138 | - * |
|
| 139 | - * @param string $oldpath |
|
| 140 | - * @param string $newpath |
|
| 141 | - */ |
|
| 142 | - const signal_post_copy = 'post_copy'; |
|
| 143 | - |
|
| 144 | - /** |
|
| 145 | - * signal emits before file/dir save |
|
| 146 | - * |
|
| 147 | - * @param string $path |
|
| 148 | - * @param bool $run changing this flag to false in hook handler will cancel event |
|
| 149 | - */ |
|
| 150 | - const signal_write = 'write'; |
|
| 151 | - |
|
| 152 | - /** |
|
| 153 | - * signal emits after file/dir save |
|
| 154 | - * |
|
| 155 | - * @param string $path |
|
| 156 | - */ |
|
| 157 | - const signal_post_write = 'post_write'; |
|
| 158 | - |
|
| 159 | - /** |
|
| 160 | - * signal emitted before file/dir update |
|
| 161 | - * |
|
| 162 | - * @param string $path |
|
| 163 | - * @param bool $run changing this flag to false in hook handler will cancel event |
|
| 164 | - */ |
|
| 165 | - const signal_update = 'update'; |
|
| 166 | - |
|
| 167 | - /** |
|
| 168 | - * signal emitted after file/dir update |
|
| 169 | - * |
|
| 170 | - * @param string $path |
|
| 171 | - * @param bool $run changing this flag to false in hook handler will cancel event |
|
| 172 | - */ |
|
| 173 | - const signal_post_update = 'post_update'; |
|
| 174 | - |
|
| 175 | - /** |
|
| 176 | - * signal emits when reading file/dir |
|
| 177 | - * |
|
| 178 | - * @param string $path |
|
| 179 | - */ |
|
| 180 | - const signal_read = 'read'; |
|
| 181 | - |
|
| 182 | - /** |
|
| 183 | - * signal emits when removing file/dir |
|
| 184 | - * |
|
| 185 | - * @param string $path |
|
| 186 | - */ |
|
| 187 | - const signal_delete = 'delete'; |
|
| 188 | - |
|
| 189 | - /** |
|
| 190 | - * parameters definitions for signals |
|
| 191 | - */ |
|
| 192 | - const signal_param_path = 'path'; |
|
| 193 | - const signal_param_oldpath = 'oldpath'; |
|
| 194 | - const signal_param_newpath = 'newpath'; |
|
| 195 | - |
|
| 196 | - /** |
|
| 197 | - * run - changing this flag to false in hook handler will cancel event |
|
| 198 | - */ |
|
| 199 | - const signal_param_run = 'run'; |
|
| 200 | - |
|
| 201 | - const signal_create_mount = 'create_mount'; |
|
| 202 | - const signal_delete_mount = 'delete_mount'; |
|
| 203 | - const signal_param_mount_type = 'mounttype'; |
|
| 204 | - const signal_param_users = 'users'; |
|
| 205 | - |
|
| 206 | - /** |
|
| 207 | - * @var \OC\Files\Storage\StorageFactory $loader |
|
| 208 | - */ |
|
| 209 | - private static $loader; |
|
| 210 | - |
|
| 211 | - /** @var bool */ |
|
| 212 | - private static $logWarningWhenAddingStorageWrapper = true; |
|
| 213 | - |
|
| 214 | - /** |
|
| 215 | - * @param bool $shouldLog |
|
| 216 | - * @return bool previous value |
|
| 217 | - * @internal |
|
| 218 | - */ |
|
| 219 | - public static function logWarningWhenAddingStorageWrapper($shouldLog) { |
|
| 220 | - $previousValue = self::$logWarningWhenAddingStorageWrapper; |
|
| 221 | - self::$logWarningWhenAddingStorageWrapper = (bool) $shouldLog; |
|
| 222 | - return $previousValue; |
|
| 223 | - } |
|
| 224 | - |
|
| 225 | - /** |
|
| 226 | - * @param string $wrapperName |
|
| 227 | - * @param callable $wrapper |
|
| 228 | - * @param int $priority |
|
| 229 | - */ |
|
| 230 | - public static function addStorageWrapper($wrapperName, $wrapper, $priority = 50) { |
|
| 231 | - if (self::$logWarningWhenAddingStorageWrapper) { |
|
| 232 | - \OC::$server->getLogger()->warning("Storage wrapper '{wrapper}' was not registered via the 'OC_Filesystem - preSetup' hook which could cause potential problems.", [ |
|
| 233 | - 'wrapper' => $wrapperName, |
|
| 234 | - 'app' => 'filesystem', |
|
| 235 | - ]); |
|
| 236 | - } |
|
| 237 | - |
|
| 238 | - $mounts = self::getMountManager()->getAll(); |
|
| 239 | - if (!self::getLoader()->addStorageWrapper($wrapperName, $wrapper, $priority, $mounts)) { |
|
| 240 | - // do not re-wrap if storage with this name already existed |
|
| 241 | - return; |
|
| 242 | - } |
|
| 243 | - } |
|
| 244 | - |
|
| 245 | - /** |
|
| 246 | - * Returns the storage factory |
|
| 247 | - * |
|
| 248 | - * @return \OCP\Files\Storage\IStorageFactory |
|
| 249 | - */ |
|
| 250 | - public static function getLoader() { |
|
| 251 | - if (!self::$loader) { |
|
| 252 | - self::$loader = new StorageFactory(); |
|
| 253 | - } |
|
| 254 | - return self::$loader; |
|
| 255 | - } |
|
| 256 | - |
|
| 257 | - /** |
|
| 258 | - * Returns the mount manager |
|
| 259 | - * |
|
| 260 | - * @return \OC\Files\Mount\Manager |
|
| 261 | - */ |
|
| 262 | - public static function getMountManager($user = '') { |
|
| 263 | - if (!self::$mounts) { |
|
| 264 | - \OC_Util::setupFS($user); |
|
| 265 | - } |
|
| 266 | - return self::$mounts; |
|
| 267 | - } |
|
| 268 | - |
|
| 269 | - /** |
|
| 270 | - * get the mountpoint of the storage object for a path |
|
| 271 | - * ( note: because a storage is not always mounted inside the fakeroot, the |
|
| 272 | - * returned mountpoint is relative to the absolute root of the filesystem |
|
| 273 | - * and doesn't take the chroot into account ) |
|
| 274 | - * |
|
| 275 | - * @param string $path |
|
| 276 | - * @return string |
|
| 277 | - */ |
|
| 278 | - static public function getMountPoint($path) { |
|
| 279 | - if (!self::$mounts) { |
|
| 280 | - \OC_Util::setupFS(); |
|
| 281 | - } |
|
| 282 | - $mount = self::$mounts->find($path); |
|
| 283 | - if ($mount) { |
|
| 284 | - return $mount->getMountPoint(); |
|
| 285 | - } else { |
|
| 286 | - return ''; |
|
| 287 | - } |
|
| 288 | - } |
|
| 289 | - |
|
| 290 | - /** |
|
| 291 | - * get a list of all mount points in a directory |
|
| 292 | - * |
|
| 293 | - * @param string $path |
|
| 294 | - * @return string[] |
|
| 295 | - */ |
|
| 296 | - static public function getMountPoints($path) { |
|
| 297 | - if (!self::$mounts) { |
|
| 298 | - \OC_Util::setupFS(); |
|
| 299 | - } |
|
| 300 | - $result = array(); |
|
| 301 | - $mounts = self::$mounts->findIn($path); |
|
| 302 | - foreach ($mounts as $mount) { |
|
| 303 | - $result[] = $mount->getMountPoint(); |
|
| 304 | - } |
|
| 305 | - return $result; |
|
| 306 | - } |
|
| 307 | - |
|
| 308 | - /** |
|
| 309 | - * get the storage mounted at $mountPoint |
|
| 310 | - * |
|
| 311 | - * @param string $mountPoint |
|
| 312 | - * @return \OC\Files\Storage\Storage |
|
| 313 | - */ |
|
| 314 | - public static function getStorage($mountPoint) { |
|
| 315 | - if (!self::$mounts) { |
|
| 316 | - \OC_Util::setupFS(); |
|
| 317 | - } |
|
| 318 | - $mount = self::$mounts->find($mountPoint); |
|
| 319 | - return $mount->getStorage(); |
|
| 320 | - } |
|
| 321 | - |
|
| 322 | - /** |
|
| 323 | - * @param string $id |
|
| 324 | - * @return Mount\MountPoint[] |
|
| 325 | - */ |
|
| 326 | - public static function getMountByStorageId($id) { |
|
| 327 | - if (!self::$mounts) { |
|
| 328 | - \OC_Util::setupFS(); |
|
| 329 | - } |
|
| 330 | - return self::$mounts->findByStorageId($id); |
|
| 331 | - } |
|
| 332 | - |
|
| 333 | - /** |
|
| 334 | - * @param int $id |
|
| 335 | - * @return Mount\MountPoint[] |
|
| 336 | - */ |
|
| 337 | - public static function getMountByNumericId($id) { |
|
| 338 | - if (!self::$mounts) { |
|
| 339 | - \OC_Util::setupFS(); |
|
| 340 | - } |
|
| 341 | - return self::$mounts->findByNumericId($id); |
|
| 342 | - } |
|
| 343 | - |
|
| 344 | - /** |
|
| 345 | - * resolve a path to a storage and internal path |
|
| 346 | - * |
|
| 347 | - * @param string $path |
|
| 348 | - * @return array an array consisting of the storage and the internal path |
|
| 349 | - */ |
|
| 350 | - static public function resolvePath($path) { |
|
| 351 | - if (!self::$mounts) { |
|
| 352 | - \OC_Util::setupFS(); |
|
| 353 | - } |
|
| 354 | - $mount = self::$mounts->find($path); |
|
| 355 | - if ($mount) { |
|
| 356 | - return array($mount->getStorage(), rtrim($mount->getInternalPath($path), '/')); |
|
| 357 | - } else { |
|
| 358 | - return array(null, null); |
|
| 359 | - } |
|
| 360 | - } |
|
| 361 | - |
|
| 362 | - static public function init($user, $root) { |
|
| 363 | - if (self::$defaultInstance) { |
|
| 364 | - return false; |
|
| 365 | - } |
|
| 366 | - self::getLoader(); |
|
| 367 | - self::$defaultInstance = new View($root); |
|
| 368 | - |
|
| 369 | - if (!self::$mounts) { |
|
| 370 | - self::$mounts = \OC::$server->getMountManager(); |
|
| 371 | - } |
|
| 372 | - |
|
| 373 | - //load custom mount config |
|
| 374 | - self::initMountPoints($user); |
|
| 375 | - |
|
| 376 | - self::$loaded = true; |
|
| 377 | - |
|
| 378 | - return true; |
|
| 379 | - } |
|
| 380 | - |
|
| 381 | - static public function initMountManager() { |
|
| 382 | - if (!self::$mounts) { |
|
| 383 | - self::$mounts = \OC::$server->getMountManager(); |
|
| 384 | - } |
|
| 385 | - } |
|
| 386 | - |
|
| 387 | - /** |
|
| 388 | - * Initialize system and personal mount points for a user |
|
| 389 | - * |
|
| 390 | - * @param string $user |
|
| 391 | - * @throws \OC\User\NoUserException if the user is not available |
|
| 392 | - */ |
|
| 393 | - public static function initMountPoints($user = '') { |
|
| 394 | - if ($user == '') { |
|
| 395 | - $user = \OC_User::getUser(); |
|
| 396 | - } |
|
| 397 | - if ($user === null || $user === false || $user === '') { |
|
| 398 | - throw new \OC\User\NoUserException('Attempted to initialize mount points for null user and no user in session'); |
|
| 399 | - } |
|
| 400 | - |
|
| 401 | - if (isset(self::$usersSetup[$user])) { |
|
| 402 | - return; |
|
| 403 | - } |
|
| 404 | - |
|
| 405 | - self::$usersSetup[$user] = true; |
|
| 406 | - |
|
| 407 | - $userManager = \OC::$server->getUserManager(); |
|
| 408 | - $userObject = $userManager->get($user); |
|
| 409 | - |
|
| 410 | - if (is_null($userObject)) { |
|
| 411 | - \OCP\Util::writeLog('files', ' Backends provided no user object for ' . $user, \OCP\Util::ERROR); |
|
| 412 | - // reset flag, this will make it possible to rethrow the exception if called again |
|
| 413 | - unset(self::$usersSetup[$user]); |
|
| 414 | - throw new \OC\User\NoUserException('Backends provided no user object for ' . $user); |
|
| 415 | - } |
|
| 416 | - |
|
| 417 | - $realUid = $userObject->getUID(); |
|
| 418 | - // workaround in case of different casings |
|
| 419 | - if ($user !== $realUid) { |
|
| 420 | - $stack = json_encode(debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS, 50)); |
|
| 421 | - \OCP\Util::writeLog('files', 'initMountPoints() called with wrong user casing. This could be a bug. Expected: "' . $realUid . '" got "' . $user . '". Stack: ' . $stack, \OCP\Util::WARN); |
|
| 422 | - $user = $realUid; |
|
| 423 | - |
|
| 424 | - // again with the correct casing |
|
| 425 | - if (isset(self::$usersSetup[$user])) { |
|
| 426 | - return; |
|
| 427 | - } |
|
| 428 | - |
|
| 429 | - self::$usersSetup[$user] = true; |
|
| 430 | - } |
|
| 431 | - |
|
| 432 | - if (\OC::$server->getLockdownManager()->canAccessFilesystem()) { |
|
| 433 | - /** @var \OC\Files\Config\MountProviderCollection $mountConfigManager */ |
|
| 434 | - $mountConfigManager = \OC::$server->getMountProviderCollection(); |
|
| 435 | - |
|
| 436 | - // home mounts are handled seperate since we need to ensure this is mounted before we call the other mount providers |
|
| 437 | - $homeMount = $mountConfigManager->getHomeMountForUser($userObject); |
|
| 438 | - |
|
| 439 | - self::getMountManager()->addMount($homeMount); |
|
| 440 | - |
|
| 441 | - \OC\Files\Filesystem::getStorage($user); |
|
| 442 | - |
|
| 443 | - // Chance to mount for other storages |
|
| 444 | - if ($userObject) { |
|
| 445 | - $mounts = $mountConfigManager->addMountForUser($userObject, self::getMountManager()); |
|
| 446 | - $mounts[] = $homeMount; |
|
| 447 | - $mountConfigManager->registerMounts($userObject, $mounts); |
|
| 448 | - } |
|
| 449 | - |
|
| 450 | - self::listenForNewMountProviders($mountConfigManager, $userManager); |
|
| 451 | - } else { |
|
| 452 | - self::getMountManager()->addMount(new MountPoint( |
|
| 453 | - new NullStorage([]), |
|
| 454 | - '/' . $user |
|
| 455 | - )); |
|
| 456 | - self::getMountManager()->addMount(new MountPoint( |
|
| 457 | - new NullStorage([]), |
|
| 458 | - '/' . $user . '/files' |
|
| 459 | - )); |
|
| 460 | - } |
|
| 461 | - \OC_Hook::emit('OC_Filesystem', 'post_initMountPoints', array('user' => $user)); |
|
| 462 | - } |
|
| 463 | - |
|
| 464 | - /** |
|
| 465 | - * Get mounts from mount providers that are registered after setup |
|
| 466 | - * |
|
| 467 | - * @param MountProviderCollection $mountConfigManager |
|
| 468 | - * @param IUserManager $userManager |
|
| 469 | - */ |
|
| 470 | - private static function listenForNewMountProviders(MountProviderCollection $mountConfigManager, IUserManager $userManager) { |
|
| 471 | - if (!self::$listeningForProviders) { |
|
| 472 | - self::$listeningForProviders = true; |
|
| 473 | - $mountConfigManager->listen('\OC\Files\Config', 'registerMountProvider', function (IMountProvider $provider) use ($userManager) { |
|
| 474 | - foreach (Filesystem::$usersSetup as $user => $setup) { |
|
| 475 | - $userObject = $userManager->get($user); |
|
| 476 | - if ($userObject) { |
|
| 477 | - $mounts = $provider->getMountsForUser($userObject, Filesystem::getLoader()); |
|
| 478 | - array_walk($mounts, array(self::$mounts, 'addMount')); |
|
| 479 | - } |
|
| 480 | - } |
|
| 481 | - }); |
|
| 482 | - } |
|
| 483 | - } |
|
| 484 | - |
|
| 485 | - /** |
|
| 486 | - * get the default filesystem view |
|
| 487 | - * |
|
| 488 | - * @return View |
|
| 489 | - */ |
|
| 490 | - static public function getView() { |
|
| 491 | - return self::$defaultInstance; |
|
| 492 | - } |
|
| 493 | - |
|
| 494 | - /** |
|
| 495 | - * tear down the filesystem, removing all storage providers |
|
| 496 | - */ |
|
| 497 | - static public function tearDown() { |
|
| 498 | - self::clearMounts(); |
|
| 499 | - self::$defaultInstance = null; |
|
| 500 | - } |
|
| 501 | - |
|
| 502 | - /** |
|
| 503 | - * get the relative path of the root data directory for the current user |
|
| 504 | - * |
|
| 505 | - * @return string |
|
| 506 | - * |
|
| 507 | - * Returns path like /admin/files |
|
| 508 | - */ |
|
| 509 | - static public function getRoot() { |
|
| 510 | - if (!self::$defaultInstance) { |
|
| 511 | - return null; |
|
| 512 | - } |
|
| 513 | - return self::$defaultInstance->getRoot(); |
|
| 514 | - } |
|
| 515 | - |
|
| 516 | - /** |
|
| 517 | - * clear all mounts and storage backends |
|
| 518 | - */ |
|
| 519 | - public static function clearMounts() { |
|
| 520 | - if (self::$mounts) { |
|
| 521 | - self::$usersSetup = array(); |
|
| 522 | - self::$mounts->clear(); |
|
| 523 | - } |
|
| 524 | - } |
|
| 525 | - |
|
| 526 | - /** |
|
| 527 | - * mount an \OC\Files\Storage\Storage in our virtual filesystem |
|
| 528 | - * |
|
| 529 | - * @param \OC\Files\Storage\Storage|string $class |
|
| 530 | - * @param array $arguments |
|
| 531 | - * @param string $mountpoint |
|
| 532 | - */ |
|
| 533 | - static public function mount($class, $arguments, $mountpoint) { |
|
| 534 | - if (!self::$mounts) { |
|
| 535 | - \OC_Util::setupFS(); |
|
| 536 | - } |
|
| 537 | - $mount = new Mount\MountPoint($class, $mountpoint, $arguments, self::getLoader()); |
|
| 538 | - self::$mounts->addMount($mount); |
|
| 539 | - } |
|
| 540 | - |
|
| 541 | - /** |
|
| 542 | - * return the path to a local version of the file |
|
| 543 | - * we need this because we can't know if a file is stored local or not from |
|
| 544 | - * outside the filestorage and for some purposes a local file is needed |
|
| 545 | - * |
|
| 546 | - * @param string $path |
|
| 547 | - * @return string |
|
| 548 | - */ |
|
| 549 | - static public function getLocalFile($path) { |
|
| 550 | - return self::$defaultInstance->getLocalFile($path); |
|
| 551 | - } |
|
| 552 | - |
|
| 553 | - /** |
|
| 554 | - * @param string $path |
|
| 555 | - * @return string |
|
| 556 | - */ |
|
| 557 | - static public function getLocalFolder($path) { |
|
| 558 | - return self::$defaultInstance->getLocalFolder($path); |
|
| 559 | - } |
|
| 560 | - |
|
| 561 | - /** |
|
| 562 | - * return path to file which reflects one visible in browser |
|
| 563 | - * |
|
| 564 | - * @param string $path |
|
| 565 | - * @return string |
|
| 566 | - */ |
|
| 567 | - static public function getLocalPath($path) { |
|
| 568 | - $datadir = \OC_User::getHome(\OC_User::getUser()) . '/files'; |
|
| 569 | - $newpath = $path; |
|
| 570 | - if (strncmp($newpath, $datadir, strlen($datadir)) == 0) { |
|
| 571 | - $newpath = substr($path, strlen($datadir)); |
|
| 572 | - } |
|
| 573 | - return $newpath; |
|
| 574 | - } |
|
| 575 | - |
|
| 576 | - /** |
|
| 577 | - * check if the requested path is valid |
|
| 578 | - * |
|
| 579 | - * @param string $path |
|
| 580 | - * @return bool |
|
| 581 | - */ |
|
| 582 | - static public function isValidPath($path) { |
|
| 583 | - $path = self::normalizePath($path); |
|
| 584 | - if (!$path || $path[0] !== '/') { |
|
| 585 | - $path = '/' . $path; |
|
| 586 | - } |
|
| 587 | - if (strpos($path, '/../') !== false || strrchr($path, '/') === '/..') { |
|
| 588 | - return false; |
|
| 589 | - } |
|
| 590 | - return true; |
|
| 591 | - } |
|
| 592 | - |
|
| 593 | - /** |
|
| 594 | - * checks if a file is blacklisted for storage in the filesystem |
|
| 595 | - * Listens to write and rename hooks |
|
| 596 | - * |
|
| 597 | - * @param array $data from hook |
|
| 598 | - */ |
|
| 599 | - static public function isBlacklisted($data) { |
|
| 600 | - if (isset($data['path'])) { |
|
| 601 | - $path = $data['path']; |
|
| 602 | - } else if (isset($data['newpath'])) { |
|
| 603 | - $path = $data['newpath']; |
|
| 604 | - } |
|
| 605 | - if (isset($path)) { |
|
| 606 | - if (self::isFileBlacklisted($path)) { |
|
| 607 | - $data['run'] = false; |
|
| 608 | - } |
|
| 609 | - } |
|
| 610 | - } |
|
| 611 | - |
|
| 612 | - /** |
|
| 613 | - * @param string $filename |
|
| 614 | - * @return bool |
|
| 615 | - */ |
|
| 616 | - static public function isFileBlacklisted($filename) { |
|
| 617 | - $filename = self::normalizePath($filename); |
|
| 618 | - |
|
| 619 | - $blacklist = \OC::$server->getConfig()->getSystemValue('blacklisted_files', array('.htaccess')); |
|
| 620 | - $filename = strtolower(basename($filename)); |
|
| 621 | - return in_array($filename, $blacklist); |
|
| 622 | - } |
|
| 623 | - |
|
| 624 | - /** |
|
| 625 | - * check if the directory should be ignored when scanning |
|
| 626 | - * NOTE: the special directories . and .. would cause never ending recursion |
|
| 627 | - * |
|
| 628 | - * @param String $dir |
|
| 629 | - * @return boolean |
|
| 630 | - */ |
|
| 631 | - static public function isIgnoredDir($dir) { |
|
| 632 | - if ($dir === '.' || $dir === '..') { |
|
| 633 | - return true; |
|
| 634 | - } |
|
| 635 | - return false; |
|
| 636 | - } |
|
| 637 | - |
|
| 638 | - /** |
|
| 639 | - * following functions are equivalent to their php builtin equivalents for arguments/return values. |
|
| 640 | - */ |
|
| 641 | - static public function mkdir($path) { |
|
| 642 | - return self::$defaultInstance->mkdir($path); |
|
| 643 | - } |
|
| 644 | - |
|
| 645 | - static public function rmdir($path) { |
|
| 646 | - return self::$defaultInstance->rmdir($path); |
|
| 647 | - } |
|
| 648 | - |
|
| 649 | - static public function is_dir($path) { |
|
| 650 | - return self::$defaultInstance->is_dir($path); |
|
| 651 | - } |
|
| 652 | - |
|
| 653 | - static public function is_file($path) { |
|
| 654 | - return self::$defaultInstance->is_file($path); |
|
| 655 | - } |
|
| 656 | - |
|
| 657 | - static public function stat($path) { |
|
| 658 | - return self::$defaultInstance->stat($path); |
|
| 659 | - } |
|
| 660 | - |
|
| 661 | - static public function filetype($path) { |
|
| 662 | - return self::$defaultInstance->filetype($path); |
|
| 663 | - } |
|
| 664 | - |
|
| 665 | - static public function filesize($path) { |
|
| 666 | - return self::$defaultInstance->filesize($path); |
|
| 667 | - } |
|
| 668 | - |
|
| 669 | - static public function readfile($path) { |
|
| 670 | - return self::$defaultInstance->readfile($path); |
|
| 671 | - } |
|
| 672 | - |
|
| 673 | - static public function isCreatable($path) { |
|
| 674 | - return self::$defaultInstance->isCreatable($path); |
|
| 675 | - } |
|
| 676 | - |
|
| 677 | - static public function isReadable($path) { |
|
| 678 | - return self::$defaultInstance->isReadable($path); |
|
| 679 | - } |
|
| 680 | - |
|
| 681 | - static public function isUpdatable($path) { |
|
| 682 | - return self::$defaultInstance->isUpdatable($path); |
|
| 683 | - } |
|
| 684 | - |
|
| 685 | - static public function isDeletable($path) { |
|
| 686 | - return self::$defaultInstance->isDeletable($path); |
|
| 687 | - } |
|
| 688 | - |
|
| 689 | - static public function isSharable($path) { |
|
| 690 | - return self::$defaultInstance->isSharable($path); |
|
| 691 | - } |
|
| 692 | - |
|
| 693 | - static public function file_exists($path) { |
|
| 694 | - return self::$defaultInstance->file_exists($path); |
|
| 695 | - } |
|
| 696 | - |
|
| 697 | - static public function filemtime($path) { |
|
| 698 | - return self::$defaultInstance->filemtime($path); |
|
| 699 | - } |
|
| 700 | - |
|
| 701 | - static public function touch($path, $mtime = null) { |
|
| 702 | - return self::$defaultInstance->touch($path, $mtime); |
|
| 703 | - } |
|
| 704 | - |
|
| 705 | - /** |
|
| 706 | - * @return string |
|
| 707 | - */ |
|
| 708 | - static public function file_get_contents($path) { |
|
| 709 | - return self::$defaultInstance->file_get_contents($path); |
|
| 710 | - } |
|
| 711 | - |
|
| 712 | - static public function file_put_contents($path, $data) { |
|
| 713 | - return self::$defaultInstance->file_put_contents($path, $data); |
|
| 714 | - } |
|
| 715 | - |
|
| 716 | - static public function unlink($path) { |
|
| 717 | - return self::$defaultInstance->unlink($path); |
|
| 718 | - } |
|
| 719 | - |
|
| 720 | - static public function rename($path1, $path2) { |
|
| 721 | - return self::$defaultInstance->rename($path1, $path2); |
|
| 722 | - } |
|
| 723 | - |
|
| 724 | - static public function copy($path1, $path2) { |
|
| 725 | - return self::$defaultInstance->copy($path1, $path2); |
|
| 726 | - } |
|
| 727 | - |
|
| 728 | - static public function fopen($path, $mode) { |
|
| 729 | - return self::$defaultInstance->fopen($path, $mode); |
|
| 730 | - } |
|
| 731 | - |
|
| 732 | - /** |
|
| 733 | - * @return string |
|
| 734 | - */ |
|
| 735 | - static public function toTmpFile($path) { |
|
| 736 | - return self::$defaultInstance->toTmpFile($path); |
|
| 737 | - } |
|
| 738 | - |
|
| 739 | - static public function fromTmpFile($tmpFile, $path) { |
|
| 740 | - return self::$defaultInstance->fromTmpFile($tmpFile, $path); |
|
| 741 | - } |
|
| 742 | - |
|
| 743 | - static public function getMimeType($path) { |
|
| 744 | - return self::$defaultInstance->getMimeType($path); |
|
| 745 | - } |
|
| 746 | - |
|
| 747 | - static public function hash($type, $path, $raw = false) { |
|
| 748 | - return self::$defaultInstance->hash($type, $path, $raw); |
|
| 749 | - } |
|
| 750 | - |
|
| 751 | - static public function free_space($path = '/') { |
|
| 752 | - return self::$defaultInstance->free_space($path); |
|
| 753 | - } |
|
| 754 | - |
|
| 755 | - static public function search($query) { |
|
| 756 | - return self::$defaultInstance->search($query); |
|
| 757 | - } |
|
| 758 | - |
|
| 759 | - /** |
|
| 760 | - * @param string $query |
|
| 761 | - */ |
|
| 762 | - static public function searchByMime($query) { |
|
| 763 | - return self::$defaultInstance->searchByMime($query); |
|
| 764 | - } |
|
| 765 | - |
|
| 766 | - /** |
|
| 767 | - * @param string|int $tag name or tag id |
|
| 768 | - * @param string $userId owner of the tags |
|
| 769 | - * @return FileInfo[] array or file info |
|
| 770 | - */ |
|
| 771 | - static public function searchByTag($tag, $userId) { |
|
| 772 | - return self::$defaultInstance->searchByTag($tag, $userId); |
|
| 773 | - } |
|
| 774 | - |
|
| 775 | - /** |
|
| 776 | - * check if a file or folder has been updated since $time |
|
| 777 | - * |
|
| 778 | - * @param string $path |
|
| 779 | - * @param int $time |
|
| 780 | - * @return bool |
|
| 781 | - */ |
|
| 782 | - static public function hasUpdated($path, $time) { |
|
| 783 | - return self::$defaultInstance->hasUpdated($path, $time); |
|
| 784 | - } |
|
| 785 | - |
|
| 786 | - /** |
|
| 787 | - * Fix common problems with a file path |
|
| 788 | - * |
|
| 789 | - * @param string $path |
|
| 790 | - * @param bool $stripTrailingSlash whether to strip the trailing slash |
|
| 791 | - * @param bool $isAbsolutePath whether the given path is absolute |
|
| 792 | - * @param bool $keepUnicode true to disable unicode normalization |
|
| 793 | - * @return string |
|
| 794 | - */ |
|
| 795 | - public static function normalizePath($path, $stripTrailingSlash = true, $isAbsolutePath = false, $keepUnicode = false) { |
|
| 796 | - if (is_null(self::$normalizedPathCache)) { |
|
| 797 | - self::$normalizedPathCache = new CappedMemoryCache(); |
|
| 798 | - } |
|
| 799 | - |
|
| 800 | - /** |
|
| 801 | - * FIXME: This is a workaround for existing classes and files which call |
|
| 802 | - * this function with another type than a valid string. This |
|
| 803 | - * conversion should get removed as soon as all existing |
|
| 804 | - * function calls have been fixed. |
|
| 805 | - */ |
|
| 806 | - $path = (string)$path; |
|
| 807 | - |
|
| 808 | - $cacheKey = json_encode([$path, $stripTrailingSlash, $isAbsolutePath, $keepUnicode]); |
|
| 809 | - |
|
| 810 | - if (isset(self::$normalizedPathCache[$cacheKey])) { |
|
| 811 | - return self::$normalizedPathCache[$cacheKey]; |
|
| 812 | - } |
|
| 813 | - |
|
| 814 | - if ($path == '') { |
|
| 815 | - return '/'; |
|
| 816 | - } |
|
| 817 | - |
|
| 818 | - //normalize unicode if possible |
|
| 819 | - if (!$keepUnicode) { |
|
| 820 | - $path = \OC_Util::normalizeUnicode($path); |
|
| 821 | - } |
|
| 822 | - |
|
| 823 | - //no windows style slashes |
|
| 824 | - $path = str_replace('\\', '/', $path); |
|
| 825 | - |
|
| 826 | - //add leading slash |
|
| 827 | - if ($path[0] !== '/') { |
|
| 828 | - $path = '/' . $path; |
|
| 829 | - } |
|
| 830 | - |
|
| 831 | - // remove '/./' |
|
| 832 | - // ugly, but str_replace() can't replace them all in one go |
|
| 833 | - // as the replacement itself is part of the search string |
|
| 834 | - // which will only be found during the next iteration |
|
| 835 | - while (strpos($path, '/./') !== false) { |
|
| 836 | - $path = str_replace('/./', '/', $path); |
|
| 837 | - } |
|
| 838 | - // remove sequences of slashes |
|
| 839 | - $path = preg_replace('#/{2,}#', '/', $path); |
|
| 840 | - |
|
| 841 | - //remove trailing slash |
|
| 842 | - if ($stripTrailingSlash and strlen($path) > 1 and substr($path, -1, 1) === '/') { |
|
| 843 | - $path = substr($path, 0, -1); |
|
| 844 | - } |
|
| 845 | - |
|
| 846 | - // remove trailing '/.' |
|
| 847 | - if (substr($path, -2) == '/.') { |
|
| 848 | - $path = substr($path, 0, -2); |
|
| 849 | - } |
|
| 850 | - |
|
| 851 | - $normalizedPath = $path; |
|
| 852 | - self::$normalizedPathCache[$cacheKey] = $normalizedPath; |
|
| 853 | - |
|
| 854 | - return $normalizedPath; |
|
| 855 | - } |
|
| 856 | - |
|
| 857 | - /** |
|
| 858 | - * get the filesystem info |
|
| 859 | - * |
|
| 860 | - * @param string $path |
|
| 861 | - * @param boolean $includeMountPoints whether to add mountpoint sizes, |
|
| 862 | - * defaults to true |
|
| 863 | - * @return \OC\Files\FileInfo|bool False if file does not exist |
|
| 864 | - */ |
|
| 865 | - public static function getFileInfo($path, $includeMountPoints = true) { |
|
| 866 | - return self::$defaultInstance->getFileInfo($path, $includeMountPoints); |
|
| 867 | - } |
|
| 868 | - |
|
| 869 | - /** |
|
| 870 | - * change file metadata |
|
| 871 | - * |
|
| 872 | - * @param string $path |
|
| 873 | - * @param array $data |
|
| 874 | - * @return int |
|
| 875 | - * |
|
| 876 | - * returns the fileid of the updated file |
|
| 877 | - */ |
|
| 878 | - public static function putFileInfo($path, $data) { |
|
| 879 | - return self::$defaultInstance->putFileInfo($path, $data); |
|
| 880 | - } |
|
| 881 | - |
|
| 882 | - /** |
|
| 883 | - * get the content of a directory |
|
| 884 | - * |
|
| 885 | - * @param string $directory path under datadirectory |
|
| 886 | - * @param string $mimetype_filter limit returned content to this mimetype or mimepart |
|
| 887 | - * @return \OC\Files\FileInfo[] |
|
| 888 | - */ |
|
| 889 | - public static function getDirectoryContent($directory, $mimetype_filter = '') { |
|
| 890 | - return self::$defaultInstance->getDirectoryContent($directory, $mimetype_filter); |
|
| 891 | - } |
|
| 892 | - |
|
| 893 | - /** |
|
| 894 | - * Get the path of a file by id |
|
| 895 | - * |
|
| 896 | - * Note that the resulting path is not guaranteed to be unique for the id, multiple paths can point to the same file |
|
| 897 | - * |
|
| 898 | - * @param int $id |
|
| 899 | - * @throws NotFoundException |
|
| 900 | - * @return string |
|
| 901 | - */ |
|
| 902 | - public static function getPath($id) { |
|
| 903 | - return self::$defaultInstance->getPath($id); |
|
| 904 | - } |
|
| 905 | - |
|
| 906 | - /** |
|
| 907 | - * Get the owner for a file or folder |
|
| 908 | - * |
|
| 909 | - * @param string $path |
|
| 910 | - * @return string |
|
| 911 | - */ |
|
| 912 | - public static function getOwner($path) { |
|
| 913 | - return self::$defaultInstance->getOwner($path); |
|
| 914 | - } |
|
| 915 | - |
|
| 916 | - /** |
|
| 917 | - * get the ETag for a file or folder |
|
| 918 | - * |
|
| 919 | - * @param string $path |
|
| 920 | - * @return string |
|
| 921 | - */ |
|
| 922 | - static public function getETag($path) { |
|
| 923 | - return self::$defaultInstance->getETag($path); |
|
| 924 | - } |
|
| 72 | + /** |
|
| 73 | + * @var Mount\Manager $mounts |
|
| 74 | + */ |
|
| 75 | + private static $mounts; |
|
| 76 | + |
|
| 77 | + public static $loaded = false; |
|
| 78 | + /** |
|
| 79 | + * @var \OC\Files\View $defaultInstance |
|
| 80 | + */ |
|
| 81 | + static private $defaultInstance; |
|
| 82 | + |
|
| 83 | + static private $usersSetup = array(); |
|
| 84 | + |
|
| 85 | + static private $normalizedPathCache = null; |
|
| 86 | + |
|
| 87 | + static private $listeningForProviders = false; |
|
| 88 | + |
|
| 89 | + /** |
|
| 90 | + * classname which used for hooks handling |
|
| 91 | + * used as signalclass in OC_Hooks::emit() |
|
| 92 | + */ |
|
| 93 | + const CLASSNAME = 'OC_Filesystem'; |
|
| 94 | + |
|
| 95 | + /** |
|
| 96 | + * signalname emitted before file renaming |
|
| 97 | + * |
|
| 98 | + * @param string $oldpath |
|
| 99 | + * @param string $newpath |
|
| 100 | + */ |
|
| 101 | + const signal_rename = 'rename'; |
|
| 102 | + |
|
| 103 | + /** |
|
| 104 | + * signal emitted after file renaming |
|
| 105 | + * |
|
| 106 | + * @param string $oldpath |
|
| 107 | + * @param string $newpath |
|
| 108 | + */ |
|
| 109 | + const signal_post_rename = 'post_rename'; |
|
| 110 | + |
|
| 111 | + /** |
|
| 112 | + * signal emitted before file/dir creation |
|
| 113 | + * |
|
| 114 | + * @param string $path |
|
| 115 | + * @param bool $run changing this flag to false in hook handler will cancel event |
|
| 116 | + */ |
|
| 117 | + const signal_create = 'create'; |
|
| 118 | + |
|
| 119 | + /** |
|
| 120 | + * signal emitted after file/dir creation |
|
| 121 | + * |
|
| 122 | + * @param string $path |
|
| 123 | + * @param bool $run changing this flag to false in hook handler will cancel event |
|
| 124 | + */ |
|
| 125 | + const signal_post_create = 'post_create'; |
|
| 126 | + |
|
| 127 | + /** |
|
| 128 | + * signal emits before file/dir copy |
|
| 129 | + * |
|
| 130 | + * @param string $oldpath |
|
| 131 | + * @param string $newpath |
|
| 132 | + * @param bool $run changing this flag to false in hook handler will cancel event |
|
| 133 | + */ |
|
| 134 | + const signal_copy = 'copy'; |
|
| 135 | + |
|
| 136 | + /** |
|
| 137 | + * signal emits after file/dir copy |
|
| 138 | + * |
|
| 139 | + * @param string $oldpath |
|
| 140 | + * @param string $newpath |
|
| 141 | + */ |
|
| 142 | + const signal_post_copy = 'post_copy'; |
|
| 143 | + |
|
| 144 | + /** |
|
| 145 | + * signal emits before file/dir save |
|
| 146 | + * |
|
| 147 | + * @param string $path |
|
| 148 | + * @param bool $run changing this flag to false in hook handler will cancel event |
|
| 149 | + */ |
|
| 150 | + const signal_write = 'write'; |
|
| 151 | + |
|
| 152 | + /** |
|
| 153 | + * signal emits after file/dir save |
|
| 154 | + * |
|
| 155 | + * @param string $path |
|
| 156 | + */ |
|
| 157 | + const signal_post_write = 'post_write'; |
|
| 158 | + |
|
| 159 | + /** |
|
| 160 | + * signal emitted before file/dir update |
|
| 161 | + * |
|
| 162 | + * @param string $path |
|
| 163 | + * @param bool $run changing this flag to false in hook handler will cancel event |
|
| 164 | + */ |
|
| 165 | + const signal_update = 'update'; |
|
| 166 | + |
|
| 167 | + /** |
|
| 168 | + * signal emitted after file/dir update |
|
| 169 | + * |
|
| 170 | + * @param string $path |
|
| 171 | + * @param bool $run changing this flag to false in hook handler will cancel event |
|
| 172 | + */ |
|
| 173 | + const signal_post_update = 'post_update'; |
|
| 174 | + |
|
| 175 | + /** |
|
| 176 | + * signal emits when reading file/dir |
|
| 177 | + * |
|
| 178 | + * @param string $path |
|
| 179 | + */ |
|
| 180 | + const signal_read = 'read'; |
|
| 181 | + |
|
| 182 | + /** |
|
| 183 | + * signal emits when removing file/dir |
|
| 184 | + * |
|
| 185 | + * @param string $path |
|
| 186 | + */ |
|
| 187 | + const signal_delete = 'delete'; |
|
| 188 | + |
|
| 189 | + /** |
|
| 190 | + * parameters definitions for signals |
|
| 191 | + */ |
|
| 192 | + const signal_param_path = 'path'; |
|
| 193 | + const signal_param_oldpath = 'oldpath'; |
|
| 194 | + const signal_param_newpath = 'newpath'; |
|
| 195 | + |
|
| 196 | + /** |
|
| 197 | + * run - changing this flag to false in hook handler will cancel event |
|
| 198 | + */ |
|
| 199 | + const signal_param_run = 'run'; |
|
| 200 | + |
|
| 201 | + const signal_create_mount = 'create_mount'; |
|
| 202 | + const signal_delete_mount = 'delete_mount'; |
|
| 203 | + const signal_param_mount_type = 'mounttype'; |
|
| 204 | + const signal_param_users = 'users'; |
|
| 205 | + |
|
| 206 | + /** |
|
| 207 | + * @var \OC\Files\Storage\StorageFactory $loader |
|
| 208 | + */ |
|
| 209 | + private static $loader; |
|
| 210 | + |
|
| 211 | + /** @var bool */ |
|
| 212 | + private static $logWarningWhenAddingStorageWrapper = true; |
|
| 213 | + |
|
| 214 | + /** |
|
| 215 | + * @param bool $shouldLog |
|
| 216 | + * @return bool previous value |
|
| 217 | + * @internal |
|
| 218 | + */ |
|
| 219 | + public static function logWarningWhenAddingStorageWrapper($shouldLog) { |
|
| 220 | + $previousValue = self::$logWarningWhenAddingStorageWrapper; |
|
| 221 | + self::$logWarningWhenAddingStorageWrapper = (bool) $shouldLog; |
|
| 222 | + return $previousValue; |
|
| 223 | + } |
|
| 224 | + |
|
| 225 | + /** |
|
| 226 | + * @param string $wrapperName |
|
| 227 | + * @param callable $wrapper |
|
| 228 | + * @param int $priority |
|
| 229 | + */ |
|
| 230 | + public static function addStorageWrapper($wrapperName, $wrapper, $priority = 50) { |
|
| 231 | + if (self::$logWarningWhenAddingStorageWrapper) { |
|
| 232 | + \OC::$server->getLogger()->warning("Storage wrapper '{wrapper}' was not registered via the 'OC_Filesystem - preSetup' hook which could cause potential problems.", [ |
|
| 233 | + 'wrapper' => $wrapperName, |
|
| 234 | + 'app' => 'filesystem', |
|
| 235 | + ]); |
|
| 236 | + } |
|
| 237 | + |
|
| 238 | + $mounts = self::getMountManager()->getAll(); |
|
| 239 | + if (!self::getLoader()->addStorageWrapper($wrapperName, $wrapper, $priority, $mounts)) { |
|
| 240 | + // do not re-wrap if storage with this name already existed |
|
| 241 | + return; |
|
| 242 | + } |
|
| 243 | + } |
|
| 244 | + |
|
| 245 | + /** |
|
| 246 | + * Returns the storage factory |
|
| 247 | + * |
|
| 248 | + * @return \OCP\Files\Storage\IStorageFactory |
|
| 249 | + */ |
|
| 250 | + public static function getLoader() { |
|
| 251 | + if (!self::$loader) { |
|
| 252 | + self::$loader = new StorageFactory(); |
|
| 253 | + } |
|
| 254 | + return self::$loader; |
|
| 255 | + } |
|
| 256 | + |
|
| 257 | + /** |
|
| 258 | + * Returns the mount manager |
|
| 259 | + * |
|
| 260 | + * @return \OC\Files\Mount\Manager |
|
| 261 | + */ |
|
| 262 | + public static function getMountManager($user = '') { |
|
| 263 | + if (!self::$mounts) { |
|
| 264 | + \OC_Util::setupFS($user); |
|
| 265 | + } |
|
| 266 | + return self::$mounts; |
|
| 267 | + } |
|
| 268 | + |
|
| 269 | + /** |
|
| 270 | + * get the mountpoint of the storage object for a path |
|
| 271 | + * ( note: because a storage is not always mounted inside the fakeroot, the |
|
| 272 | + * returned mountpoint is relative to the absolute root of the filesystem |
|
| 273 | + * and doesn't take the chroot into account ) |
|
| 274 | + * |
|
| 275 | + * @param string $path |
|
| 276 | + * @return string |
|
| 277 | + */ |
|
| 278 | + static public function getMountPoint($path) { |
|
| 279 | + if (!self::$mounts) { |
|
| 280 | + \OC_Util::setupFS(); |
|
| 281 | + } |
|
| 282 | + $mount = self::$mounts->find($path); |
|
| 283 | + if ($mount) { |
|
| 284 | + return $mount->getMountPoint(); |
|
| 285 | + } else { |
|
| 286 | + return ''; |
|
| 287 | + } |
|
| 288 | + } |
|
| 289 | + |
|
| 290 | + /** |
|
| 291 | + * get a list of all mount points in a directory |
|
| 292 | + * |
|
| 293 | + * @param string $path |
|
| 294 | + * @return string[] |
|
| 295 | + */ |
|
| 296 | + static public function getMountPoints($path) { |
|
| 297 | + if (!self::$mounts) { |
|
| 298 | + \OC_Util::setupFS(); |
|
| 299 | + } |
|
| 300 | + $result = array(); |
|
| 301 | + $mounts = self::$mounts->findIn($path); |
|
| 302 | + foreach ($mounts as $mount) { |
|
| 303 | + $result[] = $mount->getMountPoint(); |
|
| 304 | + } |
|
| 305 | + return $result; |
|
| 306 | + } |
|
| 307 | + |
|
| 308 | + /** |
|
| 309 | + * get the storage mounted at $mountPoint |
|
| 310 | + * |
|
| 311 | + * @param string $mountPoint |
|
| 312 | + * @return \OC\Files\Storage\Storage |
|
| 313 | + */ |
|
| 314 | + public static function getStorage($mountPoint) { |
|
| 315 | + if (!self::$mounts) { |
|
| 316 | + \OC_Util::setupFS(); |
|
| 317 | + } |
|
| 318 | + $mount = self::$mounts->find($mountPoint); |
|
| 319 | + return $mount->getStorage(); |
|
| 320 | + } |
|
| 321 | + |
|
| 322 | + /** |
|
| 323 | + * @param string $id |
|
| 324 | + * @return Mount\MountPoint[] |
|
| 325 | + */ |
|
| 326 | + public static function getMountByStorageId($id) { |
|
| 327 | + if (!self::$mounts) { |
|
| 328 | + \OC_Util::setupFS(); |
|
| 329 | + } |
|
| 330 | + return self::$mounts->findByStorageId($id); |
|
| 331 | + } |
|
| 332 | + |
|
| 333 | + /** |
|
| 334 | + * @param int $id |
|
| 335 | + * @return Mount\MountPoint[] |
|
| 336 | + */ |
|
| 337 | + public static function getMountByNumericId($id) { |
|
| 338 | + if (!self::$mounts) { |
|
| 339 | + \OC_Util::setupFS(); |
|
| 340 | + } |
|
| 341 | + return self::$mounts->findByNumericId($id); |
|
| 342 | + } |
|
| 343 | + |
|
| 344 | + /** |
|
| 345 | + * resolve a path to a storage and internal path |
|
| 346 | + * |
|
| 347 | + * @param string $path |
|
| 348 | + * @return array an array consisting of the storage and the internal path |
|
| 349 | + */ |
|
| 350 | + static public function resolvePath($path) { |
|
| 351 | + if (!self::$mounts) { |
|
| 352 | + \OC_Util::setupFS(); |
|
| 353 | + } |
|
| 354 | + $mount = self::$mounts->find($path); |
|
| 355 | + if ($mount) { |
|
| 356 | + return array($mount->getStorage(), rtrim($mount->getInternalPath($path), '/')); |
|
| 357 | + } else { |
|
| 358 | + return array(null, null); |
|
| 359 | + } |
|
| 360 | + } |
|
| 361 | + |
|
| 362 | + static public function init($user, $root) { |
|
| 363 | + if (self::$defaultInstance) { |
|
| 364 | + return false; |
|
| 365 | + } |
|
| 366 | + self::getLoader(); |
|
| 367 | + self::$defaultInstance = new View($root); |
|
| 368 | + |
|
| 369 | + if (!self::$mounts) { |
|
| 370 | + self::$mounts = \OC::$server->getMountManager(); |
|
| 371 | + } |
|
| 372 | + |
|
| 373 | + //load custom mount config |
|
| 374 | + self::initMountPoints($user); |
|
| 375 | + |
|
| 376 | + self::$loaded = true; |
|
| 377 | + |
|
| 378 | + return true; |
|
| 379 | + } |
|
| 380 | + |
|
| 381 | + static public function initMountManager() { |
|
| 382 | + if (!self::$mounts) { |
|
| 383 | + self::$mounts = \OC::$server->getMountManager(); |
|
| 384 | + } |
|
| 385 | + } |
|
| 386 | + |
|
| 387 | + /** |
|
| 388 | + * Initialize system and personal mount points for a user |
|
| 389 | + * |
|
| 390 | + * @param string $user |
|
| 391 | + * @throws \OC\User\NoUserException if the user is not available |
|
| 392 | + */ |
|
| 393 | + public static function initMountPoints($user = '') { |
|
| 394 | + if ($user == '') { |
|
| 395 | + $user = \OC_User::getUser(); |
|
| 396 | + } |
|
| 397 | + if ($user === null || $user === false || $user === '') { |
|
| 398 | + throw new \OC\User\NoUserException('Attempted to initialize mount points for null user and no user in session'); |
|
| 399 | + } |
|
| 400 | + |
|
| 401 | + if (isset(self::$usersSetup[$user])) { |
|
| 402 | + return; |
|
| 403 | + } |
|
| 404 | + |
|
| 405 | + self::$usersSetup[$user] = true; |
|
| 406 | + |
|
| 407 | + $userManager = \OC::$server->getUserManager(); |
|
| 408 | + $userObject = $userManager->get($user); |
|
| 409 | + |
|
| 410 | + if (is_null($userObject)) { |
|
| 411 | + \OCP\Util::writeLog('files', ' Backends provided no user object for ' . $user, \OCP\Util::ERROR); |
|
| 412 | + // reset flag, this will make it possible to rethrow the exception if called again |
|
| 413 | + unset(self::$usersSetup[$user]); |
|
| 414 | + throw new \OC\User\NoUserException('Backends provided no user object for ' . $user); |
|
| 415 | + } |
|
| 416 | + |
|
| 417 | + $realUid = $userObject->getUID(); |
|
| 418 | + // workaround in case of different casings |
|
| 419 | + if ($user !== $realUid) { |
|
| 420 | + $stack = json_encode(debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS, 50)); |
|
| 421 | + \OCP\Util::writeLog('files', 'initMountPoints() called with wrong user casing. This could be a bug. Expected: "' . $realUid . '" got "' . $user . '". Stack: ' . $stack, \OCP\Util::WARN); |
|
| 422 | + $user = $realUid; |
|
| 423 | + |
|
| 424 | + // again with the correct casing |
|
| 425 | + if (isset(self::$usersSetup[$user])) { |
|
| 426 | + return; |
|
| 427 | + } |
|
| 428 | + |
|
| 429 | + self::$usersSetup[$user] = true; |
|
| 430 | + } |
|
| 431 | + |
|
| 432 | + if (\OC::$server->getLockdownManager()->canAccessFilesystem()) { |
|
| 433 | + /** @var \OC\Files\Config\MountProviderCollection $mountConfigManager */ |
|
| 434 | + $mountConfigManager = \OC::$server->getMountProviderCollection(); |
|
| 435 | + |
|
| 436 | + // home mounts are handled seperate since we need to ensure this is mounted before we call the other mount providers |
|
| 437 | + $homeMount = $mountConfigManager->getHomeMountForUser($userObject); |
|
| 438 | + |
|
| 439 | + self::getMountManager()->addMount($homeMount); |
|
| 440 | + |
|
| 441 | + \OC\Files\Filesystem::getStorage($user); |
|
| 442 | + |
|
| 443 | + // Chance to mount for other storages |
|
| 444 | + if ($userObject) { |
|
| 445 | + $mounts = $mountConfigManager->addMountForUser($userObject, self::getMountManager()); |
|
| 446 | + $mounts[] = $homeMount; |
|
| 447 | + $mountConfigManager->registerMounts($userObject, $mounts); |
|
| 448 | + } |
|
| 449 | + |
|
| 450 | + self::listenForNewMountProviders($mountConfigManager, $userManager); |
|
| 451 | + } else { |
|
| 452 | + self::getMountManager()->addMount(new MountPoint( |
|
| 453 | + new NullStorage([]), |
|
| 454 | + '/' . $user |
|
| 455 | + )); |
|
| 456 | + self::getMountManager()->addMount(new MountPoint( |
|
| 457 | + new NullStorage([]), |
|
| 458 | + '/' . $user . '/files' |
|
| 459 | + )); |
|
| 460 | + } |
|
| 461 | + \OC_Hook::emit('OC_Filesystem', 'post_initMountPoints', array('user' => $user)); |
|
| 462 | + } |
|
| 463 | + |
|
| 464 | + /** |
|
| 465 | + * Get mounts from mount providers that are registered after setup |
|
| 466 | + * |
|
| 467 | + * @param MountProviderCollection $mountConfigManager |
|
| 468 | + * @param IUserManager $userManager |
|
| 469 | + */ |
|
| 470 | + private static function listenForNewMountProviders(MountProviderCollection $mountConfigManager, IUserManager $userManager) { |
|
| 471 | + if (!self::$listeningForProviders) { |
|
| 472 | + self::$listeningForProviders = true; |
|
| 473 | + $mountConfigManager->listen('\OC\Files\Config', 'registerMountProvider', function (IMountProvider $provider) use ($userManager) { |
|
| 474 | + foreach (Filesystem::$usersSetup as $user => $setup) { |
|
| 475 | + $userObject = $userManager->get($user); |
|
| 476 | + if ($userObject) { |
|
| 477 | + $mounts = $provider->getMountsForUser($userObject, Filesystem::getLoader()); |
|
| 478 | + array_walk($mounts, array(self::$mounts, 'addMount')); |
|
| 479 | + } |
|
| 480 | + } |
|
| 481 | + }); |
|
| 482 | + } |
|
| 483 | + } |
|
| 484 | + |
|
| 485 | + /** |
|
| 486 | + * get the default filesystem view |
|
| 487 | + * |
|
| 488 | + * @return View |
|
| 489 | + */ |
|
| 490 | + static public function getView() { |
|
| 491 | + return self::$defaultInstance; |
|
| 492 | + } |
|
| 493 | + |
|
| 494 | + /** |
|
| 495 | + * tear down the filesystem, removing all storage providers |
|
| 496 | + */ |
|
| 497 | + static public function tearDown() { |
|
| 498 | + self::clearMounts(); |
|
| 499 | + self::$defaultInstance = null; |
|
| 500 | + } |
|
| 501 | + |
|
| 502 | + /** |
|
| 503 | + * get the relative path of the root data directory for the current user |
|
| 504 | + * |
|
| 505 | + * @return string |
|
| 506 | + * |
|
| 507 | + * Returns path like /admin/files |
|
| 508 | + */ |
|
| 509 | + static public function getRoot() { |
|
| 510 | + if (!self::$defaultInstance) { |
|
| 511 | + return null; |
|
| 512 | + } |
|
| 513 | + return self::$defaultInstance->getRoot(); |
|
| 514 | + } |
|
| 515 | + |
|
| 516 | + /** |
|
| 517 | + * clear all mounts and storage backends |
|
| 518 | + */ |
|
| 519 | + public static function clearMounts() { |
|
| 520 | + if (self::$mounts) { |
|
| 521 | + self::$usersSetup = array(); |
|
| 522 | + self::$mounts->clear(); |
|
| 523 | + } |
|
| 524 | + } |
|
| 525 | + |
|
| 526 | + /** |
|
| 527 | + * mount an \OC\Files\Storage\Storage in our virtual filesystem |
|
| 528 | + * |
|
| 529 | + * @param \OC\Files\Storage\Storage|string $class |
|
| 530 | + * @param array $arguments |
|
| 531 | + * @param string $mountpoint |
|
| 532 | + */ |
|
| 533 | + static public function mount($class, $arguments, $mountpoint) { |
|
| 534 | + if (!self::$mounts) { |
|
| 535 | + \OC_Util::setupFS(); |
|
| 536 | + } |
|
| 537 | + $mount = new Mount\MountPoint($class, $mountpoint, $arguments, self::getLoader()); |
|
| 538 | + self::$mounts->addMount($mount); |
|
| 539 | + } |
|
| 540 | + |
|
| 541 | + /** |
|
| 542 | + * return the path to a local version of the file |
|
| 543 | + * we need this because we can't know if a file is stored local or not from |
|
| 544 | + * outside the filestorage and for some purposes a local file is needed |
|
| 545 | + * |
|
| 546 | + * @param string $path |
|
| 547 | + * @return string |
|
| 548 | + */ |
|
| 549 | + static public function getLocalFile($path) { |
|
| 550 | + return self::$defaultInstance->getLocalFile($path); |
|
| 551 | + } |
|
| 552 | + |
|
| 553 | + /** |
|
| 554 | + * @param string $path |
|
| 555 | + * @return string |
|
| 556 | + */ |
|
| 557 | + static public function getLocalFolder($path) { |
|
| 558 | + return self::$defaultInstance->getLocalFolder($path); |
|
| 559 | + } |
|
| 560 | + |
|
| 561 | + /** |
|
| 562 | + * return path to file which reflects one visible in browser |
|
| 563 | + * |
|
| 564 | + * @param string $path |
|
| 565 | + * @return string |
|
| 566 | + */ |
|
| 567 | + static public function getLocalPath($path) { |
|
| 568 | + $datadir = \OC_User::getHome(\OC_User::getUser()) . '/files'; |
|
| 569 | + $newpath = $path; |
|
| 570 | + if (strncmp($newpath, $datadir, strlen($datadir)) == 0) { |
|
| 571 | + $newpath = substr($path, strlen($datadir)); |
|
| 572 | + } |
|
| 573 | + return $newpath; |
|
| 574 | + } |
|
| 575 | + |
|
| 576 | + /** |
|
| 577 | + * check if the requested path is valid |
|
| 578 | + * |
|
| 579 | + * @param string $path |
|
| 580 | + * @return bool |
|
| 581 | + */ |
|
| 582 | + static public function isValidPath($path) { |
|
| 583 | + $path = self::normalizePath($path); |
|
| 584 | + if (!$path || $path[0] !== '/') { |
|
| 585 | + $path = '/' . $path; |
|
| 586 | + } |
|
| 587 | + if (strpos($path, '/../') !== false || strrchr($path, '/') === '/..') { |
|
| 588 | + return false; |
|
| 589 | + } |
|
| 590 | + return true; |
|
| 591 | + } |
|
| 592 | + |
|
| 593 | + /** |
|
| 594 | + * checks if a file is blacklisted for storage in the filesystem |
|
| 595 | + * Listens to write and rename hooks |
|
| 596 | + * |
|
| 597 | + * @param array $data from hook |
|
| 598 | + */ |
|
| 599 | + static public function isBlacklisted($data) { |
|
| 600 | + if (isset($data['path'])) { |
|
| 601 | + $path = $data['path']; |
|
| 602 | + } else if (isset($data['newpath'])) { |
|
| 603 | + $path = $data['newpath']; |
|
| 604 | + } |
|
| 605 | + if (isset($path)) { |
|
| 606 | + if (self::isFileBlacklisted($path)) { |
|
| 607 | + $data['run'] = false; |
|
| 608 | + } |
|
| 609 | + } |
|
| 610 | + } |
|
| 611 | + |
|
| 612 | + /** |
|
| 613 | + * @param string $filename |
|
| 614 | + * @return bool |
|
| 615 | + */ |
|
| 616 | + static public function isFileBlacklisted($filename) { |
|
| 617 | + $filename = self::normalizePath($filename); |
|
| 618 | + |
|
| 619 | + $blacklist = \OC::$server->getConfig()->getSystemValue('blacklisted_files', array('.htaccess')); |
|
| 620 | + $filename = strtolower(basename($filename)); |
|
| 621 | + return in_array($filename, $blacklist); |
|
| 622 | + } |
|
| 623 | + |
|
| 624 | + /** |
|
| 625 | + * check if the directory should be ignored when scanning |
|
| 626 | + * NOTE: the special directories . and .. would cause never ending recursion |
|
| 627 | + * |
|
| 628 | + * @param String $dir |
|
| 629 | + * @return boolean |
|
| 630 | + */ |
|
| 631 | + static public function isIgnoredDir($dir) { |
|
| 632 | + if ($dir === '.' || $dir === '..') { |
|
| 633 | + return true; |
|
| 634 | + } |
|
| 635 | + return false; |
|
| 636 | + } |
|
| 637 | + |
|
| 638 | + /** |
|
| 639 | + * following functions are equivalent to their php builtin equivalents for arguments/return values. |
|
| 640 | + */ |
|
| 641 | + static public function mkdir($path) { |
|
| 642 | + return self::$defaultInstance->mkdir($path); |
|
| 643 | + } |
|
| 644 | + |
|
| 645 | + static public function rmdir($path) { |
|
| 646 | + return self::$defaultInstance->rmdir($path); |
|
| 647 | + } |
|
| 648 | + |
|
| 649 | + static public function is_dir($path) { |
|
| 650 | + return self::$defaultInstance->is_dir($path); |
|
| 651 | + } |
|
| 652 | + |
|
| 653 | + static public function is_file($path) { |
|
| 654 | + return self::$defaultInstance->is_file($path); |
|
| 655 | + } |
|
| 656 | + |
|
| 657 | + static public function stat($path) { |
|
| 658 | + return self::$defaultInstance->stat($path); |
|
| 659 | + } |
|
| 660 | + |
|
| 661 | + static public function filetype($path) { |
|
| 662 | + return self::$defaultInstance->filetype($path); |
|
| 663 | + } |
|
| 664 | + |
|
| 665 | + static public function filesize($path) { |
|
| 666 | + return self::$defaultInstance->filesize($path); |
|
| 667 | + } |
|
| 668 | + |
|
| 669 | + static public function readfile($path) { |
|
| 670 | + return self::$defaultInstance->readfile($path); |
|
| 671 | + } |
|
| 672 | + |
|
| 673 | + static public function isCreatable($path) { |
|
| 674 | + return self::$defaultInstance->isCreatable($path); |
|
| 675 | + } |
|
| 676 | + |
|
| 677 | + static public function isReadable($path) { |
|
| 678 | + return self::$defaultInstance->isReadable($path); |
|
| 679 | + } |
|
| 680 | + |
|
| 681 | + static public function isUpdatable($path) { |
|
| 682 | + return self::$defaultInstance->isUpdatable($path); |
|
| 683 | + } |
|
| 684 | + |
|
| 685 | + static public function isDeletable($path) { |
|
| 686 | + return self::$defaultInstance->isDeletable($path); |
|
| 687 | + } |
|
| 688 | + |
|
| 689 | + static public function isSharable($path) { |
|
| 690 | + return self::$defaultInstance->isSharable($path); |
|
| 691 | + } |
|
| 692 | + |
|
| 693 | + static public function file_exists($path) { |
|
| 694 | + return self::$defaultInstance->file_exists($path); |
|
| 695 | + } |
|
| 696 | + |
|
| 697 | + static public function filemtime($path) { |
|
| 698 | + return self::$defaultInstance->filemtime($path); |
|
| 699 | + } |
|
| 700 | + |
|
| 701 | + static public function touch($path, $mtime = null) { |
|
| 702 | + return self::$defaultInstance->touch($path, $mtime); |
|
| 703 | + } |
|
| 704 | + |
|
| 705 | + /** |
|
| 706 | + * @return string |
|
| 707 | + */ |
|
| 708 | + static public function file_get_contents($path) { |
|
| 709 | + return self::$defaultInstance->file_get_contents($path); |
|
| 710 | + } |
|
| 711 | + |
|
| 712 | + static public function file_put_contents($path, $data) { |
|
| 713 | + return self::$defaultInstance->file_put_contents($path, $data); |
|
| 714 | + } |
|
| 715 | + |
|
| 716 | + static public function unlink($path) { |
|
| 717 | + return self::$defaultInstance->unlink($path); |
|
| 718 | + } |
|
| 719 | + |
|
| 720 | + static public function rename($path1, $path2) { |
|
| 721 | + return self::$defaultInstance->rename($path1, $path2); |
|
| 722 | + } |
|
| 723 | + |
|
| 724 | + static public function copy($path1, $path2) { |
|
| 725 | + return self::$defaultInstance->copy($path1, $path2); |
|
| 726 | + } |
|
| 727 | + |
|
| 728 | + static public function fopen($path, $mode) { |
|
| 729 | + return self::$defaultInstance->fopen($path, $mode); |
|
| 730 | + } |
|
| 731 | + |
|
| 732 | + /** |
|
| 733 | + * @return string |
|
| 734 | + */ |
|
| 735 | + static public function toTmpFile($path) { |
|
| 736 | + return self::$defaultInstance->toTmpFile($path); |
|
| 737 | + } |
|
| 738 | + |
|
| 739 | + static public function fromTmpFile($tmpFile, $path) { |
|
| 740 | + return self::$defaultInstance->fromTmpFile($tmpFile, $path); |
|
| 741 | + } |
|
| 742 | + |
|
| 743 | + static public function getMimeType($path) { |
|
| 744 | + return self::$defaultInstance->getMimeType($path); |
|
| 745 | + } |
|
| 746 | + |
|
| 747 | + static public function hash($type, $path, $raw = false) { |
|
| 748 | + return self::$defaultInstance->hash($type, $path, $raw); |
|
| 749 | + } |
|
| 750 | + |
|
| 751 | + static public function free_space($path = '/') { |
|
| 752 | + return self::$defaultInstance->free_space($path); |
|
| 753 | + } |
|
| 754 | + |
|
| 755 | + static public function search($query) { |
|
| 756 | + return self::$defaultInstance->search($query); |
|
| 757 | + } |
|
| 758 | + |
|
| 759 | + /** |
|
| 760 | + * @param string $query |
|
| 761 | + */ |
|
| 762 | + static public function searchByMime($query) { |
|
| 763 | + return self::$defaultInstance->searchByMime($query); |
|
| 764 | + } |
|
| 765 | + |
|
| 766 | + /** |
|
| 767 | + * @param string|int $tag name or tag id |
|
| 768 | + * @param string $userId owner of the tags |
|
| 769 | + * @return FileInfo[] array or file info |
|
| 770 | + */ |
|
| 771 | + static public function searchByTag($tag, $userId) { |
|
| 772 | + return self::$defaultInstance->searchByTag($tag, $userId); |
|
| 773 | + } |
|
| 774 | + |
|
| 775 | + /** |
|
| 776 | + * check if a file or folder has been updated since $time |
|
| 777 | + * |
|
| 778 | + * @param string $path |
|
| 779 | + * @param int $time |
|
| 780 | + * @return bool |
|
| 781 | + */ |
|
| 782 | + static public function hasUpdated($path, $time) { |
|
| 783 | + return self::$defaultInstance->hasUpdated($path, $time); |
|
| 784 | + } |
|
| 785 | + |
|
| 786 | + /** |
|
| 787 | + * Fix common problems with a file path |
|
| 788 | + * |
|
| 789 | + * @param string $path |
|
| 790 | + * @param bool $stripTrailingSlash whether to strip the trailing slash |
|
| 791 | + * @param bool $isAbsolutePath whether the given path is absolute |
|
| 792 | + * @param bool $keepUnicode true to disable unicode normalization |
|
| 793 | + * @return string |
|
| 794 | + */ |
|
| 795 | + public static function normalizePath($path, $stripTrailingSlash = true, $isAbsolutePath = false, $keepUnicode = false) { |
|
| 796 | + if (is_null(self::$normalizedPathCache)) { |
|
| 797 | + self::$normalizedPathCache = new CappedMemoryCache(); |
|
| 798 | + } |
|
| 799 | + |
|
| 800 | + /** |
|
| 801 | + * FIXME: This is a workaround for existing classes and files which call |
|
| 802 | + * this function with another type than a valid string. This |
|
| 803 | + * conversion should get removed as soon as all existing |
|
| 804 | + * function calls have been fixed. |
|
| 805 | + */ |
|
| 806 | + $path = (string)$path; |
|
| 807 | + |
|
| 808 | + $cacheKey = json_encode([$path, $stripTrailingSlash, $isAbsolutePath, $keepUnicode]); |
|
| 809 | + |
|
| 810 | + if (isset(self::$normalizedPathCache[$cacheKey])) { |
|
| 811 | + return self::$normalizedPathCache[$cacheKey]; |
|
| 812 | + } |
|
| 813 | + |
|
| 814 | + if ($path == '') { |
|
| 815 | + return '/'; |
|
| 816 | + } |
|
| 817 | + |
|
| 818 | + //normalize unicode if possible |
|
| 819 | + if (!$keepUnicode) { |
|
| 820 | + $path = \OC_Util::normalizeUnicode($path); |
|
| 821 | + } |
|
| 822 | + |
|
| 823 | + //no windows style slashes |
|
| 824 | + $path = str_replace('\\', '/', $path); |
|
| 825 | + |
|
| 826 | + //add leading slash |
|
| 827 | + if ($path[0] !== '/') { |
|
| 828 | + $path = '/' . $path; |
|
| 829 | + } |
|
| 830 | + |
|
| 831 | + // remove '/./' |
|
| 832 | + // ugly, but str_replace() can't replace them all in one go |
|
| 833 | + // as the replacement itself is part of the search string |
|
| 834 | + // which will only be found during the next iteration |
|
| 835 | + while (strpos($path, '/./') !== false) { |
|
| 836 | + $path = str_replace('/./', '/', $path); |
|
| 837 | + } |
|
| 838 | + // remove sequences of slashes |
|
| 839 | + $path = preg_replace('#/{2,}#', '/', $path); |
|
| 840 | + |
|
| 841 | + //remove trailing slash |
|
| 842 | + if ($stripTrailingSlash and strlen($path) > 1 and substr($path, -1, 1) === '/') { |
|
| 843 | + $path = substr($path, 0, -1); |
|
| 844 | + } |
|
| 845 | + |
|
| 846 | + // remove trailing '/.' |
|
| 847 | + if (substr($path, -2) == '/.') { |
|
| 848 | + $path = substr($path, 0, -2); |
|
| 849 | + } |
|
| 850 | + |
|
| 851 | + $normalizedPath = $path; |
|
| 852 | + self::$normalizedPathCache[$cacheKey] = $normalizedPath; |
|
| 853 | + |
|
| 854 | + return $normalizedPath; |
|
| 855 | + } |
|
| 856 | + |
|
| 857 | + /** |
|
| 858 | + * get the filesystem info |
|
| 859 | + * |
|
| 860 | + * @param string $path |
|
| 861 | + * @param boolean $includeMountPoints whether to add mountpoint sizes, |
|
| 862 | + * defaults to true |
|
| 863 | + * @return \OC\Files\FileInfo|bool False if file does not exist |
|
| 864 | + */ |
|
| 865 | + public static function getFileInfo($path, $includeMountPoints = true) { |
|
| 866 | + return self::$defaultInstance->getFileInfo($path, $includeMountPoints); |
|
| 867 | + } |
|
| 868 | + |
|
| 869 | + /** |
|
| 870 | + * change file metadata |
|
| 871 | + * |
|
| 872 | + * @param string $path |
|
| 873 | + * @param array $data |
|
| 874 | + * @return int |
|
| 875 | + * |
|
| 876 | + * returns the fileid of the updated file |
|
| 877 | + */ |
|
| 878 | + public static function putFileInfo($path, $data) { |
|
| 879 | + return self::$defaultInstance->putFileInfo($path, $data); |
|
| 880 | + } |
|
| 881 | + |
|
| 882 | + /** |
|
| 883 | + * get the content of a directory |
|
| 884 | + * |
|
| 885 | + * @param string $directory path under datadirectory |
|
| 886 | + * @param string $mimetype_filter limit returned content to this mimetype or mimepart |
|
| 887 | + * @return \OC\Files\FileInfo[] |
|
| 888 | + */ |
|
| 889 | + public static function getDirectoryContent($directory, $mimetype_filter = '') { |
|
| 890 | + return self::$defaultInstance->getDirectoryContent($directory, $mimetype_filter); |
|
| 891 | + } |
|
| 892 | + |
|
| 893 | + /** |
|
| 894 | + * Get the path of a file by id |
|
| 895 | + * |
|
| 896 | + * Note that the resulting path is not guaranteed to be unique for the id, multiple paths can point to the same file |
|
| 897 | + * |
|
| 898 | + * @param int $id |
|
| 899 | + * @throws NotFoundException |
|
| 900 | + * @return string |
|
| 901 | + */ |
|
| 902 | + public static function getPath($id) { |
|
| 903 | + return self::$defaultInstance->getPath($id); |
|
| 904 | + } |
|
| 905 | + |
|
| 906 | + /** |
|
| 907 | + * Get the owner for a file or folder |
|
| 908 | + * |
|
| 909 | + * @param string $path |
|
| 910 | + * @return string |
|
| 911 | + */ |
|
| 912 | + public static function getOwner($path) { |
|
| 913 | + return self::$defaultInstance->getOwner($path); |
|
| 914 | + } |
|
| 915 | + |
|
| 916 | + /** |
|
| 917 | + * get the ETag for a file or folder |
|
| 918 | + * |
|
| 919 | + * @param string $path |
|
| 920 | + * @return string |
|
| 921 | + */ |
|
| 922 | + static public function getETag($path) { |
|
| 923 | + return self::$defaultInstance->getETag($path); |
|
| 924 | + } |
|
| 925 | 925 | } |