2022-11-07 03:36:11 +00:00
|
|
|
<?php
|
|
|
|
|
2023-10-15 02:20:21 +00:00
|
|
|
declare(strict_types=1);
|
|
|
|
|
2022-11-07 03:36:11 +00:00
|
|
|
namespace OCA\Memories\Controller;
|
|
|
|
|
2023-03-10 02:47:59 +00:00
|
|
|
use OCA\Memories\AppInfo\Application;
|
2023-03-24 04:57:54 +00:00
|
|
|
use OCA\Memories\Db\FsManager;
|
2023-03-10 17:30:56 +00:00
|
|
|
use OCA\Memories\Db\TimelineQuery;
|
2023-04-16 23:03:59 +00:00
|
|
|
use OCA\Memories\Util;
|
2022-11-07 03:36:11 +00:00
|
|
|
use OCP\AppFramework\AuthPublicShareController;
|
2022-11-14 02:28:43 +00:00
|
|
|
use OCP\AppFramework\Http\Template\PublicTemplateResponse;
|
2022-11-07 03:36:11 +00:00
|
|
|
use OCP\AppFramework\Http\TemplateResponse;
|
|
|
|
use OCP\AppFramework\Services\IInitialState;
|
|
|
|
use OCP\EventDispatcher\IEventDispatcher;
|
2023-03-10 02:47:59 +00:00
|
|
|
use OCP\Files\IRootFolder;
|
2022-11-07 03:36:11 +00:00
|
|
|
use OCP\Files\NotFoundException;
|
|
|
|
use OCP\IConfig;
|
|
|
|
use OCP\IRequest;
|
|
|
|
use OCP\ISession;
|
|
|
|
use OCP\IURLGenerator;
|
2023-03-10 02:47:59 +00:00
|
|
|
use OCP\IUserSession;
|
2022-11-07 03:36:11 +00:00
|
|
|
use OCP\Share\IManager as IShareManager;
|
|
|
|
use OCP\Share\IShare;
|
|
|
|
|
|
|
|
class PublicController extends AuthPublicShareController
|
|
|
|
{
|
2023-10-15 19:46:35 +00:00
|
|
|
/** @psalm-suppress PropertyNotSetInConstructor */
|
2022-11-07 03:36:11 +00:00
|
|
|
protected IShare $share;
|
|
|
|
|
|
|
|
public function __construct(
|
|
|
|
IRequest $request,
|
|
|
|
ISession $session,
|
|
|
|
IURLGenerator $urlGenerator,
|
2023-10-15 01:51:17 +00:00
|
|
|
protected IEventDispatcher $eventDispatcher,
|
|
|
|
protected IInitialState $initialState,
|
|
|
|
protected IUserSession $userSession,
|
|
|
|
protected IRootFolder $rootFolder,
|
|
|
|
protected IShareManager $shareManager,
|
|
|
|
protected IConfig $config,
|
2023-10-15 01:59:00 +00:00
|
|
|
protected TimelineQuery $tq,
|
2022-11-07 03:36:11 +00:00
|
|
|
) {
|
2023-10-15 01:51:17 +00:00
|
|
|
parent::__construct(Application::APPNAME, $request, $session, $urlGenerator);
|
2022-11-07 03:36:11 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @PublicPage
|
|
|
|
*
|
|
|
|
* @NoCSRFRequired
|
|
|
|
*
|
|
|
|
* Show the authentication page
|
|
|
|
* The form has to submit to the authenticate method route
|
|
|
|
*/
|
|
|
|
public function showAuthenticate(): TemplateResponse
|
|
|
|
{
|
2023-03-10 02:47:59 +00:00
|
|
|
$this->redirectIfOwned($this->share);
|
|
|
|
|
2022-11-07 03:36:11 +00:00
|
|
|
$templateParameters = ['share' => $this->share];
|
|
|
|
|
|
|
|
return new TemplateResponse('core', 'publicshareauth', $templateParameters, 'guest');
|
|
|
|
}
|
|
|
|
|
|
|
|
public function isValidToken(): bool
|
|
|
|
{
|
|
|
|
try {
|
|
|
|
$this->share = $this->shareManager->getShareByToken($this->getToken());
|
|
|
|
|
|
|
|
return true;
|
2023-10-15 19:46:35 +00:00
|
|
|
} catch (\Exception) {
|
2022-11-07 03:36:11 +00:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @PublicPage
|
|
|
|
*
|
|
|
|
* @NoCSRFRequired
|
|
|
|
*/
|
|
|
|
public function showShare(): TemplateResponse
|
|
|
|
{
|
|
|
|
// Check whether share exists
|
|
|
|
try {
|
|
|
|
$share = $this->shareManager->getShareByToken($this->getToken());
|
2023-10-15 19:46:35 +00:00
|
|
|
} catch (\Exception) {
|
2022-11-07 03:36:11 +00:00
|
|
|
throw new NotFoundException();
|
|
|
|
}
|
|
|
|
|
2023-03-24 04:57:54 +00:00
|
|
|
if (!FsManager::validateShare($share)) {
|
2022-11-07 03:36:11 +00:00
|
|
|
throw new NotFoundException();
|
|
|
|
}
|
|
|
|
|
2023-03-10 02:47:59 +00:00
|
|
|
// Redirect to main app if user owns this share
|
|
|
|
$this->redirectIfOwned($share);
|
|
|
|
|
|
|
|
// Set incognito mode
|
|
|
|
\OC_User::setIncognitoMode(true);
|
|
|
|
|
2022-11-07 03:36:11 +00:00
|
|
|
// Scripts
|
2023-10-15 01:51:17 +00:00
|
|
|
\OCP\Util::addScript(Application::APPNAME, 'memories-main');
|
2022-11-09 10:42:42 +00:00
|
|
|
|
2023-10-20 05:32:34 +00:00
|
|
|
// Get share node
|
|
|
|
$node = $share->getNode();
|
|
|
|
|
2022-12-03 05:25:24 +00:00
|
|
|
// Share info
|
|
|
|
$this->initialState->provideInitialState('no_download', $share->getHideDownload());
|
2023-10-20 05:32:34 +00:00
|
|
|
$this->initialState->provideInitialState('share_title', $node->getName());
|
2022-12-03 05:25:24 +00:00
|
|
|
|
2023-03-10 17:30:56 +00:00
|
|
|
if ($node instanceof \OCP\Files\File) {
|
|
|
|
$this->initialState->provideInitialState('single_item', $this->getSingleItemInitialState($node));
|
2023-10-20 05:32:34 +00:00
|
|
|
$this->initialState->provideInitialState('share_type', 'file');
|
2023-05-25 06:10:57 +00:00
|
|
|
} elseif ($node instanceof \OCP\Files\Folder) {
|
2023-10-20 05:32:34 +00:00
|
|
|
$this->initialState->provideInitialState('share_type', 'folder');
|
2023-05-25 04:59:03 +00:00
|
|
|
} else {
|
|
|
|
throw new NotFoundException();
|
2023-03-10 17:30:56 +00:00
|
|
|
}
|
|
|
|
|
2023-03-14 20:20:24 +00:00
|
|
|
// Add OG metadata
|
|
|
|
$params = ['token' => $this->getToken()];
|
|
|
|
$url = $this->urlGenerator->linkToRouteAbsolute('memories.Public.showShare', $params);
|
2023-04-16 23:03:59 +00:00
|
|
|
Util::addOgMetadata($node, $node->getName(), $url, $params);
|
2023-03-14 20:20:24 +00:00
|
|
|
|
|
|
|
// Render the template
|
2023-05-21 04:53:05 +00:00
|
|
|
$response = new PublicTemplateResponse($this->appName, 'main', PageController::getMainParams());
|
2023-03-10 17:30:56 +00:00
|
|
|
$response->setHeaderTitle($node->getName());
|
2023-01-04 20:38:44 +00:00
|
|
|
$response->setFooterVisible(false); // wth is that anyway?
|
2023-01-18 03:09:02 +00:00
|
|
|
$response->setContentSecurityPolicy(PageController::getCSP());
|
2023-02-09 08:57:37 +00:00
|
|
|
$response->cacheFor(0);
|
2022-11-07 03:36:11 +00:00
|
|
|
|
|
|
|
return $response;
|
|
|
|
}
|
|
|
|
|
|
|
|
protected function showAuthFailed(): TemplateResponse
|
|
|
|
{
|
|
|
|
$templateParameters = ['share' => $this->share, 'wrongpw' => true];
|
|
|
|
|
|
|
|
return new TemplateResponse('core', 'publicshareauth', $templateParameters, 'guest');
|
|
|
|
}
|
|
|
|
|
|
|
|
protected function verifyPassword(string $password): bool
|
|
|
|
{
|
|
|
|
return $this->shareManager->checkPassword($this->share, $password);
|
|
|
|
}
|
|
|
|
|
|
|
|
protected function getPasswordHash(): string
|
|
|
|
{
|
|
|
|
return $this->share->getPassword();
|
|
|
|
}
|
|
|
|
|
|
|
|
protected function isPasswordProtected(): bool
|
|
|
|
{
|
2023-10-15 19:46:35 +00:00
|
|
|
/** @psalm-suppress RedundantConditionGivenDocblockType */
|
2022-11-07 03:36:11 +00:00
|
|
|
return null !== $this->share->getPassword();
|
|
|
|
}
|
2023-03-10 02:47:59 +00:00
|
|
|
|
2023-10-14 09:07:18 +00:00
|
|
|
protected function redirectIfOwned(IShare $share): void
|
2023-03-10 02:47:59 +00:00
|
|
|
{
|
|
|
|
$user = $this->userSession->getUser();
|
|
|
|
if (!$user) {
|
2023-10-14 09:07:18 +00:00
|
|
|
return;
|
2023-03-10 02:47:59 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/** @var \OCP\Files\Node */
|
|
|
|
$node = null;
|
|
|
|
|
|
|
|
/** @var \OCP\Files\Folder */
|
|
|
|
$userFolder = null;
|
|
|
|
|
|
|
|
// Check if the user has this folder in their root
|
|
|
|
try {
|
|
|
|
$userFolder = $this->rootFolder->getUserFolder($user->getUID());
|
|
|
|
$nodes = $userFolder->getById($share->getNodeId());
|
|
|
|
if (0 === \count($nodes)) {
|
2023-10-14 09:07:18 +00:00
|
|
|
return;
|
2023-03-10 02:47:59 +00:00
|
|
|
}
|
|
|
|
$node = $nodes[0];
|
|
|
|
} catch (NotFoundException $e) {
|
2023-10-14 09:07:18 +00:00
|
|
|
return;
|
2023-03-10 02:47:59 +00:00
|
|
|
}
|
|
|
|
|
2023-03-10 02:55:22 +00:00
|
|
|
// Check if node is a folder
|
|
|
|
if (!$node instanceof \OCP\Files\Folder) {
|
2023-10-14 09:07:18 +00:00
|
|
|
return;
|
2023-03-10 02:55:22 +00:00
|
|
|
}
|
|
|
|
|
2023-03-10 02:47:59 +00:00
|
|
|
// Remove user folder path from start of node path
|
|
|
|
$relPath = substr($node->getPath(), \strlen($userFolder->getPath()));
|
|
|
|
|
|
|
|
// Get the user's folders path
|
2023-08-30 20:25:28 +00:00
|
|
|
$foldersPath = $this->config->getUserValue($user->getUID(), Application::APPNAME, 'foldersPath', null) ?: '/';
|
|
|
|
|
|
|
|
// Sanitize folders path ensuring leading and trailing slashes
|
2023-08-30 20:27:43 +00:00
|
|
|
$foldersPath = Util::sanitizePath('/'.$foldersPath.'/');
|
2023-03-10 02:47:59 +00:00
|
|
|
|
|
|
|
// Check if relPath starts with foldersPath
|
2023-10-20 01:47:58 +00:00
|
|
|
if (empty($foldersPath) || !str_starts_with($relPath, $foldersPath)) {
|
2023-10-14 09:07:18 +00:00
|
|
|
return;
|
2023-03-10 02:47:59 +00:00
|
|
|
}
|
|
|
|
|
2023-10-20 01:47:58 +00:00
|
|
|
/** @var string $foldersPath */
|
2023-03-10 02:47:59 +00:00
|
|
|
// Remove foldersPath from start of relPath
|
|
|
|
$relPath = substr($relPath, \strlen($foldersPath));
|
|
|
|
|
|
|
|
// Redirect to the local path
|
2023-06-26 21:53:32 +00:00
|
|
|
$url = $this->urlGenerator->linkToRouteAbsolute('memories.Page.folder', [
|
|
|
|
'path' => $relPath, // path to folder
|
|
|
|
'noinit' => 1, // prevent showing first-start page
|
|
|
|
]);
|
2023-03-10 02:47:59 +00:00
|
|
|
|
|
|
|
// Cannot send a redirect response here because the return
|
|
|
|
// type is a template response for the base class
|
|
|
|
header('HTTP/1.1 302 Found');
|
|
|
|
header('Location: '.$url);
|
|
|
|
|
2023-04-20 21:12:42 +00:00
|
|
|
exit; // no other way to do this due to typing of super class
|
2023-03-10 02:47:59 +00:00
|
|
|
}
|
2023-03-10 17:30:56 +00:00
|
|
|
|
2023-06-29 15:42:56 +00:00
|
|
|
/**
|
|
|
|
* Get initial state of single item.
|
|
|
|
*
|
|
|
|
* @throws NotFoundException if file not found in index
|
|
|
|
*/
|
2023-03-25 14:58:03 +00:00
|
|
|
private function getSingleItemInitialState(\OCP\Files\File $file): array
|
2023-03-10 17:30:56 +00:00
|
|
|
{
|
2023-10-15 19:46:35 +00:00
|
|
|
return $this->tq->getSingleItem($file->getId())
|
|
|
|
?? throw new NotFoundException();
|
2023-03-10 17:30:56 +00:00
|
|
|
}
|
2022-11-07 03:36:11 +00:00
|
|
|
}
|