| Total Complexity | 74 |
| Total Lines | 647 |
| Duplicated Lines | 0 % |
| Changes | 3 | ||
| Bugs | 0 | Features | 0 |
Complex classes like ShareController often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes.
Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.
While breaking up the class, it is a good idea to analyze how other classes use ShareController, and based on these observations, apply Extract Interface, too.
| 1 | <?php |
||
| 81 | class ShareController extends AuthPublicShareController { |
||
| 82 | |||
| 83 | /** @var IConfig */ |
||
| 84 | protected $config; |
||
| 85 | /** @var IUserManager */ |
||
| 86 | protected $userManager; |
||
| 87 | /** @var ILogger */ |
||
| 88 | protected $logger; |
||
| 89 | /** @var \OCP\Activity\IManager */ |
||
| 90 | protected $activityManager; |
||
| 91 | /** @var IPreview */ |
||
| 92 | protected $previewManager; |
||
| 93 | /** @var IRootFolder */ |
||
| 94 | protected $rootFolder; |
||
| 95 | /** @var FederatedShareProvider */ |
||
| 96 | protected $federatedShareProvider; |
||
| 97 | /** @var EventDispatcherInterface */ |
||
| 98 | protected $eventDispatcher; |
||
| 99 | /** @var IL10N */ |
||
| 100 | protected $l10n; |
||
| 101 | /** @var Defaults */ |
||
| 102 | protected $defaults; |
||
| 103 | /** @var ShareManager */ |
||
| 104 | protected $shareManager; |
||
| 105 | |||
| 106 | /** @var Share\IShare */ |
||
| 107 | protected $share; |
||
| 108 | |||
| 109 | /** |
||
| 110 | * @param string $appName |
||
| 111 | * @param IRequest $request |
||
| 112 | * @param IConfig $config |
||
| 113 | * @param IURLGenerator $urlGenerator |
||
| 114 | * @param IUserManager $userManager |
||
| 115 | * @param ILogger $logger |
||
| 116 | * @param \OCP\Activity\IManager $activityManager |
||
| 117 | * @param \OCP\Share\IManager $shareManager |
||
| 118 | * @param ISession $session |
||
| 119 | * @param IPreview $previewManager |
||
| 120 | * @param IRootFolder $rootFolder |
||
| 121 | * @param FederatedShareProvider $federatedShareProvider |
||
| 122 | * @param EventDispatcherInterface $eventDispatcher |
||
| 123 | * @param IL10N $l10n |
||
| 124 | * @param Defaults $defaults |
||
| 125 | */ |
||
| 126 | public function __construct(string $appName, |
||
| 127 | IRequest $request, |
||
| 128 | IConfig $config, |
||
| 129 | IURLGenerator $urlGenerator, |
||
| 130 | IUserManager $userManager, |
||
| 131 | ILogger $logger, |
||
| 132 | \OCP\Activity\IManager $activityManager, |
||
| 133 | ShareManager $shareManager, |
||
| 134 | ISession $session, |
||
| 135 | IPreview $previewManager, |
||
| 136 | IRootFolder $rootFolder, |
||
| 137 | FederatedShareProvider $federatedShareProvider, |
||
| 138 | EventDispatcherInterface $eventDispatcher, |
||
| 139 | IL10N $l10n, |
||
| 140 | Defaults $defaults) { |
||
| 141 | parent::__construct($appName, $request, $session, $urlGenerator); |
||
| 142 | |||
| 143 | $this->config = $config; |
||
| 144 | $this->userManager = $userManager; |
||
| 145 | $this->logger = $logger; |
||
| 146 | $this->activityManager = $activityManager; |
||
| 147 | $this->previewManager = $previewManager; |
||
| 148 | $this->rootFolder = $rootFolder; |
||
| 149 | $this->federatedShareProvider = $federatedShareProvider; |
||
| 150 | $this->eventDispatcher = $eventDispatcher; |
||
| 151 | $this->l10n = $l10n; |
||
| 152 | $this->defaults = $defaults; |
||
| 153 | $this->shareManager = $shareManager; |
||
| 154 | } |
||
| 155 | |||
| 156 | /** |
||
| 157 | * @PublicPage |
||
| 158 | * @NoCSRFRequired |
||
| 159 | * |
||
| 160 | * Show the authentication page |
||
| 161 | * The form has to submit to the authenticate method route |
||
| 162 | */ |
||
| 163 | public function showAuthenticate(): TemplateResponse { |
||
| 164 | $templateParameters = ['share' => $this->share]; |
||
| 165 | |||
| 166 | $event = new GenericEvent(null, $templateParameters); |
||
| 167 | $this->eventDispatcher->dispatch('OCA\Files_Sharing::loadAdditionalScripts::publicShareAuth', $event); |
||
| 168 | |||
| 169 | $response = new TemplateResponse('core', 'publicshareauth', $templateParameters, 'guest'); |
||
| 170 | if ($this->share->getSendPasswordByTalk()) { |
||
| 171 | $csp = new ContentSecurityPolicy(); |
||
| 172 | $csp->addAllowedConnectDomain('*'); |
||
| 173 | $csp->addAllowedMediaDomain('blob:'); |
||
| 174 | $response->setContentSecurityPolicy($csp); |
||
| 175 | } |
||
| 176 | |||
| 177 | return $response; |
||
| 178 | } |
||
| 179 | |||
| 180 | /** |
||
| 181 | * The template to show when authentication failed |
||
| 182 | */ |
||
| 183 | protected function showAuthFailed(): TemplateResponse { |
||
| 184 | $templateParameters = ['share' => $this->share, 'wrongpw' => true]; |
||
| 185 | |||
| 186 | $event = new GenericEvent(null, $templateParameters); |
||
| 187 | $this->eventDispatcher->dispatch('OCA\Files_Sharing::loadAdditionalScripts::publicShareAuth', $event); |
||
| 188 | |||
| 189 | $response = new TemplateResponse('core', 'publicshareauth', $templateParameters, 'guest'); |
||
| 190 | if ($this->share->getSendPasswordByTalk()) { |
||
| 191 | $csp = new ContentSecurityPolicy(); |
||
| 192 | $csp->addAllowedConnectDomain('*'); |
||
| 193 | $csp->addAllowedMediaDomain('blob:'); |
||
| 194 | $response->setContentSecurityPolicy($csp); |
||
| 195 | } |
||
| 196 | |||
| 197 | return $response; |
||
| 198 | } |
||
| 199 | |||
| 200 | protected function verifyPassword(string $password): bool { |
||
| 201 | return $this->shareManager->checkPassword($this->share, $password); |
||
| 202 | } |
||
| 203 | |||
| 204 | protected function getPasswordHash(): string { |
||
| 205 | return $this->share->getPassword(); |
||
| 206 | } |
||
| 207 | |||
| 208 | public function isValidToken(): bool { |
||
| 209 | try { |
||
| 210 | $this->share = $this->shareManager->getShareByToken($this->getToken()); |
||
| 211 | } catch (ShareNotFound $e) { |
||
| 212 | return false; |
||
| 213 | } |
||
| 214 | |||
| 215 | return true; |
||
| 216 | } |
||
| 217 | |||
| 218 | protected function isPasswordProtected(): bool { |
||
| 219 | return $this->share->getPassword() !== null; |
||
| 220 | } |
||
| 221 | |||
| 222 | protected function authSucceeded() { |
||
| 223 | // For share this was always set so it is still used in other apps |
||
| 224 | $this->session->set('public_link_authenticated', (string)$this->share->getId()); |
||
| 225 | } |
||
| 226 | |||
| 227 | protected function authFailed() { |
||
| 228 | $this->emitAccessShareHook($this->share, 403, 'Wrong password'); |
||
| 229 | } |
||
| 230 | |||
| 231 | /** |
||
| 232 | * throws hooks when a share is attempted to be accessed |
||
| 233 | * |
||
| 234 | * @param \OCP\Share\IShare|string $share the Share instance if available, |
||
| 235 | * otherwise token |
||
| 236 | * @param int $errorCode |
||
| 237 | * @param string $errorMessage |
||
| 238 | * @throws \OC\HintException |
||
| 239 | * @throws \OC\ServerNotAvailableException |
||
| 240 | */ |
||
| 241 | protected function emitAccessShareHook($share, $errorCode = 200, $errorMessage = '') { |
||
| 266 | } |
||
| 267 | } |
||
| 268 | |||
| 269 | /** |
||
| 270 | * Validate the permissions of the share |
||
| 271 | * |
||
| 272 | * @param Share\IShare $share |
||
| 273 | * @return bool |
||
| 274 | */ |
||
| 275 | private function validateShare(\OCP\Share\IShare $share) { |
||
| 276 | // If the owner is disabled no access to the linke is granted |
||
| 277 | $owner = $this->userManager->get($share->getShareOwner()); |
||
| 278 | if ($owner === null || !$owner->isEnabled()) { |
||
| 279 | return false; |
||
| 280 | } |
||
| 281 | |||
| 282 | // If the initiator of the share is disabled no access is granted |
||
| 283 | $initiator = $this->userManager->get($share->getSharedBy()); |
||
| 284 | if ($initiator === null || !$initiator->isEnabled()) { |
||
| 285 | return false; |
||
| 286 | } |
||
| 287 | |||
| 288 | return $share->getNode()->isReadable() && $share->getNode()->isShareable(); |
||
| 289 | } |
||
| 290 | |||
| 291 | /** |
||
| 292 | * @PublicPage |
||
| 293 | * @NoCSRFRequired |
||
| 294 | * |
||
| 295 | |||
| 296 | * @param string $path |
||
| 297 | * @return TemplateResponse |
||
| 298 | * @throws NotFoundException |
||
| 299 | * @throws \Exception |
||
| 300 | */ |
||
| 301 | public function showShare($path = ''): TemplateResponse { |
||
| 302 | \OC_User::setIncognitoMode(true); |
||
| 303 | |||
| 304 | // Check whether share exists |
||
| 305 | try { |
||
| 306 | $share = $this->shareManager->getShareByToken($this->getToken()); |
||
| 307 | } catch (ShareNotFound $e) { |
||
| 308 | $this->emitAccessShareHook($this->getToken(), 404, 'Share not found'); |
||
| 309 | throw new NotFoundException(); |
||
| 310 | } |
||
| 311 | |||
| 312 | if (!$this->validateShare($share)) { |
||
| 313 | throw new NotFoundException(); |
||
| 314 | } |
||
| 315 | |||
| 316 | $shareNode = $share->getNode(); |
||
| 317 | |||
| 318 | // We can't get the path of a file share |
||
| 319 | try { |
||
| 320 | if ($shareNode instanceof \OCP\Files\File && $path !== '') { |
||
| 321 | $this->emitAccessShareHook($share, 404, 'Share not found'); |
||
| 322 | throw new NotFoundException(); |
||
| 323 | } |
||
| 324 | } catch (\Exception $e) { |
||
| 325 | $this->emitAccessShareHook($share, 404, 'Share not found'); |
||
| 326 | throw $e; |
||
| 327 | } |
||
| 328 | |||
| 329 | $shareTmpl = []; |
||
| 330 | $shareTmpl['displayName'] = $this->userManager->get($share->getShareOwner())->getDisplayName(); |
||
| 331 | $shareTmpl['owner'] = $share->getShareOwner(); |
||
| 332 | $shareTmpl['filename'] = $shareNode->getName(); |
||
| 333 | $shareTmpl['directory_path'] = $share->getTarget(); |
||
| 334 | $shareTmpl['note'] = $share->getNote(); |
||
| 335 | $shareTmpl['mimetype'] = $shareNode->getMimetype(); |
||
| 336 | $shareTmpl['previewSupported'] = $this->previewManager->isMimeSupported($shareNode->getMimetype()); |
||
| 337 | $shareTmpl['dirToken'] = $this->getToken(); |
||
| 338 | $shareTmpl['sharingToken'] = $this->getToken(); |
||
| 339 | $shareTmpl['server2serversharing'] = $this->federatedShareProvider->isOutgoingServer2serverShareEnabled(); |
||
| 340 | $shareTmpl['protected'] = $share->getPassword() !== null ? 'true' : 'false'; |
||
| 341 | $shareTmpl['dir'] = ''; |
||
| 342 | $shareTmpl['nonHumanFileSize'] = $shareNode->getSize(); |
||
| 343 | $shareTmpl['fileSize'] = \OCP\Util::humanFileSize($shareNode->getSize()); |
||
| 344 | $shareTmpl['hideDownload'] = $share->getHideDownload(); |
||
| 345 | |||
| 346 | $hideFileList = false; |
||
| 347 | |||
| 348 | if ($shareNode instanceof \OCP\Files\Folder) { |
||
| 349 | |||
| 350 | $shareIsFolder = true; |
||
| 351 | |||
| 352 | try { |
||
| 353 | $folderNode = $shareNode->get($path); |
||
| 354 | } catch (\OCP\Files\NotFoundException $e) { |
||
| 355 | $this->emitAccessShareHook($share, 404, 'Share not found'); |
||
| 356 | throw new NotFoundException(); |
||
| 357 | } |
||
| 358 | |||
| 359 | $shareTmpl['dir'] = $shareNode->getRelativePath($folderNode->getPath()); |
||
| 360 | |||
| 361 | /* |
||
| 362 | * The OC_Util methods require a view. This just uses the node API |
||
| 363 | */ |
||
| 364 | $freeSpace = $share->getNode()->getStorage()->free_space($share->getNode()->getInternalPath()); |
||
| 365 | if ($freeSpace < \OCP\Files\FileInfo::SPACE_UNLIMITED) { |
||
| 366 | $freeSpace = max($freeSpace, 0); |
||
| 367 | } else { |
||
| 368 | $freeSpace = (INF > 0) ? INF: PHP_INT_MAX; // work around https://bugs.php.net/bug.php?id=69188 |
||
| 369 | } |
||
| 370 | |||
| 371 | $hideFileList = !($share->getPermissions() & \OCP\Constants::PERMISSION_READ); |
||
| 372 | $maxUploadFilesize = $freeSpace; |
||
| 373 | |||
| 374 | $folder = new Template('files', 'list', ''); |
||
| 375 | |||
| 376 | $folder->assign('dir', $shareNode->getRelativePath($folderNode->getPath())); |
||
| 377 | $folder->assign('dirToken', $this->getToken()); |
||
| 378 | $folder->assign('permissions', \OCP\Constants::PERMISSION_READ); |
||
| 379 | $folder->assign('isPublic', true); |
||
| 380 | $folder->assign('hideFileList', $hideFileList); |
||
| 381 | $folder->assign('publicUploadEnabled', 'no'); |
||
| 382 | // default to list view |
||
| 383 | $folder->assign('showgridview', false); |
||
| 384 | $folder->assign('uploadMaxFilesize', $maxUploadFilesize); |
||
| 385 | $folder->assign('uploadMaxHumanFilesize', \OCP\Util::humanFileSize($maxUploadFilesize)); |
||
| 386 | $folder->assign('freeSpace', $freeSpace); |
||
| 387 | $folder->assign('usedSpacePercent', 0); |
||
| 388 | $folder->assign('trash', false); |
||
| 389 | $shareTmpl['folder'] = $folder->fetchPage(); |
||
| 390 | } else { |
||
| 391 | $shareIsFolder = false; |
||
| 392 | } |
||
| 393 | |||
| 394 | // default to list view |
||
| 395 | $shareTmpl['showgridview'] = false; |
||
| 396 | |||
| 397 | $shareTmpl['hideFileList'] = $hideFileList; |
||
| 398 | $shareTmpl['shareOwner'] = $this->userManager->get($share->getShareOwner())->getDisplayName(); |
||
| 399 | $shareTmpl['downloadURL'] = $this->urlGenerator->linkToRouteAbsolute('files_sharing.sharecontroller.downloadShare', ['token' => $this->getToken()]); |
||
| 400 | $shareTmpl['shareUrl'] = $this->urlGenerator->linkToRouteAbsolute('files_sharing.sharecontroller.showShare', ['token' => $this->getToken()]); |
||
| 401 | $shareTmpl['maxSizeAnimateGif'] = $this->config->getSystemValue('max_filesize_animated_gifs_public_sharing', 10); |
||
| 402 | $shareTmpl['previewEnabled'] = $this->config->getSystemValue('enable_previews', true); |
||
| 403 | $shareTmpl['previewMaxX'] = $this->config->getSystemValue('preview_max_x', 1024); |
||
| 404 | $shareTmpl['previewMaxY'] = $this->config->getSystemValue('preview_max_y', 1024); |
||
| 405 | $shareTmpl['disclaimer'] = $this->config->getAppValue('core', 'shareapi_public_link_disclaimertext', null); |
||
| 406 | $shareTmpl['previewURL'] = $shareTmpl['downloadURL']; |
||
| 407 | |||
| 408 | if ($shareTmpl['previewSupported']) { |
||
| 409 | $shareTmpl['previewImage'] = $this->urlGenerator->linkToRouteAbsolute( 'files_sharing.PublicPreview.getPreview', |
||
| 410 | ['x' => 200, 'y' => 200, 'file' => $shareTmpl['directory_path'], 'token' => $shareTmpl['dirToken']]); |
||
| 411 | $ogPreview = $shareTmpl['previewImage']; |
||
| 412 | |||
| 413 | // We just have direct previews for image files |
||
| 414 | if ($shareNode->getMimePart() === 'image') { |
||
| 415 | $shareTmpl['previewURL'] = $this->urlGenerator->linkToRouteAbsolute('files_sharing.publicpreview.directLink', ['token' => $this->getToken()]); |
||
| 416 | |||
| 417 | $ogPreview = $shareTmpl['previewURL']; |
||
| 418 | |||
| 419 | //Whatapp is kind of picky about their size requirements |
||
| 420 | if ($this->request->isUserAgent(['/^WhatsApp/'])) { |
||
| 421 | $ogPreview = $this->urlGenerator->linkToRouteAbsolute('files_sharing.PublicPreview.getPreview', [ |
||
| 422 | 'token' => $this->getToken(), |
||
| 423 | 'x' => 256, |
||
| 424 | 'y' => 256, |
||
| 425 | 'a' => true, |
||
| 426 | ]); |
||
| 427 | } |
||
| 428 | } |
||
| 429 | } else { |
||
| 430 | $shareTmpl['previewImage'] = $this->urlGenerator->getAbsoluteURL($this->urlGenerator->imagePath('core', 'favicon-fb.png')); |
||
| 431 | $ogPreview = $shareTmpl['previewImage']; |
||
| 432 | } |
||
| 433 | |||
| 434 | // Load files we need |
||
| 435 | \OCP\Util::addScript('files', 'semaphore'); |
||
| 436 | \OCP\Util::addScript('files', 'file-upload'); |
||
| 437 | \OCP\Util::addStyle('files_sharing', 'publicView'); |
||
| 438 | \OCP\Util::addScript('files_sharing', 'public'); |
||
| 439 | \OCP\Util::addScript('files_sharing', 'templates'); |
||
| 440 | \OCP\Util::addScript('files', 'fileactions'); |
||
| 441 | \OCP\Util::addScript('files', 'fileactionsmenu'); |
||
| 442 | \OCP\Util::addScript('files', 'jquery.fileupload'); |
||
| 443 | \OCP\Util::addScript('files_sharing', 'files_drop'); |
||
| 444 | |||
| 445 | if (isset($shareTmpl['folder'])) { |
||
| 446 | // JS required for folders |
||
| 447 | \OCP\Util::addStyle('files', 'merged'); |
||
| 448 | \OCP\Util::addScript('files', 'filesummary'); |
||
| 449 | \OCP\Util::addScript('files', 'templates'); |
||
| 450 | \OCP\Util::addScript('files', 'breadcrumb'); |
||
| 451 | \OCP\Util::addScript('files', 'fileinfomodel'); |
||
| 452 | \OCP\Util::addScript('files', 'newfilemenu'); |
||
| 453 | \OCP\Util::addScript('files', 'files'); |
||
| 454 | \OCP\Util::addScript('files', 'filemultiselectmenu'); |
||
| 455 | \OCP\Util::addScript('files', 'filelist'); |
||
| 456 | \OCP\Util::addScript('files', 'keyboardshortcuts'); |
||
| 457 | \OCP\Util::addScript('files', 'operationprogressbar'); |
||
| 458 | |||
| 459 | // Load Viewer scripts |
||
| 460 | if (class_exists(LoadViewer::class)) { |
||
| 461 | $this->eventDispatcher->dispatch(LoadViewer::class, new LoadViewer()); |
||
| 462 | } |
||
| 463 | } |
||
| 464 | |||
| 465 | // OpenGraph Support: http://ogp.me/ |
||
| 466 | \OCP\Util::addHeader('meta', ['property' => "og:title", 'content' => $shareTmpl['filename']]); |
||
| 467 | \OCP\Util::addHeader('meta', ['property' => "og:description", 'content' => $this->defaults->getName() . ($this->defaults->getSlogan() !== '' ? ' - ' . $this->defaults->getSlogan() : '')]); |
||
| 468 | \OCP\Util::addHeader('meta', ['property' => "og:site_name", 'content' => $this->defaults->getName()]); |
||
| 469 | \OCP\Util::addHeader('meta', ['property' => "og:url", 'content' => $shareTmpl['shareUrl']]); |
||
| 470 | \OCP\Util::addHeader('meta', ['property' => "og:type", 'content' => "object"]); |
||
| 471 | \OCP\Util::addHeader('meta', ['property' => "og:image", 'content' => $ogPreview]); |
||
| 472 | |||
| 473 | $event = new GenericEvent(null, ['share' => $share]); |
||
| 474 | $this->eventDispatcher->dispatch('OCA\Files_Sharing::loadAdditionalScripts', $event); |
||
| 475 | |||
| 476 | $csp = new \OCP\AppFramework\Http\ContentSecurityPolicy(); |
||
| 477 | $csp->addAllowedFrameDomain('\'self\''); |
||
| 478 | |||
| 479 | $response = new PublicTemplateResponse($this->appName, 'public', $shareTmpl); |
||
| 480 | $response->setHeaderTitle($shareTmpl['filename']); |
||
| 481 | $response->setHeaderDetails($this->l10n->t('shared by %s', [$shareTmpl['displayName']])); |
||
| 482 | |||
| 483 | $isNoneFileDropFolder = $shareIsFolder === false || $share->getPermissions() !== \OCP\Constants::PERMISSION_CREATE; |
||
| 484 | |||
| 485 | if ($isNoneFileDropFolder && !$share->getHideDownload()) { |
||
| 486 | \OCP\Util::addScript('files_sharing', 'public_note'); |
||
| 487 | |||
| 488 | $downloadWhite = new SimpleMenuAction('download', $this->l10n->t('Download'), 'icon-download-white', $shareTmpl['downloadURL'], 0); |
||
| 489 | $downloadAllWhite = new SimpleMenuAction('download', $this->l10n->t('Download all files'), 'icon-download-white', $shareTmpl['downloadURL'], 0); |
||
| 490 | $download = new SimpleMenuAction('download', $this->l10n->t('Download'), 'icon-download', $shareTmpl['downloadURL'], 10, $shareTmpl['fileSize']); |
||
| 491 | $downloadAll = new SimpleMenuAction('download', $this->l10n->t('Download all files'), 'icon-download', $shareTmpl['downloadURL'], 10, $shareTmpl['fileSize']); |
||
| 492 | $directLink = new LinkMenuAction($this->l10n->t('Direct link'), 'icon-public', $shareTmpl['previewURL']); |
||
| 493 | $externalShare = new ExternalShareMenuAction($this->l10n->t('Add to your Nextcloud'), 'icon-external', $shareTmpl['owner'], $shareTmpl['displayName'], $shareTmpl['filename']); |
||
| 494 | |||
| 495 | $responseComposer = []; |
||
| 496 | |||
| 497 | if ($shareIsFolder) { |
||
| 498 | $responseComposer[] = $downloadAllWhite; |
||
| 499 | $responseComposer[] = $downloadAll; |
||
| 500 | } else { |
||
| 501 | $responseComposer[] = $downloadWhite; |
||
| 502 | $responseComposer[] = $download; |
||
| 503 | } |
||
| 504 | $responseComposer[] = $directLink; |
||
| 505 | if ($this->federatedShareProvider->isOutgoingServer2serverShareEnabled()) { |
||
| 506 | $responseComposer[] = $externalShare; |
||
| 507 | } |
||
| 508 | |||
| 509 | $response->setHeaderActions($responseComposer); |
||
| 510 | } |
||
| 511 | |||
| 512 | $response->setContentSecurityPolicy($csp); |
||
| 513 | |||
| 514 | $this->emitAccessShareHook($share); |
||
| 515 | |||
| 516 | return $response; |
||
| 517 | } |
||
| 518 | |||
| 519 | /** |
||
| 520 | * @PublicPage |
||
| 521 | * @NoCSRFRequired |
||
| 522 | * |
||
| 523 | * @param string $token |
||
| 524 | * @param string $files |
||
| 525 | * @param string $path |
||
| 526 | * @param string $downloadStartSecret |
||
| 527 | * @return void|\OCP\AppFramework\Http\Response |
||
| 528 | * @throws NotFoundException |
||
| 529 | */ |
||
| 530 | public function downloadShare($token, $files = null, $path = '', $downloadStartSecret = '') { |
||
| 531 | \OC_User::setIncognitoMode(true); |
||
| 532 | |||
| 533 | $share = $this->shareManager->getShareByToken($token); |
||
| 534 | |||
| 535 | if(!($share->getPermissions() & \OCP\Constants::PERMISSION_READ)) { |
||
| 536 | return new \OCP\AppFramework\Http\DataResponse('Share is read-only'); |
||
| 537 | } |
||
| 538 | |||
| 539 | $files_list = null; |
||
| 540 | if (!is_null($files)) { // download selected files |
||
| 541 | $files_list = json_decode($files); |
||
| 542 | // in case we get only a single file |
||
| 543 | if ($files_list === null) { |
||
| 544 | $files_list = [$files]; |
||
| 545 | } |
||
| 546 | // Just in case $files is a single int like '1234' |
||
| 547 | if (!is_array($files_list)) { |
||
| 548 | $files_list = [$files_list]; |
||
| 549 | } |
||
| 550 | } |
||
| 551 | |||
| 552 | if (!$this->validateShare($share)) { |
||
| 553 | throw new NotFoundException(); |
||
| 554 | } |
||
| 555 | |||
| 556 | $userFolder = $this->rootFolder->getUserFolder($share->getShareOwner()); |
||
| 557 | $originalSharePath = $userFolder->getRelativePath($share->getNode()->getPath()); |
||
| 558 | |||
| 559 | |||
| 560 | // Single file share |
||
| 561 | if ($share->getNode() instanceof \OCP\Files\File) { |
||
| 562 | // Single file download |
||
| 563 | $this->singleFileDownloaded($share, $share->getNode()); |
||
| 564 | } |
||
| 565 | // Directory share |
||
| 566 | else { |
||
| 567 | /** @var \OCP\Files\Folder $node */ |
||
| 568 | $node = $share->getNode(); |
||
| 569 | |||
| 570 | // Try to get the path |
||
| 571 | if ($path !== '') { |
||
| 572 | try { |
||
| 573 | $node = $node->get($path); |
||
| 574 | } catch (NotFoundException $e) { |
||
| 575 | $this->emitAccessShareHook($share, 404, 'Share not found'); |
||
| 576 | return new NotFoundResponse(); |
||
| 577 | } |
||
| 578 | } |
||
| 579 | |||
| 580 | $originalSharePath = $userFolder->getRelativePath($node->getPath()); |
||
| 581 | |||
| 582 | if ($node instanceof \OCP\Files\File) { |
||
| 583 | // Single file download |
||
| 584 | $this->singleFileDownloaded($share, $share->getNode()); |
||
| 585 | } else { |
||
| 586 | try { |
||
| 587 | if (!empty($files_list)) { |
||
| 588 | $this->fileListDownloaded($share, $files_list, $node); |
||
| 589 | } else { |
||
| 590 | // The folder is downloaded |
||
| 591 | $this->singleFileDownloaded($share, $share->getNode()); |
||
| 592 | } |
||
| 593 | } catch (NotFoundException $e) { |
||
| 594 | return new NotFoundResponse(); |
||
| 595 | } |
||
| 596 | } |
||
| 597 | } |
||
| 598 | |||
| 599 | /* FIXME: We should do this all nicely in OCP */ |
||
| 600 | OC_Util::tearDownFS(); |
||
| 601 | OC_Util::setupFS($share->getShareOwner()); |
||
| 602 | |||
| 603 | /** |
||
| 604 | * this sets a cookie to be able to recognize the start of the download |
||
| 605 | * the content must not be longer than 32 characters and must only contain |
||
| 606 | * alphanumeric characters |
||
| 607 | */ |
||
| 608 | if (!empty($downloadStartSecret) |
||
| 609 | && !isset($downloadStartSecret[32]) |
||
| 610 | && preg_match('!^[a-zA-Z0-9]+$!', $downloadStartSecret) === 1) { |
||
| 611 | |||
| 612 | // FIXME: set on the response once we use an actual app framework response |
||
| 613 | setcookie('ocDownloadStarted', $downloadStartSecret, time() + 20, '/'); |
||
| 614 | } |
||
| 615 | |||
| 616 | $this->emitAccessShareHook($share); |
||
| 617 | |||
| 618 | $server_params = array( 'head' => $this->request->getMethod() === 'HEAD' ); |
||
| 619 | |||
| 620 | /** |
||
| 621 | * Http range requests support |
||
| 622 | */ |
||
| 623 | if (isset($_SERVER['HTTP_RANGE'])) { |
||
| 624 | $server_params['range'] = $this->request->getHeader('Range'); |
||
| 625 | } |
||
| 626 | |||
| 627 | // download selected files |
||
| 628 | if (!is_null($files) && $files !== '') { |
||
| 629 | // FIXME: The exit is required here because otherwise the AppFramework is trying to add headers as well |
||
| 630 | // after dispatching the request which results in a "Cannot modify header information" notice. |
||
| 631 | OC_Files::get($originalSharePath, $files_list, $server_params); |
||
| 632 | exit(); |
||
| 633 | } else { |
||
| 634 | // FIXME: The exit is required here because otherwise the AppFramework is trying to add headers as well |
||
| 635 | // after dispatching the request which results in a "Cannot modify header information" notice. |
||
| 636 | OC_Files::get(dirname($originalSharePath), basename($originalSharePath), $server_params); |
||
| 637 | exit(); |
||
| 638 | } |
||
| 639 | } |
||
| 640 | |||
| 641 | /** |
||
| 642 | * create activity for every downloaded file |
||
| 643 | * |
||
| 644 | * @param Share\IShare $share |
||
| 645 | * @param array $files_list |
||
| 646 | * @param \OCP\Files\Folder $node |
||
| 647 | * @throws NotFoundException when trying to download a folder or multiple files of a "hide download" share |
||
| 648 | */ |
||
| 649 | protected function fileListDownloaded(Share\IShare $share, array $files_list, \OCP\Files\Folder $node) { |
||
| 650 | if ($share->getHideDownload() && count($files_list) > 1) { |
||
| 651 | throw new NotFoundException('Downloading more than 1 file'); |
||
| 652 | } |
||
| 653 | |||
| 654 | foreach ($files_list as $file) { |
||
| 655 | $subNode = $node->get($file); |
||
| 656 | $this->singleFileDownloaded($share, $subNode); |
||
| 657 | } |
||
| 658 | |||
| 659 | } |
||
| 660 | |||
| 661 | /** |
||
| 662 | * create activity if a single file was downloaded from a link share |
||
| 663 | * |
||
| 664 | * @param Share\IShare $share |
||
| 665 | * @throws NotFoundException when trying to download a folder of a "hide download" share |
||
| 666 | */ |
||
| 667 | protected function singleFileDownloaded(Share\IShare $share, \OCP\Files\Node $node) { |
||
| 668 | if ($share->getHideDownload() && $node instanceof Folder) { |
||
| 669 | throw new NotFoundException('Downloading a folder'); |
||
| 670 | } |
||
| 671 | |||
| 672 | $fileId = $node->getId(); |
||
| 673 | |||
| 674 | $userFolder = $this->rootFolder->getUserFolder($share->getSharedBy()); |
||
| 675 | $userNodeList = $userFolder->getById($fileId); |
||
| 676 | $userNode = $userNodeList[0]; |
||
| 677 | $ownerFolder = $this->rootFolder->getUserFolder($share->getShareOwner()); |
||
| 678 | $userPath = $userFolder->getRelativePath($userNode->getPath()); |
||
| 679 | $ownerPath = $ownerFolder->getRelativePath($node->getPath()); |
||
| 680 | |||
| 681 | $parameters = [$userPath]; |
||
| 682 | |||
| 683 | if ($share->getShareType() === \OCP\Share::SHARE_TYPE_EMAIL) { |
||
| 684 | if ($node instanceof \OCP\Files\File) { |
||
| 685 | $subject = Downloads::SUBJECT_SHARED_FILE_BY_EMAIL_DOWNLOADED; |
||
| 686 | } else { |
||
| 687 | $subject = Downloads::SUBJECT_SHARED_FOLDER_BY_EMAIL_DOWNLOADED; |
||
| 688 | } |
||
| 689 | $parameters[] = $share->getSharedWith(); |
||
| 690 | } else { |
||
| 691 | if ($node instanceof \OCP\Files\File) { |
||
| 692 | $subject = Downloads::SUBJECT_PUBLIC_SHARED_FILE_DOWNLOADED; |
||
| 693 | } else { |
||
| 694 | $subject = Downloads::SUBJECT_PUBLIC_SHARED_FOLDER_DOWNLOADED; |
||
| 695 | } |
||
| 696 | } |
||
| 697 | |||
| 698 | $this->publishActivity($subject, $parameters, $share->getSharedBy(), $fileId, $userPath); |
||
| 699 | |||
| 700 | if ($share->getShareOwner() !== $share->getSharedBy()) { |
||
| 701 | $parameters[0] = $ownerPath; |
||
| 702 | $this->publishActivity($subject, $parameters, $share->getShareOwner(), $fileId, $ownerPath); |
||
| 703 | } |
||
| 704 | } |
||
| 705 | |||
| 706 | /** |
||
| 707 | * publish activity |
||
| 708 | * |
||
| 709 | * @param string $subject |
||
| 710 | * @param array $parameters |
||
| 711 | * @param string $affectedUser |
||
| 712 | * @param int $fileId |
||
| 713 | * @param string $filePath |
||
| 714 | */ |
||
| 715 | protected function publishActivity($subject, |
||
| 728 | } |
||
| 729 | |||
| 730 | |||
| 731 | } |
||
| 732 |
The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g.
excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths