From ad082fbba4fd5fc3a498743df37a4b8dc3846612 Mon Sep 17 00:00:00 2001 From: Antipkin-A Date: Fri, 29 Jul 2022 13:23:39 +0300 Subject: check lock through lock manager --- appinfo/application.php | 4 +++- controller/editorapicontroller.php | 44 +++++++++++++++++++++++--------------- 2 files changed, 30 insertions(+), 18 deletions(-) diff --git a/appinfo/application.php b/appinfo/application.php index bbf013a..cd6de61 100644 --- a/appinfo/application.php +++ b/appinfo/application.php @@ -32,6 +32,7 @@ use OCP\Files\Template\ITemplateManager; use OCP\Files\Template\TemplateFileCreator; use OCA\Files\Event\LoadAdditionalScriptsEvent; use OCP\Files\IMimeTypeDetector; +use OCP\Files\Lock\ILockManager; use OCP\IL10N; use OCP\IPreview; use OCP\ITagManager; @@ -199,7 +200,8 @@ class Application extends App implements IBootstrap { $this->crypt, $c->get("IManager"), $c->get("Session"), - $c->get(ITagManager::class) + $c->get(ITagManager::class), + $c->get(ILockManager::class) ); }); diff --git a/controller/editorapicontroller.php b/controller/editorapicontroller.php index ba5f333..ce03f3a 100644 --- a/controller/editorapicontroller.php +++ b/controller/editorapicontroller.php @@ -26,6 +26,11 @@ use OCP\Constants; use OCP\Files\File; use OCP\Files\Folder; use OCP\Files\IRootFolder; +use OCP\Files\Lock\ILock; +use OCP\Files\Lock\ILockManager; +use OCP\Files\Lock\NoLockProviderException; +use OCP\Files\Lock\OwnerLockedException; +use OCP\PreConditionNotMetException; use OCP\IL10N; use OCP\ILogger; use OCP\IRequest; @@ -39,8 +44,6 @@ use OCP\IUserSession; use OCP\Share\IManager; use OCA\Files_Versions\Versions\IVersionManager; -use OCA\FilesLock\Service\LockService; -use OCA\FilesLock\Exceptions\LockNotFoundException; use OCA\Onlyoffice\AppConfig; use OCA\Onlyoffice\Crypt; @@ -138,6 +141,13 @@ class EditorApiController extends OCSController { */ private $extraPermissions; + /** + * Lock manager + * + * @var ILockManager + */ + private $lockManager; + /** * Mobile regex from https://github.com/ONLYOFFICE/CommunityServer/blob/v9.1.1/web/studio/ASC.Web.Studio/web.appsettings.config#L35 */ @@ -157,6 +167,7 @@ class EditorApiController extends OCSController { * @param IManager $shareManager - Share manager * @param ISession $ISession - Session * @param ITagManager $tagManager - Tag manager + * @param ILockManager $lockManager - Lock manager */ public function __construct($AppName, IRequest $request, @@ -170,7 +181,8 @@ class EditorApiController extends OCSController { Crypt $crypt, IManager $shareManager, ISession $session, - ITagManager $tagManager + ITagManager $tagManager, + ILockManager $lockManager ) { parent::__construct($AppName, $request); @@ -183,6 +195,7 @@ class EditorApiController extends OCSController { $this->config = $config; $this->crypt = $crypt; $this->tagManager = $tagManager; + $this->lockManager = $lockManager; if (\OC::$server->getAppManager()->isInstalled("files_versions")) { try { @@ -362,24 +375,21 @@ class EditorApiController extends OCSController { $isTempLock = false; if ($version < 1 - && \OC::$server->getAppManager()->isInstalled("files_lock")) { + && $this->lockManager->isLockProviderAvailable()) { try { - $lockService = \OC::$server->get(LockService::class); - $lock = $lockService->getLockFromFileId($file->getId()); + $locks = $this->lockManager->getLocks($file->getId()); + $lock = !empty($locks) ? $locks[0] : null; - $lockOwner = null; - if (method_exists($lock, "getUserId")) { - $lockOwner = $lock->getUserId(); - } - else if(method_exists($lock, "getOwner")) { + if ($lock !== null) { + $lockType = $lock->getType(); $lockOwner = $lock->getOwner(); + if (($lockType === ILock::TYPE_APP) && $lockOwner !== $this->appName + || ($lockType === ILock::TYPE_USER || $lockType === ILock::TYPE_TOKEN) && $lockOwner !== $userId) { + $isTempLock = true; + $this->logger->debug("File" . $file->getId() . "is locked by $lockOwner", ["app" => $this->appName]); + } } - - if ($userId !== $lockOwner) { - $isTempLock = true; - $this->logger->debug("File" . $file->getId() . "is locked by $lockOwner", ["app" => $this->appName]); - } - } catch (LockNotFoundException $e) {} + } catch (PreConditionNotMetException | NoLockProviderException $e) {} } $canEdit = isset($format["edit"]) && $format["edit"]; -- cgit v1.2.3 From 5cf475c7c480f9c9c027f7e6494c7985942e011d Mon Sep 17 00:00:00 2001 From: Antipkin-A Date: Mon, 1 Aug 2022 12:45:41 +0300 Subject: lock/unlock to callback --- appinfo/application.php | 3 +- controller/callbackcontroller.php | 169 ++++++++++++++++++++++++-------------- 2 files changed, 111 insertions(+), 61 deletions(-) diff --git a/appinfo/application.php b/appinfo/application.php index cd6de61..25487b8 100644 --- a/appinfo/application.php +++ b/appinfo/application.php @@ -216,7 +216,8 @@ class Application extends App implements IBootstrap { $c->get("Logger"), $this->appConfig, $this->crypt, - $c->get("IManager") + $c->get("IManager"), + $c->get(ILockManager::class) ); }); diff --git a/controller/callbackcontroller.php b/controller/callbackcontroller.php index a2fd341..962587a 100644 --- a/controller/callbackcontroller.php +++ b/controller/callbackcontroller.php @@ -30,6 +30,12 @@ use OCP\Files\Folder; use OCP\Files\IRootFolder; use OCP\Files\NotFoundException; use OCP\Files\NotPermittedException; +use OCP\Files\Lock\ILock; +use OCP\Files\Lock\ILockManager; +use OCP\Files\Lock\LockContext; +use OCP\Files\Lock\NoLockProviderException; +use OCP\Files\Lock\OwnerLockedException; +use OCP\PreConditionNotMetException; use OCP\IL10N; use OCP\ILogger; use OCP\IRequest; @@ -119,6 +125,13 @@ class CallbackController extends Controller { */ private $versionManager; + /** + * Lock manager + * + * @var ILockManager + */ + private $lockManager; + /** * Status of the document */ @@ -140,6 +153,7 @@ class CallbackController extends Controller { * @param AppConfig $config - application configuration * @param Crypt $crypt - hash generator * @param IManager $shareManager - Share manager + * @param ILockManager $lockManager - Lock manager */ public function __construct($AppName, IRequest $request, @@ -150,7 +164,8 @@ class CallbackController extends Controller { ILogger $logger, AppConfig $config, Crypt $crypt, - IManager $shareManager + IManager $shareManager, + ILockManager $lockManager ) { parent::__construct($AppName, $request); @@ -162,6 +177,7 @@ class CallbackController extends Controller { $this->config = $config; $this->crypt = $crypt; $this->shareManager = $shareManager; + $this->lockManager = $lockManager; if (\OC::$server->getAppManager()->isInstalled("files_versions")) { try { @@ -434,77 +450,83 @@ class CallbackController extends Controller { $url = isset($payload->url) ? $payload->url : null; } - $result = 1; - switch ($status) { - case self::TrackerStatus_MustSave: - case self::TrackerStatus_Corrupted: - case self::TrackerStatus_ForceSave: - case self::TrackerStatus_CorruptedForceSave: - if (empty($url)) { - $this->logger->error("Track without url: $fileId status $status", ["app" => $this->appName]); - return new JSONResponse(["message" => "Url not found"], Http::STATUS_BAD_REQUEST); - } + $shareToken = isset($hashData->shareToken) ? $hashData->shareToken : null; + $filePath = null; - try { - $shareToken = isset($hashData->shareToken) ? $hashData->shareToken : null; - $filePath = null; + \OC_Util::tearDownFS(); - \OC_Util::tearDownFS(); + $isForcesave = $status === self::TrackerStatus_ForceSave || $status === self::TrackerStatus_CorruptedForceSave; - $isForcesave = $status === self::TrackerStatus_ForceSave || $status === self::TrackerStatus_CorruptedForceSave; + $callbackUserId = $hashData->userId; - // author of the latest changes - $userId = $this->parseUserId($users[0]); + $userId = null; + if (!empty($users)) { + // author of the latest changes + $userId = $this->parseUserId($users[0]); + } else { + $userId = $callbackUserId; + } - if ($isForcesave - && $forcesavetype === 1 - && !empty($actions)) { - // the user who clicked Save - $userId = $this->parseUserId($actions[0]["userid"]); - } + if ($isForcesave + && $forcesavetype === 1 + && !empty($actions)) { + // the user who clicked Save + $userId = $this->parseUserId($actions[0]["userid"]); + } - $user = $this->userManager->get($userId); - if (!empty($user)) { - \OC_User::setUserId($userId); - } else { - if (empty($shareToken)) { - $this->logger->error("Track without token: $fileId status $status", ["app" => $this->appName]); - return new JSONResponse(["message" => $this->trans->t("Access denied")], Http::STATUS_FORBIDDEN); - } + $user = $this->userManager->get($userId); + if (!empty($user)) { + \OC_User::setUserId($userId); + } else { + if (empty($shareToken)) { + $this->logger->error("Track without token: $fileId status $status", ["app" => $this->appName]); + return new JSONResponse(["message" => $this->trans->t("Access denied")], Http::STATUS_FORBIDDEN); + } - $this->logger->debug("Track $fileId by token for $userId", ["app" => $this->appName]); - } + $this->logger->debug("Track $fileId by token for $userId", ["app" => $this->appName]); + } - // owner of file from the callback link - $ownerId = $hashData->ownerId; - $owner = $this->userManager->get($ownerId); + // owner of file from the callback link + $ownerId = $hashData->ownerId; + $owner = $this->userManager->get($ownerId); - if (!empty($owner)) { - $userId = $ownerId; - } else { - $callbackUserId = $hashData->userId; - $callbackUser = $this->userManager->get($callbackUserId); + if (!empty($owner)) { + $userId = $ownerId; + } else { + $callbackUser = $this->userManager->get($callbackUserId); - if (!empty($callbackUser)) { - // author of the callback link - $userId = $callbackUserId; + if (!empty($callbackUser)) { + // author of the callback link + $userId = $callbackUserId; - // path for author of the callback link - $filePath = $hashData->filePath; - } - } + // path for author of the callback link + $filePath = $hashData->filePath; + } + } - if (!empty($userId)) { - \OC_Util::setupFS($userId); - } + if (!empty($userId)) { + \OC_Util::setupFS($userId); + } - list ($file, $error) = empty($shareToken) ? $this->getFile($userId, $fileId, $filePath) : $this->getFileByToken($fileId, $shareToken); + list ($file, $error) = empty($shareToken) ? $this->getFile($userId, $fileId, $filePath) : $this->getFileByToken($fileId, $shareToken); - if (isset($error)) { - $this->logger->error("track error $fileId " . json_encode($error->getData()), ["app" => $this->appName]); - return $error; - } + if (isset($error)) { + $this->logger->error("track error $fileId " . json_encode($error->getData()), ["app" => $this->appName]); + return $error; + } + $result = 1; + switch ($status) { + case self::TrackerStatus_MustSave: + case self::TrackerStatus_Corrupted: + case self::TrackerStatus_ForceSave: + case self::TrackerStatus_CorruptedForceSave: + if (empty($url)) { + $this->logger->error("Track without url: $fileId status $status", ["app" => $this->appName]); + return new JSONResponse(["message" => "Url not found"], Http::STATUS_BAD_REQUEST); + } + + try { $url = $this->config->ReplaceDocumentServerUrlToInternal($url); $prevVersion = $file->getFileInfo()->getMtime(); @@ -539,9 +561,22 @@ class CallbackController extends Controller { } $this->logger->debug("Track put content " . $file->getPath(), ["app" => $this->appName]); - $this->retryOperation(function () use ($file, $newData) { - return $file->putContent($newData); - }); + + $retryOperation = function () use ($file, $newData) { + $this->retryOperation(function () use ($file, $newData) { + return $file->putContent($newData); + }); + }; + + try { + $lockContext = new LockContext($file, ILock::TYPE_APP, $this->appName); + $this->lockManager->runInScope($lockContext, $retryOperation); + $this->lockManager->unlock($lockContext); + + $this->logger->debug("$this->appName has unlocked file $fileId", ["app" => $this->appName]); + } catch (NoLockProviderException $e) { + $retryOperation(); + } catch (PreConditionNotMetException $e) {} if ($file->getStorage()->instanceOfStorage(SharingExternalStorage::class)) { if ($isForcesave) { @@ -575,7 +610,21 @@ class CallbackController extends Controller { break; case self::TrackerStatus_Editing: + try { + $this->lockManager->lock(new LockContext($file, ILock::TYPE_APP, $this->appName)); + + $this->logger->debug("$this->appName has locked file $fileId", ["app" => $this->appName]); + } catch (PreConditionNotMetException | OwnerLockedException | NoLockProviderException $e) {} + + $result = 0; + break; case self::TrackerStatus_Closed: + try { + $this->lockManager->unlock(new LockContext($file, ILock::TYPE_APP, $this->appName)); + + $this->logger->debug("$this->appName has unlocked file $fileId", ["app" => $this->appName]); + } catch (PreConditionNotMetException | NoLockProviderException $e) {} + $result = 0; break; } -- cgit v1.2.3