Completed
Push — master ( 48c627...03822e )
by Julius
14s queued 10s
created

WopiController::checkFileInfo()   F

Complexity

Conditions 14
Paths 528

Size

Total Lines 83

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 210

Importance

Changes 0
Metric Value
dl 0
loc 83
ccs 0
cts 75
cp 0
rs 2.5753
c 0
b 0
f 0
cc 14
nc 528
nop 2
crap 210

How to fix   Long Method    Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

1
<?php
2
/**
3
 * @copyright Copyright (c) 2016-2017 Lukas Reschke <[email protected]>
4
 *
5
 * @license GNU AGPL version 3 or any later version
6
 *
7
 * This program is free software: you can redistribute it and/or modify
8
 * it under the terms of the GNU Affero General Public License as
9
 * published by the Free Software Foundation, either version 3 of the
10
 * License, or (at your option) any later version.
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
18
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
19
 *
20
 */
21
22
namespace OCA\Richdocuments\Controller;
23
24
use OC\Files\View;
25
use OCA\Richdocuments\Db\Wopi;
26
use OCA\Richdocuments\AppConfig;
27
use OCA\Richdocuments\Db\WopiMapper;
28
use OCA\Richdocuments\TemplateManager;
29
use OCA\Richdocuments\TokenManager;
30
use OCA\Richdocuments\Helper;
31
use OCP\AppFramework\Controller;
32
use OCP\AppFramework\Db\DoesNotExistException;
33
use OCP\AppFramework\Http;
34
use OCP\AppFramework\Http\JSONResponse;
35
use OCP\Files\File;
36
use OCP\Files\Folder;
37
use OCP\Files\InvalidPathException;
38
use OCP\Files\IRootFolder;
39
use OCP\Files\Node;
40
use OCP\Files\NotFoundException;
41
use OCP\Files\NotPermittedException;
42
use OCP\IConfig;
43
use OCP\ILogger;
44
use OCP\IRequest;
45
use OCP\IURLGenerator;
46
use OCP\AppFramework\Http\StreamResponse;
47
use OCP\IUserManager;
48
use OCP\IUserSession;
49
use OCP\Share\Exceptions\ShareNotFound;
50
use OCP\Share\IManager;
51
52
class WopiController extends Controller {
53
	/** @var IRootFolder */
54
	private $rootFolder;
55
	/** @var IURLGenerator */
56
	private $urlGenerator;
57
	/** @var IConfig */
58
	private $config;
59
	/** @var AppConfig */
60
	private $appConfig;
61
	/** @var TokenManager */
62
	private $tokenManager;
63
	/** @var IUserManager */
64
	private $userManager;
65
	/** @var WopiMapper */
66
	private $wopiMapper;
67
	/** @var ILogger */
68
	private $logger;
69
	/** @var IUserSession */
70
	private $userSession;
71
	/** @var TemplateManager */
72
	private $templateManager;
73
	/** @var IManager */
74
	private $shareManager;
75
76
	// Signifies LOOL that document has been changed externally in this storage
77
	const LOOL_STATUS_DOC_CHANGED = 1010;
78
79
	/**
80
	 * @param string $appName
81
	 * @param IRequest $request
82
	 * @param IRootFolder $rootFolder
83
	 * @param IURLGenerator $urlGenerator
84
	 * @param IConfig $config
85
	 * @param TokenManager $tokenManager
86
	 * @param IUserManager $userManager
87
	 * @param WopiMapper $wopiMapper
88
	 * @param ILogger $logger
89
	 * @param IUserSession $userSession
90
	 * @param TemplateManager $templateManager
91
	 */
92 View Code Duplication
	public function __construct(
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
93
		$appName,
94
		IRequest $request,
95
		IRootFolder $rootFolder,
96
		IURLGenerator $urlGenerator,
97
		IConfig $config,
98
		AppConfig $appConfig,
99
		TokenManager $tokenManager,
100
		IUserManager $userManager,
101
		WopiMapper $wopiMapper,
102
		ILogger $logger,
103
		IUserSession $userSession,
104
		TemplateManager $templateManager,
105
		IManager $shareManager
106
	) {
107
		parent::__construct($appName, $request);
108
		$this->rootFolder = $rootFolder;
109
		$this->urlGenerator = $urlGenerator;
110
		$this->config = $config;
111
		$this->appConfig = $appConfig;
112
		$this->tokenManager = $tokenManager;
113
		$this->userManager = $userManager;
114
		$this->wopiMapper = $wopiMapper;
115
		$this->logger = $logger;
116
		$this->userSession = $userSession;
117
		$this->templateManager = $templateManager;
118
		$this->shareManager = $shareManager;
119
	}
120
121
	/**
122
	 * Returns general info about a file.
123
	 *
124
	 * @NoAdminRequired
125
	 * @NoCSRFRequired
126
	 * @PublicPage
127
	 *
128
	 * @param string $fileId
129
	 * @param string $access_token
130
	 * @return JSONResponse
131
	 * @throws InvalidPathException
132
	 * @throws NotFoundException
133
	 */
134
	public function checkFileInfo($fileId, $access_token) {
135
		try {
136
			list($fileId, , $version) = Helper::parseFileId($fileId);
137
138
			$wopi = $this->wopiMapper->getWopiForToken($access_token);
139
			if ($wopi->isTemplateToken()) {
140
				$this->templateManager->setUserId($wopi->getOwnerUid());
141
				$file = $this->templateManager->get($wopi->getFileid());
142
			} else {
143
				$file = $this->getFileForWopiToken($wopi);
144
			}
145
			if(!($file instanceof File)) {
146
				throw new NotFoundException('No valid file found for ' . $fileId);
147
			}
148
		} catch (NotFoundException $e) {
149
			$this->logger->debug($e->getMessage(), ['app' => 'richdocuments', '']);
150
			return new JSONResponse([], Http::STATUS_FORBIDDEN);
151
		} catch (\Exception $e) {
152
			$this->logger->logException($e, ['app' => 'richdocuments']);
0 ignored issues
show
Documentation introduced by
$e is of type object<Exception>, but the function expects a object<Throwable>.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
153
			return new JSONResponse([], Http::STATUS_FORBIDDEN);
154
		}
155
156
		$isPublic = $wopi->getEditorUid() === null;
157
		$guestUserId = 'Guest-' . \OC::$server->getSecureRandom()->generate(8);
158
		$user = $this->userManager->get($wopi->getEditorUid());
159
		$userDisplayName = $user !== null && !$isPublic ? $user->getDisplayName() : $wopi->getGuestDisplayname();
160
		$response = [
161
			'BaseFileName' => $file->getName(),
162
			'Size' => $file->getSize(),
163
			'Version' => $version,
164
			'UserId' => !$isPublic ? $wopi->getEditorUid() : $guestUserId,
165
			'OwnerId' => $wopi->getOwnerUid(),
166
			'UserFriendlyName' => $userDisplayName,
167
			'UserExtraInfo' => [
168
			],
169
			'UserCanWrite' => $wopi->getCanwrite(),
170
			'UserCanNotWriteRelative' => \OC::$server->getEncryptionManager()->isEnabled() || $isPublic,
171
			'PostMessageOrigin' => $wopi->getServerHost(),
172
			'LastModifiedTime' => Helper::toISO8601($file->getMTime()),
173
			'SupportsRename' => true,
174
			'UserCanRename' => !$isPublic,
175
			'EnableInsertRemoteImage' => true,
176
			'EnableShare' => true,
177
			'HideUserList' => 'desktop',
178
			'DisablePrint' => $wopi->getHideDownload(),
0 ignored issues
show
Documentation Bug introduced by
The method getHideDownload does not exist on object<OCA\Richdocuments\Db\Wopi>? Since you implemented __call, maybe consider adding a @method annotation.

If you implement __call and you know which methods are available, you can improve IDE auto-completion and static analysis by adding a @method annotation to the class.

This is often the case, when __call is implemented by a parent class and only the child class knows which methods exist:

class ParentClass {
    private $data = array();

    public function __call($method, array $args) {
        if (0 === strpos($method, 'get')) {
            return $this->data[strtolower(substr($method, 3))];
        }

        throw new \LogicException(sprintf('Unsupported method: %s', $method));
    }
}

/**
 * If this class knows which fields exist, you can specify the methods here:
 *
 * @method string getName()
 */
class SomeClass extends ParentClass { }
Loading history...
179
			'DisableExport' => $wopi->getHideDownload(),
0 ignored issues
show
Documentation Bug introduced by
The method getHideDownload does not exist on object<OCA\Richdocuments\Db\Wopi>? Since you implemented __call, maybe consider adding a @method annotation.

If you implement __call and you know which methods are available, you can improve IDE auto-completion and static analysis by adding a @method annotation to the class.

This is often the case, when __call is implemented by a parent class and only the child class knows which methods exist:

class ParentClass {
    private $data = array();

    public function __call($method, array $args) {
        if (0 === strpos($method, 'get')) {
            return $this->data[strtolower(substr($method, 3))];
        }

        throw new \LogicException(sprintf('Unsupported method: %s', $method));
    }
}

/**
 * If this class knows which fields exist, you can specify the methods here:
 *
 * @method string getName()
 */
class SomeClass extends ParentClass { }
Loading history...
180
			'DisableCopy' => $wopi->getHideDownload(),
0 ignored issues
show
Documentation Bug introduced by
The method getHideDownload does not exist on object<OCA\Richdocuments\Db\Wopi>? Since you implemented __call, maybe consider adding a @method annotation.

If you implement __call and you know which methods are available, you can improve IDE auto-completion and static analysis by adding a @method annotation to the class.

This is often the case, when __call is implemented by a parent class and only the child class knows which methods exist:

class ParentClass {
    private $data = array();

    public function __call($method, array $args) {
        if (0 === strpos($method, 'get')) {
            return $this->data[strtolower(substr($method, 3))];
        }

        throw new \LogicException(sprintf('Unsupported method: %s', $method));
    }
}

/**
 * If this class knows which fields exist, you can specify the methods here:
 *
 * @method string getName()
 */
class SomeClass extends ParentClass { }
Loading history...
181
			'HideExportOption' => $wopi->getHideDownload(),
0 ignored issues
show
Documentation Bug introduced by
The method getHideDownload does not exist on object<OCA\Richdocuments\Db\Wopi>? Since you implemented __call, maybe consider adding a @method annotation.

If you implement __call and you know which methods are available, you can improve IDE auto-completion and static analysis by adding a @method annotation to the class.

This is often the case, when __call is implemented by a parent class and only the child class knows which methods exist:

class ParentClass {
    private $data = array();

    public function __call($method, array $args) {
        if (0 === strpos($method, 'get')) {
            return $this->data[strtolower(substr($method, 3))];
        }

        throw new \LogicException(sprintf('Unsupported method: %s', $method));
    }
}

/**
 * If this class knows which fields exist, you can specify the methods here:
 *
 * @method string getName()
 */
class SomeClass extends ParentClass { }
Loading history...
182
			'HidePrintOption' => $wopi->getHideDownload(),
0 ignored issues
show
Documentation Bug introduced by
The method getHideDownload does not exist on object<OCA\Richdocuments\Db\Wopi>? Since you implemented __call, maybe consider adding a @method annotation.

If you implement __call and you know which methods are available, you can improve IDE auto-completion and static analysis by adding a @method annotation to the class.

This is often the case, when __call is implemented by a parent class and only the child class knows which methods exist:

class ParentClass {
    private $data = array();

    public function __call($method, array $args) {
        if (0 === strpos($method, 'get')) {
            return $this->data[strtolower(substr($method, 3))];
        }

        throw new \LogicException(sprintf('Unsupported method: %s', $method));
    }
}

/**
 * If this class knows which fields exist, you can specify the methods here:
 *
 * @method string getName()
 */
class SomeClass extends ParentClass { }
Loading history...
183
			'DownloadAsPostMessage' => $wopi->getDirect(),
0 ignored issues
show
Documentation Bug introduced by
The method getDirect does not exist on object<OCA\Richdocuments\Db\Wopi>? Since you implemented __call, maybe consider adding a @method annotation.

If you implement __call and you know which methods are available, you can improve IDE auto-completion and static analysis by adding a @method annotation to the class.

This is often the case, when __call is implemented by a parent class and only the child class knows which methods exist:

class ParentClass {
    private $data = array();

    public function __call($method, array $args) {
        if (0 === strpos($method, 'get')) {
            return $this->data[strtolower(substr($method, 3))];
        }

        throw new \LogicException(sprintf('Unsupported method: %s', $method));
    }
}

/**
 * If this class knows which fields exist, you can specify the methods here:
 *
 * @method string getName()
 */
class SomeClass extends ParentClass { }
Loading history...
184
		];
185
186
		if ($wopi->isTemplateToken()) {
187
			$userFolder = $this->rootFolder->getUserFolder($wopi->getOwnerUid());
188
			$file = $userFolder->getById($wopi->getTemplateDestination())[0];
189
			$response['TemplateSaveAs'] = $file->getName();
190
		}
191
192
		if ($this->shouldWatermark($isPublic, $wopi->getEditorUid(), $fileId, $wopi)) {
193
			$replacements = [
194
				'userId' => $wopi->getEditorUid(),
195
				'date' => (new \DateTime())->format('Y-m-d H:i:s'),
196
				'themingName' => \OC::$server->getThemingDefaults()->getName(),
197
198
			];
199
			$watermarkTemplate = $this->appConfig->getAppValue('watermark_text');
200
			$response['WatermarkText'] = preg_replace_callback('/{(.+?)}/', function($matches) use ($replacements)
201
			{
202
				return $replacements[$matches[1]];
203
			}, $watermarkTemplate);
204
		}
205
206
		$user = $this->userManager->get($wopi->getEditorUid());
207
		if($user !== null && $user->getAvatarImage(32) !== null) {
208
			$response['UserExtraInfo']['avatar'] = $this->urlGenerator->linkToRouteAbsolute('core.avatar.getAvatar', ['userId' => $wopi->getEditorUid(), 'size' => 32]);
209
		}
210
211
		if ($wopi->getRemoteServer() !== '') {
0 ignored issues
show
Documentation Bug introduced by
The method getRemoteServer does not exist on object<OCA\Richdocuments\Db\Wopi>? Since you implemented __call, maybe consider adding a @method annotation.

If you implement __call and you know which methods are available, you can improve IDE auto-completion and static analysis by adding a @method annotation to the class.

This is often the case, when __call is implemented by a parent class and only the child class knows which methods exist:

class ParentClass {
    private $data = array();

    public function __call($method, array $args) {
        if (0 === strpos($method, 'get')) {
            return $this->data[strtolower(substr($method, 3))];
        }

        throw new \LogicException(sprintf('Unsupported method: %s', $method));
    }
}

/**
 * If this class knows which fields exist, you can specify the methods here:
 *
 * @method string getName()
 */
class SomeClass extends ParentClass { }
Loading history...
212
			$response = $this->setFederationFileInfo($wopi, $response);
213
		}
214
215
		return new JSONResponse($response);
216
	}
217
218
	private function setFederationFileInfo($wopi, $response) {
219
		$remoteUserId = $wopi->getGuestDisplayname();
220
		$cloudID = \OC::$server->getCloudIdManager()->resolveCloudId($remoteUserId);
221
		$response['UserFriendlyName'] = $cloudID->getDisplayId();
222
		$response['UserExtraInfo']['avatar'] = $this->urlGenerator->linkToRouteAbsolute('core.avatar.getAvatar', ['userId' => explode('@', $remoteUserId)[0], 'size' => 32]);
223
		$cleanCloudId = str_replace(['http://', 'https://'], '', $cloudID->getId());
224
		$addressBookEntries = \OC::$server->getContactsManager()->search($cleanCloudId, ['CLOUD']);
225
		foreach ($addressBookEntries as $entry) {
226
			if (isset($entry['CLOUD'])) {
227
				foreach ($entry['CLOUD'] as $cloudID) {
228
					if ($cloudID === $cleanCloudId) {
229
						$response['UserFriendlyName'] = $entry['FN'];
230
						break;
231
					}
232
				}
233
			}
234
		}
235
		return $response;
236
	}
237
238
	private function shouldWatermark($isPublic, $userId, $fileId, Wopi $wopi) {
239
		if ($this->config->getAppValue(AppConfig::WATERMARK_APP_NAMESPACE, 'watermark_enabled', 'no') === 'no') {
240
			return false;
241
		}
242
243
		if ($isPublic) {
244
			if ($this->config->getAppValue(AppConfig::WATERMARK_APP_NAMESPACE, 'watermark_linkAll', 'no') === 'yes') {
245
				return true;
246
			}
247 View Code Duplication
			if ($this->config->getAppValue(AppConfig::WATERMARK_APP_NAMESPACE, 'watermark_linkRead', 'no') === 'yes' && !$wopi->getCanwrite()) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
248
				return true;
249
			}
250
			if ($this->config->getAppValue(AppConfig::WATERMARK_APP_NAMESPACE, 'watermark_linkSecure', 'no') === 'yes' && $wopi->getHideDownload()) {
0 ignored issues
show
Documentation Bug introduced by
The method getHideDownload does not exist on object<OCA\Richdocuments\Db\Wopi>? Since you implemented __call, maybe consider adding a @method annotation.

If you implement __call and you know which methods are available, you can improve IDE auto-completion and static analysis by adding a @method annotation to the class.

This is often the case, when __call is implemented by a parent class and only the child class knows which methods exist:

class ParentClass {
    private $data = array();

    public function __call($method, array $args) {
        if (0 === strpos($method, 'get')) {
            return $this->data[strtolower(substr($method, 3))];
        }

        throw new \LogicException(sprintf('Unsupported method: %s', $method));
    }
}

/**
 * If this class knows which fields exist, you can specify the methods here:
 *
 * @method string getName()
 */
class SomeClass extends ParentClass { }
Loading history...
251
				return true;
252
			}
253 View Code Duplication
			if ($this->config->getAppValue(AppConfig::WATERMARK_APP_NAMESPACE, 'watermark_linkTags', 'no') === 'yes') {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
254
				$tags = $this->appConfig->getAppValueArray('watermark_linkTagsList');
255
				$fileTags = \OC::$server->getSystemTagObjectMapper()->getTagIdsForObjects([$fileId], 'files')[$fileId];
256
				foreach ($fileTags as $tagId) {
257
					if (in_array($tagId, $tags, true)) {
258
						return true;
259
					}
260
				}
261
			}
262
		} else {
263
			if ($this->config->getAppValue(AppConfig::WATERMARK_APP_NAMESPACE, 'watermark_shareAll', 'no') === 'yes') {
264
				return true;
265
			}
266 View Code Duplication
			if ($this->config->getAppValue(AppConfig::WATERMARK_APP_NAMESPACE, 'watermark_shareRead', 'no') === 'yes' && !$wopi->getCanwrite()) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
267
				return true;
268
			}
269
		}
270
		if ($this->config->getAppValue(AppConfig::WATERMARK_APP_NAMESPACE, 'watermark_allGroups', 'no') === 'yes') {
271
			$groups = $this->appConfig->getAppValueArray('watermark_allGroupsList');
272
			foreach ($groups as $group) {
0 ignored issues
show
Bug introduced by
The expression $groups of type array|string is not guaranteed to be traversable. How about adding an additional type check?

There are different options of fixing this problem.

  1. If you want to be on the safe side, you can add an additional type-check:

    $collection = json_decode($data, true);
    if ( ! is_array($collection)) {
        throw new \RuntimeException('$collection must be an array.');
    }
    
    foreach ($collection as $item) { /** ... */ }
    
  2. If you are sure that the expression is traversable, you might want to add a doc comment cast to improve IDE auto-completion and static analysis:

    /** @var array $collection */
    $collection = json_decode($data, true);
    
    foreach ($collection as $item) { /** .. */ }
    
  3. Mark the issue as a false-positive: Just hover the remove button, in the top-right corner of this issue for more options.

Loading history...
273
				if (\OC::$server->getGroupManager()->isInGroup($userId, $group)) {
274
					return true;
275
				}
276
			}
277
		}
278 View Code Duplication
		if ($this->config->getAppValue(AppConfig::WATERMARK_APP_NAMESPACE, 'watermark_allTags', 'no') === 'yes') {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
279
			$tags = $this->appConfig->getAppValueArray('watermark_allTagsList');
280
			$fileTags = \OC::$server->getSystemTagObjectMapper()->getTagIdsForObjects([$fileId], 'files');
281
			foreach ($fileTags as $tagId) {
282
				if (in_array($tagId, $tags)) {
283
					return true;
284
				}
285
			}
286
		}
287
288
		return false;
289
	}
290
291
	/**
292
	 * Given an access token and a fileId, returns the contents of the file.
293
	 * Expects a valid token in access_token parameter.
294
	 *
295
	 * @PublicPage
296
	 * @NoCSRFRequired
297
	 *
298
	 * @param string $fileId
299
	 * @param string $access_token
300
	 * @return Http\Response
301
	 * @throws DoesNotExistException
302
	 * @throws NotFoundException
303
	 * @throws NotPermittedException
304
	 */
305
	public function getFile($fileId,
306
							$access_token) {
307
		list($fileId, , $version) = Helper::parseFileId($fileId);
308
309
		$wopi = $this->wopiMapper->getWopiForToken($access_token);
310
311
		if ((int)$fileId !== $wopi->getFileid()) {
312
			return new JSONResponse([], Http::STATUS_FORBIDDEN);
313
		}
314
315
		// Template is just returned as there is no version logic
316
		if ($wopi->isTemplateToken()) {
317
			$this->templateManager->setUserId($wopi->getOwnerUid());
318
			$file = $this->templateManager->get($wopi->getFileid());
319
			$response = new StreamResponse($file->fopen('rb'));
320
			$response->addHeader('Content-Disposition', 'attachment');
321
			$response->addHeader('Content-Type', 'application/octet-stream');
322
			return $response;
323
		}
324
325
		try {
326
			/** @var File $file */
327
			$userFolder = $this->rootFolder->getUserFolder($wopi->getOwnerUid());
328
			$file = $userFolder->getById($fileId)[0];
329
			\OC_User::setIncognitoMode(true);
330
			if ($version !== '0') {
331
				$view = new View('/' . $wopi->getOwnerUid() . '/files');
332
				$relPath = $view->getRelativePath($file->getPath());
333
				$versionPath = '/files_versions/' . $relPath . '.v' . $version;
334
				$view = new View('/' . $wopi->getOwnerUid());
335
				if ($view->file_exists($versionPath)){
336
					$response = new StreamResponse($view->fopen($versionPath, 'rb'));
337
				}
338
				else {
339
					return new JSONResponse([], Http::STATUS_NOT_FOUND);
340
				}
341
			}
342
			else
343
			{
344
				$response = new StreamResponse($file->fopen('rb'));
0 ignored issues
show
Bug introduced by
The method fopen() does not seem to exist on object<OCP\Files\Node>.

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
345
			}
346
			$response->addHeader('Content-Disposition', 'attachment');
347
			$response->addHeader('Content-Type', 'application/octet-stream');
348
			return $response;
349
		} catch (\Exception $e) {
350
			$this->logger->logException($e, ['level' => ILogger::ERROR,	'app' => 'richdocuments', 'message' => 'getFile failed']);
0 ignored issues
show
Documentation introduced by
$e is of type object<Exception>, but the function expects a object<Throwable>.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
351
			return new JSONResponse([], Http::STATUS_FORBIDDEN);
352
		}
353
	}
354
355
	/**
356
	 * Given an access token and a fileId, replaces the files with the request body.
357
	 * Expects a valid token in access_token parameter.
358
	 *
359
	 * @PublicPage
360
	 * @NoCSRFRequired
361
	 *
362
	 * @param string $fileId
363
	 * @param string $access_token
364
	 * @return JSONResponse
365
	 * @throws DoesNotExistException
366
	 */
367
	public function putFile($fileId,
368
							$access_token) {
369
		list($fileId, ,) = Helper::parseFileId($fileId);
370
		$isPutRelative = ($this->request->getHeader('X-WOPI-Override') === 'PUT_RELATIVE');
371
		$isRenameFile = ($this->request->getHeader('X-WOPI-Override') === 'RENAME_FILE');
0 ignored issues
show
Unused Code introduced by
$isRenameFile is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
372
373
		$wopi = $this->wopiMapper->getWopiForToken($access_token);
374
		if (!$wopi->getCanwrite()) {
375
			return new JSONResponse([], Http::STATUS_FORBIDDEN);
376
		}
377
378
		try {
379
			if ($isPutRelative) {
380
				// the new file needs to be installed in the current user dir
381
				$userFolder = $this->rootFolder->getUserFolder($wopi->getEditorUid());
382
				$file = $userFolder->getById($fileId)[0];
383
384
				$suggested = $this->request->getHeader('X-WOPI-SuggestedTarget');
385
				$suggested = iconv('utf-7', 'utf-8', $suggested);
386
387 View Code Duplication
				if ($suggested[0] === '.') {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
388
					$path = dirname($file->getPath()) . '/New File' . $suggested;
389
				}
390
				else if ($suggested[0] !== '/') {
391
					$path = dirname($file->getPath()) . '/' . $suggested;
392
				}
393
				else {
394
					$path = $userFolder->getPath() . $suggested;
395
				}
396
397
				if ($path === '') {
398
					return new JSONResponse([
399
						'status' => 'error',
400
						'message' => 'Cannot create the file'
401
					]);
402
				}
403
404
				// create the folder first
405
				if (!$this->rootFolder->nodeExists(dirname($path))) {
406
					$this->rootFolder->newFolder(dirname($path));
407
				}
408
409
				// create a unique new file
410
				$path = $this->rootFolder->getNonExistingName($path);
411
				$this->rootFolder->newFile($path);
412
				$file = $this->rootFolder->get($path);
413
			} else {
414
				$file = $this->getFileForWopiToken($wopi);
415
				$wopiHeaderTime = $this->request->getHeader('X-LOOL-WOPI-Timestamp');
416
				if ($wopiHeaderTime !== null && $wopiHeaderTime !== Helper::toISO8601($file->getMTime())) {
417
					$this->logger->debug('Document timestamp mismatch ! WOPI client says mtime {headerTime} but storage says {storageTime}', [
418
						'headerTime' => $wopiHeaderTime,
419
						'storageTime' => Helper::toISO8601($file->getMTime())
420
					]);
421
					// Tell WOPI client about this conflict.
422
					return new JSONResponse(['LOOLStatusCode' => self::LOOL_STATUS_DOC_CHANGED], Http::STATUS_CONFLICT);
423
				}
424
			}
425
426
			$content = fopen('php://input', 'rb');
427
428
			// Set the user to register the change under his name
429
			$editor = $this->userManager->get($wopi->getEditorUid());
430
			if ($editor !== null) {
431
				$this->userSession->setUser($editor);
432
			}
433
434
			$file->putContent($content);
0 ignored issues
show
Bug introduced by
The method putContent() does not seem to exist on object<OCP\Files\Node>.

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
435
436
			if ($isPutRelative) {
437
				// generate a token for the new file (the user still has to be
438
				// logged in)
439
				list(, $wopiToken) = $this->tokenManager->getToken($file->getId(), null, $wopi->getEditorUid());
440
441
				$wopi = 'index.php/apps/richdocuments/wopi/files/' . $file->getId() . '_' . $this->config->getSystemValue('instanceid') . '?access_token=' . $wopiToken;
442
				$url = $this->urlGenerator->getAbsoluteURL($wopi);
443
444
				return new JSONResponse([ 'Name' => $file->getName(), 'Url' => $url ], Http::STATUS_OK);
445
			}
446
447
			return new JSONResponse(['LastModifiedTime' => Helper::toISO8601($file->getMTime())]);
448
		} catch (\Exception $e) {
449
			$this->logger->logException($e, ['level' => ILogger::ERROR,	'app' => 'richdocuments', 'message' => 'getFile failed']);
0 ignored issues
show
Documentation introduced by
$e is of type object<Exception>, but the function expects a object<Throwable>.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
450
			return new JSONResponse([], Http::STATUS_INTERNAL_SERVER_ERROR);
451
		}
452
	}
453
454
	/**
455
	 * Given an access token and a fileId, replaces the files with the request body.
456
	 * Expects a valid token in access_token parameter.
457
	 * Just actually routes to the PutFile, the implementation of PutFile
458
	 * handles both saving and saving as.* Given an access token and a fileId, replaces the files with the request body.
459
	 *
460
	 * @PublicPage
461
	 * @NoCSRFRequired
462
	 *
463
	 * @param string $fileId
464
	 * @param string $access_token
465
	 * @return JSONResponse
466
	 * @throws DoesNotExistException
467
	 */
468
	public function putRelativeFile($fileId,
469
					$access_token) {
470
		list($fileId, ,) = Helper::parseFileId($fileId);
471
		$wopi = $this->wopiMapper->getWopiForToken($access_token);
472
		$isRenameFile = ($this->request->getHeader('X-WOPI-Override') === 'RENAME_FILE');
473
474
		if (!$wopi->getCanwrite()) {
475
			return new JSONResponse([], Http::STATUS_FORBIDDEN);
476
		}
477
478
		// Unless the editor is empty (public link) we modify the files as the current editor
479
		$editor = $wopi->getEditorUid();
480
		if ($editor === null || $wopi->getRemoteServer() !== '') {
0 ignored issues
show
Documentation Bug introduced by
The method getRemoteServer does not exist on object<OCA\Richdocuments\Db\Wopi>? Since you implemented __call, maybe consider adding a @method annotation.

If you implement __call and you know which methods are available, you can improve IDE auto-completion and static analysis by adding a @method annotation to the class.

This is often the case, when __call is implemented by a parent class and only the child class knows which methods exist:

class ParentClass {
    private $data = array();

    public function __call($method, array $args) {
        if (0 === strpos($method, 'get')) {
            return $this->data[strtolower(substr($method, 3))];
        }

        throw new \LogicException(sprintf('Unsupported method: %s', $method));
    }
}

/**
 * If this class knows which fields exist, you can specify the methods here:
 *
 * @method string getName()
 */
class SomeClass extends ParentClass { }
Loading history...
481
			$editor = $wopi->getOwnerUid();
482
		}
483
484
		try {
485
			// the new file needs to be installed in the current user dir
486
			$userFolder = $this->rootFolder->getUserFolder($editor);
487
488
			if ($wopi->isTemplateToken()) {
489
				$this->templateManager->setUserId($wopi->getOwnerUid());
490
				$file = $userFolder->getById($wopi->getTemplateDestination())[0];
491
			} else if ($isRenameFile) {
492
				// the new file needs to be installed in the current user dir
493
				$userFolder = $this->rootFolder->getUserFolder($wopi->getEditorUid());
494
				$file = $userFolder->getById($fileId)[0];
495
496
				$suggested = $this->request->getHeader('X-WOPI-RequestedName');
497
498
				$suggested = iconv('utf-7', 'utf-8', $suggested) . '.' . $file->getExtension();
499
500
				if (strpos($suggested, '.') === 0) {
501
					$path = dirname($file->getPath()) . '/New File' . $suggested;
502
				}
503
				else if (strpos($suggested, '/') !== 0) {
504
					$path = dirname($file->getPath()) . '/' . $suggested;
505
				}
506
				else {
507
					$path = $userFolder->getPath() . $suggested;
508
				}
509
510
				if ($path === '') {
511
					return new JSONResponse([
512
						'status' => 'error',
513
						'message' => 'Cannot rename the file'
514
					]);
515
				}
516
517
				// create the folder first
518
				if (!$this->rootFolder->nodeExists(dirname($path))) {
519
					$this->rootFolder->newFolder(dirname($path));
520
				}
521
522
				// create a unique new file
523
				$path = $this->rootFolder->getNonExistingName($path);
524
				$file = $file->move($path);
525
			} else {
526
				$file = $userFolder->getById($fileId)[0];
527
528
				$suggested = $this->request->getHeader('X-WOPI-SuggestedTarget');
529
				$suggested = iconv('utf-7', 'utf-8', $suggested);
530
531 View Code Duplication
				if ($suggested[0] === '.') {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
532
					$path = dirname($file->getPath()) . '/New File' . $suggested;
533
				} else if ($suggested[0] !== '/') {
534
					$path = dirname($file->getPath()) . '/' . $suggested;
535
				} else {
536
					$path = $userFolder->getPath() . $suggested;
537
				}
538
539
				if ($path === '') {
540
					return new JSONResponse([
541
						'status' => 'error',
542
						'message' => 'Cannot create the file'
543
					]);
544
				}
545
546
				// create the folder first
547
				if (!$this->rootFolder->nodeExists(dirname($path))) {
548
					$this->rootFolder->newFolder(dirname($path));
549
				}
550
551
				// create a unique new file
552
				$path = $this->rootFolder->getNonExistingName($path);
553
				$file = $this->rootFolder->newFile($path);
554
			}
555
556
			$content = fopen('php://input', 'rb');
557
558
			// Set the user to register the change under his name
559
			$editor = $this->userManager->get($wopi->getEditorUid());
560
			if ($editor !== null) {
561
				$this->userSession->setUser($editor);
562
			}
563
564
			$file->putContent($content);
0 ignored issues
show
Bug introduced by
The method putContent() does not seem to exist on object<OCP\Files\Node>.

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
565
566
			// generate a token for the new file (the user still has to be
567
			// logged in)
568
			list(, $wopiToken) = $this->tokenManager->getToken($file->getId(), null, $wopi->getEditorUid());
569
570
			$wopi = 'index.php/apps/richdocuments/wopi/files/' . $file->getId() . '_' . $this->config->getSystemValue('instanceid') . '?access_token=' . $wopiToken;
571
			$url = $this->urlGenerator->getAbsoluteURL($wopi);
572
573
			return new JSONResponse([ 'Name' => $file->getName(), 'Url' => $url ], Http::STATUS_OK);
574
		} catch (\Exception $e) {
575
			$this->logger->logException($e, ['level' => ILogger::ERROR,	'app' => 'richdocuments', 'message' => 'putRelativeFile failed']);
0 ignored issues
show
Documentation introduced by
$e is of type object<Exception>, but the function expects a object<Throwable>.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
576
			return new JSONResponse([], Http::STATUS_INTERNAL_SERVER_ERROR);
577
		}
578
	}
579
580
	/**
581
	 * @param Wopi $wopi
582
	 * @return File|Folder|Node|null
583
	 * @throws NotFoundException
584
	 * @throws ShareNotFound
585
	 */
586
	private function getFileForWopiToken(Wopi $wopi) {
587
		$file = null;
0 ignored issues
show
Unused Code introduced by
$file is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
588
589
		if ($wopi->getRemoteServer() !== '') {
0 ignored issues
show
Documentation Bug introduced by
The method getRemoteServer does not exist on object<OCA\Richdocuments\Db\Wopi>? Since you implemented __call, maybe consider adding a @method annotation.

If you implement __call and you know which methods are available, you can improve IDE auto-completion and static analysis by adding a @method annotation to the class.

This is often the case, when __call is implemented by a parent class and only the child class knows which methods exist:

class ParentClass {
    private $data = array();

    public function __call($method, array $args) {
        if (0 === strpos($method, 'get')) {
            return $this->data[strtolower(substr($method, 3))];
        }

        throw new \LogicException(sprintf('Unsupported method: %s', $method));
    }
}

/**
 * If this class knows which fields exist, you can specify the methods here:
 *
 * @method string getName()
 */
class SomeClass extends ParentClass { }
Loading history...
590
			$share = $this->shareManager->getShareByToken($wopi->getEditorUid());
591
			$node = $share->getNode();
592
			if ($node instanceof Folder) {
593
				$file = $node->getById($wopi->getFileid())[0];
594
			} else {
595
				$file = $node;
596
			}
597
		} else {
598
			// Unless the editor is empty (public link) we modify the files as the current editor
599
			// TODO: add related share token to the wopi table so we can obtain the
600
			$editor = $wopi->getEditorUid();
601
			if ($editor === null) {
602
				$editor = $wopi->getOwnerUid();
603
			}
604
605
			$userFolder = $this->rootFolder->getUserFolder($editor);
606
			$file = $userFolder->getById($wopi->getFileid())[0];
607
		}
608
		return $file;
609
	}
610
}
611