1<?php
2/**
3 * @author Thomas Müller <thomas.mueller@tmit.eu>
4 *
5 * @copyright Copyright (c) 2018, ownCloud GmbH
6 * @license AGPL-3.0
7 *
8 * This code is free software: you can redistribute it and/or modify
9 * it under the terms of the GNU Affero General Public License, version 3,
10 * as published by the Free Software Foundation.
11 *
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU Affero General Public License for more details.
16 *
17 * You should have received a copy of the GNU Affero General Public License, version 3,
18 * along with this program.  If not, see <http://www.gnu.org/licenses/>
19 *
20 */
21
22namespace OCA\DAV\DAV;
23
24use OCA\DAV\JobStatus\Entity\JobStatus;
25use OCA\DAV\JobStatus\Entity\JobStatusMapper;
26use OCP\ILogger;
27use OCP\IURLGenerator;
28use OCP\IUserSession;
29use OCP\Shutdown\IShutdownManager;
30use Sabre\DAV\Exception;
31use Sabre\DAV\Server;
32use Sabre\DAV\ServerPlugin;
33use Sabre\DAV\UUIDUtil;
34use Sabre\HTTP\RequestInterface;
35use Sabre\HTTP\Response;
36use Sabre\HTTP\ResponseInterface;
37
38/**
39 * Class LazyOpsPlugin
40 *
41 * @package OCA\DAV\DAV
42 */
43class LazyOpsPlugin extends ServerPlugin {
44
45	/** @var Server */
46	private $server;
47	/** @var string */
48	private $jobId;
49	/** @var JobStatus */
50	private $entity;
51	/** @var IUserSession */
52	private $userSession;
53	/** @var IURLGenerator */
54	private $urlGenerator;
55	/** @var IShutdownManager */
56	private $shutdownManager;
57	/** @var ILogger */
58	private $logger;
59	/** @var JobStatusMapper */
60	private $mapper;
61
62	public function __construct(
63		IUserSession $userSession,
64		IURLGenerator $urlGenerator,
65		IShutdownManager $shutdownManager,
66		JobStatusMapper $jobStatusMapper,
67		ILogger $logger
68	) {
69		$this->userSession = $userSession;
70		$this->urlGenerator = $urlGenerator;
71		$this->shutdownManager = $shutdownManager;
72		$this->logger = $logger;
73		$this->mapper = $jobStatusMapper;
74	}
75
76	/**
77	 * @param Server $server
78	 */
79	public function initialize(Server $server) {
80		$this->server = $server;
81		$server->on('method:MOVE', [$this, 'httpMove'], 90);
82	}
83
84	/**
85	 * @param RequestInterface $request
86	 * @param ResponseInterface $response
87	 * @return bool
88	 * @throws Exception\NotAuthenticated
89	 */
90	public function httpMove(RequestInterface $request, ResponseInterface $response) {
91		if (!$request->getHeader('OC-LazyOps')) {
92			return true;
93		}
94
95		$this->jobId = UUIDUtil::getUUID();
96		$this->setJobStatus([
97			'status' => 'init'
98		]);
99		$userId = $this->getUserId();
100		$location = $this->urlGenerator
101				->linkTo('', 'remote.php') . "/dav/job-status/{$userId}/{$this->jobId}";
102
103		$response->setStatus(202);
104		$response->setHeader('Connection', 'close');
105		$response->setHeader('OC-JobStatus-Location', $location);
106
107		$this->shutdownManager->register(function () use ($request, $response) {
108			return $this->afterResponse($request, $response);
109		}, IShutdownManager::HIGH);
110
111		return false;
112	}
113
114	public function afterResponse(RequestInterface $request, ResponseInterface $response) {
115		if (!$request->getHeader('OC-LazyOps')) {
116			return true;
117		}
118
119		\flush();
120		$request->removeHeader('OC-LazyOps');
121		$responseDummy = new Response();
122		try {
123			$this->setJobStatus([
124				'status' => 'started'
125			]);
126			$this->server->emit('method:MOVE', [$request, $responseDummy]);
127
128			$this->setJobStatus([
129				'status' => 'finished',
130				'fileId' => $response->getHeader('OC-FileId'),
131				'ETag' => $response->getHeader('ETag')
132			]);
133		} catch (\Exception $ex) {
134			$this->logger->logException($ex);
135
136			$this->setJobStatus([
137				'status' => 'error',
138				'errorCode' => $ex instanceof Exception ? $ex->getHTTPCode() : 500,
139				'errorMessage' => $ex->getMessage()
140			]);
141		}
142		return false;
143	}
144
145	private function setJobStatus(array $status) {
146		if ($this->entity === null) {
147			$userId = $this->getUserId();
148
149			$this->entity = new JobStatus();
150			$this->entity->setStatusInfo(\json_encode($status));
151			$this->entity->setUserId($userId);
152			$this->entity->setUuid($this->jobId);
153			$this->mapper->insert($this->entity);
154		} else {
155			$this->entity->setStatusInfo(\json_encode($status));
156			$this->mapper->update($this->entity);
157		}
158	}
159
160	/**
161	 * @return string
162	 * @throws Exception\NotAuthenticated
163	 */
164	private function getUserId() {
165		$user = $this->userSession->getUser();
166		if ($user === null) {
167			throw new Exception\NotAuthenticated();
168		}
169		return $user->getUID();
170	}
171}
172