| Server IP : 93.86.61.54 / Your IP : 216.73.216.60 Web Server : Apache/2.4.62 (Ubuntu) System : Linux rasin.ddns.net 6.8.0-124-generic #124~22.04.1-Ubuntu SMP PREEMPT_DYNAMIC Tue May 26 21:05:19 UTC x86_64 User : www-data ( 33) PHP Version : 8.4.22 Disable Function : NONE MySQL : OFF | cURL : ON | WGET : ON | Perl : ON | Python : OFF | Sudo : ON | Pkexec : ON Directory : /var/www/html/projects/nextcloud/apps/richdocuments/lib/Controller/ |
Upload File : |
<?php
/**
* @copyright Copyright (c) 2016-2017 Lukas Reschke <lukas@statuscode.ch>
*
* @license GNU AGPL version 3 or any later version
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
namespace OCA\Richdocuments\Controller;
use OC\Files\View;
use OCA\Richdocuments\AppConfig;
use OCA\Richdocuments\Db\Wopi;
use OCA\Richdocuments\Db\WopiMapper;
use OCA\Richdocuments\Helper;
use OCA\Richdocuments\Service\UserScopeService;
use OCA\Richdocuments\TemplateManager;
use OCA\Richdocuments\TokenManager;
use OCP\AppFramework\Controller;
use OCP\AppFramework\Db\DoesNotExistException;
use OCP\AppFramework\Http;
use OCP\AppFramework\Http\JSONResponse;
use OCP\AppFramework\Http\StreamResponse;
use OCP\AppFramework\QueryException;
use OCP\Encryption\IManager as IEncryptionManager;
use OCP\Files\File;
use OCP\Files\Folder;
use OCP\Files\GenericFileException;
use OCP\Files\InvalidPathException;
use OCP\Files\IRootFolder;
use OCP\Files\Node;
use OCP\Files\NotFoundException;
use OCP\Files\NotPermittedException;
use OCP\IConfig;
use OCP\ILogger;
use OCP\IRequest;
use OCP\IURLGenerator;
use OCP\IUserManager;
use OCP\Lock\LockedException;
use OCP\Share\Exceptions\ShareNotFound;
use OCP\Share\IManager as IShareManager;
class WopiController extends Controller {
/** @var IRootFolder */
private $rootFolder;
/** @var IURLGenerator */
private $urlGenerator;
/** @var IConfig */
private $config;
/** @var AppConfig */
private $appConfig;
/** @var TokenManager */
private $tokenManager;
/** @var IUserManager */
private $userManager;
/** @var WopiMapper */
private $wopiMapper;
/** @var ILogger */
private $logger;
/** @var TemplateManager */
private $templateManager;
/** @var IShareManager */
private $shareManager;
/** @var UserScopeService */
private $userScopeService;
/** @var IEncryptionManager */
private $encryptionManager;
// Signifies LOOL that document has been changed externally in this storage
const LOOL_STATUS_DOC_CHANGED = 1010;
/**
* @param string $appName
* @param IRequest $request
* @param IRootFolder $rootFolder
* @param IURLGenerator $urlGenerator
* @param IConfig $config
* @param AppConfig $appConfig
* @param TokenManager $tokenManager
* @param IUserManager $userManager
* @param WopiMapper $wopiMapper
* @param ILogger $logger
* @param TemplateManager $templateManager
* @param IShareManager $shareManager
* @param UserScopeService $userScopeService
* @param IEncryptionManager $encryptionManager
*/
public function __construct(
$appName,
IRequest $request,
IRootFolder $rootFolder,
IURLGenerator $urlGenerator,
IConfig $config,
AppConfig $appConfig,
TokenManager $tokenManager,
IUserManager $userManager,
WopiMapper $wopiMapper,
ILogger $logger,
TemplateManager $templateManager,
IShareManager $shareManager,
UserScopeService $userScopeService,
IEncryptionManager $encryptionManager
) {
parent::__construct($appName, $request);
$this->rootFolder = $rootFolder;
$this->urlGenerator = $urlGenerator;
$this->config = $config;
$this->appConfig = $appConfig;
$this->tokenManager = $tokenManager;
$this->userManager = $userManager;
$this->wopiMapper = $wopiMapper;
$this->logger = $logger;
$this->templateManager = $templateManager;
$this->shareManager = $shareManager;
$this->userScopeService = $userScopeService;
$this->encryptionManager = $encryptionManager;
}
/**
* Returns general info about a file.
*
* @NoAdminRequired
* @NoCSRFRequired
* @PublicPage
*
* @param string $fileId
* @param string $access_token
* @return JSONResponse
* @throws InvalidPathException
* @throws NotFoundException
*/
public function checkFileInfo($fileId, $access_token) {
try {
list($fileId, , $version) = Helper::parseFileId($fileId);
$wopi = $this->wopiMapper->getWopiForToken($access_token);
if ($wopi->isTemplateToken()) {
$this->templateManager->setUserId($wopi->getOwnerUid());
$file = $this->templateManager->get($wopi->getFileid());
} else {
$file = $this->getFileForWopiToken($wopi);
}
if(!($file instanceof File)) {
throw new NotFoundException('No valid file found for ' . $fileId);
}
} catch (NotFoundException $e) {
$this->logger->debug($e->getMessage(), ['app' => 'richdocuments', '']);
return new JSONResponse([], Http::STATUS_FORBIDDEN);
} catch (DoesNotExistException $e) {
$this->logger->debug($e->getMessage(), ['app' => 'richdocuments', '']);
return new JSONResponse([], Http::STATUS_FORBIDDEN);
} catch (\Exception $e) {
$this->logger->logException($e, ['app' => 'richdocuments']);
return new JSONResponse([], Http::STATUS_FORBIDDEN);
}
$isPublic = empty($wopi->getEditorUid());
$guestUserId = 'Guest-' . \OC::$server->getSecureRandom()->generate(8);
$user = $this->userManager->get($wopi->getEditorUid());
$userDisplayName = $user !== null && !$isPublic ? $user->getDisplayName() : $wopi->getGuestDisplayname();
$isVersion = $version !== '0';
$response = [
'BaseFileName' => $file->getName(),
'Size' => $file->getSize(),
'Version' => $version,
'UserId' => !$isPublic ? $wopi->getEditorUid() : $guestUserId,
'OwnerId' => $wopi->getOwnerUid(),
'UserFriendlyName' => $userDisplayName,
'UserExtraInfo' => [
],
'UserCanWrite' => (bool)$wopi->getCanwrite(),
'UserCanNotWriteRelative' => $this->encryptionManager->isEnabled() || $isPublic,
'PostMessageOrigin' => $wopi->getServerHost(),
'LastModifiedTime' => Helper::toISO8601($file->getMTime()),
'SupportsRename' => !$isVersion,
'UserCanRename' => !$isPublic && !$isVersion,
'EnableInsertRemoteImage' => true,
'EnableShare' => $file->isShareable() && !$isVersion,
'HideUserList' => 'desktop',
'DisablePrint' => $wopi->getHideDownload(),
'DisableExport' => $wopi->getHideDownload(),
'DisableCopy' => $wopi->getHideDownload(),
'HideExportOption' => $wopi->getHideDownload(),
'HidePrintOption' => $wopi->getHideDownload(),
'DownloadAsPostMessage' => $wopi->getDirect(),
];
if ($wopi->isTemplateToken()) {
$userFolder = $this->rootFolder->getUserFolder($wopi->getOwnerUid());
$file = $userFolder->getById($wopi->getTemplateDestination())[0];
$response['TemplateSaveAs'] = $file->getName();
}
if ($this->shouldWatermark($isPublic, $wopi->getEditorUid(), $fileId, $wopi)) {
$email = $user !== null && !$isPublic ? $user->getEMailAddress() : "";
$replacements = [
'userId' => $wopi->getEditorUid(),
'date' => (new \DateTime())->format('Y-m-d H:i:s'),
'themingName' => \OC::$server->getThemingDefaults()->getName(),
'userDisplayName' => $userDisplayName,
'email' => $email,
];
$watermarkTemplate = $this->appConfig->getAppValue('watermark_text');
$response['WatermarkText'] = preg_replace_callback('/{(.+?)}/', function ($matches) use ($replacements) {
return $replacements[$matches[1]];
}, $watermarkTemplate);
}
/**
* New approach for generating files from templates by creating an empty file
* and providing an URL which returns the actual template
*/
if ($wopi->hasTemplateId()) {
$templateUrl = 'index.php/apps/richdocuments/wopi/template/' . $wopi->getTemplateId() . '?access_token=' . $wopi->getToken();
$templateUrl = $this->urlGenerator->getAbsoluteURL($templateUrl);
$response['TemplateSource'] = $templateUrl;
}
$user = $this->userManager->get($wopi->getEditorUid());
if($user !== null) {
$response['UserExtraInfo']['avatar'] = $this->urlGenerator->linkToRouteAbsolute('core.avatar.getAvatar', ['userId' => $wopi->getEditorUid(), 'size' => 32]);
}
if (!empty($wopi->getRemoteServer())) {
$response = $this->setFederationFileInfo($wopi, $response);
}
return new JSONResponse($response);
}
private function setFederationFileInfo($wopi, $response) {
$remoteUserId = $wopi->getGuestDisplayname();
$cloudID = \OC::$server->getCloudIdManager()->resolveCloudId($remoteUserId);
$response['UserFriendlyName'] = $cloudID->getDisplayId();
$response['UserExtraInfo']['avatar'] = $this->urlGenerator->linkToRouteAbsolute('core.avatar.getAvatar', ['userId' => explode('@', $remoteUserId)[0], 'size' => 32]);
$cleanCloudId = str_replace(['http://', 'https://'], '', $cloudID->getId());
$addressBookEntries = \OC::$server->getContactsManager()->search($cleanCloudId, ['CLOUD']);
foreach ($addressBookEntries as $entry) {
if (isset($entry['CLOUD'])) {
foreach ($entry['CLOUD'] as $cloudID) {
if ($cloudID === $cleanCloudId) {
$response['UserFriendlyName'] = $entry['FN'];
break;
}
}
}
}
return $response;
}
private function shouldWatermark($isPublic, $userId, $fileId, Wopi $wopi) {
if ($this->config->getAppValue(AppConfig::WATERMARK_APP_NAMESPACE, 'watermark_enabled', 'no') === 'no') {
return false;
}
if ($isPublic) {
if ($this->config->getAppValue(AppConfig::WATERMARK_APP_NAMESPACE, 'watermark_linkAll', 'no') === 'yes') {
return true;
}
if ($this->config->getAppValue(AppConfig::WATERMARK_APP_NAMESPACE, 'watermark_linkRead', 'no') === 'yes' && !$wopi->getCanwrite()) {
return true;
}
if ($this->config->getAppValue(AppConfig::WATERMARK_APP_NAMESPACE, 'watermark_linkSecure', 'no') === 'yes' && $wopi->getHideDownload()) {
return true;
}
if ($this->config->getAppValue(AppConfig::WATERMARK_APP_NAMESPACE, 'watermark_linkTags', 'no') === 'yes') {
$tags = $this->appConfig->getAppValueArray('watermark_linkTagsList');
$fileTags = \OC::$server->getSystemTagObjectMapper()->getTagIdsForObjects([$fileId], 'files')[$fileId];
foreach ($fileTags as $tagId) {
if (in_array($tagId, $tags, true)) {
return true;
}
}
}
} else {
if ($this->config->getAppValue(AppConfig::WATERMARK_APP_NAMESPACE, 'watermark_shareAll', 'no') === 'yes') {
$files = $this->rootFolder->getUserFolder($userId)->getById($fileId);
if (count($files) !== 0 && $files[0]->getOwner()->getUID() !== $userId) {
return true;
}
}
if ($this->config->getAppValue(AppConfig::WATERMARK_APP_NAMESPACE, 'watermark_shareRead', 'no') === 'yes' && !$wopi->getCanwrite()) {
return true;
}
}
if ($this->config->getAppValue(AppConfig::WATERMARK_APP_NAMESPACE, 'watermark_allGroups', 'no') === 'yes') {
$groups = $this->appConfig->getAppValueArray('watermark_allGroupsList');
foreach ($groups as $group) {
if (\OC::$server->getGroupManager()->isInGroup($userId, $group)) {
return true;
}
}
}
if ($this->config->getAppValue(AppConfig::WATERMARK_APP_NAMESPACE, 'watermark_allTags', 'no') === 'yes') {
$tags = $this->appConfig->getAppValueArray('watermark_allTagsList');
$fileTags = \OC::$server->getSystemTagObjectMapper()->getTagIdsForObjects([$fileId], 'files')[$fileId];
foreach ($fileTags as $tagId) {
if (in_array($tagId, $tags, true)) {
return true;
}
}
}
return false;
}
/**
* Given an access token and a fileId, returns the contents of the file.
* Expects a valid token in access_token parameter.
*
* @PublicPage
* @NoCSRFRequired
*
* @param string $fileId
* @param string $access_token
* @return Http\Response
* @throws DoesNotExistException
* @throws NotFoundException
* @throws NotPermittedException
*/
public function getFile($fileId,
$access_token) {
list($fileId, , $version) = Helper::parseFileId($fileId);
$wopi = $this->wopiMapper->getWopiForToken($access_token);
if ((int)$fileId !== $wopi->getFileid()) {
return new JSONResponse([], Http::STATUS_FORBIDDEN);
}
// Template is just returned as there is no version logic
if ($wopi->isTemplateToken()) {
$this->templateManager->setUserId($wopi->getOwnerUid());
$file = $this->templateManager->get($wopi->getFileid());
$response = new StreamResponse($file->fopen('rb'));
$response->addHeader('Content-Disposition', 'attachment');
$response->addHeader('Content-Type', 'application/octet-stream');
return $response;
}
try {
/** @var File $file */
$userFolder = $this->rootFolder->getUserFolder($wopi->getOwnerUid());
$file = $userFolder->getById($fileId)[0];
\OC_User::setIncognitoMode(true);
if ($version !== '0') {
$view = new View('/' . $wopi->getOwnerUid() . '/files');
$relPath = $view->getRelativePath($file->getPath());
$versionPath = '/files_versions/' . $relPath . '.v' . $version;
$view = new View('/' . $wopi->getOwnerUid());
if ($view->file_exists($versionPath)){
$info = $view->getFileInfo($versionPath);
if ($info->getSize() === 0) {
$response = new Http\Response();
} else {
$response = new StreamResponse($view->fopen($versionPath, 'rb'));
}
}
else {
return new JSONResponse([], Http::STATUS_NOT_FOUND);
}
}
else {
if ($file->getSize() === 0) {
$response = new Http\Response();
} else {
$response = new StreamResponse($file->fopen('rb'));
}
}
$response->addHeader('Content-Disposition', 'attachment');
$response->addHeader('Content-Type', 'application/octet-stream');
return $response;
} catch (\Exception $e) {
$this->logger->logException($e, ['level' => ILogger::ERROR, 'app' => 'richdocuments', 'message' => 'getFile failed']);
return new JSONResponse([], Http::STATUS_FORBIDDEN);
}
}
/**
* Given an access token and a fileId, replaces the files with the request body.
* Expects a valid token in access_token parameter.
*
* @PublicPage
* @NoCSRFRequired
*
* @param string $fileId
* @param string $access_token
* @return JSONResponse
* @throws DoesNotExistException
*/
public function putFile($fileId,
$access_token) {
list($fileId, ,) = Helper::parseFileId($fileId);
$isPutRelative = ($this->request->getHeader('X-WOPI-Override') === 'PUT_RELATIVE');
$wopi = $this->wopiMapper->getWopiForToken($access_token);
if (!$wopi->getCanwrite()) {
return new JSONResponse([], Http::STATUS_FORBIDDEN);
}
if (!$this->encryptionManager->isEnabled() || $this->isMasterKeyEnabled()) {
// Set the user to register the change under his name
$this->userScopeService->setUserScope($wopi->getUserForFileAccess());
$this->userScopeService->setFilesystemScope($isPutRelative ? $wopi->getEditorUid() : $wopi->getUserForFileAccess());
} else {
// Per-user encryption is enabled so that collabora isn't able to store the file by using the
// user's private key. Because of that we have to use the incognito mode for writing the file.
\OC_User::setIncognitoMode(true);
}
try {
if ($isPutRelative) {
// the new file needs to be installed in the current user dir
$userFolder = $this->rootFolder->getUserFolder($wopi->getEditorUid());
$file = $userFolder->getById($fileId);
if (count($file) === 0) {
return new JSONResponse([], Http::STATUS_NOT_FOUND);
}
$file = $file[0];
$suggested = $this->request->getHeader('X-WOPI-SuggestedTarget');
$suggested = iconv('utf-7', 'utf-8', $suggested);
if ($suggested[0] === '.') {
$path = dirname($file->getPath()) . '/New File' . $suggested;
}
else if ($suggested[0] !== '/') {
$path = dirname($file->getPath()) . '/' . $suggested;
}
else {
$path = $userFolder->getPath() . $suggested;
}
if ($path === '') {
return new JSONResponse([
'status' => 'error',
'message' => 'Cannot create the file'
]);
}
// create the folder first
if (!$this->rootFolder->nodeExists(dirname($path))) {
$this->rootFolder->newFolder(dirname($path));
}
// create a unique new file
$path = $this->rootFolder->getNonExistingName($path);
$this->rootFolder->newFile($path);
$file = $this->rootFolder->get($path);
} else {
$file = $this->getFileForWopiToken($wopi);
$wopiHeaderTime = $this->request->getHeader('X-LOOL-WOPI-Timestamp');
if ($wopiHeaderTime !== null && $wopiHeaderTime !== Helper::toISO8601($file->getMTime() ?? 0)) {
$this->logger->debug('Document timestamp mismatch ! WOPI client says mtime {headerTime} but storage says {storageTime}', [
'headerTime' => $wopiHeaderTime,
'storageTime' => Helper::toISO8601($file->getMTime() ?? 0)
]);
// Tell WOPI client about this conflict.
return new JSONResponse(['LOOLStatusCode' => self::LOOL_STATUS_DOC_CHANGED], Http::STATUS_CONFLICT);
}
}
$content = fopen('php://input', 'rb');
try {
$this->retryOperation(function () use ($file, $content){
return $file->putContent($content);
});
} catch (LockedException $e) {
$this->logger->logException($e);
return new JSONResponse(['message' => 'File locked'], Http::STATUS_INTERNAL_SERVER_ERROR);
}
if ($isPutRelative) {
// generate a token for the new file (the user still has to be
// logged in)
list(, $wopiToken) = $this->tokenManager->getToken($file->getId(), null, $wopi->getEditorUid());
$wopi = 'index.php/apps/richdocuments/wopi/files/' . $file->getId() . '_' . $this->config->getSystemValue('instanceid') . '?access_token=' . $wopiToken;
$url = $this->urlGenerator->getAbsoluteURL($wopi);
return new JSONResponse([ 'Name' => $file->getName(), 'Url' => $url ], Http::STATUS_OK);
}
if ($wopi->hasTemplateId()) {
$wopi->setTemplateId(null);
$this->wopiMapper->update($wopi);
}
return new JSONResponse(['LastModifiedTime' => Helper::toISO8601($file->getMTime())]);
} catch (\Exception $e) {
$this->logger->logException($e, ['level' => ILogger::ERROR, 'app' => 'richdocuments', 'message' => 'getFile failed']);
return new JSONResponse([], Http::STATUS_INTERNAL_SERVER_ERROR);
}
}
/**
* Given an access token and a fileId, replaces the files with the request body.
* Expects a valid token in access_token parameter.
* Just actually routes to the PutFile, the implementation of PutFile
* handles both saving and saving as.* Given an access token and a fileId, replaces the files with the request body.
*
* FIXME Cleanup this code as is a lot of shared logic between putFile and putRelativeFile
*
* @PublicPage
* @NoCSRFRequired
*
* @param string $fileId
* @param string $access_token
* @return JSONResponse
* @throws DoesNotExistException
*/
public function putRelativeFile($fileId,
$access_token) {
list($fileId, ,) = Helper::parseFileId($fileId);
$wopi = $this->wopiMapper->getWopiForToken($access_token);
$isRenameFile = ($this->request->getHeader('X-WOPI-Override') === 'RENAME_FILE');
if (!$wopi->getCanwrite()) {
return new JSONResponse([], Http::STATUS_FORBIDDEN);
}
// Unless the editor is empty (public link) we modify the files as the current editor
$editor = $wopi->getEditorUid();
if ($editor === null || !empty($wopi->getRemoteServer())) {
$editor = $wopi->getOwnerUid();
}
try {
// the new file needs to be installed in the current user dir
$userFolder = $this->rootFolder->getUserFolder($editor);
if ($wopi->isTemplateToken()) {
$this->templateManager->setUserId($wopi->getOwnerUid());
$file = $userFolder->getById($wopi->getTemplateDestination())[0];
} else if ($isRenameFile) {
// the new file needs to be installed in the current user dir
$userFolder = $this->rootFolder->getUserFolder($wopi->getEditorUid());
$file = $userFolder->getById($fileId)[0];
$suggested = $this->request->getHeader('X-WOPI-RequestedName');
$suggested = iconv('utf-7', 'utf-8', $suggested) . '.' . $file->getExtension();
if (strpos($suggested, '.') === 0) {
$path = dirname($file->getPath()) . '/New File' . $suggested;
}
else if (strpos($suggested, '/') !== 0) {
$path = dirname($file->getPath()) . '/' . $suggested;
}
else {
$path = $userFolder->getPath() . $suggested;
}
if ($path === '') {
return new JSONResponse([
'status' => 'error',
'message' => 'Cannot rename the file'
]);
}
// create the folder first
if (!$this->rootFolder->nodeExists(dirname($path))) {
$this->rootFolder->newFolder(dirname($path));
}
// create a unique new file
$path = $this->rootFolder->getNonExistingName($path);
$file = $file->move($path);
} else {
$file = $userFolder->getById($fileId);
if (count($file) === 0) {
return new JSONResponse([], Http::STATUS_NOT_FOUND);
}
$file = $file[0];
$suggested = $this->request->getHeader('X-WOPI-SuggestedTarget');
$suggested = iconv('utf-7', 'utf-8', $suggested);
if ($suggested[0] === '.') {
$path = dirname($file->getPath()) . '/New File' . $suggested;
} else if ($suggested[0] !== '/') {
$path = dirname($file->getPath()) . '/' . $suggested;
} else {
$path = $userFolder->getPath() . $suggested;
}
if ($path === '') {
return new JSONResponse([
'status' => 'error',
'message' => 'Cannot create the file'
]);
}
// create the folder first
if (!$this->rootFolder->nodeExists(dirname($path))) {
$this->rootFolder->newFolder(dirname($path));
}
// create a unique new file
$path = $this->rootFolder->getNonExistingName($path);
$file = $this->rootFolder->newFile($path);
}
$content = fopen('php://input', 'rb');
// Set the user to register the change under his name
$this->userScopeService->setUserScope($wopi->getEditorUid());
$this->userScopeService->setFilesystemScope($wopi->getEditorUid());
try {
$this->retryOperation(function () use ($file, $content){
return $file->putContent($content);
});
} catch (LockedException $e) {
return new JSONResponse(['message' => 'File locked'], Http::STATUS_INTERNAL_SERVER_ERROR);
}
// generate a token for the new file (the user still has to be
// logged in)
list(, $wopiToken) = $this->tokenManager->getToken($file->getId(), null, $wopi->getEditorUid());
$wopi = 'index.php/apps/richdocuments/wopi/files/' . $file->getId() . '_' . $this->config->getSystemValue('instanceid') . '?access_token=' . $wopiToken;
$url = $this->urlGenerator->getAbsoluteURL($wopi);
return new JSONResponse([ 'Name' => $file->getName(), 'Url' => $url ], Http::STATUS_OK);
} catch (\Exception $e) {
$this->logger->logException($e, ['level' => ILogger::ERROR, 'app' => 'richdocuments', 'message' => 'putRelativeFile failed']);
return new JSONResponse([], Http::STATUS_INTERNAL_SERVER_ERROR);
}
}
/**
* Retry operation if a LockedException occurred
* Other exceptions will still be thrown
* @param callable $operation
* @throws LockedException
* @throws GenericFileException
*/
private function retryOperation(callable $operation) {
for ($i = 0; $i < 5; $i++) {
try {
if ($operation() !== false) {
return;
}
} catch (LockedException $e) {
if ($i === 4) {
throw $e;
}
usleep(500000);
}
}
throw new GenericFileException('Operation failed after multiple retries');
}
/**
* @param Wopi $wopi
* @return File|Folder|Node|null
* @throws NotFoundException
* @throws ShareNotFound
*/
private function getFileForWopiToken(Wopi $wopi) {
$file = null;
if (!empty($wopi->getRemoteServer())) {
$share = $this->shareManager->getShareByToken($wopi->getEditorUid());
$node = $share->getNode();
if ($node instanceof Folder) {
$file = $node->getById($wopi->getFileid())[0];
} else {
$file = $node;
}
} else {
// Unless the editor is empty (public link) we modify the files as the current editor
// TODO: add related share token to the wopi table so we can obtain the
$userFolder = $this->rootFolder->getUserFolder($wopi->getUserForFileAccess());
$files = $userFolder->getById($wopi->getFileid());
if (isset($files[0]) && $files[0] instanceof File) {
$file = $files[0];
} else {
throw new NotFoundException('No valid file found for wopi token');
}
}
return $file;
}
/**
* Endpoint to return the template file that is requested by collabora to create a new document
*
* @PublicPage
* @NoCSRFRequired
*
* @param $fileId
* @param $access_token
* @return JSONResponse|StreamResponse
*/
public function getTemplate($fileId, $access_token) {
try {
$wopi = $this->wopiMapper->getPathForToken($access_token);
} catch (DoesNotExistException $e) {
return new JSONResponse([], Http::STATUS_FORBIDDEN);
}
if ((int)$fileId !== $wopi->getTemplateId()) {
return new JSONResponse([], Http::STATUS_FORBIDDEN);
}
try {
$this->templateManager->setUserId($wopi->getOwnerUid());
$file = $this->templateManager->get($wopi->getTemplateId());
$response = new StreamResponse($file->fopen('rb'));
$response->addHeader('Content-Disposition', 'attachment');
$response->addHeader('Content-Type', 'application/octet-stream');
return $response;
} catch (\Exception $e) {
$this->logger->logException($e, ['level' => ILogger::ERROR, 'app' => 'richdocuments', 'message' => 'getTemplate failed']);
return new JSONResponse([], Http::STATUS_INTERNAL_SERVER_ERROR);
}
}
/**
* Check if the encryption module uses a master key.
*/
private function isMasterKeyEnabled(): bool {
try {
$util = \OC::$server->query(\OCA\Encryption\Util::class);
return $util->isMasterKeyEnabled();
} catch (QueryException $e) {
// No encryption module enabled
return false;
}
}
}