@@ -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 | } |
@@ -82,2057 +82,2057 @@ |
||
| 82 | 82 | * \OC\Files\Storage\Storage object |
| 83 | 83 | */ |
| 84 | 84 | class View { |
| 85 | - /** @var string */ |
|
| 86 | - private $fakeRoot = ''; |
|
| 87 | - |
|
| 88 | - /** |
|
| 89 | - * @var \OCP\Lock\ILockingProvider |
|
| 90 | - */ |
|
| 91 | - protected $lockingProvider; |
|
| 92 | - |
|
| 93 | - private $lockingEnabled; |
|
| 94 | - |
|
| 95 | - private $updaterEnabled = true; |
|
| 96 | - |
|
| 97 | - /** @var \OC\User\Manager */ |
|
| 98 | - private $userManager; |
|
| 99 | - |
|
| 100 | - /** @var \OCP\ILogger */ |
|
| 101 | - private $logger; |
|
| 102 | - |
|
| 103 | - /** |
|
| 104 | - * @param string $root |
|
| 105 | - * @throws \Exception If $root contains an invalid path |
|
| 106 | - */ |
|
| 107 | - public function __construct($root = '') { |
|
| 108 | - if (is_null($root)) { |
|
| 109 | - throw new \InvalidArgumentException('Root can\'t be null'); |
|
| 110 | - } |
|
| 111 | - if (!Filesystem::isValidPath($root)) { |
|
| 112 | - throw new \Exception(); |
|
| 113 | - } |
|
| 114 | - |
|
| 115 | - $this->fakeRoot = $root; |
|
| 116 | - $this->lockingProvider = \OC::$server->getLockingProvider(); |
|
| 117 | - $this->lockingEnabled = !($this->lockingProvider instanceof \OC\Lock\NoopLockingProvider); |
|
| 118 | - $this->userManager = \OC::$server->getUserManager(); |
|
| 119 | - $this->logger = \OC::$server->getLogger(); |
|
| 120 | - } |
|
| 121 | - |
|
| 122 | - public function getAbsolutePath($path = '/') { |
|
| 123 | - if ($path === null) { |
|
| 124 | - return null; |
|
| 125 | - } |
|
| 126 | - $this->assertPathLength($path); |
|
| 127 | - if ($path === '') { |
|
| 128 | - $path = '/'; |
|
| 129 | - } |
|
| 130 | - if ($path[0] !== '/') { |
|
| 131 | - $path = '/' . $path; |
|
| 132 | - } |
|
| 133 | - return $this->fakeRoot . $path; |
|
| 134 | - } |
|
| 135 | - |
|
| 136 | - /** |
|
| 137 | - * change the root to a fake root |
|
| 138 | - * |
|
| 139 | - * @param string $fakeRoot |
|
| 140 | - * @return boolean|null |
|
| 141 | - */ |
|
| 142 | - public function chroot($fakeRoot) { |
|
| 143 | - if (!$fakeRoot == '') { |
|
| 144 | - if ($fakeRoot[0] !== '/') { |
|
| 145 | - $fakeRoot = '/' . $fakeRoot; |
|
| 146 | - } |
|
| 147 | - } |
|
| 148 | - $this->fakeRoot = $fakeRoot; |
|
| 149 | - } |
|
| 150 | - |
|
| 151 | - /** |
|
| 152 | - * get the fake root |
|
| 153 | - * |
|
| 154 | - * @return string |
|
| 155 | - */ |
|
| 156 | - public function getRoot() { |
|
| 157 | - return $this->fakeRoot; |
|
| 158 | - } |
|
| 159 | - |
|
| 160 | - /** |
|
| 161 | - * get path relative to the root of the view |
|
| 162 | - * |
|
| 163 | - * @param string $path |
|
| 164 | - * @return string |
|
| 165 | - */ |
|
| 166 | - public function getRelativePath($path) { |
|
| 167 | - $this->assertPathLength($path); |
|
| 168 | - if ($this->fakeRoot == '') { |
|
| 169 | - return $path; |
|
| 170 | - } |
|
| 171 | - |
|
| 172 | - if (rtrim($path, '/') === rtrim($this->fakeRoot, '/')) { |
|
| 173 | - return '/'; |
|
| 174 | - } |
|
| 175 | - |
|
| 176 | - // missing slashes can cause wrong matches! |
|
| 177 | - $root = rtrim($this->fakeRoot, '/') . '/'; |
|
| 178 | - |
|
| 179 | - if (strpos($path, $root) !== 0) { |
|
| 180 | - return null; |
|
| 181 | - } else { |
|
| 182 | - $path = substr($path, strlen($this->fakeRoot)); |
|
| 183 | - if (strlen($path) === 0) { |
|
| 184 | - return '/'; |
|
| 185 | - } else { |
|
| 186 | - return $path; |
|
| 187 | - } |
|
| 188 | - } |
|
| 189 | - } |
|
| 190 | - |
|
| 191 | - /** |
|
| 192 | - * get the mountpoint of the storage object for a path |
|
| 193 | - * ( note: because a storage is not always mounted inside the fakeroot, the |
|
| 194 | - * returned mountpoint is relative to the absolute root of the filesystem |
|
| 195 | - * and does not take the chroot into account ) |
|
| 196 | - * |
|
| 197 | - * @param string $path |
|
| 198 | - * @return string |
|
| 199 | - */ |
|
| 200 | - public function getMountPoint($path) { |
|
| 201 | - return Filesystem::getMountPoint($this->getAbsolutePath($path)); |
|
| 202 | - } |
|
| 203 | - |
|
| 204 | - /** |
|
| 205 | - * get the mountpoint of the storage object for a path |
|
| 206 | - * ( note: because a storage is not always mounted inside the fakeroot, the |
|
| 207 | - * returned mountpoint is relative to the absolute root of the filesystem |
|
| 208 | - * and does not take the chroot into account ) |
|
| 209 | - * |
|
| 210 | - * @param string $path |
|
| 211 | - * @return \OCP\Files\Mount\IMountPoint |
|
| 212 | - */ |
|
| 213 | - public function getMount($path) { |
|
| 214 | - return Filesystem::getMountManager()->find($this->getAbsolutePath($path)); |
|
| 215 | - } |
|
| 216 | - |
|
| 217 | - /** |
|
| 218 | - * resolve a path to a storage and internal path |
|
| 219 | - * |
|
| 220 | - * @param string $path |
|
| 221 | - * @return array an array consisting of the storage and the internal path |
|
| 222 | - */ |
|
| 223 | - public function resolvePath($path) { |
|
| 224 | - $a = $this->getAbsolutePath($path); |
|
| 225 | - $p = Filesystem::normalizePath($a); |
|
| 226 | - return Filesystem::resolvePath($p); |
|
| 227 | - } |
|
| 228 | - |
|
| 229 | - /** |
|
| 230 | - * return the path to a local version of the file |
|
| 231 | - * we need this because we can't know if a file is stored local or not from |
|
| 232 | - * outside the filestorage and for some purposes a local file is needed |
|
| 233 | - * |
|
| 234 | - * @param string $path |
|
| 235 | - * @return string |
|
| 236 | - */ |
|
| 237 | - public function getLocalFile($path) { |
|
| 238 | - $parent = substr($path, 0, strrpos($path, '/')); |
|
| 239 | - $path = $this->getAbsolutePath($path); |
|
| 240 | - list($storage, $internalPath) = Filesystem::resolvePath($path); |
|
| 241 | - if (Filesystem::isValidPath($parent) and $storage) { |
|
| 242 | - return $storage->getLocalFile($internalPath); |
|
| 243 | - } else { |
|
| 244 | - return null; |
|
| 245 | - } |
|
| 246 | - } |
|
| 247 | - |
|
| 248 | - /** |
|
| 249 | - * @param string $path |
|
| 250 | - * @return string |
|
| 251 | - */ |
|
| 252 | - public function getLocalFolder($path) { |
|
| 253 | - $parent = substr($path, 0, strrpos($path, '/')); |
|
| 254 | - $path = $this->getAbsolutePath($path); |
|
| 255 | - list($storage, $internalPath) = Filesystem::resolvePath($path); |
|
| 256 | - if (Filesystem::isValidPath($parent) and $storage) { |
|
| 257 | - return $storage->getLocalFolder($internalPath); |
|
| 258 | - } else { |
|
| 259 | - return null; |
|
| 260 | - } |
|
| 261 | - } |
|
| 262 | - |
|
| 263 | - /** |
|
| 264 | - * the following functions operate with arguments and return values identical |
|
| 265 | - * to those of their PHP built-in equivalents. Mostly they are merely wrappers |
|
| 266 | - * for \OC\Files\Storage\Storage via basicOperation(). |
|
| 267 | - */ |
|
| 268 | - public function mkdir($path) { |
|
| 269 | - return $this->basicOperation('mkdir', $path, array('create', 'write')); |
|
| 270 | - } |
|
| 271 | - |
|
| 272 | - /** |
|
| 273 | - * remove mount point |
|
| 274 | - * |
|
| 275 | - * @param \OC\Files\Mount\MoveableMount $mount |
|
| 276 | - * @param string $path relative to data/ |
|
| 277 | - * @return boolean |
|
| 278 | - */ |
|
| 279 | - protected function removeMount($mount, $path) { |
|
| 280 | - if ($mount instanceof MoveableMount) { |
|
| 281 | - // cut of /user/files to get the relative path to data/user/files |
|
| 282 | - $pathParts = explode('/', $path, 4); |
|
| 283 | - $relPath = '/' . $pathParts[3]; |
|
| 284 | - $this->lockFile($relPath, ILockingProvider::LOCK_SHARED, true); |
|
| 285 | - \OC_Hook::emit( |
|
| 286 | - Filesystem::CLASSNAME, "umount", |
|
| 287 | - array(Filesystem::signal_param_path => $relPath) |
|
| 288 | - ); |
|
| 289 | - $this->changeLock($relPath, ILockingProvider::LOCK_EXCLUSIVE, true); |
|
| 290 | - $result = $mount->removeMount(); |
|
| 291 | - $this->changeLock($relPath, ILockingProvider::LOCK_SHARED, true); |
|
| 292 | - if ($result) { |
|
| 293 | - \OC_Hook::emit( |
|
| 294 | - Filesystem::CLASSNAME, "post_umount", |
|
| 295 | - array(Filesystem::signal_param_path => $relPath) |
|
| 296 | - ); |
|
| 297 | - } |
|
| 298 | - $this->unlockFile($relPath, ILockingProvider::LOCK_SHARED, true); |
|
| 299 | - return $result; |
|
| 300 | - } else { |
|
| 301 | - // do not allow deleting the storage's root / the mount point |
|
| 302 | - // because for some storages it might delete the whole contents |
|
| 303 | - // but isn't supposed to work that way |
|
| 304 | - return false; |
|
| 305 | - } |
|
| 306 | - } |
|
| 307 | - |
|
| 308 | - public function disableCacheUpdate() { |
|
| 309 | - $this->updaterEnabled = false; |
|
| 310 | - } |
|
| 311 | - |
|
| 312 | - public function enableCacheUpdate() { |
|
| 313 | - $this->updaterEnabled = true; |
|
| 314 | - } |
|
| 315 | - |
|
| 316 | - protected function writeUpdate(Storage $storage, $internalPath, $time = null) { |
|
| 317 | - if ($this->updaterEnabled) { |
|
| 318 | - if (is_null($time)) { |
|
| 319 | - $time = time(); |
|
| 320 | - } |
|
| 321 | - $storage->getUpdater()->update($internalPath, $time); |
|
| 322 | - } |
|
| 323 | - } |
|
| 324 | - |
|
| 325 | - protected function removeUpdate(Storage $storage, $internalPath) { |
|
| 326 | - if ($this->updaterEnabled) { |
|
| 327 | - $storage->getUpdater()->remove($internalPath); |
|
| 328 | - } |
|
| 329 | - } |
|
| 330 | - |
|
| 331 | - protected function renameUpdate(Storage $sourceStorage, Storage $targetStorage, $sourceInternalPath, $targetInternalPath) { |
|
| 332 | - if ($this->updaterEnabled) { |
|
| 333 | - $targetStorage->getUpdater()->renameFromStorage($sourceStorage, $sourceInternalPath, $targetInternalPath); |
|
| 334 | - } |
|
| 335 | - } |
|
| 336 | - |
|
| 337 | - /** |
|
| 338 | - * @param string $path |
|
| 339 | - * @return bool|mixed |
|
| 340 | - */ |
|
| 341 | - public function rmdir($path) { |
|
| 342 | - $absolutePath = $this->getAbsolutePath($path); |
|
| 343 | - $mount = Filesystem::getMountManager()->find($absolutePath); |
|
| 344 | - if ($mount->getInternalPath($absolutePath) === '') { |
|
| 345 | - return $this->removeMount($mount, $absolutePath); |
|
| 346 | - } |
|
| 347 | - if ($this->is_dir($path)) { |
|
| 348 | - $result = $this->basicOperation('rmdir', $path, array('delete')); |
|
| 349 | - } else { |
|
| 350 | - $result = false; |
|
| 351 | - } |
|
| 352 | - |
|
| 353 | - if (!$result && !$this->file_exists($path)) { //clear ghost files from the cache on delete |
|
| 354 | - $storage = $mount->getStorage(); |
|
| 355 | - $internalPath = $mount->getInternalPath($absolutePath); |
|
| 356 | - $storage->getUpdater()->remove($internalPath); |
|
| 357 | - } |
|
| 358 | - return $result; |
|
| 359 | - } |
|
| 360 | - |
|
| 361 | - /** |
|
| 362 | - * @param string $path |
|
| 363 | - * @return resource |
|
| 364 | - */ |
|
| 365 | - public function opendir($path) { |
|
| 366 | - return $this->basicOperation('opendir', $path, array('read')); |
|
| 367 | - } |
|
| 368 | - |
|
| 369 | - /** |
|
| 370 | - * @param $handle |
|
| 371 | - * @return mixed |
|
| 372 | - */ |
|
| 373 | - public function readdir($handle) { |
|
| 374 | - $fsLocal = new Storage\Local(array('datadir' => '/')); |
|
| 375 | - return $fsLocal->readdir($handle); |
|
| 376 | - } |
|
| 377 | - |
|
| 378 | - /** |
|
| 379 | - * @param string $path |
|
| 380 | - * @return bool|mixed |
|
| 381 | - */ |
|
| 382 | - public function is_dir($path) { |
|
| 383 | - if ($path == '/') { |
|
| 384 | - return true; |
|
| 385 | - } |
|
| 386 | - return $this->basicOperation('is_dir', $path); |
|
| 387 | - } |
|
| 388 | - |
|
| 389 | - /** |
|
| 390 | - * @param string $path |
|
| 391 | - * @return bool|mixed |
|
| 392 | - */ |
|
| 393 | - public function is_file($path) { |
|
| 394 | - if ($path == '/') { |
|
| 395 | - return false; |
|
| 396 | - } |
|
| 397 | - return $this->basicOperation('is_file', $path); |
|
| 398 | - } |
|
| 399 | - |
|
| 400 | - /** |
|
| 401 | - * @param string $path |
|
| 402 | - * @return mixed |
|
| 403 | - */ |
|
| 404 | - public function stat($path) { |
|
| 405 | - return $this->basicOperation('stat', $path); |
|
| 406 | - } |
|
| 407 | - |
|
| 408 | - /** |
|
| 409 | - * @param string $path |
|
| 410 | - * @return mixed |
|
| 411 | - */ |
|
| 412 | - public function filetype($path) { |
|
| 413 | - return $this->basicOperation('filetype', $path); |
|
| 414 | - } |
|
| 415 | - |
|
| 416 | - /** |
|
| 417 | - * @param string $path |
|
| 418 | - * @return mixed |
|
| 419 | - */ |
|
| 420 | - public function filesize($path) { |
|
| 421 | - return $this->basicOperation('filesize', $path); |
|
| 422 | - } |
|
| 423 | - |
|
| 424 | - /** |
|
| 425 | - * @param string $path |
|
| 426 | - * @return bool|mixed |
|
| 427 | - * @throws \OCP\Files\InvalidPathException |
|
| 428 | - */ |
|
| 429 | - public function readfile($path) { |
|
| 430 | - $this->assertPathLength($path); |
|
| 431 | - @ob_end_clean(); |
|
| 432 | - $handle = $this->fopen($path, 'rb'); |
|
| 433 | - if ($handle) { |
|
| 434 | - $chunkSize = 8192; // 8 kB chunks |
|
| 435 | - while (!feof($handle)) { |
|
| 436 | - echo fread($handle, $chunkSize); |
|
| 437 | - flush(); |
|
| 438 | - } |
|
| 439 | - fclose($handle); |
|
| 440 | - $size = $this->filesize($path); |
|
| 441 | - return $size; |
|
| 442 | - } |
|
| 443 | - return false; |
|
| 444 | - } |
|
| 445 | - |
|
| 446 | - /** |
|
| 447 | - * @param string $path |
|
| 448 | - * @param int $from |
|
| 449 | - * @param int $to |
|
| 450 | - * @return bool|mixed |
|
| 451 | - * @throws \OCP\Files\InvalidPathException |
|
| 452 | - * @throws \OCP\Files\UnseekableException |
|
| 453 | - */ |
|
| 454 | - public function readfilePart($path, $from, $to) { |
|
| 455 | - $this->assertPathLength($path); |
|
| 456 | - @ob_end_clean(); |
|
| 457 | - $handle = $this->fopen($path, 'rb'); |
|
| 458 | - if ($handle) { |
|
| 459 | - if (fseek($handle, $from) === 0) { |
|
| 460 | - $chunkSize = 8192; // 8 kB chunks |
|
| 461 | - $end = $to + 1; |
|
| 462 | - while (!feof($handle) && ftell($handle) < $end) { |
|
| 463 | - $len = $end - ftell($handle); |
|
| 464 | - if ($len > $chunkSize) { |
|
| 465 | - $len = $chunkSize; |
|
| 466 | - } |
|
| 467 | - echo fread($handle, $len); |
|
| 468 | - flush(); |
|
| 469 | - } |
|
| 470 | - $size = ftell($handle) - $from; |
|
| 471 | - return $size; |
|
| 472 | - } |
|
| 473 | - |
|
| 474 | - throw new \OCP\Files\UnseekableException('fseek error'); |
|
| 475 | - } |
|
| 476 | - return false; |
|
| 477 | - } |
|
| 478 | - |
|
| 479 | - /** |
|
| 480 | - * @param string $path |
|
| 481 | - * @return mixed |
|
| 482 | - */ |
|
| 483 | - public function isCreatable($path) { |
|
| 484 | - return $this->basicOperation('isCreatable', $path); |
|
| 485 | - } |
|
| 486 | - |
|
| 487 | - /** |
|
| 488 | - * @param string $path |
|
| 489 | - * @return mixed |
|
| 490 | - */ |
|
| 491 | - public function isReadable($path) { |
|
| 492 | - return $this->basicOperation('isReadable', $path); |
|
| 493 | - } |
|
| 494 | - |
|
| 495 | - /** |
|
| 496 | - * @param string $path |
|
| 497 | - * @return mixed |
|
| 498 | - */ |
|
| 499 | - public function isUpdatable($path) { |
|
| 500 | - return $this->basicOperation('isUpdatable', $path); |
|
| 501 | - } |
|
| 502 | - |
|
| 503 | - /** |
|
| 504 | - * @param string $path |
|
| 505 | - * @return bool|mixed |
|
| 506 | - */ |
|
| 507 | - public function isDeletable($path) { |
|
| 508 | - $absolutePath = $this->getAbsolutePath($path); |
|
| 509 | - $mount = Filesystem::getMountManager()->find($absolutePath); |
|
| 510 | - if ($mount->getInternalPath($absolutePath) === '') { |
|
| 511 | - return $mount instanceof MoveableMount; |
|
| 512 | - } |
|
| 513 | - return $this->basicOperation('isDeletable', $path); |
|
| 514 | - } |
|
| 515 | - |
|
| 516 | - /** |
|
| 517 | - * @param string $path |
|
| 518 | - * @return mixed |
|
| 519 | - */ |
|
| 520 | - public function isSharable($path) { |
|
| 521 | - return $this->basicOperation('isSharable', $path); |
|
| 522 | - } |
|
| 523 | - |
|
| 524 | - /** |
|
| 525 | - * @param string $path |
|
| 526 | - * @return bool|mixed |
|
| 527 | - */ |
|
| 528 | - public function file_exists($path) { |
|
| 529 | - if ($path == '/') { |
|
| 530 | - return true; |
|
| 531 | - } |
|
| 532 | - return $this->basicOperation('file_exists', $path); |
|
| 533 | - } |
|
| 534 | - |
|
| 535 | - /** |
|
| 536 | - * @param string $path |
|
| 537 | - * @return mixed |
|
| 538 | - */ |
|
| 539 | - public function filemtime($path) { |
|
| 540 | - return $this->basicOperation('filemtime', $path); |
|
| 541 | - } |
|
| 542 | - |
|
| 543 | - /** |
|
| 544 | - * @param string $path |
|
| 545 | - * @param int|string $mtime |
|
| 546 | - * @return bool |
|
| 547 | - */ |
|
| 548 | - public function touch($path, $mtime = null) { |
|
| 549 | - if (!is_null($mtime) and !is_numeric($mtime)) { |
|
| 550 | - $mtime = strtotime($mtime); |
|
| 551 | - } |
|
| 552 | - |
|
| 553 | - $hooks = array('touch'); |
|
| 554 | - |
|
| 555 | - if (!$this->file_exists($path)) { |
|
| 556 | - $hooks[] = 'create'; |
|
| 557 | - $hooks[] = 'write'; |
|
| 558 | - } |
|
| 559 | - $result = $this->basicOperation('touch', $path, $hooks, $mtime); |
|
| 560 | - if (!$result) { |
|
| 561 | - // If create file fails because of permissions on external storage like SMB folders, |
|
| 562 | - // check file exists and return false if not. |
|
| 563 | - if (!$this->file_exists($path)) { |
|
| 564 | - return false; |
|
| 565 | - } |
|
| 566 | - if (is_null($mtime)) { |
|
| 567 | - $mtime = time(); |
|
| 568 | - } |
|
| 569 | - //if native touch fails, we emulate it by changing the mtime in the cache |
|
| 570 | - $this->putFileInfo($path, array('mtime' => floor($mtime))); |
|
| 571 | - } |
|
| 572 | - return true; |
|
| 573 | - } |
|
| 574 | - |
|
| 575 | - /** |
|
| 576 | - * @param string $path |
|
| 577 | - * @return mixed |
|
| 578 | - */ |
|
| 579 | - public function file_get_contents($path) { |
|
| 580 | - return $this->basicOperation('file_get_contents', $path, array('read')); |
|
| 581 | - } |
|
| 582 | - |
|
| 583 | - /** |
|
| 584 | - * @param bool $exists |
|
| 585 | - * @param string $path |
|
| 586 | - * @param bool $run |
|
| 587 | - */ |
|
| 588 | - protected function emit_file_hooks_pre($exists, $path, &$run) { |
|
| 589 | - if (!$exists) { |
|
| 590 | - \OC_Hook::emit(Filesystem::CLASSNAME, Filesystem::signal_create, array( |
|
| 591 | - Filesystem::signal_param_path => $this->getHookPath($path), |
|
| 592 | - Filesystem::signal_param_run => &$run, |
|
| 593 | - )); |
|
| 594 | - } else { |
|
| 595 | - \OC_Hook::emit(Filesystem::CLASSNAME, Filesystem::signal_update, array( |
|
| 596 | - Filesystem::signal_param_path => $this->getHookPath($path), |
|
| 597 | - Filesystem::signal_param_run => &$run, |
|
| 598 | - )); |
|
| 599 | - } |
|
| 600 | - \OC_Hook::emit(Filesystem::CLASSNAME, Filesystem::signal_write, array( |
|
| 601 | - Filesystem::signal_param_path => $this->getHookPath($path), |
|
| 602 | - Filesystem::signal_param_run => &$run, |
|
| 603 | - )); |
|
| 604 | - } |
|
| 605 | - |
|
| 606 | - /** |
|
| 607 | - * @param bool $exists |
|
| 608 | - * @param string $path |
|
| 609 | - */ |
|
| 610 | - protected function emit_file_hooks_post($exists, $path) { |
|
| 611 | - if (!$exists) { |
|
| 612 | - \OC_Hook::emit(Filesystem::CLASSNAME, Filesystem::signal_post_create, array( |
|
| 613 | - Filesystem::signal_param_path => $this->getHookPath($path), |
|
| 614 | - )); |
|
| 615 | - } else { |
|
| 616 | - \OC_Hook::emit(Filesystem::CLASSNAME, Filesystem::signal_post_update, array( |
|
| 617 | - Filesystem::signal_param_path => $this->getHookPath($path), |
|
| 618 | - )); |
|
| 619 | - } |
|
| 620 | - \OC_Hook::emit(Filesystem::CLASSNAME, Filesystem::signal_post_write, array( |
|
| 621 | - Filesystem::signal_param_path => $this->getHookPath($path), |
|
| 622 | - )); |
|
| 623 | - } |
|
| 624 | - |
|
| 625 | - /** |
|
| 626 | - * @param string $path |
|
| 627 | - * @param mixed $data |
|
| 628 | - * @return bool|mixed |
|
| 629 | - * @throws \Exception |
|
| 630 | - */ |
|
| 631 | - public function file_put_contents($path, $data) { |
|
| 632 | - if (is_resource($data)) { //not having to deal with streams in file_put_contents makes life easier |
|
| 633 | - $absolutePath = Filesystem::normalizePath($this->getAbsolutePath($path)); |
|
| 634 | - if (Filesystem::isValidPath($path) |
|
| 635 | - and !Filesystem::isFileBlacklisted($path) |
|
| 636 | - ) { |
|
| 637 | - $path = $this->getRelativePath($absolutePath); |
|
| 638 | - |
|
| 639 | - $this->lockFile($path, ILockingProvider::LOCK_SHARED); |
|
| 640 | - |
|
| 641 | - $exists = $this->file_exists($path); |
|
| 642 | - $run = true; |
|
| 643 | - if ($this->shouldEmitHooks($path)) { |
|
| 644 | - $this->emit_file_hooks_pre($exists, $path, $run); |
|
| 645 | - } |
|
| 646 | - if (!$run) { |
|
| 647 | - $this->unlockFile($path, ILockingProvider::LOCK_SHARED); |
|
| 648 | - return false; |
|
| 649 | - } |
|
| 650 | - |
|
| 651 | - $this->changeLock($path, ILockingProvider::LOCK_EXCLUSIVE); |
|
| 652 | - |
|
| 653 | - /** @var \OC\Files\Storage\Storage $storage */ |
|
| 654 | - list($storage, $internalPath) = $this->resolvePath($path); |
|
| 655 | - $target = $storage->fopen($internalPath, 'w'); |
|
| 656 | - if ($target) { |
|
| 657 | - list (, $result) = \OC_Helper::streamCopy($data, $target); |
|
| 658 | - fclose($target); |
|
| 659 | - fclose($data); |
|
| 660 | - |
|
| 661 | - $this->writeUpdate($storage, $internalPath); |
|
| 662 | - |
|
| 663 | - $this->changeLock($path, ILockingProvider::LOCK_SHARED); |
|
| 664 | - |
|
| 665 | - if ($this->shouldEmitHooks($path) && $result !== false) { |
|
| 666 | - $this->emit_file_hooks_post($exists, $path); |
|
| 667 | - } |
|
| 668 | - $this->unlockFile($path, ILockingProvider::LOCK_SHARED); |
|
| 669 | - return $result; |
|
| 670 | - } else { |
|
| 671 | - $this->unlockFile($path, ILockingProvider::LOCK_EXCLUSIVE); |
|
| 672 | - return false; |
|
| 673 | - } |
|
| 674 | - } else { |
|
| 675 | - return false; |
|
| 676 | - } |
|
| 677 | - } else { |
|
| 678 | - $hooks = ($this->file_exists($path)) ? array('update', 'write') : array('create', 'write'); |
|
| 679 | - return $this->basicOperation('file_put_contents', $path, $hooks, $data); |
|
| 680 | - } |
|
| 681 | - } |
|
| 682 | - |
|
| 683 | - /** |
|
| 684 | - * @param string $path |
|
| 685 | - * @return bool|mixed |
|
| 686 | - */ |
|
| 687 | - public function unlink($path) { |
|
| 688 | - if ($path === '' || $path === '/') { |
|
| 689 | - // do not allow deleting the root |
|
| 690 | - return false; |
|
| 691 | - } |
|
| 692 | - $postFix = (substr($path, -1, 1) === '/') ? '/' : ''; |
|
| 693 | - $absolutePath = Filesystem::normalizePath($this->getAbsolutePath($path)); |
|
| 694 | - $mount = Filesystem::getMountManager()->find($absolutePath . $postFix); |
|
| 695 | - if ($mount and $mount->getInternalPath($absolutePath) === '') { |
|
| 696 | - return $this->removeMount($mount, $absolutePath); |
|
| 697 | - } |
|
| 698 | - if ($this->is_dir($path)) { |
|
| 699 | - $result = $this->basicOperation('rmdir', $path, ['delete']); |
|
| 700 | - } else { |
|
| 701 | - $result = $this->basicOperation('unlink', $path, ['delete']); |
|
| 702 | - } |
|
| 703 | - if (!$result && !$this->file_exists($path)) { //clear ghost files from the cache on delete |
|
| 704 | - $storage = $mount->getStorage(); |
|
| 705 | - $internalPath = $mount->getInternalPath($absolutePath); |
|
| 706 | - $storage->getUpdater()->remove($internalPath); |
|
| 707 | - return true; |
|
| 708 | - } else { |
|
| 709 | - return $result; |
|
| 710 | - } |
|
| 711 | - } |
|
| 712 | - |
|
| 713 | - /** |
|
| 714 | - * @param string $directory |
|
| 715 | - * @return bool|mixed |
|
| 716 | - */ |
|
| 717 | - public function deleteAll($directory) { |
|
| 718 | - return $this->rmdir($directory); |
|
| 719 | - } |
|
| 720 | - |
|
| 721 | - /** |
|
| 722 | - * Rename/move a file or folder from the source path to target path. |
|
| 723 | - * |
|
| 724 | - * @param string $path1 source path |
|
| 725 | - * @param string $path2 target path |
|
| 726 | - * |
|
| 727 | - * @return bool|mixed |
|
| 728 | - */ |
|
| 729 | - public function rename($path1, $path2) { |
|
| 730 | - $absolutePath1 = Filesystem::normalizePath($this->getAbsolutePath($path1)); |
|
| 731 | - $absolutePath2 = Filesystem::normalizePath($this->getAbsolutePath($path2)); |
|
| 732 | - $result = false; |
|
| 733 | - if ( |
|
| 734 | - Filesystem::isValidPath($path2) |
|
| 735 | - and Filesystem::isValidPath($path1) |
|
| 736 | - and !Filesystem::isFileBlacklisted($path2) |
|
| 737 | - ) { |
|
| 738 | - $path1 = $this->getRelativePath($absolutePath1); |
|
| 739 | - $path2 = $this->getRelativePath($absolutePath2); |
|
| 740 | - $exists = $this->file_exists($path2); |
|
| 741 | - |
|
| 742 | - if ($path1 == null or $path2 == null) { |
|
| 743 | - return false; |
|
| 744 | - } |
|
| 745 | - |
|
| 746 | - $this->lockFile($path1, ILockingProvider::LOCK_SHARED, true); |
|
| 747 | - try { |
|
| 748 | - $this->lockFile($path2, ILockingProvider::LOCK_SHARED, true); |
|
| 749 | - } catch (LockedException $e) { |
|
| 750 | - $this->unlockFile($path1, ILockingProvider::LOCK_SHARED); |
|
| 751 | - throw $e; |
|
| 752 | - } |
|
| 753 | - |
|
| 754 | - $run = true; |
|
| 755 | - if ($this->shouldEmitHooks($path1) && (Cache\Scanner::isPartialFile($path1) && !Cache\Scanner::isPartialFile($path2))) { |
|
| 756 | - // if it was a rename from a part file to a regular file it was a write and not a rename operation |
|
| 757 | - $this->emit_file_hooks_pre($exists, $path2, $run); |
|
| 758 | - } elseif ($this->shouldEmitHooks($path1)) { |
|
| 759 | - \OC_Hook::emit( |
|
| 760 | - Filesystem::CLASSNAME, Filesystem::signal_rename, |
|
| 761 | - array( |
|
| 762 | - Filesystem::signal_param_oldpath => $this->getHookPath($path1), |
|
| 763 | - Filesystem::signal_param_newpath => $this->getHookPath($path2), |
|
| 764 | - Filesystem::signal_param_run => &$run |
|
| 765 | - ) |
|
| 766 | - ); |
|
| 767 | - } |
|
| 768 | - if ($run) { |
|
| 769 | - $this->verifyPath(dirname($path2), basename($path2)); |
|
| 770 | - |
|
| 771 | - $manager = Filesystem::getMountManager(); |
|
| 772 | - $mount1 = $this->getMount($path1); |
|
| 773 | - $mount2 = $this->getMount($path2); |
|
| 774 | - $storage1 = $mount1->getStorage(); |
|
| 775 | - $storage2 = $mount2->getStorage(); |
|
| 776 | - $internalPath1 = $mount1->getInternalPath($absolutePath1); |
|
| 777 | - $internalPath2 = $mount2->getInternalPath($absolutePath2); |
|
| 778 | - |
|
| 779 | - $this->changeLock($path1, ILockingProvider::LOCK_EXCLUSIVE, true); |
|
| 780 | - $this->changeLock($path2, ILockingProvider::LOCK_EXCLUSIVE, true); |
|
| 781 | - |
|
| 782 | - if ($internalPath1 === '' and $mount1 instanceof MoveableMount) { |
|
| 783 | - if ($this->isTargetAllowed($absolutePath2)) { |
|
| 784 | - /** |
|
| 785 | - * @var \OC\Files\Mount\MountPoint | \OC\Files\Mount\MoveableMount $mount1 |
|
| 786 | - */ |
|
| 787 | - $sourceMountPoint = $mount1->getMountPoint(); |
|
| 788 | - $result = $mount1->moveMount($absolutePath2); |
|
| 789 | - $manager->moveMount($sourceMountPoint, $mount1->getMountPoint()); |
|
| 790 | - } else { |
|
| 791 | - $result = false; |
|
| 792 | - } |
|
| 793 | - // moving a file/folder within the same mount point |
|
| 794 | - } elseif ($storage1 === $storage2) { |
|
| 795 | - if ($storage1) { |
|
| 796 | - $result = $storage1->rename($internalPath1, $internalPath2); |
|
| 797 | - } else { |
|
| 798 | - $result = false; |
|
| 799 | - } |
|
| 800 | - // moving a file/folder between storages (from $storage1 to $storage2) |
|
| 801 | - } else { |
|
| 802 | - $result = $storage2->moveFromStorage($storage1, $internalPath1, $internalPath2); |
|
| 803 | - } |
|
| 804 | - |
|
| 805 | - if ((Cache\Scanner::isPartialFile($path1) && !Cache\Scanner::isPartialFile($path2)) && $result !== false) { |
|
| 806 | - // if it was a rename from a part file to a regular file it was a write and not a rename operation |
|
| 807 | - |
|
| 808 | - $this->writeUpdate($storage2, $internalPath2); |
|
| 809 | - } else if ($result) { |
|
| 810 | - if ($internalPath1 !== '') { // don't do a cache update for moved mounts |
|
| 811 | - $this->renameUpdate($storage1, $storage2, $internalPath1, $internalPath2); |
|
| 812 | - } |
|
| 813 | - } |
|
| 814 | - |
|
| 815 | - $this->changeLock($path1, ILockingProvider::LOCK_SHARED, true); |
|
| 816 | - $this->changeLock($path2, ILockingProvider::LOCK_SHARED, true); |
|
| 817 | - |
|
| 818 | - if ((Cache\Scanner::isPartialFile($path1) && !Cache\Scanner::isPartialFile($path2)) && $result !== false) { |
|
| 819 | - if ($this->shouldEmitHooks()) { |
|
| 820 | - $this->emit_file_hooks_post($exists, $path2); |
|
| 821 | - } |
|
| 822 | - } elseif ($result) { |
|
| 823 | - if ($this->shouldEmitHooks($path1) and $this->shouldEmitHooks($path2)) { |
|
| 824 | - \OC_Hook::emit( |
|
| 825 | - Filesystem::CLASSNAME, |
|
| 826 | - Filesystem::signal_post_rename, |
|
| 827 | - array( |
|
| 828 | - Filesystem::signal_param_oldpath => $this->getHookPath($path1), |
|
| 829 | - Filesystem::signal_param_newpath => $this->getHookPath($path2) |
|
| 830 | - ) |
|
| 831 | - ); |
|
| 832 | - } |
|
| 833 | - } |
|
| 834 | - } |
|
| 835 | - $this->unlockFile($path1, ILockingProvider::LOCK_SHARED, true); |
|
| 836 | - $this->unlockFile($path2, ILockingProvider::LOCK_SHARED, true); |
|
| 837 | - } |
|
| 838 | - return $result; |
|
| 839 | - } |
|
| 840 | - |
|
| 841 | - /** |
|
| 842 | - * Copy a file/folder from the source path to target path |
|
| 843 | - * |
|
| 844 | - * @param string $path1 source path |
|
| 845 | - * @param string $path2 target path |
|
| 846 | - * @param bool $preserveMtime whether to preserve mtime on the copy |
|
| 847 | - * |
|
| 848 | - * @return bool|mixed |
|
| 849 | - */ |
|
| 850 | - public function copy($path1, $path2, $preserveMtime = false) { |
|
| 851 | - $absolutePath1 = Filesystem::normalizePath($this->getAbsolutePath($path1)); |
|
| 852 | - $absolutePath2 = Filesystem::normalizePath($this->getAbsolutePath($path2)); |
|
| 853 | - $result = false; |
|
| 854 | - if ( |
|
| 855 | - Filesystem::isValidPath($path2) |
|
| 856 | - and Filesystem::isValidPath($path1) |
|
| 857 | - and !Filesystem::isFileBlacklisted($path2) |
|
| 858 | - ) { |
|
| 859 | - $path1 = $this->getRelativePath($absolutePath1); |
|
| 860 | - $path2 = $this->getRelativePath($absolutePath2); |
|
| 861 | - |
|
| 862 | - if ($path1 == null or $path2 == null) { |
|
| 863 | - return false; |
|
| 864 | - } |
|
| 865 | - $run = true; |
|
| 866 | - |
|
| 867 | - $this->lockFile($path2, ILockingProvider::LOCK_SHARED); |
|
| 868 | - $this->lockFile($path1, ILockingProvider::LOCK_SHARED); |
|
| 869 | - $lockTypePath1 = ILockingProvider::LOCK_SHARED; |
|
| 870 | - $lockTypePath2 = ILockingProvider::LOCK_SHARED; |
|
| 871 | - |
|
| 872 | - try { |
|
| 873 | - |
|
| 874 | - $exists = $this->file_exists($path2); |
|
| 875 | - if ($this->shouldEmitHooks()) { |
|
| 876 | - \OC_Hook::emit( |
|
| 877 | - Filesystem::CLASSNAME, |
|
| 878 | - Filesystem::signal_copy, |
|
| 879 | - array( |
|
| 880 | - Filesystem::signal_param_oldpath => $this->getHookPath($path1), |
|
| 881 | - Filesystem::signal_param_newpath => $this->getHookPath($path2), |
|
| 882 | - Filesystem::signal_param_run => &$run |
|
| 883 | - ) |
|
| 884 | - ); |
|
| 885 | - $this->emit_file_hooks_pre($exists, $path2, $run); |
|
| 886 | - } |
|
| 887 | - if ($run) { |
|
| 888 | - $mount1 = $this->getMount($path1); |
|
| 889 | - $mount2 = $this->getMount($path2); |
|
| 890 | - $storage1 = $mount1->getStorage(); |
|
| 891 | - $internalPath1 = $mount1->getInternalPath($absolutePath1); |
|
| 892 | - $storage2 = $mount2->getStorage(); |
|
| 893 | - $internalPath2 = $mount2->getInternalPath($absolutePath2); |
|
| 894 | - |
|
| 895 | - $this->changeLock($path2, ILockingProvider::LOCK_EXCLUSIVE); |
|
| 896 | - $lockTypePath2 = ILockingProvider::LOCK_EXCLUSIVE; |
|
| 897 | - |
|
| 898 | - if ($mount1->getMountPoint() == $mount2->getMountPoint()) { |
|
| 899 | - if ($storage1) { |
|
| 900 | - $result = $storage1->copy($internalPath1, $internalPath2); |
|
| 901 | - } else { |
|
| 902 | - $result = false; |
|
| 903 | - } |
|
| 904 | - } else { |
|
| 905 | - $result = $storage2->copyFromStorage($storage1, $internalPath1, $internalPath2); |
|
| 906 | - } |
|
| 907 | - |
|
| 908 | - $this->writeUpdate($storage2, $internalPath2); |
|
| 909 | - |
|
| 910 | - $this->changeLock($path2, ILockingProvider::LOCK_SHARED); |
|
| 911 | - $lockTypePath2 = ILockingProvider::LOCK_SHARED; |
|
| 912 | - |
|
| 913 | - if ($this->shouldEmitHooks() && $result !== false) { |
|
| 914 | - \OC_Hook::emit( |
|
| 915 | - Filesystem::CLASSNAME, |
|
| 916 | - Filesystem::signal_post_copy, |
|
| 917 | - array( |
|
| 918 | - Filesystem::signal_param_oldpath => $this->getHookPath($path1), |
|
| 919 | - Filesystem::signal_param_newpath => $this->getHookPath($path2) |
|
| 920 | - ) |
|
| 921 | - ); |
|
| 922 | - $this->emit_file_hooks_post($exists, $path2); |
|
| 923 | - } |
|
| 924 | - |
|
| 925 | - } |
|
| 926 | - } catch (\Exception $e) { |
|
| 927 | - $this->unlockFile($path2, $lockTypePath2); |
|
| 928 | - $this->unlockFile($path1, $lockTypePath1); |
|
| 929 | - throw $e; |
|
| 930 | - } |
|
| 931 | - |
|
| 932 | - $this->unlockFile($path2, $lockTypePath2); |
|
| 933 | - $this->unlockFile($path1, $lockTypePath1); |
|
| 934 | - |
|
| 935 | - } |
|
| 936 | - return $result; |
|
| 937 | - } |
|
| 938 | - |
|
| 939 | - /** |
|
| 940 | - * @param string $path |
|
| 941 | - * @param string $mode 'r' or 'w' |
|
| 942 | - * @return resource |
|
| 943 | - */ |
|
| 944 | - public function fopen($path, $mode) { |
|
| 945 | - $mode = str_replace('b', '', $mode); // the binary flag is a windows only feature which we do not support |
|
| 946 | - $hooks = array(); |
|
| 947 | - switch ($mode) { |
|
| 948 | - case 'r': |
|
| 949 | - $hooks[] = 'read'; |
|
| 950 | - break; |
|
| 951 | - case 'r+': |
|
| 952 | - case 'w+': |
|
| 953 | - case 'x+': |
|
| 954 | - case 'a+': |
|
| 955 | - $hooks[] = 'read'; |
|
| 956 | - $hooks[] = 'write'; |
|
| 957 | - break; |
|
| 958 | - case 'w': |
|
| 959 | - case 'x': |
|
| 960 | - case 'a': |
|
| 961 | - $hooks[] = 'write'; |
|
| 962 | - break; |
|
| 963 | - default: |
|
| 964 | - \OCP\Util::writeLog('core', 'invalid mode (' . $mode . ') for ' . $path, \OCP\Util::ERROR); |
|
| 965 | - } |
|
| 966 | - |
|
| 967 | - if ($mode !== 'r' && $mode !== 'w') { |
|
| 968 | - \OC::$server->getLogger()->info('Trying to open a file with a mode other than "r" or "w" can cause severe performance issues with some backends'); |
|
| 969 | - } |
|
| 970 | - |
|
| 971 | - return $this->basicOperation('fopen', $path, $hooks, $mode); |
|
| 972 | - } |
|
| 973 | - |
|
| 974 | - /** |
|
| 975 | - * @param string $path |
|
| 976 | - * @return bool|string |
|
| 977 | - * @throws \OCP\Files\InvalidPathException |
|
| 978 | - */ |
|
| 979 | - public function toTmpFile($path) { |
|
| 980 | - $this->assertPathLength($path); |
|
| 981 | - if (Filesystem::isValidPath($path)) { |
|
| 982 | - $source = $this->fopen($path, 'r'); |
|
| 983 | - if ($source) { |
|
| 984 | - $extension = pathinfo($path, PATHINFO_EXTENSION); |
|
| 985 | - $tmpFile = \OC::$server->getTempManager()->getTemporaryFile($extension); |
|
| 986 | - file_put_contents($tmpFile, $source); |
|
| 987 | - return $tmpFile; |
|
| 988 | - } else { |
|
| 989 | - return false; |
|
| 990 | - } |
|
| 991 | - } else { |
|
| 992 | - return false; |
|
| 993 | - } |
|
| 994 | - } |
|
| 995 | - |
|
| 996 | - /** |
|
| 997 | - * @param string $tmpFile |
|
| 998 | - * @param string $path |
|
| 999 | - * @return bool|mixed |
|
| 1000 | - * @throws \OCP\Files\InvalidPathException |
|
| 1001 | - */ |
|
| 1002 | - public function fromTmpFile($tmpFile, $path) { |
|
| 1003 | - $this->assertPathLength($path); |
|
| 1004 | - if (Filesystem::isValidPath($path)) { |
|
| 1005 | - |
|
| 1006 | - // Get directory that the file is going into |
|
| 1007 | - $filePath = dirname($path); |
|
| 1008 | - |
|
| 1009 | - // Create the directories if any |
|
| 1010 | - if (!$this->file_exists($filePath)) { |
|
| 1011 | - $result = $this->createParentDirectories($filePath); |
|
| 1012 | - if ($result === false) { |
|
| 1013 | - return false; |
|
| 1014 | - } |
|
| 1015 | - } |
|
| 1016 | - |
|
| 1017 | - $source = fopen($tmpFile, 'r'); |
|
| 1018 | - if ($source) { |
|
| 1019 | - $result = $this->file_put_contents($path, $source); |
|
| 1020 | - // $this->file_put_contents() might have already closed |
|
| 1021 | - // the resource, so we check it, before trying to close it |
|
| 1022 | - // to avoid messages in the error log. |
|
| 1023 | - if (is_resource($source)) { |
|
| 1024 | - fclose($source); |
|
| 1025 | - } |
|
| 1026 | - unlink($tmpFile); |
|
| 1027 | - return $result; |
|
| 1028 | - } else { |
|
| 1029 | - return false; |
|
| 1030 | - } |
|
| 1031 | - } else { |
|
| 1032 | - return false; |
|
| 1033 | - } |
|
| 1034 | - } |
|
| 1035 | - |
|
| 1036 | - |
|
| 1037 | - /** |
|
| 1038 | - * @param string $path |
|
| 1039 | - * @return mixed |
|
| 1040 | - * @throws \OCP\Files\InvalidPathException |
|
| 1041 | - */ |
|
| 1042 | - public function getMimeType($path) { |
|
| 1043 | - $this->assertPathLength($path); |
|
| 1044 | - return $this->basicOperation('getMimeType', $path); |
|
| 1045 | - } |
|
| 1046 | - |
|
| 1047 | - /** |
|
| 1048 | - * @param string $type |
|
| 1049 | - * @param string $path |
|
| 1050 | - * @param bool $raw |
|
| 1051 | - * @return bool|null|string |
|
| 1052 | - */ |
|
| 1053 | - public function hash($type, $path, $raw = false) { |
|
| 1054 | - $postFix = (substr($path, -1, 1) === '/') ? '/' : ''; |
|
| 1055 | - $absolutePath = Filesystem::normalizePath($this->getAbsolutePath($path)); |
|
| 1056 | - if (Filesystem::isValidPath($path)) { |
|
| 1057 | - $path = $this->getRelativePath($absolutePath); |
|
| 1058 | - if ($path == null) { |
|
| 1059 | - return false; |
|
| 1060 | - } |
|
| 1061 | - if ($this->shouldEmitHooks($path)) { |
|
| 1062 | - \OC_Hook::emit( |
|
| 1063 | - Filesystem::CLASSNAME, |
|
| 1064 | - Filesystem::signal_read, |
|
| 1065 | - array(Filesystem::signal_param_path => $this->getHookPath($path)) |
|
| 1066 | - ); |
|
| 1067 | - } |
|
| 1068 | - list($storage, $internalPath) = Filesystem::resolvePath($absolutePath . $postFix); |
|
| 1069 | - if ($storage) { |
|
| 1070 | - $result = $storage->hash($type, $internalPath, $raw); |
|
| 1071 | - return $result; |
|
| 1072 | - } |
|
| 1073 | - } |
|
| 1074 | - return null; |
|
| 1075 | - } |
|
| 1076 | - |
|
| 1077 | - /** |
|
| 1078 | - * @param string $path |
|
| 1079 | - * @return mixed |
|
| 1080 | - * @throws \OCP\Files\InvalidPathException |
|
| 1081 | - */ |
|
| 1082 | - public function free_space($path = '/') { |
|
| 1083 | - $this->assertPathLength($path); |
|
| 1084 | - $result = $this->basicOperation('free_space', $path); |
|
| 1085 | - if ($result === null) { |
|
| 1086 | - throw new InvalidPathException(); |
|
| 1087 | - } |
|
| 1088 | - return $result; |
|
| 1089 | - } |
|
| 1090 | - |
|
| 1091 | - /** |
|
| 1092 | - * abstraction layer for basic filesystem functions: wrapper for \OC\Files\Storage\Storage |
|
| 1093 | - * |
|
| 1094 | - * @param string $operation |
|
| 1095 | - * @param string $path |
|
| 1096 | - * @param array $hooks (optional) |
|
| 1097 | - * @param mixed $extraParam (optional) |
|
| 1098 | - * @return mixed |
|
| 1099 | - * @throws \Exception |
|
| 1100 | - * |
|
| 1101 | - * This method takes requests for basic filesystem functions (e.g. reading & writing |
|
| 1102 | - * files), processes hooks and proxies, sanitises paths, and finally passes them on to |
|
| 1103 | - * \OC\Files\Storage\Storage for delegation to a storage backend for execution |
|
| 1104 | - */ |
|
| 1105 | - private function basicOperation($operation, $path, $hooks = [], $extraParam = null) { |
|
| 1106 | - $postFix = (substr($path, -1, 1) === '/') ? '/' : ''; |
|
| 1107 | - $absolutePath = Filesystem::normalizePath($this->getAbsolutePath($path)); |
|
| 1108 | - if (Filesystem::isValidPath($path) |
|
| 1109 | - and !Filesystem::isFileBlacklisted($path) |
|
| 1110 | - ) { |
|
| 1111 | - $path = $this->getRelativePath($absolutePath); |
|
| 1112 | - if ($path == null) { |
|
| 1113 | - return false; |
|
| 1114 | - } |
|
| 1115 | - |
|
| 1116 | - if (in_array('write', $hooks) || in_array('delete', $hooks) || in_array('read', $hooks)) { |
|
| 1117 | - // always a shared lock during pre-hooks so the hook can read the file |
|
| 1118 | - $this->lockFile($path, ILockingProvider::LOCK_SHARED); |
|
| 1119 | - } |
|
| 1120 | - |
|
| 1121 | - $run = $this->runHooks($hooks, $path); |
|
| 1122 | - /** @var \OC\Files\Storage\Storage $storage */ |
|
| 1123 | - list($storage, $internalPath) = Filesystem::resolvePath($absolutePath . $postFix); |
|
| 1124 | - if ($run and $storage) { |
|
| 1125 | - if (in_array('write', $hooks) || in_array('delete', $hooks)) { |
|
| 1126 | - $this->changeLock($path, ILockingProvider::LOCK_EXCLUSIVE); |
|
| 1127 | - } |
|
| 1128 | - try { |
|
| 1129 | - if (!is_null($extraParam)) { |
|
| 1130 | - $result = $storage->$operation($internalPath, $extraParam); |
|
| 1131 | - } else { |
|
| 1132 | - $result = $storage->$operation($internalPath); |
|
| 1133 | - } |
|
| 1134 | - } catch (\Exception $e) { |
|
| 1135 | - if (in_array('write', $hooks) || in_array('delete', $hooks)) { |
|
| 1136 | - $this->unlockFile($path, ILockingProvider::LOCK_EXCLUSIVE); |
|
| 1137 | - } else if (in_array('read', $hooks)) { |
|
| 1138 | - $this->unlockFile($path, ILockingProvider::LOCK_SHARED); |
|
| 1139 | - } |
|
| 1140 | - throw $e; |
|
| 1141 | - } |
|
| 1142 | - |
|
| 1143 | - if ($result && in_array('delete', $hooks) and $result) { |
|
| 1144 | - $this->removeUpdate($storage, $internalPath); |
|
| 1145 | - } |
|
| 1146 | - if ($result && in_array('write', $hooks) and $operation !== 'fopen') { |
|
| 1147 | - $this->writeUpdate($storage, $internalPath); |
|
| 1148 | - } |
|
| 1149 | - if ($result && in_array('touch', $hooks)) { |
|
| 1150 | - $this->writeUpdate($storage, $internalPath, $extraParam); |
|
| 1151 | - } |
|
| 1152 | - |
|
| 1153 | - if ((in_array('write', $hooks) || in_array('delete', $hooks)) && ($operation !== 'fopen' || $result === false)) { |
|
| 1154 | - $this->changeLock($path, ILockingProvider::LOCK_SHARED); |
|
| 1155 | - } |
|
| 1156 | - |
|
| 1157 | - $unlockLater = false; |
|
| 1158 | - if ($this->lockingEnabled && $operation === 'fopen' && is_resource($result)) { |
|
| 1159 | - $unlockLater = true; |
|
| 1160 | - // make sure our unlocking callback will still be called if connection is aborted |
|
| 1161 | - ignore_user_abort(true); |
|
| 1162 | - $result = CallbackWrapper::wrap($result, null, null, function () use ($hooks, $path) { |
|
| 1163 | - if (in_array('write', $hooks)) { |
|
| 1164 | - $this->unlockFile($path, ILockingProvider::LOCK_EXCLUSIVE); |
|
| 1165 | - } else if (in_array('read', $hooks)) { |
|
| 1166 | - $this->unlockFile($path, ILockingProvider::LOCK_SHARED); |
|
| 1167 | - } |
|
| 1168 | - }); |
|
| 1169 | - } |
|
| 1170 | - |
|
| 1171 | - if ($this->shouldEmitHooks($path) && $result !== false) { |
|
| 1172 | - if ($operation != 'fopen') { //no post hooks for fopen, the file stream is still open |
|
| 1173 | - $this->runHooks($hooks, $path, true); |
|
| 1174 | - } |
|
| 1175 | - } |
|
| 1176 | - |
|
| 1177 | - if (!$unlockLater |
|
| 1178 | - && (in_array('write', $hooks) || in_array('delete', $hooks) || in_array('read', $hooks)) |
|
| 1179 | - ) { |
|
| 1180 | - $this->unlockFile($path, ILockingProvider::LOCK_SHARED); |
|
| 1181 | - } |
|
| 1182 | - return $result; |
|
| 1183 | - } else { |
|
| 1184 | - $this->unlockFile($path, ILockingProvider::LOCK_SHARED); |
|
| 1185 | - } |
|
| 1186 | - } |
|
| 1187 | - return null; |
|
| 1188 | - } |
|
| 1189 | - |
|
| 1190 | - /** |
|
| 1191 | - * get the path relative to the default root for hook usage |
|
| 1192 | - * |
|
| 1193 | - * @param string $path |
|
| 1194 | - * @return string |
|
| 1195 | - */ |
|
| 1196 | - private function getHookPath($path) { |
|
| 1197 | - if (!Filesystem::getView()) { |
|
| 1198 | - return $path; |
|
| 1199 | - } |
|
| 1200 | - return Filesystem::getView()->getRelativePath($this->getAbsolutePath($path)); |
|
| 1201 | - } |
|
| 1202 | - |
|
| 1203 | - private function shouldEmitHooks($path = '') { |
|
| 1204 | - if ($path && Cache\Scanner::isPartialFile($path)) { |
|
| 1205 | - return false; |
|
| 1206 | - } |
|
| 1207 | - if (!Filesystem::$loaded) { |
|
| 1208 | - return false; |
|
| 1209 | - } |
|
| 1210 | - $defaultRoot = Filesystem::getRoot(); |
|
| 1211 | - if ($defaultRoot === null) { |
|
| 1212 | - return false; |
|
| 1213 | - } |
|
| 1214 | - if ($this->fakeRoot === $defaultRoot) { |
|
| 1215 | - return true; |
|
| 1216 | - } |
|
| 1217 | - $fullPath = $this->getAbsolutePath($path); |
|
| 1218 | - |
|
| 1219 | - if ($fullPath === $defaultRoot) { |
|
| 1220 | - return true; |
|
| 1221 | - } |
|
| 1222 | - |
|
| 1223 | - return (strlen($fullPath) > strlen($defaultRoot)) && (substr($fullPath, 0, strlen($defaultRoot) + 1) === $defaultRoot . '/'); |
|
| 1224 | - } |
|
| 1225 | - |
|
| 1226 | - /** |
|
| 1227 | - * @param string[] $hooks |
|
| 1228 | - * @param string $path |
|
| 1229 | - * @param bool $post |
|
| 1230 | - * @return bool |
|
| 1231 | - */ |
|
| 1232 | - private function runHooks($hooks, $path, $post = false) { |
|
| 1233 | - $relativePath = $path; |
|
| 1234 | - $path = $this->getHookPath($path); |
|
| 1235 | - $prefix = ($post) ? 'post_' : ''; |
|
| 1236 | - $run = true; |
|
| 1237 | - if ($this->shouldEmitHooks($relativePath)) { |
|
| 1238 | - foreach ($hooks as $hook) { |
|
| 1239 | - if ($hook != 'read') { |
|
| 1240 | - \OC_Hook::emit( |
|
| 1241 | - Filesystem::CLASSNAME, |
|
| 1242 | - $prefix . $hook, |
|
| 1243 | - array( |
|
| 1244 | - Filesystem::signal_param_run => &$run, |
|
| 1245 | - Filesystem::signal_param_path => $path |
|
| 1246 | - ) |
|
| 1247 | - ); |
|
| 1248 | - } elseif (!$post) { |
|
| 1249 | - \OC_Hook::emit( |
|
| 1250 | - Filesystem::CLASSNAME, |
|
| 1251 | - $prefix . $hook, |
|
| 1252 | - array( |
|
| 1253 | - Filesystem::signal_param_path => $path |
|
| 1254 | - ) |
|
| 1255 | - ); |
|
| 1256 | - } |
|
| 1257 | - } |
|
| 1258 | - } |
|
| 1259 | - return $run; |
|
| 1260 | - } |
|
| 1261 | - |
|
| 1262 | - /** |
|
| 1263 | - * check if a file or folder has been updated since $time |
|
| 1264 | - * |
|
| 1265 | - * @param string $path |
|
| 1266 | - * @param int $time |
|
| 1267 | - * @return bool |
|
| 1268 | - */ |
|
| 1269 | - public function hasUpdated($path, $time) { |
|
| 1270 | - return $this->basicOperation('hasUpdated', $path, array(), $time); |
|
| 1271 | - } |
|
| 1272 | - |
|
| 1273 | - /** |
|
| 1274 | - * @param string $ownerId |
|
| 1275 | - * @return \OC\User\User |
|
| 1276 | - */ |
|
| 1277 | - private function getUserObjectForOwner($ownerId) { |
|
| 1278 | - $owner = $this->userManager->get($ownerId); |
|
| 1279 | - if ($owner instanceof IUser) { |
|
| 1280 | - return $owner; |
|
| 1281 | - } else { |
|
| 1282 | - return new User($ownerId, null); |
|
| 1283 | - } |
|
| 1284 | - } |
|
| 1285 | - |
|
| 1286 | - /** |
|
| 1287 | - * Get file info from cache |
|
| 1288 | - * |
|
| 1289 | - * If the file is not in cached it will be scanned |
|
| 1290 | - * If the file has changed on storage the cache will be updated |
|
| 1291 | - * |
|
| 1292 | - * @param \OC\Files\Storage\Storage $storage |
|
| 1293 | - * @param string $internalPath |
|
| 1294 | - * @param string $relativePath |
|
| 1295 | - * @return ICacheEntry|bool |
|
| 1296 | - */ |
|
| 1297 | - private function getCacheEntry($storage, $internalPath, $relativePath) { |
|
| 1298 | - $cache = $storage->getCache($internalPath); |
|
| 1299 | - $data = $cache->get($internalPath); |
|
| 1300 | - $watcher = $storage->getWatcher($internalPath); |
|
| 1301 | - |
|
| 1302 | - try { |
|
| 1303 | - // if the file is not in the cache or needs to be updated, trigger the scanner and reload the data |
|
| 1304 | - if (!$data || $data['size'] === -1) { |
|
| 1305 | - $this->lockFile($relativePath, ILockingProvider::LOCK_SHARED); |
|
| 1306 | - if (!$storage->file_exists($internalPath)) { |
|
| 1307 | - $this->unlockFile($relativePath, ILockingProvider::LOCK_SHARED); |
|
| 1308 | - return false; |
|
| 1309 | - } |
|
| 1310 | - $scanner = $storage->getScanner($internalPath); |
|
| 1311 | - $scanner->scan($internalPath, Cache\Scanner::SCAN_SHALLOW); |
|
| 1312 | - $data = $cache->get($internalPath); |
|
| 1313 | - $this->unlockFile($relativePath, ILockingProvider::LOCK_SHARED); |
|
| 1314 | - } else if (!Cache\Scanner::isPartialFile($internalPath) && $watcher->needsUpdate($internalPath, $data)) { |
|
| 1315 | - $this->lockFile($relativePath, ILockingProvider::LOCK_SHARED); |
|
| 1316 | - $watcher->update($internalPath, $data); |
|
| 1317 | - $storage->getPropagator()->propagateChange($internalPath, time()); |
|
| 1318 | - $data = $cache->get($internalPath); |
|
| 1319 | - $this->unlockFile($relativePath, ILockingProvider::LOCK_SHARED); |
|
| 1320 | - } |
|
| 1321 | - } catch (LockedException $e) { |
|
| 1322 | - // if the file is locked we just use the old cache info |
|
| 1323 | - } |
|
| 1324 | - |
|
| 1325 | - return $data; |
|
| 1326 | - } |
|
| 1327 | - |
|
| 1328 | - /** |
|
| 1329 | - * get the filesystem info |
|
| 1330 | - * |
|
| 1331 | - * @param string $path |
|
| 1332 | - * @param boolean|string $includeMountPoints true to add mountpoint sizes, |
|
| 1333 | - * 'ext' to add only ext storage mount point sizes. Defaults to true. |
|
| 1334 | - * defaults to true |
|
| 1335 | - * @return \OC\Files\FileInfo|false False if file does not exist |
|
| 1336 | - */ |
|
| 1337 | - public function getFileInfo($path, $includeMountPoints = true) { |
|
| 1338 | - $this->assertPathLength($path); |
|
| 1339 | - if (!Filesystem::isValidPath($path)) { |
|
| 1340 | - return false; |
|
| 1341 | - } |
|
| 1342 | - if (Cache\Scanner::isPartialFile($path)) { |
|
| 1343 | - return $this->getPartFileInfo($path); |
|
| 1344 | - } |
|
| 1345 | - $relativePath = $path; |
|
| 1346 | - $path = Filesystem::normalizePath($this->fakeRoot . '/' . $path); |
|
| 1347 | - |
|
| 1348 | - $mount = Filesystem::getMountManager()->find($path); |
|
| 1349 | - $storage = $mount->getStorage(); |
|
| 1350 | - $internalPath = $mount->getInternalPath($path); |
|
| 1351 | - if ($storage) { |
|
| 1352 | - $data = $this->getCacheEntry($storage, $internalPath, $relativePath); |
|
| 1353 | - |
|
| 1354 | - if (!$data instanceof ICacheEntry) { |
|
| 1355 | - return false; |
|
| 1356 | - } |
|
| 1357 | - |
|
| 1358 | - if ($mount instanceof MoveableMount && $internalPath === '') { |
|
| 1359 | - $data['permissions'] |= \OCP\Constants::PERMISSION_DELETE; |
|
| 1360 | - } |
|
| 1361 | - |
|
| 1362 | - $owner = $this->getUserObjectForOwner($storage->getOwner($internalPath)); |
|
| 1363 | - $info = new FileInfo($path, $storage, $internalPath, $data, $mount, $owner); |
|
| 1364 | - |
|
| 1365 | - if ($data and isset($data['fileid'])) { |
|
| 1366 | - if ($includeMountPoints and $data['mimetype'] === 'httpd/unix-directory') { |
|
| 1367 | - //add the sizes of other mount points to the folder |
|
| 1368 | - $extOnly = ($includeMountPoints === 'ext'); |
|
| 1369 | - $mounts = Filesystem::getMountManager()->findIn($path); |
|
| 1370 | - $info->setSubMounts(array_filter($mounts, function (IMountPoint $mount) use ($extOnly) { |
|
| 1371 | - $subStorage = $mount->getStorage(); |
|
| 1372 | - return !($extOnly && $subStorage instanceof \OCA\Files_Sharing\SharedStorage); |
|
| 1373 | - })); |
|
| 1374 | - } |
|
| 1375 | - } |
|
| 1376 | - |
|
| 1377 | - return $info; |
|
| 1378 | - } |
|
| 1379 | - |
|
| 1380 | - return false; |
|
| 1381 | - } |
|
| 1382 | - |
|
| 1383 | - /** |
|
| 1384 | - * get the content of a directory |
|
| 1385 | - * |
|
| 1386 | - * @param string $directory path under datadirectory |
|
| 1387 | - * @param string $mimetype_filter limit returned content to this mimetype or mimepart |
|
| 1388 | - * @return FileInfo[] |
|
| 1389 | - */ |
|
| 1390 | - public function getDirectoryContent($directory, $mimetype_filter = '') { |
|
| 1391 | - $this->assertPathLength($directory); |
|
| 1392 | - if (!Filesystem::isValidPath($directory)) { |
|
| 1393 | - return []; |
|
| 1394 | - } |
|
| 1395 | - $path = $this->getAbsolutePath($directory); |
|
| 1396 | - $path = Filesystem::normalizePath($path); |
|
| 1397 | - $mount = $this->getMount($directory); |
|
| 1398 | - $storage = $mount->getStorage(); |
|
| 1399 | - $internalPath = $mount->getInternalPath($path); |
|
| 1400 | - if ($storage) { |
|
| 1401 | - $cache = $storage->getCache($internalPath); |
|
| 1402 | - $user = \OC_User::getUser(); |
|
| 1403 | - |
|
| 1404 | - $data = $this->getCacheEntry($storage, $internalPath, $directory); |
|
| 1405 | - |
|
| 1406 | - if (!$data instanceof ICacheEntry || !isset($data['fileid']) || !($data->getPermissions() && Constants::PERMISSION_READ)) { |
|
| 1407 | - return []; |
|
| 1408 | - } |
|
| 1409 | - |
|
| 1410 | - $folderId = $data['fileid']; |
|
| 1411 | - $contents = $cache->getFolderContentsById($folderId); //TODO: mimetype_filter |
|
| 1412 | - |
|
| 1413 | - $sharingDisabled = \OCP\Util::isSharingDisabledForUser(); |
|
| 1414 | - /** |
|
| 1415 | - * @var \OC\Files\FileInfo[] $files |
|
| 1416 | - */ |
|
| 1417 | - $files = array_map(function (ICacheEntry $content) use ($path, $storage, $mount, $sharingDisabled) { |
|
| 1418 | - if ($sharingDisabled) { |
|
| 1419 | - $content['permissions'] = $content['permissions'] & ~\OCP\Constants::PERMISSION_SHARE; |
|
| 1420 | - } |
|
| 1421 | - $owner = $this->getUserObjectForOwner($storage->getOwner($content['path'])); |
|
| 1422 | - return new FileInfo($path . '/' . $content['name'], $storage, $content['path'], $content, $mount, $owner); |
|
| 1423 | - }, $contents); |
|
| 1424 | - |
|
| 1425 | - //add a folder for any mountpoint in this directory and add the sizes of other mountpoints to the folders |
|
| 1426 | - $mounts = Filesystem::getMountManager()->findIn($path); |
|
| 1427 | - $dirLength = strlen($path); |
|
| 1428 | - foreach ($mounts as $mount) { |
|
| 1429 | - $mountPoint = $mount->getMountPoint(); |
|
| 1430 | - $subStorage = $mount->getStorage(); |
|
| 1431 | - if ($subStorage) { |
|
| 1432 | - $subCache = $subStorage->getCache(''); |
|
| 1433 | - |
|
| 1434 | - $rootEntry = $subCache->get(''); |
|
| 1435 | - if (!$rootEntry) { |
|
| 1436 | - $subScanner = $subStorage->getScanner(''); |
|
| 1437 | - try { |
|
| 1438 | - $subScanner->scanFile(''); |
|
| 1439 | - } catch (\OCP\Files\StorageNotAvailableException $e) { |
|
| 1440 | - continue; |
|
| 1441 | - } catch (\OCP\Files\StorageInvalidException $e) { |
|
| 1442 | - continue; |
|
| 1443 | - } catch (\Exception $e) { |
|
| 1444 | - // sometimes when the storage is not available it can be any exception |
|
| 1445 | - \OCP\Util::writeLog( |
|
| 1446 | - 'core', |
|
| 1447 | - 'Exception while scanning storage "' . $subStorage->getId() . '": ' . |
|
| 1448 | - get_class($e) . ': ' . $e->getMessage(), |
|
| 1449 | - \OCP\Util::ERROR |
|
| 1450 | - ); |
|
| 1451 | - continue; |
|
| 1452 | - } |
|
| 1453 | - $rootEntry = $subCache->get(''); |
|
| 1454 | - } |
|
| 1455 | - |
|
| 1456 | - if ($rootEntry && ($rootEntry->getPermissions() && Constants::PERMISSION_READ)) { |
|
| 1457 | - $relativePath = trim(substr($mountPoint, $dirLength), '/'); |
|
| 1458 | - if ($pos = strpos($relativePath, '/')) { |
|
| 1459 | - //mountpoint inside subfolder add size to the correct folder |
|
| 1460 | - $entryName = substr($relativePath, 0, $pos); |
|
| 1461 | - foreach ($files as &$entry) { |
|
| 1462 | - if ($entry->getName() === $entryName) { |
|
| 1463 | - $entry->addSubEntry($rootEntry, $mountPoint); |
|
| 1464 | - } |
|
| 1465 | - } |
|
| 1466 | - } else { //mountpoint in this folder, add an entry for it |
|
| 1467 | - $rootEntry['name'] = $relativePath; |
|
| 1468 | - $rootEntry['type'] = $rootEntry['mimetype'] === 'httpd/unix-directory' ? 'dir' : 'file'; |
|
| 1469 | - $permissions = $rootEntry['permissions']; |
|
| 1470 | - // do not allow renaming/deleting the mount point if they are not shared files/folders |
|
| 1471 | - // for shared files/folders we use the permissions given by the owner |
|
| 1472 | - if ($mount instanceof MoveableMount) { |
|
| 1473 | - $rootEntry['permissions'] = $permissions | \OCP\Constants::PERMISSION_UPDATE | \OCP\Constants::PERMISSION_DELETE; |
|
| 1474 | - } else { |
|
| 1475 | - $rootEntry['permissions'] = $permissions & (\OCP\Constants::PERMISSION_ALL - (\OCP\Constants::PERMISSION_UPDATE | \OCP\Constants::PERMISSION_DELETE)); |
|
| 1476 | - } |
|
| 1477 | - |
|
| 1478 | - //remove any existing entry with the same name |
|
| 1479 | - foreach ($files as $i => $file) { |
|
| 1480 | - if ($file['name'] === $rootEntry['name']) { |
|
| 1481 | - unset($files[$i]); |
|
| 1482 | - break; |
|
| 1483 | - } |
|
| 1484 | - } |
|
| 1485 | - $rootEntry['path'] = substr(Filesystem::normalizePath($path . '/' . $rootEntry['name']), strlen($user) + 2); // full path without /$user/ |
|
| 1486 | - |
|
| 1487 | - // if sharing was disabled for the user we remove the share permissions |
|
| 1488 | - if (\OCP\Util::isSharingDisabledForUser()) { |
|
| 1489 | - $rootEntry['permissions'] = $rootEntry['permissions'] & ~\OCP\Constants::PERMISSION_SHARE; |
|
| 1490 | - } |
|
| 1491 | - |
|
| 1492 | - $owner = $this->getUserObjectForOwner($subStorage->getOwner('')); |
|
| 1493 | - $files[] = new FileInfo($path . '/' . $rootEntry['name'], $subStorage, '', $rootEntry, $mount, $owner); |
|
| 1494 | - } |
|
| 1495 | - } |
|
| 1496 | - } |
|
| 1497 | - } |
|
| 1498 | - |
|
| 1499 | - if ($mimetype_filter) { |
|
| 1500 | - $files = array_filter($files, function (FileInfo $file) use ($mimetype_filter) { |
|
| 1501 | - if (strpos($mimetype_filter, '/')) { |
|
| 1502 | - return $file->getMimetype() === $mimetype_filter; |
|
| 1503 | - } else { |
|
| 1504 | - return $file->getMimePart() === $mimetype_filter; |
|
| 1505 | - } |
|
| 1506 | - }); |
|
| 1507 | - } |
|
| 1508 | - |
|
| 1509 | - return $files; |
|
| 1510 | - } else { |
|
| 1511 | - return []; |
|
| 1512 | - } |
|
| 1513 | - } |
|
| 1514 | - |
|
| 1515 | - /** |
|
| 1516 | - * change file metadata |
|
| 1517 | - * |
|
| 1518 | - * @param string $path |
|
| 1519 | - * @param array|\OCP\Files\FileInfo $data |
|
| 1520 | - * @return int |
|
| 1521 | - * |
|
| 1522 | - * returns the fileid of the updated file |
|
| 1523 | - */ |
|
| 1524 | - public function putFileInfo($path, $data) { |
|
| 1525 | - $this->assertPathLength($path); |
|
| 1526 | - if ($data instanceof FileInfo) { |
|
| 1527 | - $data = $data->getData(); |
|
| 1528 | - } |
|
| 1529 | - $path = Filesystem::normalizePath($this->fakeRoot . '/' . $path); |
|
| 1530 | - /** |
|
| 1531 | - * @var \OC\Files\Storage\Storage $storage |
|
| 1532 | - * @var string $internalPath |
|
| 1533 | - */ |
|
| 1534 | - list($storage, $internalPath) = Filesystem::resolvePath($path); |
|
| 1535 | - if ($storage) { |
|
| 1536 | - $cache = $storage->getCache($path); |
|
| 1537 | - |
|
| 1538 | - if (!$cache->inCache($internalPath)) { |
|
| 1539 | - $scanner = $storage->getScanner($internalPath); |
|
| 1540 | - $scanner->scan($internalPath, Cache\Scanner::SCAN_SHALLOW); |
|
| 1541 | - } |
|
| 1542 | - |
|
| 1543 | - return $cache->put($internalPath, $data); |
|
| 1544 | - } else { |
|
| 1545 | - return -1; |
|
| 1546 | - } |
|
| 1547 | - } |
|
| 1548 | - |
|
| 1549 | - /** |
|
| 1550 | - * search for files with the name matching $query |
|
| 1551 | - * |
|
| 1552 | - * @param string $query |
|
| 1553 | - * @return FileInfo[] |
|
| 1554 | - */ |
|
| 1555 | - public function search($query) { |
|
| 1556 | - return $this->searchCommon('search', array('%' . $query . '%')); |
|
| 1557 | - } |
|
| 1558 | - |
|
| 1559 | - /** |
|
| 1560 | - * search for files with the name matching $query |
|
| 1561 | - * |
|
| 1562 | - * @param string $query |
|
| 1563 | - * @return FileInfo[] |
|
| 1564 | - */ |
|
| 1565 | - public function searchRaw($query) { |
|
| 1566 | - return $this->searchCommon('search', array($query)); |
|
| 1567 | - } |
|
| 1568 | - |
|
| 1569 | - /** |
|
| 1570 | - * search for files by mimetype |
|
| 1571 | - * |
|
| 1572 | - * @param string $mimetype |
|
| 1573 | - * @return FileInfo[] |
|
| 1574 | - */ |
|
| 1575 | - public function searchByMime($mimetype) { |
|
| 1576 | - return $this->searchCommon('searchByMime', array($mimetype)); |
|
| 1577 | - } |
|
| 1578 | - |
|
| 1579 | - /** |
|
| 1580 | - * search for files by tag |
|
| 1581 | - * |
|
| 1582 | - * @param string|int $tag name or tag id |
|
| 1583 | - * @param string $userId owner of the tags |
|
| 1584 | - * @return FileInfo[] |
|
| 1585 | - */ |
|
| 1586 | - public function searchByTag($tag, $userId) { |
|
| 1587 | - return $this->searchCommon('searchByTag', array($tag, $userId)); |
|
| 1588 | - } |
|
| 1589 | - |
|
| 1590 | - /** |
|
| 1591 | - * @param string $method cache method |
|
| 1592 | - * @param array $args |
|
| 1593 | - * @return FileInfo[] |
|
| 1594 | - */ |
|
| 1595 | - private function searchCommon($method, $args) { |
|
| 1596 | - $files = array(); |
|
| 1597 | - $rootLength = strlen($this->fakeRoot); |
|
| 1598 | - |
|
| 1599 | - $mount = $this->getMount(''); |
|
| 1600 | - $mountPoint = $mount->getMountPoint(); |
|
| 1601 | - $storage = $mount->getStorage(); |
|
| 1602 | - if ($storage) { |
|
| 1603 | - $cache = $storage->getCache(''); |
|
| 1604 | - |
|
| 1605 | - $results = call_user_func_array(array($cache, $method), $args); |
|
| 1606 | - foreach ($results as $result) { |
|
| 1607 | - if (substr($mountPoint . $result['path'], 0, $rootLength + 1) === $this->fakeRoot . '/') { |
|
| 1608 | - $internalPath = $result['path']; |
|
| 1609 | - $path = $mountPoint . $result['path']; |
|
| 1610 | - $result['path'] = substr($mountPoint . $result['path'], $rootLength); |
|
| 1611 | - $owner = \OC::$server->getUserManager()->get($storage->getOwner($internalPath)); |
|
| 1612 | - $files[] = new FileInfo($path, $storage, $internalPath, $result, $mount, $owner); |
|
| 1613 | - } |
|
| 1614 | - } |
|
| 1615 | - |
|
| 1616 | - $mounts = Filesystem::getMountManager()->findIn($this->fakeRoot); |
|
| 1617 | - foreach ($mounts as $mount) { |
|
| 1618 | - $mountPoint = $mount->getMountPoint(); |
|
| 1619 | - $storage = $mount->getStorage(); |
|
| 1620 | - if ($storage) { |
|
| 1621 | - $cache = $storage->getCache(''); |
|
| 1622 | - |
|
| 1623 | - $relativeMountPoint = substr($mountPoint, $rootLength); |
|
| 1624 | - $results = call_user_func_array(array($cache, $method), $args); |
|
| 1625 | - if ($results) { |
|
| 1626 | - foreach ($results as $result) { |
|
| 1627 | - $internalPath = $result['path']; |
|
| 1628 | - $result['path'] = rtrim($relativeMountPoint . $result['path'], '/'); |
|
| 1629 | - $path = rtrim($mountPoint . $internalPath, '/'); |
|
| 1630 | - $owner = \OC::$server->getUserManager()->get($storage->getOwner($internalPath)); |
|
| 1631 | - $files[] = new FileInfo($path, $storage, $internalPath, $result, $mount, $owner); |
|
| 1632 | - } |
|
| 1633 | - } |
|
| 1634 | - } |
|
| 1635 | - } |
|
| 1636 | - } |
|
| 1637 | - return $files; |
|
| 1638 | - } |
|
| 1639 | - |
|
| 1640 | - /** |
|
| 1641 | - * Get the owner for a file or folder |
|
| 1642 | - * |
|
| 1643 | - * @param string $path |
|
| 1644 | - * @return string the user id of the owner |
|
| 1645 | - * @throws NotFoundException |
|
| 1646 | - */ |
|
| 1647 | - public function getOwner($path) { |
|
| 1648 | - $info = $this->getFileInfo($path); |
|
| 1649 | - if (!$info) { |
|
| 1650 | - throw new NotFoundException($path . ' not found while trying to get owner'); |
|
| 1651 | - } |
|
| 1652 | - return $info->getOwner()->getUID(); |
|
| 1653 | - } |
|
| 1654 | - |
|
| 1655 | - /** |
|
| 1656 | - * get the ETag for a file or folder |
|
| 1657 | - * |
|
| 1658 | - * @param string $path |
|
| 1659 | - * @return string |
|
| 1660 | - */ |
|
| 1661 | - public function getETag($path) { |
|
| 1662 | - /** |
|
| 1663 | - * @var Storage\Storage $storage |
|
| 1664 | - * @var string $internalPath |
|
| 1665 | - */ |
|
| 1666 | - list($storage, $internalPath) = $this->resolvePath($path); |
|
| 1667 | - if ($storage) { |
|
| 1668 | - return $storage->getETag($internalPath); |
|
| 1669 | - } else { |
|
| 1670 | - return null; |
|
| 1671 | - } |
|
| 1672 | - } |
|
| 1673 | - |
|
| 1674 | - /** |
|
| 1675 | - * Get the path of a file by id, relative to the view |
|
| 1676 | - * |
|
| 1677 | - * Note that the resulting path is not guarantied to be unique for the id, multiple paths can point to the same file |
|
| 1678 | - * |
|
| 1679 | - * @param int $id |
|
| 1680 | - * @throws NotFoundException |
|
| 1681 | - * @return string |
|
| 1682 | - */ |
|
| 1683 | - public function getPath($id) { |
|
| 1684 | - $id = (int)$id; |
|
| 1685 | - $manager = Filesystem::getMountManager(); |
|
| 1686 | - $mounts = $manager->findIn($this->fakeRoot); |
|
| 1687 | - $mounts[] = $manager->find($this->fakeRoot); |
|
| 1688 | - // reverse the array so we start with the storage this view is in |
|
| 1689 | - // which is the most likely to contain the file we're looking for |
|
| 1690 | - $mounts = array_reverse($mounts); |
|
| 1691 | - foreach ($mounts as $mount) { |
|
| 1692 | - /** |
|
| 1693 | - * @var \OC\Files\Mount\MountPoint $mount |
|
| 1694 | - */ |
|
| 1695 | - if ($mount->getStorage()) { |
|
| 1696 | - $cache = $mount->getStorage()->getCache(); |
|
| 1697 | - $internalPath = $cache->getPathById($id); |
|
| 1698 | - if (is_string($internalPath)) { |
|
| 1699 | - $fullPath = $mount->getMountPoint() . $internalPath; |
|
| 1700 | - if (!is_null($path = $this->getRelativePath($fullPath))) { |
|
| 1701 | - return $path; |
|
| 1702 | - } |
|
| 1703 | - } |
|
| 1704 | - } |
|
| 1705 | - } |
|
| 1706 | - throw new NotFoundException(sprintf('File with id "%s" has not been found.', $id)); |
|
| 1707 | - } |
|
| 1708 | - |
|
| 1709 | - /** |
|
| 1710 | - * @param string $path |
|
| 1711 | - * @throws InvalidPathException |
|
| 1712 | - */ |
|
| 1713 | - private function assertPathLength($path) { |
|
| 1714 | - $maxLen = min(PHP_MAXPATHLEN, 4000); |
|
| 1715 | - // Check for the string length - performed using isset() instead of strlen() |
|
| 1716 | - // because isset() is about 5x-40x faster. |
|
| 1717 | - if (isset($path[$maxLen])) { |
|
| 1718 | - $pathLen = strlen($path); |
|
| 1719 | - throw new \OCP\Files\InvalidPathException("Path length($pathLen) exceeds max path length($maxLen): $path"); |
|
| 1720 | - } |
|
| 1721 | - } |
|
| 1722 | - |
|
| 1723 | - /** |
|
| 1724 | - * check if it is allowed to move a mount point to a given target. |
|
| 1725 | - * It is not allowed to move a mount point into a different mount point or |
|
| 1726 | - * into an already shared folder |
|
| 1727 | - * |
|
| 1728 | - * @param string $target path |
|
| 1729 | - * @return boolean |
|
| 1730 | - */ |
|
| 1731 | - private function isTargetAllowed($target) { |
|
| 1732 | - |
|
| 1733 | - list($targetStorage, $targetInternalPath) = \OC\Files\Filesystem::resolvePath($target); |
|
| 1734 | - if (!$targetStorage->instanceOfStorage('\OCP\Files\IHomeStorage')) { |
|
| 1735 | - \OCP\Util::writeLog('files', |
|
| 1736 | - 'It is not allowed to move one mount point into another one', |
|
| 1737 | - \OCP\Util::DEBUG); |
|
| 1738 | - return false; |
|
| 1739 | - } |
|
| 1740 | - |
|
| 1741 | - // note: cannot use the view because the target is already locked |
|
| 1742 | - $fileId = (int)$targetStorage->getCache()->getId($targetInternalPath); |
|
| 1743 | - if ($fileId === -1) { |
|
| 1744 | - // target might not exist, need to check parent instead |
|
| 1745 | - $fileId = (int)$targetStorage->getCache()->getId(dirname($targetInternalPath)); |
|
| 1746 | - } |
|
| 1747 | - |
|
| 1748 | - // check if any of the parents were shared by the current owner (include collections) |
|
| 1749 | - $shares = \OCP\Share::getItemShared( |
|
| 1750 | - 'folder', |
|
| 1751 | - $fileId, |
|
| 1752 | - \OCP\Share::FORMAT_NONE, |
|
| 1753 | - null, |
|
| 1754 | - true |
|
| 1755 | - ); |
|
| 1756 | - |
|
| 1757 | - if (count($shares) > 0) { |
|
| 1758 | - \OCP\Util::writeLog('files', |
|
| 1759 | - 'It is not allowed to move one mount point into a shared folder', |
|
| 1760 | - \OCP\Util::DEBUG); |
|
| 1761 | - return false; |
|
| 1762 | - } |
|
| 1763 | - |
|
| 1764 | - return true; |
|
| 1765 | - } |
|
| 1766 | - |
|
| 1767 | - /** |
|
| 1768 | - * Get a fileinfo object for files that are ignored in the cache (part files) |
|
| 1769 | - * |
|
| 1770 | - * @param string $path |
|
| 1771 | - * @return \OCP\Files\FileInfo |
|
| 1772 | - */ |
|
| 1773 | - private function getPartFileInfo($path) { |
|
| 1774 | - $mount = $this->getMount($path); |
|
| 1775 | - $storage = $mount->getStorage(); |
|
| 1776 | - $internalPath = $mount->getInternalPath($this->getAbsolutePath($path)); |
|
| 1777 | - $owner = \OC::$server->getUserManager()->get($storage->getOwner($internalPath)); |
|
| 1778 | - return new FileInfo( |
|
| 1779 | - $this->getAbsolutePath($path), |
|
| 1780 | - $storage, |
|
| 1781 | - $internalPath, |
|
| 1782 | - [ |
|
| 1783 | - 'fileid' => null, |
|
| 1784 | - 'mimetype' => $storage->getMimeType($internalPath), |
|
| 1785 | - 'name' => basename($path), |
|
| 1786 | - 'etag' => null, |
|
| 1787 | - 'size' => $storage->filesize($internalPath), |
|
| 1788 | - 'mtime' => $storage->filemtime($internalPath), |
|
| 1789 | - 'encrypted' => false, |
|
| 1790 | - 'permissions' => \OCP\Constants::PERMISSION_ALL |
|
| 1791 | - ], |
|
| 1792 | - $mount, |
|
| 1793 | - $owner |
|
| 1794 | - ); |
|
| 1795 | - } |
|
| 1796 | - |
|
| 1797 | - /** |
|
| 1798 | - * @param string $path |
|
| 1799 | - * @param string $fileName |
|
| 1800 | - * @throws InvalidPathException |
|
| 1801 | - */ |
|
| 1802 | - public function verifyPath($path, $fileName) { |
|
| 1803 | - try { |
|
| 1804 | - /** @type \OCP\Files\Storage $storage */ |
|
| 1805 | - list($storage, $internalPath) = $this->resolvePath($path); |
|
| 1806 | - $storage->verifyPath($internalPath, $fileName); |
|
| 1807 | - } catch (ReservedWordException $ex) { |
|
| 1808 | - $l = \OC::$server->getL10N('lib'); |
|
| 1809 | - throw new InvalidPathException($l->t('File name is a reserved word')); |
|
| 1810 | - } catch (InvalidCharacterInPathException $ex) { |
|
| 1811 | - $l = \OC::$server->getL10N('lib'); |
|
| 1812 | - throw new InvalidPathException($l->t('File name contains at least one invalid character')); |
|
| 1813 | - } catch (FileNameTooLongException $ex) { |
|
| 1814 | - $l = \OC::$server->getL10N('lib'); |
|
| 1815 | - throw new InvalidPathException($l->t('File name is too long')); |
|
| 1816 | - } catch (InvalidDirectoryException $ex) { |
|
| 1817 | - $l = \OC::$server->getL10N('lib'); |
|
| 1818 | - throw new InvalidPathException($l->t('Dot files are not allowed')); |
|
| 1819 | - } catch (EmptyFileNameException $ex) { |
|
| 1820 | - $l = \OC::$server->getL10N('lib'); |
|
| 1821 | - throw new InvalidPathException($l->t('Empty filename is not allowed')); |
|
| 1822 | - } |
|
| 1823 | - } |
|
| 1824 | - |
|
| 1825 | - /** |
|
| 1826 | - * get all parent folders of $path |
|
| 1827 | - * |
|
| 1828 | - * @param string $path |
|
| 1829 | - * @return string[] |
|
| 1830 | - */ |
|
| 1831 | - private function getParents($path) { |
|
| 1832 | - $path = trim($path, '/'); |
|
| 1833 | - if (!$path) { |
|
| 1834 | - return []; |
|
| 1835 | - } |
|
| 1836 | - |
|
| 1837 | - $parts = explode('/', $path); |
|
| 1838 | - |
|
| 1839 | - // remove the single file |
|
| 1840 | - array_pop($parts); |
|
| 1841 | - $result = array('/'); |
|
| 1842 | - $resultPath = ''; |
|
| 1843 | - foreach ($parts as $part) { |
|
| 1844 | - if ($part) { |
|
| 1845 | - $resultPath .= '/' . $part; |
|
| 1846 | - $result[] = $resultPath; |
|
| 1847 | - } |
|
| 1848 | - } |
|
| 1849 | - return $result; |
|
| 1850 | - } |
|
| 1851 | - |
|
| 1852 | - /** |
|
| 1853 | - * Returns the mount point for which to lock |
|
| 1854 | - * |
|
| 1855 | - * @param string $absolutePath absolute path |
|
| 1856 | - * @param bool $useParentMount true to return parent mount instead of whatever |
|
| 1857 | - * is mounted directly on the given path, false otherwise |
|
| 1858 | - * @return \OC\Files\Mount\MountPoint mount point for which to apply locks |
|
| 1859 | - */ |
|
| 1860 | - private function getMountForLock($absolutePath, $useParentMount = false) { |
|
| 1861 | - $results = []; |
|
| 1862 | - $mount = Filesystem::getMountManager()->find($absolutePath); |
|
| 1863 | - if (!$mount) { |
|
| 1864 | - return $results; |
|
| 1865 | - } |
|
| 1866 | - |
|
| 1867 | - if ($useParentMount) { |
|
| 1868 | - // find out if something is mounted directly on the path |
|
| 1869 | - $internalPath = $mount->getInternalPath($absolutePath); |
|
| 1870 | - if ($internalPath === '') { |
|
| 1871 | - // resolve the parent mount instead |
|
| 1872 | - $mount = Filesystem::getMountManager()->find(dirname($absolutePath)); |
|
| 1873 | - } |
|
| 1874 | - } |
|
| 1875 | - |
|
| 1876 | - return $mount; |
|
| 1877 | - } |
|
| 1878 | - |
|
| 1879 | - /** |
|
| 1880 | - * Lock the given path |
|
| 1881 | - * |
|
| 1882 | - * @param string $path the path of the file to lock, relative to the view |
|
| 1883 | - * @param int $type \OCP\Lock\ILockingProvider::LOCK_SHARED or \OCP\Lock\ILockingProvider::LOCK_EXCLUSIVE |
|
| 1884 | - * @param bool $lockMountPoint true to lock the mount point, false to lock the attached mount/storage |
|
| 1885 | - * |
|
| 1886 | - * @return bool False if the path is excluded from locking, true otherwise |
|
| 1887 | - * @throws \OCP\Lock\LockedException if the path is already locked |
|
| 1888 | - */ |
|
| 1889 | - private function lockPath($path, $type, $lockMountPoint = false) { |
|
| 1890 | - $absolutePath = $this->getAbsolutePath($path); |
|
| 1891 | - $absolutePath = Filesystem::normalizePath($absolutePath); |
|
| 1892 | - if (!$this->shouldLockFile($absolutePath)) { |
|
| 1893 | - return false; |
|
| 1894 | - } |
|
| 1895 | - |
|
| 1896 | - $mount = $this->getMountForLock($absolutePath, $lockMountPoint); |
|
| 1897 | - if ($mount) { |
|
| 1898 | - try { |
|
| 1899 | - $storage = $mount->getStorage(); |
|
| 1900 | - if ($storage->instanceOfStorage('\OCP\Files\Storage\ILockingStorage')) { |
|
| 1901 | - $storage->acquireLock( |
|
| 1902 | - $mount->getInternalPath($absolutePath), |
|
| 1903 | - $type, |
|
| 1904 | - $this->lockingProvider |
|
| 1905 | - ); |
|
| 1906 | - } |
|
| 1907 | - } catch (\OCP\Lock\LockedException $e) { |
|
| 1908 | - // rethrow with the a human-readable path |
|
| 1909 | - throw new \OCP\Lock\LockedException( |
|
| 1910 | - $this->getPathRelativeToFiles($absolutePath), |
|
| 1911 | - $e |
|
| 1912 | - ); |
|
| 1913 | - } |
|
| 1914 | - } |
|
| 1915 | - |
|
| 1916 | - return true; |
|
| 1917 | - } |
|
| 1918 | - |
|
| 1919 | - /** |
|
| 1920 | - * Change the lock type |
|
| 1921 | - * |
|
| 1922 | - * @param string $path the path of the file to lock, relative to the view |
|
| 1923 | - * @param int $type \OCP\Lock\ILockingProvider::LOCK_SHARED or \OCP\Lock\ILockingProvider::LOCK_EXCLUSIVE |
|
| 1924 | - * @param bool $lockMountPoint true to lock the mount point, false to lock the attached mount/storage |
|
| 1925 | - * |
|
| 1926 | - * @return bool False if the path is excluded from locking, true otherwise |
|
| 1927 | - * @throws \OCP\Lock\LockedException if the path is already locked |
|
| 1928 | - */ |
|
| 1929 | - public function changeLock($path, $type, $lockMountPoint = false) { |
|
| 1930 | - $path = Filesystem::normalizePath($path); |
|
| 1931 | - $absolutePath = $this->getAbsolutePath($path); |
|
| 1932 | - $absolutePath = Filesystem::normalizePath($absolutePath); |
|
| 1933 | - if (!$this->shouldLockFile($absolutePath)) { |
|
| 1934 | - return false; |
|
| 1935 | - } |
|
| 1936 | - |
|
| 1937 | - $mount = $this->getMountForLock($absolutePath, $lockMountPoint); |
|
| 1938 | - if ($mount) { |
|
| 1939 | - try { |
|
| 1940 | - $storage = $mount->getStorage(); |
|
| 1941 | - if ($storage->instanceOfStorage('\OCP\Files\Storage\ILockingStorage')) { |
|
| 1942 | - $storage->changeLock( |
|
| 1943 | - $mount->getInternalPath($absolutePath), |
|
| 1944 | - $type, |
|
| 1945 | - $this->lockingProvider |
|
| 1946 | - ); |
|
| 1947 | - } |
|
| 1948 | - } catch (\OCP\Lock\LockedException $e) { |
|
| 1949 | - // rethrow with the a human-readable path |
|
| 1950 | - throw new \OCP\Lock\LockedException( |
|
| 1951 | - $this->getPathRelativeToFiles($absolutePath), |
|
| 1952 | - $e |
|
| 1953 | - ); |
|
| 1954 | - } |
|
| 1955 | - } |
|
| 1956 | - |
|
| 1957 | - return true; |
|
| 1958 | - } |
|
| 1959 | - |
|
| 1960 | - /** |
|
| 1961 | - * Unlock the given path |
|
| 1962 | - * |
|
| 1963 | - * @param string $path the path of the file to unlock, relative to the view |
|
| 1964 | - * @param int $type \OCP\Lock\ILockingProvider::LOCK_SHARED or \OCP\Lock\ILockingProvider::LOCK_EXCLUSIVE |
|
| 1965 | - * @param bool $lockMountPoint true to lock the mount point, false to lock the attached mount/storage |
|
| 1966 | - * |
|
| 1967 | - * @return bool False if the path is excluded from locking, true otherwise |
|
| 1968 | - */ |
|
| 1969 | - private function unlockPath($path, $type, $lockMountPoint = false) { |
|
| 1970 | - $absolutePath = $this->getAbsolutePath($path); |
|
| 1971 | - $absolutePath = Filesystem::normalizePath($absolutePath); |
|
| 1972 | - if (!$this->shouldLockFile($absolutePath)) { |
|
| 1973 | - return false; |
|
| 1974 | - } |
|
| 1975 | - |
|
| 1976 | - $mount = $this->getMountForLock($absolutePath, $lockMountPoint); |
|
| 1977 | - if ($mount) { |
|
| 1978 | - $storage = $mount->getStorage(); |
|
| 1979 | - if ($storage && $storage->instanceOfStorage('\OCP\Files\Storage\ILockingStorage')) { |
|
| 1980 | - $storage->releaseLock( |
|
| 1981 | - $mount->getInternalPath($absolutePath), |
|
| 1982 | - $type, |
|
| 1983 | - $this->lockingProvider |
|
| 1984 | - ); |
|
| 1985 | - } |
|
| 1986 | - } |
|
| 1987 | - |
|
| 1988 | - return true; |
|
| 1989 | - } |
|
| 1990 | - |
|
| 1991 | - /** |
|
| 1992 | - * Lock a path and all its parents up to the root of the view |
|
| 1993 | - * |
|
| 1994 | - * @param string $path the path of the file to lock relative to the view |
|
| 1995 | - * @param int $type \OCP\Lock\ILockingProvider::LOCK_SHARED or \OCP\Lock\ILockingProvider::LOCK_EXCLUSIVE |
|
| 1996 | - * @param bool $lockMountPoint true to lock the mount point, false to lock the attached mount/storage |
|
| 1997 | - * |
|
| 1998 | - * @return bool False if the path is excluded from locking, true otherwise |
|
| 1999 | - */ |
|
| 2000 | - public function lockFile($path, $type, $lockMountPoint = false) { |
|
| 2001 | - $absolutePath = $this->getAbsolutePath($path); |
|
| 2002 | - $absolutePath = Filesystem::normalizePath($absolutePath); |
|
| 2003 | - if (!$this->shouldLockFile($absolutePath)) { |
|
| 2004 | - return false; |
|
| 2005 | - } |
|
| 2006 | - |
|
| 2007 | - $this->lockPath($path, $type, $lockMountPoint); |
|
| 2008 | - |
|
| 2009 | - $parents = $this->getParents($path); |
|
| 2010 | - foreach ($parents as $parent) { |
|
| 2011 | - $this->lockPath($parent, ILockingProvider::LOCK_SHARED); |
|
| 2012 | - } |
|
| 2013 | - |
|
| 2014 | - return true; |
|
| 2015 | - } |
|
| 2016 | - |
|
| 2017 | - /** |
|
| 2018 | - * Unlock a path and all its parents up to the root of the view |
|
| 2019 | - * |
|
| 2020 | - * @param string $path the path of the file to lock relative to the view |
|
| 2021 | - * @param int $type \OCP\Lock\ILockingProvider::LOCK_SHARED or \OCP\Lock\ILockingProvider::LOCK_EXCLUSIVE |
|
| 2022 | - * @param bool $lockMountPoint true to lock the mount point, false to lock the attached mount/storage |
|
| 2023 | - * |
|
| 2024 | - * @return bool False if the path is excluded from locking, true otherwise |
|
| 2025 | - */ |
|
| 2026 | - public function unlockFile($path, $type, $lockMountPoint = false) { |
|
| 2027 | - $absolutePath = $this->getAbsolutePath($path); |
|
| 2028 | - $absolutePath = Filesystem::normalizePath($absolutePath); |
|
| 2029 | - if (!$this->shouldLockFile($absolutePath)) { |
|
| 2030 | - return false; |
|
| 2031 | - } |
|
| 2032 | - |
|
| 2033 | - $this->unlockPath($path, $type, $lockMountPoint); |
|
| 2034 | - |
|
| 2035 | - $parents = $this->getParents($path); |
|
| 2036 | - foreach ($parents as $parent) { |
|
| 2037 | - $this->unlockPath($parent, ILockingProvider::LOCK_SHARED); |
|
| 2038 | - } |
|
| 2039 | - |
|
| 2040 | - return true; |
|
| 2041 | - } |
|
| 2042 | - |
|
| 2043 | - /** |
|
| 2044 | - * Only lock files in data/user/files/ |
|
| 2045 | - * |
|
| 2046 | - * @param string $path Absolute path to the file/folder we try to (un)lock |
|
| 2047 | - * @return bool |
|
| 2048 | - */ |
|
| 2049 | - protected function shouldLockFile($path) { |
|
| 2050 | - $path = Filesystem::normalizePath($path); |
|
| 2051 | - |
|
| 2052 | - $pathSegments = explode('/', $path); |
|
| 2053 | - if (isset($pathSegments[2])) { |
|
| 2054 | - // E.g.: /username/files/path-to-file |
|
| 2055 | - return ($pathSegments[2] === 'files') && (count($pathSegments) > 3); |
|
| 2056 | - } |
|
| 2057 | - |
|
| 2058 | - return true; |
|
| 2059 | - } |
|
| 2060 | - |
|
| 2061 | - /** |
|
| 2062 | - * Shortens the given absolute path to be relative to |
|
| 2063 | - * "$user/files". |
|
| 2064 | - * |
|
| 2065 | - * @param string $absolutePath absolute path which is under "files" |
|
| 2066 | - * |
|
| 2067 | - * @return string path relative to "files" with trimmed slashes or null |
|
| 2068 | - * if the path was NOT relative to files |
|
| 2069 | - * |
|
| 2070 | - * @throws \InvalidArgumentException if the given path was not under "files" |
|
| 2071 | - * @since 8.1.0 |
|
| 2072 | - */ |
|
| 2073 | - public function getPathRelativeToFiles($absolutePath) { |
|
| 2074 | - $path = Filesystem::normalizePath($absolutePath); |
|
| 2075 | - $parts = explode('/', trim($path, '/'), 3); |
|
| 2076 | - // "$user", "files", "path/to/dir" |
|
| 2077 | - if (!isset($parts[1]) || $parts[1] !== 'files') { |
|
| 2078 | - $this->logger->error( |
|
| 2079 | - '$absolutePath must be relative to "files", value is "%s"', |
|
| 2080 | - [ |
|
| 2081 | - $absolutePath |
|
| 2082 | - ] |
|
| 2083 | - ); |
|
| 2084 | - throw new \InvalidArgumentException('$absolutePath must be relative to "files"'); |
|
| 2085 | - } |
|
| 2086 | - if (isset($parts[2])) { |
|
| 2087 | - return $parts[2]; |
|
| 2088 | - } |
|
| 2089 | - return ''; |
|
| 2090 | - } |
|
| 2091 | - |
|
| 2092 | - /** |
|
| 2093 | - * @param string $filename |
|
| 2094 | - * @return array |
|
| 2095 | - * @throws \OC\User\NoUserException |
|
| 2096 | - * @throws NotFoundException |
|
| 2097 | - */ |
|
| 2098 | - public function getUidAndFilename($filename) { |
|
| 2099 | - $info = $this->getFileInfo($filename); |
|
| 2100 | - if (!$info instanceof \OCP\Files\FileInfo) { |
|
| 2101 | - throw new NotFoundException($this->getAbsolutePath($filename) . ' not found'); |
|
| 2102 | - } |
|
| 2103 | - $uid = $info->getOwner()->getUID(); |
|
| 2104 | - if ($uid != \OCP\User::getUser()) { |
|
| 2105 | - Filesystem::initMountPoints($uid); |
|
| 2106 | - $ownerView = new View('/' . $uid . '/files'); |
|
| 2107 | - try { |
|
| 2108 | - $filename = $ownerView->getPath($info['fileid']); |
|
| 2109 | - } catch (NotFoundException $e) { |
|
| 2110 | - throw new NotFoundException('File with id ' . $info['fileid'] . ' not found for user ' . $uid); |
|
| 2111 | - } |
|
| 2112 | - } |
|
| 2113 | - return [$uid, $filename]; |
|
| 2114 | - } |
|
| 2115 | - |
|
| 2116 | - /** |
|
| 2117 | - * Creates parent non-existing folders |
|
| 2118 | - * |
|
| 2119 | - * @param string $filePath |
|
| 2120 | - * @return bool |
|
| 2121 | - */ |
|
| 2122 | - private function createParentDirectories($filePath) { |
|
| 2123 | - $directoryParts = explode('/', $filePath); |
|
| 2124 | - $directoryParts = array_filter($directoryParts); |
|
| 2125 | - foreach ($directoryParts as $key => $part) { |
|
| 2126 | - $currentPathElements = array_slice($directoryParts, 0, $key); |
|
| 2127 | - $currentPath = '/' . implode('/', $currentPathElements); |
|
| 2128 | - if ($this->is_file($currentPath)) { |
|
| 2129 | - return false; |
|
| 2130 | - } |
|
| 2131 | - if (!$this->file_exists($currentPath)) { |
|
| 2132 | - $this->mkdir($currentPath); |
|
| 2133 | - } |
|
| 2134 | - } |
|
| 2135 | - |
|
| 2136 | - return true; |
|
| 2137 | - } |
|
| 85 | + /** @var string */ |
|
| 86 | + private $fakeRoot = ''; |
|
| 87 | + |
|
| 88 | + /** |
|
| 89 | + * @var \OCP\Lock\ILockingProvider |
|
| 90 | + */ |
|
| 91 | + protected $lockingProvider; |
|
| 92 | + |
|
| 93 | + private $lockingEnabled; |
|
| 94 | + |
|
| 95 | + private $updaterEnabled = true; |
|
| 96 | + |
|
| 97 | + /** @var \OC\User\Manager */ |
|
| 98 | + private $userManager; |
|
| 99 | + |
|
| 100 | + /** @var \OCP\ILogger */ |
|
| 101 | + private $logger; |
|
| 102 | + |
|
| 103 | + /** |
|
| 104 | + * @param string $root |
|
| 105 | + * @throws \Exception If $root contains an invalid path |
|
| 106 | + */ |
|
| 107 | + public function __construct($root = '') { |
|
| 108 | + if (is_null($root)) { |
|
| 109 | + throw new \InvalidArgumentException('Root can\'t be null'); |
|
| 110 | + } |
|
| 111 | + if (!Filesystem::isValidPath($root)) { |
|
| 112 | + throw new \Exception(); |
|
| 113 | + } |
|
| 114 | + |
|
| 115 | + $this->fakeRoot = $root; |
|
| 116 | + $this->lockingProvider = \OC::$server->getLockingProvider(); |
|
| 117 | + $this->lockingEnabled = !($this->lockingProvider instanceof \OC\Lock\NoopLockingProvider); |
|
| 118 | + $this->userManager = \OC::$server->getUserManager(); |
|
| 119 | + $this->logger = \OC::$server->getLogger(); |
|
| 120 | + } |
|
| 121 | + |
|
| 122 | + public function getAbsolutePath($path = '/') { |
|
| 123 | + if ($path === null) { |
|
| 124 | + return null; |
|
| 125 | + } |
|
| 126 | + $this->assertPathLength($path); |
|
| 127 | + if ($path === '') { |
|
| 128 | + $path = '/'; |
|
| 129 | + } |
|
| 130 | + if ($path[0] !== '/') { |
|
| 131 | + $path = '/' . $path; |
|
| 132 | + } |
|
| 133 | + return $this->fakeRoot . $path; |
|
| 134 | + } |
|
| 135 | + |
|
| 136 | + /** |
|
| 137 | + * change the root to a fake root |
|
| 138 | + * |
|
| 139 | + * @param string $fakeRoot |
|
| 140 | + * @return boolean|null |
|
| 141 | + */ |
|
| 142 | + public function chroot($fakeRoot) { |
|
| 143 | + if (!$fakeRoot == '') { |
|
| 144 | + if ($fakeRoot[0] !== '/') { |
|
| 145 | + $fakeRoot = '/' . $fakeRoot; |
|
| 146 | + } |
|
| 147 | + } |
|
| 148 | + $this->fakeRoot = $fakeRoot; |
|
| 149 | + } |
|
| 150 | + |
|
| 151 | + /** |
|
| 152 | + * get the fake root |
|
| 153 | + * |
|
| 154 | + * @return string |
|
| 155 | + */ |
|
| 156 | + public function getRoot() { |
|
| 157 | + return $this->fakeRoot; |
|
| 158 | + } |
|
| 159 | + |
|
| 160 | + /** |
|
| 161 | + * get path relative to the root of the view |
|
| 162 | + * |
|
| 163 | + * @param string $path |
|
| 164 | + * @return string |
|
| 165 | + */ |
|
| 166 | + public function getRelativePath($path) { |
|
| 167 | + $this->assertPathLength($path); |
|
| 168 | + if ($this->fakeRoot == '') { |
|
| 169 | + return $path; |
|
| 170 | + } |
|
| 171 | + |
|
| 172 | + if (rtrim($path, '/') === rtrim($this->fakeRoot, '/')) { |
|
| 173 | + return '/'; |
|
| 174 | + } |
|
| 175 | + |
|
| 176 | + // missing slashes can cause wrong matches! |
|
| 177 | + $root = rtrim($this->fakeRoot, '/') . '/'; |
|
| 178 | + |
|
| 179 | + if (strpos($path, $root) !== 0) { |
|
| 180 | + return null; |
|
| 181 | + } else { |
|
| 182 | + $path = substr($path, strlen($this->fakeRoot)); |
|
| 183 | + if (strlen($path) === 0) { |
|
| 184 | + return '/'; |
|
| 185 | + } else { |
|
| 186 | + return $path; |
|
| 187 | + } |
|
| 188 | + } |
|
| 189 | + } |
|
| 190 | + |
|
| 191 | + /** |
|
| 192 | + * get the mountpoint of the storage object for a path |
|
| 193 | + * ( note: because a storage is not always mounted inside the fakeroot, the |
|
| 194 | + * returned mountpoint is relative to the absolute root of the filesystem |
|
| 195 | + * and does not take the chroot into account ) |
|
| 196 | + * |
|
| 197 | + * @param string $path |
|
| 198 | + * @return string |
|
| 199 | + */ |
|
| 200 | + public function getMountPoint($path) { |
|
| 201 | + return Filesystem::getMountPoint($this->getAbsolutePath($path)); |
|
| 202 | + } |
|
| 203 | + |
|
| 204 | + /** |
|
| 205 | + * get the mountpoint of the storage object for a path |
|
| 206 | + * ( note: because a storage is not always mounted inside the fakeroot, the |
|
| 207 | + * returned mountpoint is relative to the absolute root of the filesystem |
|
| 208 | + * and does not take the chroot into account ) |
|
| 209 | + * |
|
| 210 | + * @param string $path |
|
| 211 | + * @return \OCP\Files\Mount\IMountPoint |
|
| 212 | + */ |
|
| 213 | + public function getMount($path) { |
|
| 214 | + return Filesystem::getMountManager()->find($this->getAbsolutePath($path)); |
|
| 215 | + } |
|
| 216 | + |
|
| 217 | + /** |
|
| 218 | + * resolve a path to a storage and internal path |
|
| 219 | + * |
|
| 220 | + * @param string $path |
|
| 221 | + * @return array an array consisting of the storage and the internal path |
|
| 222 | + */ |
|
| 223 | + public function resolvePath($path) { |
|
| 224 | + $a = $this->getAbsolutePath($path); |
|
| 225 | + $p = Filesystem::normalizePath($a); |
|
| 226 | + return Filesystem::resolvePath($p); |
|
| 227 | + } |
|
| 228 | + |
|
| 229 | + /** |
|
| 230 | + * return the path to a local version of the file |
|
| 231 | + * we need this because we can't know if a file is stored local or not from |
|
| 232 | + * outside the filestorage and for some purposes a local file is needed |
|
| 233 | + * |
|
| 234 | + * @param string $path |
|
| 235 | + * @return string |
|
| 236 | + */ |
|
| 237 | + public function getLocalFile($path) { |
|
| 238 | + $parent = substr($path, 0, strrpos($path, '/')); |
|
| 239 | + $path = $this->getAbsolutePath($path); |
|
| 240 | + list($storage, $internalPath) = Filesystem::resolvePath($path); |
|
| 241 | + if (Filesystem::isValidPath($parent) and $storage) { |
|
| 242 | + return $storage->getLocalFile($internalPath); |
|
| 243 | + } else { |
|
| 244 | + return null; |
|
| 245 | + } |
|
| 246 | + } |
|
| 247 | + |
|
| 248 | + /** |
|
| 249 | + * @param string $path |
|
| 250 | + * @return string |
|
| 251 | + */ |
|
| 252 | + public function getLocalFolder($path) { |
|
| 253 | + $parent = substr($path, 0, strrpos($path, '/')); |
|
| 254 | + $path = $this->getAbsolutePath($path); |
|
| 255 | + list($storage, $internalPath) = Filesystem::resolvePath($path); |
|
| 256 | + if (Filesystem::isValidPath($parent) and $storage) { |
|
| 257 | + return $storage->getLocalFolder($internalPath); |
|
| 258 | + } else { |
|
| 259 | + return null; |
|
| 260 | + } |
|
| 261 | + } |
|
| 262 | + |
|
| 263 | + /** |
|
| 264 | + * the following functions operate with arguments and return values identical |
|
| 265 | + * to those of their PHP built-in equivalents. Mostly they are merely wrappers |
|
| 266 | + * for \OC\Files\Storage\Storage via basicOperation(). |
|
| 267 | + */ |
|
| 268 | + public function mkdir($path) { |
|
| 269 | + return $this->basicOperation('mkdir', $path, array('create', 'write')); |
|
| 270 | + } |
|
| 271 | + |
|
| 272 | + /** |
|
| 273 | + * remove mount point |
|
| 274 | + * |
|
| 275 | + * @param \OC\Files\Mount\MoveableMount $mount |
|
| 276 | + * @param string $path relative to data/ |
|
| 277 | + * @return boolean |
|
| 278 | + */ |
|
| 279 | + protected function removeMount($mount, $path) { |
|
| 280 | + if ($mount instanceof MoveableMount) { |
|
| 281 | + // cut of /user/files to get the relative path to data/user/files |
|
| 282 | + $pathParts = explode('/', $path, 4); |
|
| 283 | + $relPath = '/' . $pathParts[3]; |
|
| 284 | + $this->lockFile($relPath, ILockingProvider::LOCK_SHARED, true); |
|
| 285 | + \OC_Hook::emit( |
|
| 286 | + Filesystem::CLASSNAME, "umount", |
|
| 287 | + array(Filesystem::signal_param_path => $relPath) |
|
| 288 | + ); |
|
| 289 | + $this->changeLock($relPath, ILockingProvider::LOCK_EXCLUSIVE, true); |
|
| 290 | + $result = $mount->removeMount(); |
|
| 291 | + $this->changeLock($relPath, ILockingProvider::LOCK_SHARED, true); |
|
| 292 | + if ($result) { |
|
| 293 | + \OC_Hook::emit( |
|
| 294 | + Filesystem::CLASSNAME, "post_umount", |
|
| 295 | + array(Filesystem::signal_param_path => $relPath) |
|
| 296 | + ); |
|
| 297 | + } |
|
| 298 | + $this->unlockFile($relPath, ILockingProvider::LOCK_SHARED, true); |
|
| 299 | + return $result; |
|
| 300 | + } else { |
|
| 301 | + // do not allow deleting the storage's root / the mount point |
|
| 302 | + // because for some storages it might delete the whole contents |
|
| 303 | + // but isn't supposed to work that way |
|
| 304 | + return false; |
|
| 305 | + } |
|
| 306 | + } |
|
| 307 | + |
|
| 308 | + public function disableCacheUpdate() { |
|
| 309 | + $this->updaterEnabled = false; |
|
| 310 | + } |
|
| 311 | + |
|
| 312 | + public function enableCacheUpdate() { |
|
| 313 | + $this->updaterEnabled = true; |
|
| 314 | + } |
|
| 315 | + |
|
| 316 | + protected function writeUpdate(Storage $storage, $internalPath, $time = null) { |
|
| 317 | + if ($this->updaterEnabled) { |
|
| 318 | + if (is_null($time)) { |
|
| 319 | + $time = time(); |
|
| 320 | + } |
|
| 321 | + $storage->getUpdater()->update($internalPath, $time); |
|
| 322 | + } |
|
| 323 | + } |
|
| 324 | + |
|
| 325 | + protected function removeUpdate(Storage $storage, $internalPath) { |
|
| 326 | + if ($this->updaterEnabled) { |
|
| 327 | + $storage->getUpdater()->remove($internalPath); |
|
| 328 | + } |
|
| 329 | + } |
|
| 330 | + |
|
| 331 | + protected function renameUpdate(Storage $sourceStorage, Storage $targetStorage, $sourceInternalPath, $targetInternalPath) { |
|
| 332 | + if ($this->updaterEnabled) { |
|
| 333 | + $targetStorage->getUpdater()->renameFromStorage($sourceStorage, $sourceInternalPath, $targetInternalPath); |
|
| 334 | + } |
|
| 335 | + } |
|
| 336 | + |
|
| 337 | + /** |
|
| 338 | + * @param string $path |
|
| 339 | + * @return bool|mixed |
|
| 340 | + */ |
|
| 341 | + public function rmdir($path) { |
|
| 342 | + $absolutePath = $this->getAbsolutePath($path); |
|
| 343 | + $mount = Filesystem::getMountManager()->find($absolutePath); |
|
| 344 | + if ($mount->getInternalPath($absolutePath) === '') { |
|
| 345 | + return $this->removeMount($mount, $absolutePath); |
|
| 346 | + } |
|
| 347 | + if ($this->is_dir($path)) { |
|
| 348 | + $result = $this->basicOperation('rmdir', $path, array('delete')); |
|
| 349 | + } else { |
|
| 350 | + $result = false; |
|
| 351 | + } |
|
| 352 | + |
|
| 353 | + if (!$result && !$this->file_exists($path)) { //clear ghost files from the cache on delete |
|
| 354 | + $storage = $mount->getStorage(); |
|
| 355 | + $internalPath = $mount->getInternalPath($absolutePath); |
|
| 356 | + $storage->getUpdater()->remove($internalPath); |
|
| 357 | + } |
|
| 358 | + return $result; |
|
| 359 | + } |
|
| 360 | + |
|
| 361 | + /** |
|
| 362 | + * @param string $path |
|
| 363 | + * @return resource |
|
| 364 | + */ |
|
| 365 | + public function opendir($path) { |
|
| 366 | + return $this->basicOperation('opendir', $path, array('read')); |
|
| 367 | + } |
|
| 368 | + |
|
| 369 | + /** |
|
| 370 | + * @param $handle |
|
| 371 | + * @return mixed |
|
| 372 | + */ |
|
| 373 | + public function readdir($handle) { |
|
| 374 | + $fsLocal = new Storage\Local(array('datadir' => '/')); |
|
| 375 | + return $fsLocal->readdir($handle); |
|
| 376 | + } |
|
| 377 | + |
|
| 378 | + /** |
|
| 379 | + * @param string $path |
|
| 380 | + * @return bool|mixed |
|
| 381 | + */ |
|
| 382 | + public function is_dir($path) { |
|
| 383 | + if ($path == '/') { |
|
| 384 | + return true; |
|
| 385 | + } |
|
| 386 | + return $this->basicOperation('is_dir', $path); |
|
| 387 | + } |
|
| 388 | + |
|
| 389 | + /** |
|
| 390 | + * @param string $path |
|
| 391 | + * @return bool|mixed |
|
| 392 | + */ |
|
| 393 | + public function is_file($path) { |
|
| 394 | + if ($path == '/') { |
|
| 395 | + return false; |
|
| 396 | + } |
|
| 397 | + return $this->basicOperation('is_file', $path); |
|
| 398 | + } |
|
| 399 | + |
|
| 400 | + /** |
|
| 401 | + * @param string $path |
|
| 402 | + * @return mixed |
|
| 403 | + */ |
|
| 404 | + public function stat($path) { |
|
| 405 | + return $this->basicOperation('stat', $path); |
|
| 406 | + } |
|
| 407 | + |
|
| 408 | + /** |
|
| 409 | + * @param string $path |
|
| 410 | + * @return mixed |
|
| 411 | + */ |
|
| 412 | + public function filetype($path) { |
|
| 413 | + return $this->basicOperation('filetype', $path); |
|
| 414 | + } |
|
| 415 | + |
|
| 416 | + /** |
|
| 417 | + * @param string $path |
|
| 418 | + * @return mixed |
|
| 419 | + */ |
|
| 420 | + public function filesize($path) { |
|
| 421 | + return $this->basicOperation('filesize', $path); |
|
| 422 | + } |
|
| 423 | + |
|
| 424 | + /** |
|
| 425 | + * @param string $path |
|
| 426 | + * @return bool|mixed |
|
| 427 | + * @throws \OCP\Files\InvalidPathException |
|
| 428 | + */ |
|
| 429 | + public function readfile($path) { |
|
| 430 | + $this->assertPathLength($path); |
|
| 431 | + @ob_end_clean(); |
|
| 432 | + $handle = $this->fopen($path, 'rb'); |
|
| 433 | + if ($handle) { |
|
| 434 | + $chunkSize = 8192; // 8 kB chunks |
|
| 435 | + while (!feof($handle)) { |
|
| 436 | + echo fread($handle, $chunkSize); |
|
| 437 | + flush(); |
|
| 438 | + } |
|
| 439 | + fclose($handle); |
|
| 440 | + $size = $this->filesize($path); |
|
| 441 | + return $size; |
|
| 442 | + } |
|
| 443 | + return false; |
|
| 444 | + } |
|
| 445 | + |
|
| 446 | + /** |
|
| 447 | + * @param string $path |
|
| 448 | + * @param int $from |
|
| 449 | + * @param int $to |
|
| 450 | + * @return bool|mixed |
|
| 451 | + * @throws \OCP\Files\InvalidPathException |
|
| 452 | + * @throws \OCP\Files\UnseekableException |
|
| 453 | + */ |
|
| 454 | + public function readfilePart($path, $from, $to) { |
|
| 455 | + $this->assertPathLength($path); |
|
| 456 | + @ob_end_clean(); |
|
| 457 | + $handle = $this->fopen($path, 'rb'); |
|
| 458 | + if ($handle) { |
|
| 459 | + if (fseek($handle, $from) === 0) { |
|
| 460 | + $chunkSize = 8192; // 8 kB chunks |
|
| 461 | + $end = $to + 1; |
|
| 462 | + while (!feof($handle) && ftell($handle) < $end) { |
|
| 463 | + $len = $end - ftell($handle); |
|
| 464 | + if ($len > $chunkSize) { |
|
| 465 | + $len = $chunkSize; |
|
| 466 | + } |
|
| 467 | + echo fread($handle, $len); |
|
| 468 | + flush(); |
|
| 469 | + } |
|
| 470 | + $size = ftell($handle) - $from; |
|
| 471 | + return $size; |
|
| 472 | + } |
|
| 473 | + |
|
| 474 | + throw new \OCP\Files\UnseekableException('fseek error'); |
|
| 475 | + } |
|
| 476 | + return false; |
|
| 477 | + } |
|
| 478 | + |
|
| 479 | + /** |
|
| 480 | + * @param string $path |
|
| 481 | + * @return mixed |
|
| 482 | + */ |
|
| 483 | + public function isCreatable($path) { |
|
| 484 | + return $this->basicOperation('isCreatable', $path); |
|
| 485 | + } |
|
| 486 | + |
|
| 487 | + /** |
|
| 488 | + * @param string $path |
|
| 489 | + * @return mixed |
|
| 490 | + */ |
|
| 491 | + public function isReadable($path) { |
|
| 492 | + return $this->basicOperation('isReadable', $path); |
|
| 493 | + } |
|
| 494 | + |
|
| 495 | + /** |
|
| 496 | + * @param string $path |
|
| 497 | + * @return mixed |
|
| 498 | + */ |
|
| 499 | + public function isUpdatable($path) { |
|
| 500 | + return $this->basicOperation('isUpdatable', $path); |
|
| 501 | + } |
|
| 502 | + |
|
| 503 | + /** |
|
| 504 | + * @param string $path |
|
| 505 | + * @return bool|mixed |
|
| 506 | + */ |
|
| 507 | + public function isDeletable($path) { |
|
| 508 | + $absolutePath = $this->getAbsolutePath($path); |
|
| 509 | + $mount = Filesystem::getMountManager()->find($absolutePath); |
|
| 510 | + if ($mount->getInternalPath($absolutePath) === '') { |
|
| 511 | + return $mount instanceof MoveableMount; |
|
| 512 | + } |
|
| 513 | + return $this->basicOperation('isDeletable', $path); |
|
| 514 | + } |
|
| 515 | + |
|
| 516 | + /** |
|
| 517 | + * @param string $path |
|
| 518 | + * @return mixed |
|
| 519 | + */ |
|
| 520 | + public function isSharable($path) { |
|
| 521 | + return $this->basicOperation('isSharable', $path); |
|
| 522 | + } |
|
| 523 | + |
|
| 524 | + /** |
|
| 525 | + * @param string $path |
|
| 526 | + * @return bool|mixed |
|
| 527 | + */ |
|
| 528 | + public function file_exists($path) { |
|
| 529 | + if ($path == '/') { |
|
| 530 | + return true; |
|
| 531 | + } |
|
| 532 | + return $this->basicOperation('file_exists', $path); |
|
| 533 | + } |
|
| 534 | + |
|
| 535 | + /** |
|
| 536 | + * @param string $path |
|
| 537 | + * @return mixed |
|
| 538 | + */ |
|
| 539 | + public function filemtime($path) { |
|
| 540 | + return $this->basicOperation('filemtime', $path); |
|
| 541 | + } |
|
| 542 | + |
|
| 543 | + /** |
|
| 544 | + * @param string $path |
|
| 545 | + * @param int|string $mtime |
|
| 546 | + * @return bool |
|
| 547 | + */ |
|
| 548 | + public function touch($path, $mtime = null) { |
|
| 549 | + if (!is_null($mtime) and !is_numeric($mtime)) { |
|
| 550 | + $mtime = strtotime($mtime); |
|
| 551 | + } |
|
| 552 | + |
|
| 553 | + $hooks = array('touch'); |
|
| 554 | + |
|
| 555 | + if (!$this->file_exists($path)) { |
|
| 556 | + $hooks[] = 'create'; |
|
| 557 | + $hooks[] = 'write'; |
|
| 558 | + } |
|
| 559 | + $result = $this->basicOperation('touch', $path, $hooks, $mtime); |
|
| 560 | + if (!$result) { |
|
| 561 | + // If create file fails because of permissions on external storage like SMB folders, |
|
| 562 | + // check file exists and return false if not. |
|
| 563 | + if (!$this->file_exists($path)) { |
|
| 564 | + return false; |
|
| 565 | + } |
|
| 566 | + if (is_null($mtime)) { |
|
| 567 | + $mtime = time(); |
|
| 568 | + } |
|
| 569 | + //if native touch fails, we emulate it by changing the mtime in the cache |
|
| 570 | + $this->putFileInfo($path, array('mtime' => floor($mtime))); |
|
| 571 | + } |
|
| 572 | + return true; |
|
| 573 | + } |
|
| 574 | + |
|
| 575 | + /** |
|
| 576 | + * @param string $path |
|
| 577 | + * @return mixed |
|
| 578 | + */ |
|
| 579 | + public function file_get_contents($path) { |
|
| 580 | + return $this->basicOperation('file_get_contents', $path, array('read')); |
|
| 581 | + } |
|
| 582 | + |
|
| 583 | + /** |
|
| 584 | + * @param bool $exists |
|
| 585 | + * @param string $path |
|
| 586 | + * @param bool $run |
|
| 587 | + */ |
|
| 588 | + protected function emit_file_hooks_pre($exists, $path, &$run) { |
|
| 589 | + if (!$exists) { |
|
| 590 | + \OC_Hook::emit(Filesystem::CLASSNAME, Filesystem::signal_create, array( |
|
| 591 | + Filesystem::signal_param_path => $this->getHookPath($path), |
|
| 592 | + Filesystem::signal_param_run => &$run, |
|
| 593 | + )); |
|
| 594 | + } else { |
|
| 595 | + \OC_Hook::emit(Filesystem::CLASSNAME, Filesystem::signal_update, array( |
|
| 596 | + Filesystem::signal_param_path => $this->getHookPath($path), |
|
| 597 | + Filesystem::signal_param_run => &$run, |
|
| 598 | + )); |
|
| 599 | + } |
|
| 600 | + \OC_Hook::emit(Filesystem::CLASSNAME, Filesystem::signal_write, array( |
|
| 601 | + Filesystem::signal_param_path => $this->getHookPath($path), |
|
| 602 | + Filesystem::signal_param_run => &$run, |
|
| 603 | + )); |
|
| 604 | + } |
|
| 605 | + |
|
| 606 | + /** |
|
| 607 | + * @param bool $exists |
|
| 608 | + * @param string $path |
|
| 609 | + */ |
|
| 610 | + protected function emit_file_hooks_post($exists, $path) { |
|
| 611 | + if (!$exists) { |
|
| 612 | + \OC_Hook::emit(Filesystem::CLASSNAME, Filesystem::signal_post_create, array( |
|
| 613 | + Filesystem::signal_param_path => $this->getHookPath($path), |
|
| 614 | + )); |
|
| 615 | + } else { |
|
| 616 | + \OC_Hook::emit(Filesystem::CLASSNAME, Filesystem::signal_post_update, array( |
|
| 617 | + Filesystem::signal_param_path => $this->getHookPath($path), |
|
| 618 | + )); |
|
| 619 | + } |
|
| 620 | + \OC_Hook::emit(Filesystem::CLASSNAME, Filesystem::signal_post_write, array( |
|
| 621 | + Filesystem::signal_param_path => $this->getHookPath($path), |
|
| 622 | + )); |
|
| 623 | + } |
|
| 624 | + |
|
| 625 | + /** |
|
| 626 | + * @param string $path |
|
| 627 | + * @param mixed $data |
|
| 628 | + * @return bool|mixed |
|
| 629 | + * @throws \Exception |
|
| 630 | + */ |
|
| 631 | + public function file_put_contents($path, $data) { |
|
| 632 | + if (is_resource($data)) { //not having to deal with streams in file_put_contents makes life easier |
|
| 633 | + $absolutePath = Filesystem::normalizePath($this->getAbsolutePath($path)); |
|
| 634 | + if (Filesystem::isValidPath($path) |
|
| 635 | + and !Filesystem::isFileBlacklisted($path) |
|
| 636 | + ) { |
|
| 637 | + $path = $this->getRelativePath($absolutePath); |
|
| 638 | + |
|
| 639 | + $this->lockFile($path, ILockingProvider::LOCK_SHARED); |
|
| 640 | + |
|
| 641 | + $exists = $this->file_exists($path); |
|
| 642 | + $run = true; |
|
| 643 | + if ($this->shouldEmitHooks($path)) { |
|
| 644 | + $this->emit_file_hooks_pre($exists, $path, $run); |
|
| 645 | + } |
|
| 646 | + if (!$run) { |
|
| 647 | + $this->unlockFile($path, ILockingProvider::LOCK_SHARED); |
|
| 648 | + return false; |
|
| 649 | + } |
|
| 650 | + |
|
| 651 | + $this->changeLock($path, ILockingProvider::LOCK_EXCLUSIVE); |
|
| 652 | + |
|
| 653 | + /** @var \OC\Files\Storage\Storage $storage */ |
|
| 654 | + list($storage, $internalPath) = $this->resolvePath($path); |
|
| 655 | + $target = $storage->fopen($internalPath, 'w'); |
|
| 656 | + if ($target) { |
|
| 657 | + list (, $result) = \OC_Helper::streamCopy($data, $target); |
|
| 658 | + fclose($target); |
|
| 659 | + fclose($data); |
|
| 660 | + |
|
| 661 | + $this->writeUpdate($storage, $internalPath); |
|
| 662 | + |
|
| 663 | + $this->changeLock($path, ILockingProvider::LOCK_SHARED); |
|
| 664 | + |
|
| 665 | + if ($this->shouldEmitHooks($path) && $result !== false) { |
|
| 666 | + $this->emit_file_hooks_post($exists, $path); |
|
| 667 | + } |
|
| 668 | + $this->unlockFile($path, ILockingProvider::LOCK_SHARED); |
|
| 669 | + return $result; |
|
| 670 | + } else { |
|
| 671 | + $this->unlockFile($path, ILockingProvider::LOCK_EXCLUSIVE); |
|
| 672 | + return false; |
|
| 673 | + } |
|
| 674 | + } else { |
|
| 675 | + return false; |
|
| 676 | + } |
|
| 677 | + } else { |
|
| 678 | + $hooks = ($this->file_exists($path)) ? array('update', 'write') : array('create', 'write'); |
|
| 679 | + return $this->basicOperation('file_put_contents', $path, $hooks, $data); |
|
| 680 | + } |
|
| 681 | + } |
|
| 682 | + |
|
| 683 | + /** |
|
| 684 | + * @param string $path |
|
| 685 | + * @return bool|mixed |
|
| 686 | + */ |
|
| 687 | + public function unlink($path) { |
|
| 688 | + if ($path === '' || $path === '/') { |
|
| 689 | + // do not allow deleting the root |
|
| 690 | + return false; |
|
| 691 | + } |
|
| 692 | + $postFix = (substr($path, -1, 1) === '/') ? '/' : ''; |
|
| 693 | + $absolutePath = Filesystem::normalizePath($this->getAbsolutePath($path)); |
|
| 694 | + $mount = Filesystem::getMountManager()->find($absolutePath . $postFix); |
|
| 695 | + if ($mount and $mount->getInternalPath($absolutePath) === '') { |
|
| 696 | + return $this->removeMount($mount, $absolutePath); |
|
| 697 | + } |
|
| 698 | + if ($this->is_dir($path)) { |
|
| 699 | + $result = $this->basicOperation('rmdir', $path, ['delete']); |
|
| 700 | + } else { |
|
| 701 | + $result = $this->basicOperation('unlink', $path, ['delete']); |
|
| 702 | + } |
|
| 703 | + if (!$result && !$this->file_exists($path)) { //clear ghost files from the cache on delete |
|
| 704 | + $storage = $mount->getStorage(); |
|
| 705 | + $internalPath = $mount->getInternalPath($absolutePath); |
|
| 706 | + $storage->getUpdater()->remove($internalPath); |
|
| 707 | + return true; |
|
| 708 | + } else { |
|
| 709 | + return $result; |
|
| 710 | + } |
|
| 711 | + } |
|
| 712 | + |
|
| 713 | + /** |
|
| 714 | + * @param string $directory |
|
| 715 | + * @return bool|mixed |
|
| 716 | + */ |
|
| 717 | + public function deleteAll($directory) { |
|
| 718 | + return $this->rmdir($directory); |
|
| 719 | + } |
|
| 720 | + |
|
| 721 | + /** |
|
| 722 | + * Rename/move a file or folder from the source path to target path. |
|
| 723 | + * |
|
| 724 | + * @param string $path1 source path |
|
| 725 | + * @param string $path2 target path |
|
| 726 | + * |
|
| 727 | + * @return bool|mixed |
|
| 728 | + */ |
|
| 729 | + public function rename($path1, $path2) { |
|
| 730 | + $absolutePath1 = Filesystem::normalizePath($this->getAbsolutePath($path1)); |
|
| 731 | + $absolutePath2 = Filesystem::normalizePath($this->getAbsolutePath($path2)); |
|
| 732 | + $result = false; |
|
| 733 | + if ( |
|
| 734 | + Filesystem::isValidPath($path2) |
|
| 735 | + and Filesystem::isValidPath($path1) |
|
| 736 | + and !Filesystem::isFileBlacklisted($path2) |
|
| 737 | + ) { |
|
| 738 | + $path1 = $this->getRelativePath($absolutePath1); |
|
| 739 | + $path2 = $this->getRelativePath($absolutePath2); |
|
| 740 | + $exists = $this->file_exists($path2); |
|
| 741 | + |
|
| 742 | + if ($path1 == null or $path2 == null) { |
|
| 743 | + return false; |
|
| 744 | + } |
|
| 745 | + |
|
| 746 | + $this->lockFile($path1, ILockingProvider::LOCK_SHARED, true); |
|
| 747 | + try { |
|
| 748 | + $this->lockFile($path2, ILockingProvider::LOCK_SHARED, true); |
|
| 749 | + } catch (LockedException $e) { |
|
| 750 | + $this->unlockFile($path1, ILockingProvider::LOCK_SHARED); |
|
| 751 | + throw $e; |
|
| 752 | + } |
|
| 753 | + |
|
| 754 | + $run = true; |
|
| 755 | + if ($this->shouldEmitHooks($path1) && (Cache\Scanner::isPartialFile($path1) && !Cache\Scanner::isPartialFile($path2))) { |
|
| 756 | + // if it was a rename from a part file to a regular file it was a write and not a rename operation |
|
| 757 | + $this->emit_file_hooks_pre($exists, $path2, $run); |
|
| 758 | + } elseif ($this->shouldEmitHooks($path1)) { |
|
| 759 | + \OC_Hook::emit( |
|
| 760 | + Filesystem::CLASSNAME, Filesystem::signal_rename, |
|
| 761 | + array( |
|
| 762 | + Filesystem::signal_param_oldpath => $this->getHookPath($path1), |
|
| 763 | + Filesystem::signal_param_newpath => $this->getHookPath($path2), |
|
| 764 | + Filesystem::signal_param_run => &$run |
|
| 765 | + ) |
|
| 766 | + ); |
|
| 767 | + } |
|
| 768 | + if ($run) { |
|
| 769 | + $this->verifyPath(dirname($path2), basename($path2)); |
|
| 770 | + |
|
| 771 | + $manager = Filesystem::getMountManager(); |
|
| 772 | + $mount1 = $this->getMount($path1); |
|
| 773 | + $mount2 = $this->getMount($path2); |
|
| 774 | + $storage1 = $mount1->getStorage(); |
|
| 775 | + $storage2 = $mount2->getStorage(); |
|
| 776 | + $internalPath1 = $mount1->getInternalPath($absolutePath1); |
|
| 777 | + $internalPath2 = $mount2->getInternalPath($absolutePath2); |
|
| 778 | + |
|
| 779 | + $this->changeLock($path1, ILockingProvider::LOCK_EXCLUSIVE, true); |
|
| 780 | + $this->changeLock($path2, ILockingProvider::LOCK_EXCLUSIVE, true); |
|
| 781 | + |
|
| 782 | + if ($internalPath1 === '' and $mount1 instanceof MoveableMount) { |
|
| 783 | + if ($this->isTargetAllowed($absolutePath2)) { |
|
| 784 | + /** |
|
| 785 | + * @var \OC\Files\Mount\MountPoint | \OC\Files\Mount\MoveableMount $mount1 |
|
| 786 | + */ |
|
| 787 | + $sourceMountPoint = $mount1->getMountPoint(); |
|
| 788 | + $result = $mount1->moveMount($absolutePath2); |
|
| 789 | + $manager->moveMount($sourceMountPoint, $mount1->getMountPoint()); |
|
| 790 | + } else { |
|
| 791 | + $result = false; |
|
| 792 | + } |
|
| 793 | + // moving a file/folder within the same mount point |
|
| 794 | + } elseif ($storage1 === $storage2) { |
|
| 795 | + if ($storage1) { |
|
| 796 | + $result = $storage1->rename($internalPath1, $internalPath2); |
|
| 797 | + } else { |
|
| 798 | + $result = false; |
|
| 799 | + } |
|
| 800 | + // moving a file/folder between storages (from $storage1 to $storage2) |
|
| 801 | + } else { |
|
| 802 | + $result = $storage2->moveFromStorage($storage1, $internalPath1, $internalPath2); |
|
| 803 | + } |
|
| 804 | + |
|
| 805 | + if ((Cache\Scanner::isPartialFile($path1) && !Cache\Scanner::isPartialFile($path2)) && $result !== false) { |
|
| 806 | + // if it was a rename from a part file to a regular file it was a write and not a rename operation |
|
| 807 | + |
|
| 808 | + $this->writeUpdate($storage2, $internalPath2); |
|
| 809 | + } else if ($result) { |
|
| 810 | + if ($internalPath1 !== '') { // don't do a cache update for moved mounts |
|
| 811 | + $this->renameUpdate($storage1, $storage2, $internalPath1, $internalPath2); |
|
| 812 | + } |
|
| 813 | + } |
|
| 814 | + |
|
| 815 | + $this->changeLock($path1, ILockingProvider::LOCK_SHARED, true); |
|
| 816 | + $this->changeLock($path2, ILockingProvider::LOCK_SHARED, true); |
|
| 817 | + |
|
| 818 | + if ((Cache\Scanner::isPartialFile($path1) && !Cache\Scanner::isPartialFile($path2)) && $result !== false) { |
|
| 819 | + if ($this->shouldEmitHooks()) { |
|
| 820 | + $this->emit_file_hooks_post($exists, $path2); |
|
| 821 | + } |
|
| 822 | + } elseif ($result) { |
|
| 823 | + if ($this->shouldEmitHooks($path1) and $this->shouldEmitHooks($path2)) { |
|
| 824 | + \OC_Hook::emit( |
|
| 825 | + Filesystem::CLASSNAME, |
|
| 826 | + Filesystem::signal_post_rename, |
|
| 827 | + array( |
|
| 828 | + Filesystem::signal_param_oldpath => $this->getHookPath($path1), |
|
| 829 | + Filesystem::signal_param_newpath => $this->getHookPath($path2) |
|
| 830 | + ) |
|
| 831 | + ); |
|
| 832 | + } |
|
| 833 | + } |
|
| 834 | + } |
|
| 835 | + $this->unlockFile($path1, ILockingProvider::LOCK_SHARED, true); |
|
| 836 | + $this->unlockFile($path2, ILockingProvider::LOCK_SHARED, true); |
|
| 837 | + } |
|
| 838 | + return $result; |
|
| 839 | + } |
|
| 840 | + |
|
| 841 | + /** |
|
| 842 | + * Copy a file/folder from the source path to target path |
|
| 843 | + * |
|
| 844 | + * @param string $path1 source path |
|
| 845 | + * @param string $path2 target path |
|
| 846 | + * @param bool $preserveMtime whether to preserve mtime on the copy |
|
| 847 | + * |
|
| 848 | + * @return bool|mixed |
|
| 849 | + */ |
|
| 850 | + public function copy($path1, $path2, $preserveMtime = false) { |
|
| 851 | + $absolutePath1 = Filesystem::normalizePath($this->getAbsolutePath($path1)); |
|
| 852 | + $absolutePath2 = Filesystem::normalizePath($this->getAbsolutePath($path2)); |
|
| 853 | + $result = false; |
|
| 854 | + if ( |
|
| 855 | + Filesystem::isValidPath($path2) |
|
| 856 | + and Filesystem::isValidPath($path1) |
|
| 857 | + and !Filesystem::isFileBlacklisted($path2) |
|
| 858 | + ) { |
|
| 859 | + $path1 = $this->getRelativePath($absolutePath1); |
|
| 860 | + $path2 = $this->getRelativePath($absolutePath2); |
|
| 861 | + |
|
| 862 | + if ($path1 == null or $path2 == null) { |
|
| 863 | + return false; |
|
| 864 | + } |
|
| 865 | + $run = true; |
|
| 866 | + |
|
| 867 | + $this->lockFile($path2, ILockingProvider::LOCK_SHARED); |
|
| 868 | + $this->lockFile($path1, ILockingProvider::LOCK_SHARED); |
|
| 869 | + $lockTypePath1 = ILockingProvider::LOCK_SHARED; |
|
| 870 | + $lockTypePath2 = ILockingProvider::LOCK_SHARED; |
|
| 871 | + |
|
| 872 | + try { |
|
| 873 | + |
|
| 874 | + $exists = $this->file_exists($path2); |
|
| 875 | + if ($this->shouldEmitHooks()) { |
|
| 876 | + \OC_Hook::emit( |
|
| 877 | + Filesystem::CLASSNAME, |
|
| 878 | + Filesystem::signal_copy, |
|
| 879 | + array( |
|
| 880 | + Filesystem::signal_param_oldpath => $this->getHookPath($path1), |
|
| 881 | + Filesystem::signal_param_newpath => $this->getHookPath($path2), |
|
| 882 | + Filesystem::signal_param_run => &$run |
|
| 883 | + ) |
|
| 884 | + ); |
|
| 885 | + $this->emit_file_hooks_pre($exists, $path2, $run); |
|
| 886 | + } |
|
| 887 | + if ($run) { |
|
| 888 | + $mount1 = $this->getMount($path1); |
|
| 889 | + $mount2 = $this->getMount($path2); |
|
| 890 | + $storage1 = $mount1->getStorage(); |
|
| 891 | + $internalPath1 = $mount1->getInternalPath($absolutePath1); |
|
| 892 | + $storage2 = $mount2->getStorage(); |
|
| 893 | + $internalPath2 = $mount2->getInternalPath($absolutePath2); |
|
| 894 | + |
|
| 895 | + $this->changeLock($path2, ILockingProvider::LOCK_EXCLUSIVE); |
|
| 896 | + $lockTypePath2 = ILockingProvider::LOCK_EXCLUSIVE; |
|
| 897 | + |
|
| 898 | + if ($mount1->getMountPoint() == $mount2->getMountPoint()) { |
|
| 899 | + if ($storage1) { |
|
| 900 | + $result = $storage1->copy($internalPath1, $internalPath2); |
|
| 901 | + } else { |
|
| 902 | + $result = false; |
|
| 903 | + } |
|
| 904 | + } else { |
|
| 905 | + $result = $storage2->copyFromStorage($storage1, $internalPath1, $internalPath2); |
|
| 906 | + } |
|
| 907 | + |
|
| 908 | + $this->writeUpdate($storage2, $internalPath2); |
|
| 909 | + |
|
| 910 | + $this->changeLock($path2, ILockingProvider::LOCK_SHARED); |
|
| 911 | + $lockTypePath2 = ILockingProvider::LOCK_SHARED; |
|
| 912 | + |
|
| 913 | + if ($this->shouldEmitHooks() && $result !== false) { |
|
| 914 | + \OC_Hook::emit( |
|
| 915 | + Filesystem::CLASSNAME, |
|
| 916 | + Filesystem::signal_post_copy, |
|
| 917 | + array( |
|
| 918 | + Filesystem::signal_param_oldpath => $this->getHookPath($path1), |
|
| 919 | + Filesystem::signal_param_newpath => $this->getHookPath($path2) |
|
| 920 | + ) |
|
| 921 | + ); |
|
| 922 | + $this->emit_file_hooks_post($exists, $path2); |
|
| 923 | + } |
|
| 924 | + |
|
| 925 | + } |
|
| 926 | + } catch (\Exception $e) { |
|
| 927 | + $this->unlockFile($path2, $lockTypePath2); |
|
| 928 | + $this->unlockFile($path1, $lockTypePath1); |
|
| 929 | + throw $e; |
|
| 930 | + } |
|
| 931 | + |
|
| 932 | + $this->unlockFile($path2, $lockTypePath2); |
|
| 933 | + $this->unlockFile($path1, $lockTypePath1); |
|
| 934 | + |
|
| 935 | + } |
|
| 936 | + return $result; |
|
| 937 | + } |
|
| 938 | + |
|
| 939 | + /** |
|
| 940 | + * @param string $path |
|
| 941 | + * @param string $mode 'r' or 'w' |
|
| 942 | + * @return resource |
|
| 943 | + */ |
|
| 944 | + public function fopen($path, $mode) { |
|
| 945 | + $mode = str_replace('b', '', $mode); // the binary flag is a windows only feature which we do not support |
|
| 946 | + $hooks = array(); |
|
| 947 | + switch ($mode) { |
|
| 948 | + case 'r': |
|
| 949 | + $hooks[] = 'read'; |
|
| 950 | + break; |
|
| 951 | + case 'r+': |
|
| 952 | + case 'w+': |
|
| 953 | + case 'x+': |
|
| 954 | + case 'a+': |
|
| 955 | + $hooks[] = 'read'; |
|
| 956 | + $hooks[] = 'write'; |
|
| 957 | + break; |
|
| 958 | + case 'w': |
|
| 959 | + case 'x': |
|
| 960 | + case 'a': |
|
| 961 | + $hooks[] = 'write'; |
|
| 962 | + break; |
|
| 963 | + default: |
|
| 964 | + \OCP\Util::writeLog('core', 'invalid mode (' . $mode . ') for ' . $path, \OCP\Util::ERROR); |
|
| 965 | + } |
|
| 966 | + |
|
| 967 | + if ($mode !== 'r' && $mode !== 'w') { |
|
| 968 | + \OC::$server->getLogger()->info('Trying to open a file with a mode other than "r" or "w" can cause severe performance issues with some backends'); |
|
| 969 | + } |
|
| 970 | + |
|
| 971 | + return $this->basicOperation('fopen', $path, $hooks, $mode); |
|
| 972 | + } |
|
| 973 | + |
|
| 974 | + /** |
|
| 975 | + * @param string $path |
|
| 976 | + * @return bool|string |
|
| 977 | + * @throws \OCP\Files\InvalidPathException |
|
| 978 | + */ |
|
| 979 | + public function toTmpFile($path) { |
|
| 980 | + $this->assertPathLength($path); |
|
| 981 | + if (Filesystem::isValidPath($path)) { |
|
| 982 | + $source = $this->fopen($path, 'r'); |
|
| 983 | + if ($source) { |
|
| 984 | + $extension = pathinfo($path, PATHINFO_EXTENSION); |
|
| 985 | + $tmpFile = \OC::$server->getTempManager()->getTemporaryFile($extension); |
|
| 986 | + file_put_contents($tmpFile, $source); |
|
| 987 | + return $tmpFile; |
|
| 988 | + } else { |
|
| 989 | + return false; |
|
| 990 | + } |
|
| 991 | + } else { |
|
| 992 | + return false; |
|
| 993 | + } |
|
| 994 | + } |
|
| 995 | + |
|
| 996 | + /** |
|
| 997 | + * @param string $tmpFile |
|
| 998 | + * @param string $path |
|
| 999 | + * @return bool|mixed |
|
| 1000 | + * @throws \OCP\Files\InvalidPathException |
|
| 1001 | + */ |
|
| 1002 | + public function fromTmpFile($tmpFile, $path) { |
|
| 1003 | + $this->assertPathLength($path); |
|
| 1004 | + if (Filesystem::isValidPath($path)) { |
|
| 1005 | + |
|
| 1006 | + // Get directory that the file is going into |
|
| 1007 | + $filePath = dirname($path); |
|
| 1008 | + |
|
| 1009 | + // Create the directories if any |
|
| 1010 | + if (!$this->file_exists($filePath)) { |
|
| 1011 | + $result = $this->createParentDirectories($filePath); |
|
| 1012 | + if ($result === false) { |
|
| 1013 | + return false; |
|
| 1014 | + } |
|
| 1015 | + } |
|
| 1016 | + |
|
| 1017 | + $source = fopen($tmpFile, 'r'); |
|
| 1018 | + if ($source) { |
|
| 1019 | + $result = $this->file_put_contents($path, $source); |
|
| 1020 | + // $this->file_put_contents() might have already closed |
|
| 1021 | + // the resource, so we check it, before trying to close it |
|
| 1022 | + // to avoid messages in the error log. |
|
| 1023 | + if (is_resource($source)) { |
|
| 1024 | + fclose($source); |
|
| 1025 | + } |
|
| 1026 | + unlink($tmpFile); |
|
| 1027 | + return $result; |
|
| 1028 | + } else { |
|
| 1029 | + return false; |
|
| 1030 | + } |
|
| 1031 | + } else { |
|
| 1032 | + return false; |
|
| 1033 | + } |
|
| 1034 | + } |
|
| 1035 | + |
|
| 1036 | + |
|
| 1037 | + /** |
|
| 1038 | + * @param string $path |
|
| 1039 | + * @return mixed |
|
| 1040 | + * @throws \OCP\Files\InvalidPathException |
|
| 1041 | + */ |
|
| 1042 | + public function getMimeType($path) { |
|
| 1043 | + $this->assertPathLength($path); |
|
| 1044 | + return $this->basicOperation('getMimeType', $path); |
|
| 1045 | + } |
|
| 1046 | + |
|
| 1047 | + /** |
|
| 1048 | + * @param string $type |
|
| 1049 | + * @param string $path |
|
| 1050 | + * @param bool $raw |
|
| 1051 | + * @return bool|null|string |
|
| 1052 | + */ |
|
| 1053 | + public function hash($type, $path, $raw = false) { |
|
| 1054 | + $postFix = (substr($path, -1, 1) === '/') ? '/' : ''; |
|
| 1055 | + $absolutePath = Filesystem::normalizePath($this->getAbsolutePath($path)); |
|
| 1056 | + if (Filesystem::isValidPath($path)) { |
|
| 1057 | + $path = $this->getRelativePath($absolutePath); |
|
| 1058 | + if ($path == null) { |
|
| 1059 | + return false; |
|
| 1060 | + } |
|
| 1061 | + if ($this->shouldEmitHooks($path)) { |
|
| 1062 | + \OC_Hook::emit( |
|
| 1063 | + Filesystem::CLASSNAME, |
|
| 1064 | + Filesystem::signal_read, |
|
| 1065 | + array(Filesystem::signal_param_path => $this->getHookPath($path)) |
|
| 1066 | + ); |
|
| 1067 | + } |
|
| 1068 | + list($storage, $internalPath) = Filesystem::resolvePath($absolutePath . $postFix); |
|
| 1069 | + if ($storage) { |
|
| 1070 | + $result = $storage->hash($type, $internalPath, $raw); |
|
| 1071 | + return $result; |
|
| 1072 | + } |
|
| 1073 | + } |
|
| 1074 | + return null; |
|
| 1075 | + } |
|
| 1076 | + |
|
| 1077 | + /** |
|
| 1078 | + * @param string $path |
|
| 1079 | + * @return mixed |
|
| 1080 | + * @throws \OCP\Files\InvalidPathException |
|
| 1081 | + */ |
|
| 1082 | + public function free_space($path = '/') { |
|
| 1083 | + $this->assertPathLength($path); |
|
| 1084 | + $result = $this->basicOperation('free_space', $path); |
|
| 1085 | + if ($result === null) { |
|
| 1086 | + throw new InvalidPathException(); |
|
| 1087 | + } |
|
| 1088 | + return $result; |
|
| 1089 | + } |
|
| 1090 | + |
|
| 1091 | + /** |
|
| 1092 | + * abstraction layer for basic filesystem functions: wrapper for \OC\Files\Storage\Storage |
|
| 1093 | + * |
|
| 1094 | + * @param string $operation |
|
| 1095 | + * @param string $path |
|
| 1096 | + * @param array $hooks (optional) |
|
| 1097 | + * @param mixed $extraParam (optional) |
|
| 1098 | + * @return mixed |
|
| 1099 | + * @throws \Exception |
|
| 1100 | + * |
|
| 1101 | + * This method takes requests for basic filesystem functions (e.g. reading & writing |
|
| 1102 | + * files), processes hooks and proxies, sanitises paths, and finally passes them on to |
|
| 1103 | + * \OC\Files\Storage\Storage for delegation to a storage backend for execution |
|
| 1104 | + */ |
|
| 1105 | + private function basicOperation($operation, $path, $hooks = [], $extraParam = null) { |
|
| 1106 | + $postFix = (substr($path, -1, 1) === '/') ? '/' : ''; |
|
| 1107 | + $absolutePath = Filesystem::normalizePath($this->getAbsolutePath($path)); |
|
| 1108 | + if (Filesystem::isValidPath($path) |
|
| 1109 | + and !Filesystem::isFileBlacklisted($path) |
|
| 1110 | + ) { |
|
| 1111 | + $path = $this->getRelativePath($absolutePath); |
|
| 1112 | + if ($path == null) { |
|
| 1113 | + return false; |
|
| 1114 | + } |
|
| 1115 | + |
|
| 1116 | + if (in_array('write', $hooks) || in_array('delete', $hooks) || in_array('read', $hooks)) { |
|
| 1117 | + // always a shared lock during pre-hooks so the hook can read the file |
|
| 1118 | + $this->lockFile($path, ILockingProvider::LOCK_SHARED); |
|
| 1119 | + } |
|
| 1120 | + |
|
| 1121 | + $run = $this->runHooks($hooks, $path); |
|
| 1122 | + /** @var \OC\Files\Storage\Storage $storage */ |
|
| 1123 | + list($storage, $internalPath) = Filesystem::resolvePath($absolutePath . $postFix); |
|
| 1124 | + if ($run and $storage) { |
|
| 1125 | + if (in_array('write', $hooks) || in_array('delete', $hooks)) { |
|
| 1126 | + $this->changeLock($path, ILockingProvider::LOCK_EXCLUSIVE); |
|
| 1127 | + } |
|
| 1128 | + try { |
|
| 1129 | + if (!is_null($extraParam)) { |
|
| 1130 | + $result = $storage->$operation($internalPath, $extraParam); |
|
| 1131 | + } else { |
|
| 1132 | + $result = $storage->$operation($internalPath); |
|
| 1133 | + } |
|
| 1134 | + } catch (\Exception $e) { |
|
| 1135 | + if (in_array('write', $hooks) || in_array('delete', $hooks)) { |
|
| 1136 | + $this->unlockFile($path, ILockingProvider::LOCK_EXCLUSIVE); |
|
| 1137 | + } else if (in_array('read', $hooks)) { |
|
| 1138 | + $this->unlockFile($path, ILockingProvider::LOCK_SHARED); |
|
| 1139 | + } |
|
| 1140 | + throw $e; |
|
| 1141 | + } |
|
| 1142 | + |
|
| 1143 | + if ($result && in_array('delete', $hooks) and $result) { |
|
| 1144 | + $this->removeUpdate($storage, $internalPath); |
|
| 1145 | + } |
|
| 1146 | + if ($result && in_array('write', $hooks) and $operation !== 'fopen') { |
|
| 1147 | + $this->writeUpdate($storage, $internalPath); |
|
| 1148 | + } |
|
| 1149 | + if ($result && in_array('touch', $hooks)) { |
|
| 1150 | + $this->writeUpdate($storage, $internalPath, $extraParam); |
|
| 1151 | + } |
|
| 1152 | + |
|
| 1153 | + if ((in_array('write', $hooks) || in_array('delete', $hooks)) && ($operation !== 'fopen' || $result === false)) { |
|
| 1154 | + $this->changeLock($path, ILockingProvider::LOCK_SHARED); |
|
| 1155 | + } |
|
| 1156 | + |
|
| 1157 | + $unlockLater = false; |
|
| 1158 | + if ($this->lockingEnabled && $operation === 'fopen' && is_resource($result)) { |
|
| 1159 | + $unlockLater = true; |
|
| 1160 | + // make sure our unlocking callback will still be called if connection is aborted |
|
| 1161 | + ignore_user_abort(true); |
|
| 1162 | + $result = CallbackWrapper::wrap($result, null, null, function () use ($hooks, $path) { |
|
| 1163 | + if (in_array('write', $hooks)) { |
|
| 1164 | + $this->unlockFile($path, ILockingProvider::LOCK_EXCLUSIVE); |
|
| 1165 | + } else if (in_array('read', $hooks)) { |
|
| 1166 | + $this->unlockFile($path, ILockingProvider::LOCK_SHARED); |
|
| 1167 | + } |
|
| 1168 | + }); |
|
| 1169 | + } |
|
| 1170 | + |
|
| 1171 | + if ($this->shouldEmitHooks($path) && $result !== false) { |
|
| 1172 | + if ($operation != 'fopen') { //no post hooks for fopen, the file stream is still open |
|
| 1173 | + $this->runHooks($hooks, $path, true); |
|
| 1174 | + } |
|
| 1175 | + } |
|
| 1176 | + |
|
| 1177 | + if (!$unlockLater |
|
| 1178 | + && (in_array('write', $hooks) || in_array('delete', $hooks) || in_array('read', $hooks)) |
|
| 1179 | + ) { |
|
| 1180 | + $this->unlockFile($path, ILockingProvider::LOCK_SHARED); |
|
| 1181 | + } |
|
| 1182 | + return $result; |
|
| 1183 | + } else { |
|
| 1184 | + $this->unlockFile($path, ILockingProvider::LOCK_SHARED); |
|
| 1185 | + } |
|
| 1186 | + } |
|
| 1187 | + return null; |
|
| 1188 | + } |
|
| 1189 | + |
|
| 1190 | + /** |
|
| 1191 | + * get the path relative to the default root for hook usage |
|
| 1192 | + * |
|
| 1193 | + * @param string $path |
|
| 1194 | + * @return string |
|
| 1195 | + */ |
|
| 1196 | + private function getHookPath($path) { |
|
| 1197 | + if (!Filesystem::getView()) { |
|
| 1198 | + return $path; |
|
| 1199 | + } |
|
| 1200 | + return Filesystem::getView()->getRelativePath($this->getAbsolutePath($path)); |
|
| 1201 | + } |
|
| 1202 | + |
|
| 1203 | + private function shouldEmitHooks($path = '') { |
|
| 1204 | + if ($path && Cache\Scanner::isPartialFile($path)) { |
|
| 1205 | + return false; |
|
| 1206 | + } |
|
| 1207 | + if (!Filesystem::$loaded) { |
|
| 1208 | + return false; |
|
| 1209 | + } |
|
| 1210 | + $defaultRoot = Filesystem::getRoot(); |
|
| 1211 | + if ($defaultRoot === null) { |
|
| 1212 | + return false; |
|
| 1213 | + } |
|
| 1214 | + if ($this->fakeRoot === $defaultRoot) { |
|
| 1215 | + return true; |
|
| 1216 | + } |
|
| 1217 | + $fullPath = $this->getAbsolutePath($path); |
|
| 1218 | + |
|
| 1219 | + if ($fullPath === $defaultRoot) { |
|
| 1220 | + return true; |
|
| 1221 | + } |
|
| 1222 | + |
|
| 1223 | + return (strlen($fullPath) > strlen($defaultRoot)) && (substr($fullPath, 0, strlen($defaultRoot) + 1) === $defaultRoot . '/'); |
|
| 1224 | + } |
|
| 1225 | + |
|
| 1226 | + /** |
|
| 1227 | + * @param string[] $hooks |
|
| 1228 | + * @param string $path |
|
| 1229 | + * @param bool $post |
|
| 1230 | + * @return bool |
|
| 1231 | + */ |
|
| 1232 | + private function runHooks($hooks, $path, $post = false) { |
|
| 1233 | + $relativePath = $path; |
|
| 1234 | + $path = $this->getHookPath($path); |
|
| 1235 | + $prefix = ($post) ? 'post_' : ''; |
|
| 1236 | + $run = true; |
|
| 1237 | + if ($this->shouldEmitHooks($relativePath)) { |
|
| 1238 | + foreach ($hooks as $hook) { |
|
| 1239 | + if ($hook != 'read') { |
|
| 1240 | + \OC_Hook::emit( |
|
| 1241 | + Filesystem::CLASSNAME, |
|
| 1242 | + $prefix . $hook, |
|
| 1243 | + array( |
|
| 1244 | + Filesystem::signal_param_run => &$run, |
|
| 1245 | + Filesystem::signal_param_path => $path |
|
| 1246 | + ) |
|
| 1247 | + ); |
|
| 1248 | + } elseif (!$post) { |
|
| 1249 | + \OC_Hook::emit( |
|
| 1250 | + Filesystem::CLASSNAME, |
|
| 1251 | + $prefix . $hook, |
|
| 1252 | + array( |
|
| 1253 | + Filesystem::signal_param_path => $path |
|
| 1254 | + ) |
|
| 1255 | + ); |
|
| 1256 | + } |
|
| 1257 | + } |
|
| 1258 | + } |
|
| 1259 | + return $run; |
|
| 1260 | + } |
|
| 1261 | + |
|
| 1262 | + /** |
|
| 1263 | + * check if a file or folder has been updated since $time |
|
| 1264 | + * |
|
| 1265 | + * @param string $path |
|
| 1266 | + * @param int $time |
|
| 1267 | + * @return bool |
|
| 1268 | + */ |
|
| 1269 | + public function hasUpdated($path, $time) { |
|
| 1270 | + return $this->basicOperation('hasUpdated', $path, array(), $time); |
|
| 1271 | + } |
|
| 1272 | + |
|
| 1273 | + /** |
|
| 1274 | + * @param string $ownerId |
|
| 1275 | + * @return \OC\User\User |
|
| 1276 | + */ |
|
| 1277 | + private function getUserObjectForOwner($ownerId) { |
|
| 1278 | + $owner = $this->userManager->get($ownerId); |
|
| 1279 | + if ($owner instanceof IUser) { |
|
| 1280 | + return $owner; |
|
| 1281 | + } else { |
|
| 1282 | + return new User($ownerId, null); |
|
| 1283 | + } |
|
| 1284 | + } |
|
| 1285 | + |
|
| 1286 | + /** |
|
| 1287 | + * Get file info from cache |
|
| 1288 | + * |
|
| 1289 | + * If the file is not in cached it will be scanned |
|
| 1290 | + * If the file has changed on storage the cache will be updated |
|
| 1291 | + * |
|
| 1292 | + * @param \OC\Files\Storage\Storage $storage |
|
| 1293 | + * @param string $internalPath |
|
| 1294 | + * @param string $relativePath |
|
| 1295 | + * @return ICacheEntry|bool |
|
| 1296 | + */ |
|
| 1297 | + private function getCacheEntry($storage, $internalPath, $relativePath) { |
|
| 1298 | + $cache = $storage->getCache($internalPath); |
|
| 1299 | + $data = $cache->get($internalPath); |
|
| 1300 | + $watcher = $storage->getWatcher($internalPath); |
|
| 1301 | + |
|
| 1302 | + try { |
|
| 1303 | + // if the file is not in the cache or needs to be updated, trigger the scanner and reload the data |
|
| 1304 | + if (!$data || $data['size'] === -1) { |
|
| 1305 | + $this->lockFile($relativePath, ILockingProvider::LOCK_SHARED); |
|
| 1306 | + if (!$storage->file_exists($internalPath)) { |
|
| 1307 | + $this->unlockFile($relativePath, ILockingProvider::LOCK_SHARED); |
|
| 1308 | + return false; |
|
| 1309 | + } |
|
| 1310 | + $scanner = $storage->getScanner($internalPath); |
|
| 1311 | + $scanner->scan($internalPath, Cache\Scanner::SCAN_SHALLOW); |
|
| 1312 | + $data = $cache->get($internalPath); |
|
| 1313 | + $this->unlockFile($relativePath, ILockingProvider::LOCK_SHARED); |
|
| 1314 | + } else if (!Cache\Scanner::isPartialFile($internalPath) && $watcher->needsUpdate($internalPath, $data)) { |
|
| 1315 | + $this->lockFile($relativePath, ILockingProvider::LOCK_SHARED); |
|
| 1316 | + $watcher->update($internalPath, $data); |
|
| 1317 | + $storage->getPropagator()->propagateChange($internalPath, time()); |
|
| 1318 | + $data = $cache->get($internalPath); |
|
| 1319 | + $this->unlockFile($relativePath, ILockingProvider::LOCK_SHARED); |
|
| 1320 | + } |
|
| 1321 | + } catch (LockedException $e) { |
|
| 1322 | + // if the file is locked we just use the old cache info |
|
| 1323 | + } |
|
| 1324 | + |
|
| 1325 | + return $data; |
|
| 1326 | + } |
|
| 1327 | + |
|
| 1328 | + /** |
|
| 1329 | + * get the filesystem info |
|
| 1330 | + * |
|
| 1331 | + * @param string $path |
|
| 1332 | + * @param boolean|string $includeMountPoints true to add mountpoint sizes, |
|
| 1333 | + * 'ext' to add only ext storage mount point sizes. Defaults to true. |
|
| 1334 | + * defaults to true |
|
| 1335 | + * @return \OC\Files\FileInfo|false False if file does not exist |
|
| 1336 | + */ |
|
| 1337 | + public function getFileInfo($path, $includeMountPoints = true) { |
|
| 1338 | + $this->assertPathLength($path); |
|
| 1339 | + if (!Filesystem::isValidPath($path)) { |
|
| 1340 | + return false; |
|
| 1341 | + } |
|
| 1342 | + if (Cache\Scanner::isPartialFile($path)) { |
|
| 1343 | + return $this->getPartFileInfo($path); |
|
| 1344 | + } |
|
| 1345 | + $relativePath = $path; |
|
| 1346 | + $path = Filesystem::normalizePath($this->fakeRoot . '/' . $path); |
|
| 1347 | + |
|
| 1348 | + $mount = Filesystem::getMountManager()->find($path); |
|
| 1349 | + $storage = $mount->getStorage(); |
|
| 1350 | + $internalPath = $mount->getInternalPath($path); |
|
| 1351 | + if ($storage) { |
|
| 1352 | + $data = $this->getCacheEntry($storage, $internalPath, $relativePath); |
|
| 1353 | + |
|
| 1354 | + if (!$data instanceof ICacheEntry) { |
|
| 1355 | + return false; |
|
| 1356 | + } |
|
| 1357 | + |
|
| 1358 | + if ($mount instanceof MoveableMount && $internalPath === '') { |
|
| 1359 | + $data['permissions'] |= \OCP\Constants::PERMISSION_DELETE; |
|
| 1360 | + } |
|
| 1361 | + |
|
| 1362 | + $owner = $this->getUserObjectForOwner($storage->getOwner($internalPath)); |
|
| 1363 | + $info = new FileInfo($path, $storage, $internalPath, $data, $mount, $owner); |
|
| 1364 | + |
|
| 1365 | + if ($data and isset($data['fileid'])) { |
|
| 1366 | + if ($includeMountPoints and $data['mimetype'] === 'httpd/unix-directory') { |
|
| 1367 | + //add the sizes of other mount points to the folder |
|
| 1368 | + $extOnly = ($includeMountPoints === 'ext'); |
|
| 1369 | + $mounts = Filesystem::getMountManager()->findIn($path); |
|
| 1370 | + $info->setSubMounts(array_filter($mounts, function (IMountPoint $mount) use ($extOnly) { |
|
| 1371 | + $subStorage = $mount->getStorage(); |
|
| 1372 | + return !($extOnly && $subStorage instanceof \OCA\Files_Sharing\SharedStorage); |
|
| 1373 | + })); |
|
| 1374 | + } |
|
| 1375 | + } |
|
| 1376 | + |
|
| 1377 | + return $info; |
|
| 1378 | + } |
|
| 1379 | + |
|
| 1380 | + return false; |
|
| 1381 | + } |
|
| 1382 | + |
|
| 1383 | + /** |
|
| 1384 | + * get the content of a directory |
|
| 1385 | + * |
|
| 1386 | + * @param string $directory path under datadirectory |
|
| 1387 | + * @param string $mimetype_filter limit returned content to this mimetype or mimepart |
|
| 1388 | + * @return FileInfo[] |
|
| 1389 | + */ |
|
| 1390 | + public function getDirectoryContent($directory, $mimetype_filter = '') { |
|
| 1391 | + $this->assertPathLength($directory); |
|
| 1392 | + if (!Filesystem::isValidPath($directory)) { |
|
| 1393 | + return []; |
|
| 1394 | + } |
|
| 1395 | + $path = $this->getAbsolutePath($directory); |
|
| 1396 | + $path = Filesystem::normalizePath($path); |
|
| 1397 | + $mount = $this->getMount($directory); |
|
| 1398 | + $storage = $mount->getStorage(); |
|
| 1399 | + $internalPath = $mount->getInternalPath($path); |
|
| 1400 | + if ($storage) { |
|
| 1401 | + $cache = $storage->getCache($internalPath); |
|
| 1402 | + $user = \OC_User::getUser(); |
|
| 1403 | + |
|
| 1404 | + $data = $this->getCacheEntry($storage, $internalPath, $directory); |
|
| 1405 | + |
|
| 1406 | + if (!$data instanceof ICacheEntry || !isset($data['fileid']) || !($data->getPermissions() && Constants::PERMISSION_READ)) { |
|
| 1407 | + return []; |
|
| 1408 | + } |
|
| 1409 | + |
|
| 1410 | + $folderId = $data['fileid']; |
|
| 1411 | + $contents = $cache->getFolderContentsById($folderId); //TODO: mimetype_filter |
|
| 1412 | + |
|
| 1413 | + $sharingDisabled = \OCP\Util::isSharingDisabledForUser(); |
|
| 1414 | + /** |
|
| 1415 | + * @var \OC\Files\FileInfo[] $files |
|
| 1416 | + */ |
|
| 1417 | + $files = array_map(function (ICacheEntry $content) use ($path, $storage, $mount, $sharingDisabled) { |
|
| 1418 | + if ($sharingDisabled) { |
|
| 1419 | + $content['permissions'] = $content['permissions'] & ~\OCP\Constants::PERMISSION_SHARE; |
|
| 1420 | + } |
|
| 1421 | + $owner = $this->getUserObjectForOwner($storage->getOwner($content['path'])); |
|
| 1422 | + return new FileInfo($path . '/' . $content['name'], $storage, $content['path'], $content, $mount, $owner); |
|
| 1423 | + }, $contents); |
|
| 1424 | + |
|
| 1425 | + //add a folder for any mountpoint in this directory and add the sizes of other mountpoints to the folders |
|
| 1426 | + $mounts = Filesystem::getMountManager()->findIn($path); |
|
| 1427 | + $dirLength = strlen($path); |
|
| 1428 | + foreach ($mounts as $mount) { |
|
| 1429 | + $mountPoint = $mount->getMountPoint(); |
|
| 1430 | + $subStorage = $mount->getStorage(); |
|
| 1431 | + if ($subStorage) { |
|
| 1432 | + $subCache = $subStorage->getCache(''); |
|
| 1433 | + |
|
| 1434 | + $rootEntry = $subCache->get(''); |
|
| 1435 | + if (!$rootEntry) { |
|
| 1436 | + $subScanner = $subStorage->getScanner(''); |
|
| 1437 | + try { |
|
| 1438 | + $subScanner->scanFile(''); |
|
| 1439 | + } catch (\OCP\Files\StorageNotAvailableException $e) { |
|
| 1440 | + continue; |
|
| 1441 | + } catch (\OCP\Files\StorageInvalidException $e) { |
|
| 1442 | + continue; |
|
| 1443 | + } catch (\Exception $e) { |
|
| 1444 | + // sometimes when the storage is not available it can be any exception |
|
| 1445 | + \OCP\Util::writeLog( |
|
| 1446 | + 'core', |
|
| 1447 | + 'Exception while scanning storage "' . $subStorage->getId() . '": ' . |
|
| 1448 | + get_class($e) . ': ' . $e->getMessage(), |
|
| 1449 | + \OCP\Util::ERROR |
|
| 1450 | + ); |
|
| 1451 | + continue; |
|
| 1452 | + } |
|
| 1453 | + $rootEntry = $subCache->get(''); |
|
| 1454 | + } |
|
| 1455 | + |
|
| 1456 | + if ($rootEntry && ($rootEntry->getPermissions() && Constants::PERMISSION_READ)) { |
|
| 1457 | + $relativePath = trim(substr($mountPoint, $dirLength), '/'); |
|
| 1458 | + if ($pos = strpos($relativePath, '/')) { |
|
| 1459 | + //mountpoint inside subfolder add size to the correct folder |
|
| 1460 | + $entryName = substr($relativePath, 0, $pos); |
|
| 1461 | + foreach ($files as &$entry) { |
|
| 1462 | + if ($entry->getName() === $entryName) { |
|
| 1463 | + $entry->addSubEntry($rootEntry, $mountPoint); |
|
| 1464 | + } |
|
| 1465 | + } |
|
| 1466 | + } else { //mountpoint in this folder, add an entry for it |
|
| 1467 | + $rootEntry['name'] = $relativePath; |
|
| 1468 | + $rootEntry['type'] = $rootEntry['mimetype'] === 'httpd/unix-directory' ? 'dir' : 'file'; |
|
| 1469 | + $permissions = $rootEntry['permissions']; |
|
| 1470 | + // do not allow renaming/deleting the mount point if they are not shared files/folders |
|
| 1471 | + // for shared files/folders we use the permissions given by the owner |
|
| 1472 | + if ($mount instanceof MoveableMount) { |
|
| 1473 | + $rootEntry['permissions'] = $permissions | \OCP\Constants::PERMISSION_UPDATE | \OCP\Constants::PERMISSION_DELETE; |
|
| 1474 | + } else { |
|
| 1475 | + $rootEntry['permissions'] = $permissions & (\OCP\Constants::PERMISSION_ALL - (\OCP\Constants::PERMISSION_UPDATE | \OCP\Constants::PERMISSION_DELETE)); |
|
| 1476 | + } |
|
| 1477 | + |
|
| 1478 | + //remove any existing entry with the same name |
|
| 1479 | + foreach ($files as $i => $file) { |
|
| 1480 | + if ($file['name'] === $rootEntry['name']) { |
|
| 1481 | + unset($files[$i]); |
|
| 1482 | + break; |
|
| 1483 | + } |
|
| 1484 | + } |
|
| 1485 | + $rootEntry['path'] = substr(Filesystem::normalizePath($path . '/' . $rootEntry['name']), strlen($user) + 2); // full path without /$user/ |
|
| 1486 | + |
|
| 1487 | + // if sharing was disabled for the user we remove the share permissions |
|
| 1488 | + if (\OCP\Util::isSharingDisabledForUser()) { |
|
| 1489 | + $rootEntry['permissions'] = $rootEntry['permissions'] & ~\OCP\Constants::PERMISSION_SHARE; |
|
| 1490 | + } |
|
| 1491 | + |
|
| 1492 | + $owner = $this->getUserObjectForOwner($subStorage->getOwner('')); |
|
| 1493 | + $files[] = new FileInfo($path . '/' . $rootEntry['name'], $subStorage, '', $rootEntry, $mount, $owner); |
|
| 1494 | + } |
|
| 1495 | + } |
|
| 1496 | + } |
|
| 1497 | + } |
|
| 1498 | + |
|
| 1499 | + if ($mimetype_filter) { |
|
| 1500 | + $files = array_filter($files, function (FileInfo $file) use ($mimetype_filter) { |
|
| 1501 | + if (strpos($mimetype_filter, '/')) { |
|
| 1502 | + return $file->getMimetype() === $mimetype_filter; |
|
| 1503 | + } else { |
|
| 1504 | + return $file->getMimePart() === $mimetype_filter; |
|
| 1505 | + } |
|
| 1506 | + }); |
|
| 1507 | + } |
|
| 1508 | + |
|
| 1509 | + return $files; |
|
| 1510 | + } else { |
|
| 1511 | + return []; |
|
| 1512 | + } |
|
| 1513 | + } |
|
| 1514 | + |
|
| 1515 | + /** |
|
| 1516 | + * change file metadata |
|
| 1517 | + * |
|
| 1518 | + * @param string $path |
|
| 1519 | + * @param array|\OCP\Files\FileInfo $data |
|
| 1520 | + * @return int |
|
| 1521 | + * |
|
| 1522 | + * returns the fileid of the updated file |
|
| 1523 | + */ |
|
| 1524 | + public function putFileInfo($path, $data) { |
|
| 1525 | + $this->assertPathLength($path); |
|
| 1526 | + if ($data instanceof FileInfo) { |
|
| 1527 | + $data = $data->getData(); |
|
| 1528 | + } |
|
| 1529 | + $path = Filesystem::normalizePath($this->fakeRoot . '/' . $path); |
|
| 1530 | + /** |
|
| 1531 | + * @var \OC\Files\Storage\Storage $storage |
|
| 1532 | + * @var string $internalPath |
|
| 1533 | + */ |
|
| 1534 | + list($storage, $internalPath) = Filesystem::resolvePath($path); |
|
| 1535 | + if ($storage) { |
|
| 1536 | + $cache = $storage->getCache($path); |
|
| 1537 | + |
|
| 1538 | + if (!$cache->inCache($internalPath)) { |
|
| 1539 | + $scanner = $storage->getScanner($internalPath); |
|
| 1540 | + $scanner->scan($internalPath, Cache\Scanner::SCAN_SHALLOW); |
|
| 1541 | + } |
|
| 1542 | + |
|
| 1543 | + return $cache->put($internalPath, $data); |
|
| 1544 | + } else { |
|
| 1545 | + return -1; |
|
| 1546 | + } |
|
| 1547 | + } |
|
| 1548 | + |
|
| 1549 | + /** |
|
| 1550 | + * search for files with the name matching $query |
|
| 1551 | + * |
|
| 1552 | + * @param string $query |
|
| 1553 | + * @return FileInfo[] |
|
| 1554 | + */ |
|
| 1555 | + public function search($query) { |
|
| 1556 | + return $this->searchCommon('search', array('%' . $query . '%')); |
|
| 1557 | + } |
|
| 1558 | + |
|
| 1559 | + /** |
|
| 1560 | + * search for files with the name matching $query |
|
| 1561 | + * |
|
| 1562 | + * @param string $query |
|
| 1563 | + * @return FileInfo[] |
|
| 1564 | + */ |
|
| 1565 | + public function searchRaw($query) { |
|
| 1566 | + return $this->searchCommon('search', array($query)); |
|
| 1567 | + } |
|
| 1568 | + |
|
| 1569 | + /** |
|
| 1570 | + * search for files by mimetype |
|
| 1571 | + * |
|
| 1572 | + * @param string $mimetype |
|
| 1573 | + * @return FileInfo[] |
|
| 1574 | + */ |
|
| 1575 | + public function searchByMime($mimetype) { |
|
| 1576 | + return $this->searchCommon('searchByMime', array($mimetype)); |
|
| 1577 | + } |
|
| 1578 | + |
|
| 1579 | + /** |
|
| 1580 | + * search for files by tag |
|
| 1581 | + * |
|
| 1582 | + * @param string|int $tag name or tag id |
|
| 1583 | + * @param string $userId owner of the tags |
|
| 1584 | + * @return FileInfo[] |
|
| 1585 | + */ |
|
| 1586 | + public function searchByTag($tag, $userId) { |
|
| 1587 | + return $this->searchCommon('searchByTag', array($tag, $userId)); |
|
| 1588 | + } |
|
| 1589 | + |
|
| 1590 | + /** |
|
| 1591 | + * @param string $method cache method |
|
| 1592 | + * @param array $args |
|
| 1593 | + * @return FileInfo[] |
|
| 1594 | + */ |
|
| 1595 | + private function searchCommon($method, $args) { |
|
| 1596 | + $files = array(); |
|
| 1597 | + $rootLength = strlen($this->fakeRoot); |
|
| 1598 | + |
|
| 1599 | + $mount = $this->getMount(''); |
|
| 1600 | + $mountPoint = $mount->getMountPoint(); |
|
| 1601 | + $storage = $mount->getStorage(); |
|
| 1602 | + if ($storage) { |
|
| 1603 | + $cache = $storage->getCache(''); |
|
| 1604 | + |
|
| 1605 | + $results = call_user_func_array(array($cache, $method), $args); |
|
| 1606 | + foreach ($results as $result) { |
|
| 1607 | + if (substr($mountPoint . $result['path'], 0, $rootLength + 1) === $this->fakeRoot . '/') { |
|
| 1608 | + $internalPath = $result['path']; |
|
| 1609 | + $path = $mountPoint . $result['path']; |
|
| 1610 | + $result['path'] = substr($mountPoint . $result['path'], $rootLength); |
|
| 1611 | + $owner = \OC::$server->getUserManager()->get($storage->getOwner($internalPath)); |
|
| 1612 | + $files[] = new FileInfo($path, $storage, $internalPath, $result, $mount, $owner); |
|
| 1613 | + } |
|
| 1614 | + } |
|
| 1615 | + |
|
| 1616 | + $mounts = Filesystem::getMountManager()->findIn($this->fakeRoot); |
|
| 1617 | + foreach ($mounts as $mount) { |
|
| 1618 | + $mountPoint = $mount->getMountPoint(); |
|
| 1619 | + $storage = $mount->getStorage(); |
|
| 1620 | + if ($storage) { |
|
| 1621 | + $cache = $storage->getCache(''); |
|
| 1622 | + |
|
| 1623 | + $relativeMountPoint = substr($mountPoint, $rootLength); |
|
| 1624 | + $results = call_user_func_array(array($cache, $method), $args); |
|
| 1625 | + if ($results) { |
|
| 1626 | + foreach ($results as $result) { |
|
| 1627 | + $internalPath = $result['path']; |
|
| 1628 | + $result['path'] = rtrim($relativeMountPoint . $result['path'], '/'); |
|
| 1629 | + $path = rtrim($mountPoint . $internalPath, '/'); |
|
| 1630 | + $owner = \OC::$server->getUserManager()->get($storage->getOwner($internalPath)); |
|
| 1631 | + $files[] = new FileInfo($path, $storage, $internalPath, $result, $mount, $owner); |
|
| 1632 | + } |
|
| 1633 | + } |
|
| 1634 | + } |
|
| 1635 | + } |
|
| 1636 | + } |
|
| 1637 | + return $files; |
|
| 1638 | + } |
|
| 1639 | + |
|
| 1640 | + /** |
|
| 1641 | + * Get the owner for a file or folder |
|
| 1642 | + * |
|
| 1643 | + * @param string $path |
|
| 1644 | + * @return string the user id of the owner |
|
| 1645 | + * @throws NotFoundException |
|
| 1646 | + */ |
|
| 1647 | + public function getOwner($path) { |
|
| 1648 | + $info = $this->getFileInfo($path); |
|
| 1649 | + if (!$info) { |
|
| 1650 | + throw new NotFoundException($path . ' not found while trying to get owner'); |
|
| 1651 | + } |
|
| 1652 | + return $info->getOwner()->getUID(); |
|
| 1653 | + } |
|
| 1654 | + |
|
| 1655 | + /** |
|
| 1656 | + * get the ETag for a file or folder |
|
| 1657 | + * |
|
| 1658 | + * @param string $path |
|
| 1659 | + * @return string |
|
| 1660 | + */ |
|
| 1661 | + public function getETag($path) { |
|
| 1662 | + /** |
|
| 1663 | + * @var Storage\Storage $storage |
|
| 1664 | + * @var string $internalPath |
|
| 1665 | + */ |
|
| 1666 | + list($storage, $internalPath) = $this->resolvePath($path); |
|
| 1667 | + if ($storage) { |
|
| 1668 | + return $storage->getETag($internalPath); |
|
| 1669 | + } else { |
|
| 1670 | + return null; |
|
| 1671 | + } |
|
| 1672 | + } |
|
| 1673 | + |
|
| 1674 | + /** |
|
| 1675 | + * Get the path of a file by id, relative to the view |
|
| 1676 | + * |
|
| 1677 | + * Note that the resulting path is not guarantied to be unique for the id, multiple paths can point to the same file |
|
| 1678 | + * |
|
| 1679 | + * @param int $id |
|
| 1680 | + * @throws NotFoundException |
|
| 1681 | + * @return string |
|
| 1682 | + */ |
|
| 1683 | + public function getPath($id) { |
|
| 1684 | + $id = (int)$id; |
|
| 1685 | + $manager = Filesystem::getMountManager(); |
|
| 1686 | + $mounts = $manager->findIn($this->fakeRoot); |
|
| 1687 | + $mounts[] = $manager->find($this->fakeRoot); |
|
| 1688 | + // reverse the array so we start with the storage this view is in |
|
| 1689 | + // which is the most likely to contain the file we're looking for |
|
| 1690 | + $mounts = array_reverse($mounts); |
|
| 1691 | + foreach ($mounts as $mount) { |
|
| 1692 | + /** |
|
| 1693 | + * @var \OC\Files\Mount\MountPoint $mount |
|
| 1694 | + */ |
|
| 1695 | + if ($mount->getStorage()) { |
|
| 1696 | + $cache = $mount->getStorage()->getCache(); |
|
| 1697 | + $internalPath = $cache->getPathById($id); |
|
| 1698 | + if (is_string($internalPath)) { |
|
| 1699 | + $fullPath = $mount->getMountPoint() . $internalPath; |
|
| 1700 | + if (!is_null($path = $this->getRelativePath($fullPath))) { |
|
| 1701 | + return $path; |
|
| 1702 | + } |
|
| 1703 | + } |
|
| 1704 | + } |
|
| 1705 | + } |
|
| 1706 | + throw new NotFoundException(sprintf('File with id "%s" has not been found.', $id)); |
|
| 1707 | + } |
|
| 1708 | + |
|
| 1709 | + /** |
|
| 1710 | + * @param string $path |
|
| 1711 | + * @throws InvalidPathException |
|
| 1712 | + */ |
|
| 1713 | + private function assertPathLength($path) { |
|
| 1714 | + $maxLen = min(PHP_MAXPATHLEN, 4000); |
|
| 1715 | + // Check for the string length - performed using isset() instead of strlen() |
|
| 1716 | + // because isset() is about 5x-40x faster. |
|
| 1717 | + if (isset($path[$maxLen])) { |
|
| 1718 | + $pathLen = strlen($path); |
|
| 1719 | + throw new \OCP\Files\InvalidPathException("Path length($pathLen) exceeds max path length($maxLen): $path"); |
|
| 1720 | + } |
|
| 1721 | + } |
|
| 1722 | + |
|
| 1723 | + /** |
|
| 1724 | + * check if it is allowed to move a mount point to a given target. |
|
| 1725 | + * It is not allowed to move a mount point into a different mount point or |
|
| 1726 | + * into an already shared folder |
|
| 1727 | + * |
|
| 1728 | + * @param string $target path |
|
| 1729 | + * @return boolean |
|
| 1730 | + */ |
|
| 1731 | + private function isTargetAllowed($target) { |
|
| 1732 | + |
|
| 1733 | + list($targetStorage, $targetInternalPath) = \OC\Files\Filesystem::resolvePath($target); |
|
| 1734 | + if (!$targetStorage->instanceOfStorage('\OCP\Files\IHomeStorage')) { |
|
| 1735 | + \OCP\Util::writeLog('files', |
|
| 1736 | + 'It is not allowed to move one mount point into another one', |
|
| 1737 | + \OCP\Util::DEBUG); |
|
| 1738 | + return false; |
|
| 1739 | + } |
|
| 1740 | + |
|
| 1741 | + // note: cannot use the view because the target is already locked |
|
| 1742 | + $fileId = (int)$targetStorage->getCache()->getId($targetInternalPath); |
|
| 1743 | + if ($fileId === -1) { |
|
| 1744 | + // target might not exist, need to check parent instead |
|
| 1745 | + $fileId = (int)$targetStorage->getCache()->getId(dirname($targetInternalPath)); |
|
| 1746 | + } |
|
| 1747 | + |
|
| 1748 | + // check if any of the parents were shared by the current owner (include collections) |
|
| 1749 | + $shares = \OCP\Share::getItemShared( |
|
| 1750 | + 'folder', |
|
| 1751 | + $fileId, |
|
| 1752 | + \OCP\Share::FORMAT_NONE, |
|
| 1753 | + null, |
|
| 1754 | + true |
|
| 1755 | + ); |
|
| 1756 | + |
|
| 1757 | + if (count($shares) > 0) { |
|
| 1758 | + \OCP\Util::writeLog('files', |
|
| 1759 | + 'It is not allowed to move one mount point into a shared folder', |
|
| 1760 | + \OCP\Util::DEBUG); |
|
| 1761 | + return false; |
|
| 1762 | + } |
|
| 1763 | + |
|
| 1764 | + return true; |
|
| 1765 | + } |
|
| 1766 | + |
|
| 1767 | + /** |
|
| 1768 | + * Get a fileinfo object for files that are ignored in the cache (part files) |
|
| 1769 | + * |
|
| 1770 | + * @param string $path |
|
| 1771 | + * @return \OCP\Files\FileInfo |
|
| 1772 | + */ |
|
| 1773 | + private function getPartFileInfo($path) { |
|
| 1774 | + $mount = $this->getMount($path); |
|
| 1775 | + $storage = $mount->getStorage(); |
|
| 1776 | + $internalPath = $mount->getInternalPath($this->getAbsolutePath($path)); |
|
| 1777 | + $owner = \OC::$server->getUserManager()->get($storage->getOwner($internalPath)); |
|
| 1778 | + return new FileInfo( |
|
| 1779 | + $this->getAbsolutePath($path), |
|
| 1780 | + $storage, |
|
| 1781 | + $internalPath, |
|
| 1782 | + [ |
|
| 1783 | + 'fileid' => null, |
|
| 1784 | + 'mimetype' => $storage->getMimeType($internalPath), |
|
| 1785 | + 'name' => basename($path), |
|
| 1786 | + 'etag' => null, |
|
| 1787 | + 'size' => $storage->filesize($internalPath), |
|
| 1788 | + 'mtime' => $storage->filemtime($internalPath), |
|
| 1789 | + 'encrypted' => false, |
|
| 1790 | + 'permissions' => \OCP\Constants::PERMISSION_ALL |
|
| 1791 | + ], |
|
| 1792 | + $mount, |
|
| 1793 | + $owner |
|
| 1794 | + ); |
|
| 1795 | + } |
|
| 1796 | + |
|
| 1797 | + /** |
|
| 1798 | + * @param string $path |
|
| 1799 | + * @param string $fileName |
|
| 1800 | + * @throws InvalidPathException |
|
| 1801 | + */ |
|
| 1802 | + public function verifyPath($path, $fileName) { |
|
| 1803 | + try { |
|
| 1804 | + /** @type \OCP\Files\Storage $storage */ |
|
| 1805 | + list($storage, $internalPath) = $this->resolvePath($path); |
|
| 1806 | + $storage->verifyPath($internalPath, $fileName); |
|
| 1807 | + } catch (ReservedWordException $ex) { |
|
| 1808 | + $l = \OC::$server->getL10N('lib'); |
|
| 1809 | + throw new InvalidPathException($l->t('File name is a reserved word')); |
|
| 1810 | + } catch (InvalidCharacterInPathException $ex) { |
|
| 1811 | + $l = \OC::$server->getL10N('lib'); |
|
| 1812 | + throw new InvalidPathException($l->t('File name contains at least one invalid character')); |
|
| 1813 | + } catch (FileNameTooLongException $ex) { |
|
| 1814 | + $l = \OC::$server->getL10N('lib'); |
|
| 1815 | + throw new InvalidPathException($l->t('File name is too long')); |
|
| 1816 | + } catch (InvalidDirectoryException $ex) { |
|
| 1817 | + $l = \OC::$server->getL10N('lib'); |
|
| 1818 | + throw new InvalidPathException($l->t('Dot files are not allowed')); |
|
| 1819 | + } catch (EmptyFileNameException $ex) { |
|
| 1820 | + $l = \OC::$server->getL10N('lib'); |
|
| 1821 | + throw new InvalidPathException($l->t('Empty filename is not allowed')); |
|
| 1822 | + } |
|
| 1823 | + } |
|
| 1824 | + |
|
| 1825 | + /** |
|
| 1826 | + * get all parent folders of $path |
|
| 1827 | + * |
|
| 1828 | + * @param string $path |
|
| 1829 | + * @return string[] |
|
| 1830 | + */ |
|
| 1831 | + private function getParents($path) { |
|
| 1832 | + $path = trim($path, '/'); |
|
| 1833 | + if (!$path) { |
|
| 1834 | + return []; |
|
| 1835 | + } |
|
| 1836 | + |
|
| 1837 | + $parts = explode('/', $path); |
|
| 1838 | + |
|
| 1839 | + // remove the single file |
|
| 1840 | + array_pop($parts); |
|
| 1841 | + $result = array('/'); |
|
| 1842 | + $resultPath = ''; |
|
| 1843 | + foreach ($parts as $part) { |
|
| 1844 | + if ($part) { |
|
| 1845 | + $resultPath .= '/' . $part; |
|
| 1846 | + $result[] = $resultPath; |
|
| 1847 | + } |
|
| 1848 | + } |
|
| 1849 | + return $result; |
|
| 1850 | + } |
|
| 1851 | + |
|
| 1852 | + /** |
|
| 1853 | + * Returns the mount point for which to lock |
|
| 1854 | + * |
|
| 1855 | + * @param string $absolutePath absolute path |
|
| 1856 | + * @param bool $useParentMount true to return parent mount instead of whatever |
|
| 1857 | + * is mounted directly on the given path, false otherwise |
|
| 1858 | + * @return \OC\Files\Mount\MountPoint mount point for which to apply locks |
|
| 1859 | + */ |
|
| 1860 | + private function getMountForLock($absolutePath, $useParentMount = false) { |
|
| 1861 | + $results = []; |
|
| 1862 | + $mount = Filesystem::getMountManager()->find($absolutePath); |
|
| 1863 | + if (!$mount) { |
|
| 1864 | + return $results; |
|
| 1865 | + } |
|
| 1866 | + |
|
| 1867 | + if ($useParentMount) { |
|
| 1868 | + // find out if something is mounted directly on the path |
|
| 1869 | + $internalPath = $mount->getInternalPath($absolutePath); |
|
| 1870 | + if ($internalPath === '') { |
|
| 1871 | + // resolve the parent mount instead |
|
| 1872 | + $mount = Filesystem::getMountManager()->find(dirname($absolutePath)); |
|
| 1873 | + } |
|
| 1874 | + } |
|
| 1875 | + |
|
| 1876 | + return $mount; |
|
| 1877 | + } |
|
| 1878 | + |
|
| 1879 | + /** |
|
| 1880 | + * Lock the given path |
|
| 1881 | + * |
|
| 1882 | + * @param string $path the path of the file to lock, relative to the view |
|
| 1883 | + * @param int $type \OCP\Lock\ILockingProvider::LOCK_SHARED or \OCP\Lock\ILockingProvider::LOCK_EXCLUSIVE |
|
| 1884 | + * @param bool $lockMountPoint true to lock the mount point, false to lock the attached mount/storage |
|
| 1885 | + * |
|
| 1886 | + * @return bool False if the path is excluded from locking, true otherwise |
|
| 1887 | + * @throws \OCP\Lock\LockedException if the path is already locked |
|
| 1888 | + */ |
|
| 1889 | + private function lockPath($path, $type, $lockMountPoint = false) { |
|
| 1890 | + $absolutePath = $this->getAbsolutePath($path); |
|
| 1891 | + $absolutePath = Filesystem::normalizePath($absolutePath); |
|
| 1892 | + if (!$this->shouldLockFile($absolutePath)) { |
|
| 1893 | + return false; |
|
| 1894 | + } |
|
| 1895 | + |
|
| 1896 | + $mount = $this->getMountForLock($absolutePath, $lockMountPoint); |
|
| 1897 | + if ($mount) { |
|
| 1898 | + try { |
|
| 1899 | + $storage = $mount->getStorage(); |
|
| 1900 | + if ($storage->instanceOfStorage('\OCP\Files\Storage\ILockingStorage')) { |
|
| 1901 | + $storage->acquireLock( |
|
| 1902 | + $mount->getInternalPath($absolutePath), |
|
| 1903 | + $type, |
|
| 1904 | + $this->lockingProvider |
|
| 1905 | + ); |
|
| 1906 | + } |
|
| 1907 | + } catch (\OCP\Lock\LockedException $e) { |
|
| 1908 | + // rethrow with the a human-readable path |
|
| 1909 | + throw new \OCP\Lock\LockedException( |
|
| 1910 | + $this->getPathRelativeToFiles($absolutePath), |
|
| 1911 | + $e |
|
| 1912 | + ); |
|
| 1913 | + } |
|
| 1914 | + } |
|
| 1915 | + |
|
| 1916 | + return true; |
|
| 1917 | + } |
|
| 1918 | + |
|
| 1919 | + /** |
|
| 1920 | + * Change the lock type |
|
| 1921 | + * |
|
| 1922 | + * @param string $path the path of the file to lock, relative to the view |
|
| 1923 | + * @param int $type \OCP\Lock\ILockingProvider::LOCK_SHARED or \OCP\Lock\ILockingProvider::LOCK_EXCLUSIVE |
|
| 1924 | + * @param bool $lockMountPoint true to lock the mount point, false to lock the attached mount/storage |
|
| 1925 | + * |
|
| 1926 | + * @return bool False if the path is excluded from locking, true otherwise |
|
| 1927 | + * @throws \OCP\Lock\LockedException if the path is already locked |
|
| 1928 | + */ |
|
| 1929 | + public function changeLock($path, $type, $lockMountPoint = false) { |
|
| 1930 | + $path = Filesystem::normalizePath($path); |
|
| 1931 | + $absolutePath = $this->getAbsolutePath($path); |
|
| 1932 | + $absolutePath = Filesystem::normalizePath($absolutePath); |
|
| 1933 | + if (!$this->shouldLockFile($absolutePath)) { |
|
| 1934 | + return false; |
|
| 1935 | + } |
|
| 1936 | + |
|
| 1937 | + $mount = $this->getMountForLock($absolutePath, $lockMountPoint); |
|
| 1938 | + if ($mount) { |
|
| 1939 | + try { |
|
| 1940 | + $storage = $mount->getStorage(); |
|
| 1941 | + if ($storage->instanceOfStorage('\OCP\Files\Storage\ILockingStorage')) { |
|
| 1942 | + $storage->changeLock( |
|
| 1943 | + $mount->getInternalPath($absolutePath), |
|
| 1944 | + $type, |
|
| 1945 | + $this->lockingProvider |
|
| 1946 | + ); |
|
| 1947 | + } |
|
| 1948 | + } catch (\OCP\Lock\LockedException $e) { |
|
| 1949 | + // rethrow with the a human-readable path |
|
| 1950 | + throw new \OCP\Lock\LockedException( |
|
| 1951 | + $this->getPathRelativeToFiles($absolutePath), |
|
| 1952 | + $e |
|
| 1953 | + ); |
|
| 1954 | + } |
|
| 1955 | + } |
|
| 1956 | + |
|
| 1957 | + return true; |
|
| 1958 | + } |
|
| 1959 | + |
|
| 1960 | + /** |
|
| 1961 | + * Unlock the given path |
|
| 1962 | + * |
|
| 1963 | + * @param string $path the path of the file to unlock, relative to the view |
|
| 1964 | + * @param int $type \OCP\Lock\ILockingProvider::LOCK_SHARED or \OCP\Lock\ILockingProvider::LOCK_EXCLUSIVE |
|
| 1965 | + * @param bool $lockMountPoint true to lock the mount point, false to lock the attached mount/storage |
|
| 1966 | + * |
|
| 1967 | + * @return bool False if the path is excluded from locking, true otherwise |
|
| 1968 | + */ |
|
| 1969 | + private function unlockPath($path, $type, $lockMountPoint = false) { |
|
| 1970 | + $absolutePath = $this->getAbsolutePath($path); |
|
| 1971 | + $absolutePath = Filesystem::normalizePath($absolutePath); |
|
| 1972 | + if (!$this->shouldLockFile($absolutePath)) { |
|
| 1973 | + return false; |
|
| 1974 | + } |
|
| 1975 | + |
|
| 1976 | + $mount = $this->getMountForLock($absolutePath, $lockMountPoint); |
|
| 1977 | + if ($mount) { |
|
| 1978 | + $storage = $mount->getStorage(); |
|
| 1979 | + if ($storage && $storage->instanceOfStorage('\OCP\Files\Storage\ILockingStorage')) { |
|
| 1980 | + $storage->releaseLock( |
|
| 1981 | + $mount->getInternalPath($absolutePath), |
|
| 1982 | + $type, |
|
| 1983 | + $this->lockingProvider |
|
| 1984 | + ); |
|
| 1985 | + } |
|
| 1986 | + } |
|
| 1987 | + |
|
| 1988 | + return true; |
|
| 1989 | + } |
|
| 1990 | + |
|
| 1991 | + /** |
|
| 1992 | + * Lock a path and all its parents up to the root of the view |
|
| 1993 | + * |
|
| 1994 | + * @param string $path the path of the file to lock relative to the view |
|
| 1995 | + * @param int $type \OCP\Lock\ILockingProvider::LOCK_SHARED or \OCP\Lock\ILockingProvider::LOCK_EXCLUSIVE |
|
| 1996 | + * @param bool $lockMountPoint true to lock the mount point, false to lock the attached mount/storage |
|
| 1997 | + * |
|
| 1998 | + * @return bool False if the path is excluded from locking, true otherwise |
|
| 1999 | + */ |
|
| 2000 | + public function lockFile($path, $type, $lockMountPoint = false) { |
|
| 2001 | + $absolutePath = $this->getAbsolutePath($path); |
|
| 2002 | + $absolutePath = Filesystem::normalizePath($absolutePath); |
|
| 2003 | + if (!$this->shouldLockFile($absolutePath)) { |
|
| 2004 | + return false; |
|
| 2005 | + } |
|
| 2006 | + |
|
| 2007 | + $this->lockPath($path, $type, $lockMountPoint); |
|
| 2008 | + |
|
| 2009 | + $parents = $this->getParents($path); |
|
| 2010 | + foreach ($parents as $parent) { |
|
| 2011 | + $this->lockPath($parent, ILockingProvider::LOCK_SHARED); |
|
| 2012 | + } |
|
| 2013 | + |
|
| 2014 | + return true; |
|
| 2015 | + } |
|
| 2016 | + |
|
| 2017 | + /** |
|
| 2018 | + * Unlock a path and all its parents up to the root of the view |
|
| 2019 | + * |
|
| 2020 | + * @param string $path the path of the file to lock relative to the view |
|
| 2021 | + * @param int $type \OCP\Lock\ILockingProvider::LOCK_SHARED or \OCP\Lock\ILockingProvider::LOCK_EXCLUSIVE |
|
| 2022 | + * @param bool $lockMountPoint true to lock the mount point, false to lock the attached mount/storage |
|
| 2023 | + * |
|
| 2024 | + * @return bool False if the path is excluded from locking, true otherwise |
|
| 2025 | + */ |
|
| 2026 | + public function unlockFile($path, $type, $lockMountPoint = false) { |
|
| 2027 | + $absolutePath = $this->getAbsolutePath($path); |
|
| 2028 | + $absolutePath = Filesystem::normalizePath($absolutePath); |
|
| 2029 | + if (!$this->shouldLockFile($absolutePath)) { |
|
| 2030 | + return false; |
|
| 2031 | + } |
|
| 2032 | + |
|
| 2033 | + $this->unlockPath($path, $type, $lockMountPoint); |
|
| 2034 | + |
|
| 2035 | + $parents = $this->getParents($path); |
|
| 2036 | + foreach ($parents as $parent) { |
|
| 2037 | + $this->unlockPath($parent, ILockingProvider::LOCK_SHARED); |
|
| 2038 | + } |
|
| 2039 | + |
|
| 2040 | + return true; |
|
| 2041 | + } |
|
| 2042 | + |
|
| 2043 | + /** |
|
| 2044 | + * Only lock files in data/user/files/ |
|
| 2045 | + * |
|
| 2046 | + * @param string $path Absolute path to the file/folder we try to (un)lock |
|
| 2047 | + * @return bool |
|
| 2048 | + */ |
|
| 2049 | + protected function shouldLockFile($path) { |
|
| 2050 | + $path = Filesystem::normalizePath($path); |
|
| 2051 | + |
|
| 2052 | + $pathSegments = explode('/', $path); |
|
| 2053 | + if (isset($pathSegments[2])) { |
|
| 2054 | + // E.g.: /username/files/path-to-file |
|
| 2055 | + return ($pathSegments[2] === 'files') && (count($pathSegments) > 3); |
|
| 2056 | + } |
|
| 2057 | + |
|
| 2058 | + return true; |
|
| 2059 | + } |
|
| 2060 | + |
|
| 2061 | + /** |
|
| 2062 | + * Shortens the given absolute path to be relative to |
|
| 2063 | + * "$user/files". |
|
| 2064 | + * |
|
| 2065 | + * @param string $absolutePath absolute path which is under "files" |
|
| 2066 | + * |
|
| 2067 | + * @return string path relative to "files" with trimmed slashes or null |
|
| 2068 | + * if the path was NOT relative to files |
|
| 2069 | + * |
|
| 2070 | + * @throws \InvalidArgumentException if the given path was not under "files" |
|
| 2071 | + * @since 8.1.0 |
|
| 2072 | + */ |
|
| 2073 | + public function getPathRelativeToFiles($absolutePath) { |
|
| 2074 | + $path = Filesystem::normalizePath($absolutePath); |
|
| 2075 | + $parts = explode('/', trim($path, '/'), 3); |
|
| 2076 | + // "$user", "files", "path/to/dir" |
|
| 2077 | + if (!isset($parts[1]) || $parts[1] !== 'files') { |
|
| 2078 | + $this->logger->error( |
|
| 2079 | + '$absolutePath must be relative to "files", value is "%s"', |
|
| 2080 | + [ |
|
| 2081 | + $absolutePath |
|
| 2082 | + ] |
|
| 2083 | + ); |
|
| 2084 | + throw new \InvalidArgumentException('$absolutePath must be relative to "files"'); |
|
| 2085 | + } |
|
| 2086 | + if (isset($parts[2])) { |
|
| 2087 | + return $parts[2]; |
|
| 2088 | + } |
|
| 2089 | + return ''; |
|
| 2090 | + } |
|
| 2091 | + |
|
| 2092 | + /** |
|
| 2093 | + * @param string $filename |
|
| 2094 | + * @return array |
|
| 2095 | + * @throws \OC\User\NoUserException |
|
| 2096 | + * @throws NotFoundException |
|
| 2097 | + */ |
|
| 2098 | + public function getUidAndFilename($filename) { |
|
| 2099 | + $info = $this->getFileInfo($filename); |
|
| 2100 | + if (!$info instanceof \OCP\Files\FileInfo) { |
|
| 2101 | + throw new NotFoundException($this->getAbsolutePath($filename) . ' not found'); |
|
| 2102 | + } |
|
| 2103 | + $uid = $info->getOwner()->getUID(); |
|
| 2104 | + if ($uid != \OCP\User::getUser()) { |
|
| 2105 | + Filesystem::initMountPoints($uid); |
|
| 2106 | + $ownerView = new View('/' . $uid . '/files'); |
|
| 2107 | + try { |
|
| 2108 | + $filename = $ownerView->getPath($info['fileid']); |
|
| 2109 | + } catch (NotFoundException $e) { |
|
| 2110 | + throw new NotFoundException('File with id ' . $info['fileid'] . ' not found for user ' . $uid); |
|
| 2111 | + } |
|
| 2112 | + } |
|
| 2113 | + return [$uid, $filename]; |
|
| 2114 | + } |
|
| 2115 | + |
|
| 2116 | + /** |
|
| 2117 | + * Creates parent non-existing folders |
|
| 2118 | + * |
|
| 2119 | + * @param string $filePath |
|
| 2120 | + * @return bool |
|
| 2121 | + */ |
|
| 2122 | + private function createParentDirectories($filePath) { |
|
| 2123 | + $directoryParts = explode('/', $filePath); |
|
| 2124 | + $directoryParts = array_filter($directoryParts); |
|
| 2125 | + foreach ($directoryParts as $key => $part) { |
|
| 2126 | + $currentPathElements = array_slice($directoryParts, 0, $key); |
|
| 2127 | + $currentPath = '/' . implode('/', $currentPathElements); |
|
| 2128 | + if ($this->is_file($currentPath)) { |
|
| 2129 | + return false; |
|
| 2130 | + } |
|
| 2131 | + if (!$this->file_exists($currentPath)) { |
|
| 2132 | + $this->mkdir($currentPath); |
|
| 2133 | + } |
|
| 2134 | + } |
|
| 2135 | + |
|
| 2136 | + return true; |
|
| 2137 | + } |
|
| 2138 | 2138 | } |
@@ -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 | } |
@@ -163,7 +163,7 @@ discard block |
||
| 163 | 163 | throw new \Exception("API Key or password, Username, Bucket and Region have to be configured."); |
| 164 | 164 | } |
| 165 | 165 | |
| 166 | - $this->id = 'swift::' . $params['user'] . md5($params['bucket']); |
|
| 166 | + $this->id = 'swift::'.$params['user'].md5($params['bucket']); |
|
| 167 | 167 | |
| 168 | 168 | $bucketUrl = Url::factory($params['bucket']); |
| 169 | 169 | if ($bucketUrl->isAbsolute()) { |
@@ -236,16 +236,16 @@ discard block |
||
| 236 | 236 | continue; |
| 237 | 237 | } |
| 238 | 238 | |
| 239 | - if ($this->is_dir($path . '/' . $file)) { |
|
| 240 | - $this->rmdir($path . '/' . $file); |
|
| 239 | + if ($this->is_dir($path.'/'.$file)) { |
|
| 240 | + $this->rmdir($path.'/'.$file); |
|
| 241 | 241 | } else { |
| 242 | - $this->unlink($path . '/' . $file); |
|
| 242 | + $this->unlink($path.'/'.$file); |
|
| 243 | 243 | } |
| 244 | 244 | } |
| 245 | 245 | |
| 246 | 246 | try { |
| 247 | - $this->getContainer()->dataObject()->setName($path . '/')->delete(); |
|
| 248 | - $this->objectCache->remove($path . '/'); |
|
| 247 | + $this->getContainer()->dataObject()->setName($path.'/')->delete(); |
|
| 248 | + $this->objectCache->remove($path.'/'); |
|
| 249 | 249 | } catch (Exceptions\DeleteError $e) { |
| 250 | 250 | \OCP\Util::writeLog('files_external', $e->getMessage(), \OCP\Util::ERROR); |
| 251 | 251 | return false; |
@@ -326,7 +326,7 @@ discard block |
||
| 326 | 326 | } |
| 327 | 327 | |
| 328 | 328 | $stat = array(); |
| 329 | - $stat['size'] = (int)$object->getContentLength(); |
|
| 329 | + $stat['size'] = (int) $object->getContentLength(); |
|
| 330 | 330 | $stat['mtime'] = $mtime; |
| 331 | 331 | $stat['atime'] = time(); |
| 332 | 332 | return $stat; |
@@ -358,7 +358,7 @@ discard block |
||
| 358 | 358 | try { |
| 359 | 359 | $this->getContainer()->dataObject()->setName($path)->delete(); |
| 360 | 360 | $this->objectCache->remove($path); |
| 361 | - $this->objectCache->remove($path . '/'); |
|
| 361 | + $this->objectCache->remove($path.'/'); |
|
| 362 | 362 | } catch (ClientErrorResponseException $e) { |
| 363 | 363 | if ($e->getResponse()->getStatusCode() !== 404) { |
| 364 | 364 | \OCP\Util::writeLog('files_external', $e->getMessage(), \OCP\Util::ERROR); |
@@ -387,8 +387,8 @@ discard block |
||
| 387 | 387 | $streamInterface = $streamFactory->fromRequest($client->get($c->getUrl($path))); |
| 388 | 388 | $streamInterface->rewind(); |
| 389 | 389 | $stream = $streamInterface->getStream(); |
| 390 | - stream_context_set_option($stream, 'swift','content', $streamInterface); |
|
| 391 | - if(!strrpos($streamInterface |
|
| 390 | + stream_context_set_option($stream, 'swift', 'content', $streamInterface); |
|
| 391 | + if (!strrpos($streamInterface |
|
| 392 | 392 | ->getMetaData('wrapper_data')[0], '404 Not Found')) { |
| 393 | 393 | return $stream; |
| 394 | 394 | } |
@@ -422,7 +422,7 @@ discard block |
||
| 422 | 422 | file_put_contents($tmpFile, $source); |
| 423 | 423 | } |
| 424 | 424 | $handle = fopen($tmpFile, $mode); |
| 425 | - return CallbackWrapper::wrap($handle, null, null, function () use ($path, $tmpFile) { |
|
| 425 | + return CallbackWrapper::wrap($handle, null, null, function() use ($path, $tmpFile) { |
|
| 426 | 426 | $this->writeBack($tmpFile, $path); |
| 427 | 427 | }); |
| 428 | 428 | } |
@@ -469,10 +469,10 @@ discard block |
||
| 469 | 469 | |
| 470 | 470 | try { |
| 471 | 471 | $source = $this->fetchObject($path1); |
| 472 | - $source->copy($this->bucket . '/' . $path2); |
|
| 472 | + $source->copy($this->bucket.'/'.$path2); |
|
| 473 | 473 | // invalidate target object to force repopulation on fetch |
| 474 | 474 | $this->objectCache->remove($path2); |
| 475 | - $this->objectCache->remove($path2 . '/'); |
|
| 475 | + $this->objectCache->remove($path2.'/'); |
|
| 476 | 476 | } catch (ClientErrorResponseException $e) { |
| 477 | 477 | \OCP\Util::writeLog('files_external', $e->getMessage(), \OCP\Util::ERROR); |
| 478 | 478 | return false; |
@@ -484,11 +484,11 @@ discard block |
||
| 484 | 484 | $this->unlink($path2); |
| 485 | 485 | |
| 486 | 486 | try { |
| 487 | - $source = $this->fetchObject($path1 . '/'); |
|
| 488 | - $source->copy($this->bucket . '/' . $path2 . '/'); |
|
| 487 | + $source = $this->fetchObject($path1.'/'); |
|
| 488 | + $source->copy($this->bucket.'/'.$path2.'/'); |
|
| 489 | 489 | // invalidate target object to force repopulation on fetch |
| 490 | 490 | $this->objectCache->remove($path2); |
| 491 | - $this->objectCache->remove($path2 . '/'); |
|
| 491 | + $this->objectCache->remove($path2.'/'); |
|
| 492 | 492 | } catch (ClientErrorResponseException $e) { |
| 493 | 493 | \OCP\Util::writeLog('files_external', $e->getMessage(), \OCP\Util::ERROR); |
| 494 | 494 | return false; |
@@ -500,8 +500,8 @@ discard block |
||
| 500 | 500 | continue; |
| 501 | 501 | } |
| 502 | 502 | |
| 503 | - $source = $path1 . '/' . $file; |
|
| 504 | - $target = $path2 . '/' . $file; |
|
| 503 | + $source = $path1.'/'.$file; |
|
| 504 | + $target = $path2.'/'.$file; |
|
| 505 | 505 | $this->copy($source, $target); |
| 506 | 506 | } |
| 507 | 507 | |
@@ -635,7 +635,7 @@ discard block |
||
| 635 | 635 | $path = ''; |
| 636 | 636 | } |
| 637 | 637 | $cachedContent = $this->getCache()->getFolderContents($path); |
| 638 | - $cachedNames = array_map(function ($content) { |
|
| 638 | + $cachedNames = array_map(function($content) { |
|
| 639 | 639 | return $content['name']; |
| 640 | 640 | }, $cachedContent); |
| 641 | 641 | sort($cachedNames); |
@@ -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 | }); |