share = $share->load('files'); // A share at its download limit stays open for the recipient who took its last download. if (! $share->isCompleted() || $share->isExpired() || ($share->hasReachedDownloadLimit() && $shareService->downloadWindowEndsAt($share, session()->driver()) === null)) { abort(404); } $this->authenticated = ! $share->isPasswordProtected() || (bool) session('share_key_'.$share->token); } public function verifyPassword(ShareService $shareService): void { $rateLimitKey = 'share-password:'.$this->share->token.'|'.request()->ip(); if (RateLimiter::tooManyAttempts($rateLimitKey, 5)) { $seconds = RateLimiter::availableIn($rateLimitKey); $this->addError('password', __('Too many attempts. Please try again in :seconds seconds.', ['seconds' => $seconds])); return; } $this->validate(); if (! $shareService->verifyPassword($this->share, $this->password)) { RateLimiter::hit($rateLimitKey, 60); $this->addError('password', __('The password is incorrect.')); return; } RateLimiter::clear($rateLimitKey); $encryptionKey = $shareService->getDecryptionKey($this->share, $this->password); session(['share_key_'.$this->share->token => $encryptionKey]); $this->authenticated = true; } /** * The share's private text, for the recipient who pressed "Show text": counted as their * download, as a file would be. Returned to the browser only, never kept in a property, so it * is not in the component's snapshot. Null when the text is no longer available; no abort(), * which would open Livewire's error modal. Renderless: a re-render would morph the download * note Alpine just switched back into the one it hid. */ #[Renderless] public function revealText(ShareService $shareService): ?string { $share = $this->share; if (! $share->isCompleted() || $share->isExpired() || ! $share->hasText() || ($share->isPasswordProtected() && ! session('share_key_'.$share->token)) || ! $shareService->claimDownload($share, session()->driver())) { return null; } return $shareService->readText($share, $shareService->sessionDecryptionKey($share, session()->driver())); } public function render(): mixed { $shareService = app(ShareService::class); return view('livewire.share-download', [ 'files' => $this->share->files->where('is_text', false)->values(), 'hasText' => $this->share->files->contains('is_text', true), 'downloadWindowEndsAt' => $shareService->downloadWindowEndsAt($this->share, session()->driver()), 'remainingDownloads' => $this->share->max_downloads ? max($this->share->max_downloads - $this->share->download_count, 0) : null, 'downloadWindow' => CarbonInterval::minutes(ShareService::DOWNLOAD_WINDOW_MINUTES)->cascade()->forHumans(), ]); } }