mirror of
https://github.com/nextcloud/richdocuments.git
synced 2025-12-17 21:12:14 +01:00
347 lines
9.9 KiB
PHP
347 lines
9.9 KiB
PHP
<?php
|
|
/**
|
|
* SPDX-FileCopyrightText: 2018 Nextcloud GmbH and Nextcloud contributors
|
|
* SPDX-License-Identifier: AGPL-3.0-or-later
|
|
*/
|
|
namespace OCA\Richdocuments\Controller;
|
|
|
|
use Exception;
|
|
use GuzzleHttp\Exception\BadResponseException;
|
|
use OCA\Richdocuments\Db\DirectMapper;
|
|
use OCA\Richdocuments\Exceptions\ExpiredTokenException;
|
|
use OCA\Richdocuments\Exceptions\UnknownTokenException;
|
|
use OCA\Richdocuments\Service\FederationService;
|
|
use OCA\Richdocuments\TemplateManager;
|
|
use OCA\Richdocuments\TokenManager;
|
|
use OCP\AppFramework\Http;
|
|
use OCP\AppFramework\Http\DataResponse;
|
|
use OCP\AppFramework\OCS\OCSBadRequestException;
|
|
use OCP\AppFramework\OCS\OCSException;
|
|
use OCP\AppFramework\OCS\OCSForbiddenException;
|
|
use OCP\AppFramework\OCS\OCSNotFoundException;
|
|
use OCP\Constants;
|
|
use OCP\Files\Folder;
|
|
use OCP\Files\IRootFolder;
|
|
use OCP\Files\NotFoundException;
|
|
use OCP\IRequest;
|
|
use OCP\IURLGenerator;
|
|
use OCP\Share\Exceptions\ShareNotFound;
|
|
use OCP\Share\IManager;
|
|
use Psr\Log\LoggerInterface;
|
|
|
|
class OCSController extends \OCP\AppFramework\OCSController {
|
|
/** @var IRootFolder */
|
|
private $rootFolder;
|
|
|
|
/** @var string */
|
|
private $userId;
|
|
|
|
/** @var DirectMapper */
|
|
private $directMapper;
|
|
|
|
/** @var IURLGenerator */
|
|
private $urlGenerator;
|
|
|
|
/** @var TemplateManager */
|
|
private $manager;
|
|
|
|
/** @var TokenManager */
|
|
private $tokenManager;
|
|
|
|
/** @var IManager */
|
|
private $shareManager;
|
|
|
|
/** @var FederationService */
|
|
private $federationService;
|
|
|
|
/** @var LoggerInterface */
|
|
private $logger;
|
|
|
|
public function __construct(string $appName,
|
|
IRequest $request,
|
|
IRootFolder $rootFolder,
|
|
$userId,
|
|
DirectMapper $directMapper,
|
|
IURLGenerator $urlGenerator,
|
|
TemplateManager $manager,
|
|
TokenManager $tokenManager,
|
|
IManager $shareManager,
|
|
FederationService $federationService,
|
|
LoggerInterface $logger
|
|
) {
|
|
parent::__construct($appName, $request);
|
|
|
|
$this->rootFolder = $rootFolder;
|
|
$this->userId = $userId;
|
|
$this->directMapper = $directMapper;
|
|
$this->urlGenerator = $urlGenerator;
|
|
$this->manager = $manager;
|
|
$this->tokenManager = $tokenManager;
|
|
$this->shareManager = $shareManager;
|
|
$this->federationService = $federationService;
|
|
$this->logger = $logger;
|
|
}
|
|
|
|
/**
|
|
* @NoAdminRequired
|
|
*
|
|
* Init a direct editing session
|
|
*
|
|
* @param int $fileId
|
|
* @return DataResponse
|
|
* @throws OCSNotFoundException|OCSBadRequestException
|
|
*/
|
|
public function createDirect($fileId) {
|
|
try {
|
|
$userFolder = $this->rootFolder->getUserFolder($this->userId);
|
|
$nodes = $userFolder->getById($fileId);
|
|
|
|
if ($nodes === []) {
|
|
throw new OCSNotFoundException();
|
|
}
|
|
|
|
$node = $nodes[0];
|
|
if ($node instanceof Folder) {
|
|
throw new OCSBadRequestException('Cannot view folder');
|
|
}
|
|
|
|
$direct = $this->directMapper->newDirect($this->userId, $fileId);
|
|
|
|
return new DataResponse([
|
|
'url' => $this->urlGenerator->linkToRouteAbsolute('richdocuments.directView.show', [
|
|
'token' => $direct->getToken()
|
|
])
|
|
]);
|
|
} catch (NotFoundException $e) {
|
|
throw new OCSNotFoundException();
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Generate a direct editing link for a file in a public share to open with the current user
|
|
*
|
|
* @NoAdminRequired
|
|
* @BruteForceProtection(action=richdocumentsCreatePublic)
|
|
* @PublicPage
|
|
* @throws OCSForbiddenException
|
|
*/
|
|
public function createPublic(
|
|
string $shareToken,
|
|
?string $host = null,
|
|
string $path = '',
|
|
?string $password = null
|
|
): DataResponse {
|
|
if ($host) {
|
|
$remoteCollabora = $this->federationService->getRemoteCollaboraURL($host);
|
|
if ($remoteCollabora === '') {
|
|
throw new OCSNotFoundException('Failed to connect to remote collabora instance.');
|
|
}
|
|
|
|
$wopi = $this->tokenManager->newInitiatorToken($host, null, $shareToken, true, $this->userId);
|
|
|
|
$client = \OC::$server->getHTTPClientService()->newClient();
|
|
try {
|
|
$response = $client->post(rtrim($host, '/') . '/ocs/v2.php/apps/richdocuments/api/v1/direct/share/initiator?format=json', [
|
|
'body' => [
|
|
'initiatorServer' => \OC::$server->getURLGenerator()->getAbsoluteURL(''),
|
|
'initiatorToken' => $wopi->getToken(),
|
|
'shareToken' => $shareToken,
|
|
'path' => $path,
|
|
'password' => $password
|
|
],
|
|
'timeout' => 30
|
|
]);
|
|
} catch (BadResponseException $e) {
|
|
$status = $e->getResponse()->getStatusCode();
|
|
if ($status === Http::STATUS_NOT_FOUND || $status === Http::STATUS_FORBIDDEN) {
|
|
$this->logger->debug('Failed to create link from initiator token. Remote denied access.');
|
|
$response = new DataResponse([], HTTP::STATUS_FORBIDDEN);
|
|
$response->throttle();
|
|
return $response;
|
|
}
|
|
|
|
$this->logger->error('Failed to create link from initiator token. Unexpected status code ' . $status, ['exception' => $e]);
|
|
return new DataResponse([], HTTP::STATUS_INTERNAL_SERVER_ERROR);
|
|
} catch (Exception $e) {
|
|
$this->logger->error('Failed to create link from initiator token. Unexpected response.', ['exception' => $e]);
|
|
return new DataResponse([], HTTP::STATUS_INTERNAL_SERVER_ERROR);
|
|
}
|
|
$url = \json_decode($response->getBody(), true)['ocs']['data']['url'];
|
|
|
|
return new DataResponse([
|
|
'url' => $url,
|
|
]);
|
|
}
|
|
|
|
try {
|
|
$share = $this->shareManager->getShareByToken($shareToken);
|
|
} catch (ShareNotFound $ex) {
|
|
$response = new DataResponse([], HTTP::STATUS_NOT_FOUND);
|
|
$response->throttle();
|
|
return $response;
|
|
}
|
|
|
|
if ($share->getPassword() && !$this->shareManager->checkPassword($share, $password)) {
|
|
$response = new DataResponse([], HTTP::STATUS_FORBIDDEN);
|
|
$response->throttle();
|
|
return $response;
|
|
}
|
|
|
|
if (($share->getPermissions() & Constants::PERMISSION_READ) === 0) {
|
|
$response = new DataResponse([], HTTP::STATUS_FORBIDDEN);
|
|
$response->throttle();
|
|
return $response;
|
|
}
|
|
|
|
$node = $share->getNode();
|
|
if ($node instanceof Folder) {
|
|
$node = $node->get($path);
|
|
}
|
|
$direct = $this->directMapper->newDirect($this->userId, $node->getId(), 0, $shareToken);
|
|
|
|
return new DataResponse([
|
|
'url' => $this->urlGenerator->linkToRouteAbsolute('richdocuments.directView.show', [
|
|
'token' => $direct->getToken()
|
|
])
|
|
]);
|
|
}
|
|
|
|
/**
|
|
* @PublicPage
|
|
* @NoCSRFRequired
|
|
* @BruteForceProtection(action=richdocumentsCreatePublicFromInitiator)
|
|
* @throws OCSForbiddenException
|
|
*/
|
|
public function createPublicFromInitiator(
|
|
string $initiatorServer,
|
|
string $initiatorToken,
|
|
string $shareToken,
|
|
string $path = '',
|
|
?string $password = null
|
|
): DataResponse {
|
|
try {
|
|
$share = $this->shareManager->getShareByToken($shareToken);
|
|
} catch (ShareNotFound $ex) {
|
|
$response = new DataResponse([], HTTP::STATUS_NOT_FOUND);
|
|
$response->throttle();
|
|
return $response;
|
|
}
|
|
|
|
if ($share->getPassword() && !$this->shareManager->checkPassword($share, $password)) {
|
|
$response = new DataResponse([], HTTP::STATUS_FORBIDDEN);
|
|
$response->throttle();
|
|
return $response;
|
|
}
|
|
|
|
$node = $share->getNode();
|
|
if ($node instanceof Folder) {
|
|
$node = $node->get($path);
|
|
}
|
|
|
|
if (($share->getPermissions() & Constants::PERMISSION_READ) === 0) {
|
|
return new DataResponse([], Http::STATUS_FORBIDDEN);
|
|
}
|
|
|
|
$direct = $this->directMapper->newDirect(null, $node->getId(), null, $shareToken, $initiatorServer, $initiatorToken);
|
|
|
|
return new DataResponse([
|
|
'url' => $this->urlGenerator->linkToRouteAbsolute('richdocuments.directView.show', [
|
|
'token' => $direct->getToken()
|
|
])
|
|
]);
|
|
}
|
|
|
|
/**
|
|
* Generate a direct editing link for a file in a public share to open with the current user
|
|
*
|
|
* @NoAdminRequired
|
|
* @BruteForceProtection(action=richdocumentsCreatePublic)
|
|
* @PublicPage
|
|
*/
|
|
public function updateGuestName(string $access_token, string $guestName): DataResponse {
|
|
try {
|
|
$this->tokenManager->updateGuestName($access_token, $guestName);
|
|
return new DataResponse([], Http::STATUS_OK);
|
|
} catch (UnknownTokenException $e) {
|
|
$response = new DataResponse([], Http::STATUS_FORBIDDEN);
|
|
$response->throttle();
|
|
return $response;
|
|
} catch (ExpiredTokenException $e) {
|
|
$response = new DataResponse([], Http::STATUS_UNAUTHORIZED);
|
|
$response->throttle();
|
|
return $response;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @NoAdminRequired
|
|
* @PublicPage
|
|
*
|
|
* @param string $type The template type
|
|
* @return DataResponse
|
|
* @throws OCSBadRequestException
|
|
*/
|
|
public function getTemplates($type) {
|
|
if (array_key_exists($type, TemplateManager::$tplTypes)) {
|
|
$templates = $this->manager->getAllFormatted($type);
|
|
return new DataResponse($templates);
|
|
}
|
|
throw new OCSBadRequestException('Wrong type');
|
|
}
|
|
|
|
/**
|
|
* @NoAdminRequired
|
|
*
|
|
* @param string $path Where to create the document
|
|
* @param int $template The template id
|
|
*/
|
|
public function createFromTemplate($path, $template) {
|
|
if ($path === null || $template === null) {
|
|
throw new OCSBadRequestException('path and template must be set');
|
|
}
|
|
|
|
if (!$this->manager->isTemplate($template)) {
|
|
throw new OCSBadRequestException('Invalid template provided');
|
|
}
|
|
|
|
try {
|
|
$info = $this->mb_pathinfo($path);
|
|
|
|
$userFolder = $this->rootFolder->getUserFolder($this->userId);
|
|
$folder = isset($info['dirname']) ? $userFolder->get($info['dirname']) : $userFolder;
|
|
$name = $folder->getNonExistingName($info['basename']);
|
|
$file = $folder->newFile($name);
|
|
|
|
$direct = $this->directMapper->newDirect($this->userId, $template, $file->getId());
|
|
|
|
return new DataResponse([
|
|
'url' => $this->urlGenerator->linkToRouteAbsolute('richdocuments.directView.show', [
|
|
'token' => $direct->getToken()
|
|
])
|
|
]);
|
|
} catch (NotFoundException $e) {
|
|
throw new OCSNotFoundException();
|
|
} catch (\Exception $e) {
|
|
$this->logger->error($e->getMessage(), ['exception' => $e]);
|
|
throw new OCSException('Failed to create new file from template.');
|
|
}
|
|
}
|
|
|
|
private function mb_pathinfo($filepath) {
|
|
$result = [];
|
|
preg_match('%^(.*?)[\\\\/]*(([^/\\\\]*?)(\.([^\.\\\\/]+?)|))[\\\\/\.]*$%im', ltrim('/' . $filepath), $matches);
|
|
if ($matches[1]) {
|
|
$result['dirname'] = $matches[1];
|
|
}
|
|
if ($matches[2]) {
|
|
$result['basename'] = $matches[2];
|
|
}
|
|
if ($matches[5]) {
|
|
$result['extension'] = $matches[5];
|
|
}
|
|
if ($matches[3]) {
|
|
$result['filename'] = $matches[3];
|
|
}
|
|
return $result;
|
|
}
|
|
}
|