Completed
Push — master ( 125f9f...2c140b )
by Julius
02:26 queued 10s
created

WopiController::putFile()   C

Complexity

Conditions 12
Paths 179

Size

Total Lines 86

Duplication

Lines 9
Ratio 10.47 %

Code Coverage

Tests 0
CRAP Score 156

Importance

Changes 0
Metric Value
dl 9
loc 86
ccs 0
cts 65
cp 0
rs 5.3521
c 0
b 0
f 0
cc 12
nc 179
nop 2
crap 156

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\Db\WopiMapper;
27
use OCA\Richdocuments\TemplateManager;
28
use OCA\Richdocuments\TokenManager;
29
use OCA\Richdocuments\Helper;
30
use OCP\AppFramework\Controller;
31
use OCP\AppFramework\Db\DoesNotExistException;
32
use OCP\AppFramework\Http;
33
use OCP\AppFramework\Http\JSONResponse;
34
use OCP\Files\File;
35
use OCP\Files\Folder;
36
use OCP\Files\InvalidPathException;
37
use OCP\Files\IRootFolder;
38
use OCP\Files\Node;
39
use OCP\Files\NotFoundException;
40
use OCP\Files\NotPermittedException;
41
use OCP\IConfig;
42
use OCP\ILogger;
43
use OCP\IRequest;
44
use OCP\IURLGenerator;
45
use OCP\AppFramework\Http\StreamResponse;
46
use OCP\IUserManager;
47
use OCP\IUserSession;
48
use OCP\Share\Exceptions\ShareNotFound;
49
use OCP\Share\IManager;
50
51
class WopiController extends Controller {
52
	/** @var IRootFolder */
53
	private $rootFolder;
54
	/** @var IURLGenerator */
55
	private $urlGenerator;
56
	/** @var IConfig */
57
	private $config;
58
	/** @var TokenManager */
59
	private $tokenManager;
60
	/** @var IUserManager */
61
	private $userManager;
62
	/** @var WopiMapper */
63
	private $wopiMapper;
64
	/** @var ILogger */
65
	private $logger;
66
	/** @var IUserSession */
67
	private $userSession;
68
	/** @var TemplateManager */
69
	private $templateManager;
70
	/** @var IManager */
71
	private $shareManager;
72
73
	// Signifies LOOL that document has been changed externally in this storage
74
	const LOOL_STATUS_DOC_CHANGED = 1010;
75
76
	/**
77
	 * @param string $appName
78
	 * @param IRequest $request
79
	 * @param IRootFolder $rootFolder
80
	 * @param IURLGenerator $urlGenerator
81
	 * @param IConfig $config
82
	 * @param TokenManager $tokenManager
83
	 * @param IUserManager $userManager
84
	 * @param WopiMapper $wopiMapper
85
	 * @param ILogger $logger
86
	 * @param IUserSession $userSession
87
	 * @param TemplateManager $templateManager
88
	 */
89 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...
90
		$appName,
91
		IRequest $request,
92
		IRootFolder $rootFolder,
93
		IURLGenerator $urlGenerator,
94
		IConfig $config,
95
		TokenManager $tokenManager,
96
		IUserManager $userManager,
97
		WopiMapper $wopiMapper,
98
		ILogger $logger,
99
		IUserSession $userSession,
100
		TemplateManager $templateManager,
101
		IManager $shareManager
102
	) {
103
		parent::__construct($appName, $request);
104
		$this->rootFolder = $rootFolder;
105
		$this->urlGenerator = $urlGenerator;
106
		$this->config = $config;
107
		$this->tokenManager = $tokenManager;
108
		$this->userManager = $userManager;
109
		$this->wopiMapper = $wopiMapper;
110
		$this->logger = $logger;
111
		$this->userSession = $userSession;
112
		$this->templateManager = $templateManager;
113
		$this->shareManager = $shareManager;
114
	}
115
116
	/**
117
	 * Returns general info about a file.
118
	 *
119
	 * @NoAdminRequired
120
	 * @NoCSRFRequired
121
	 * @PublicPage
122
	 *
123
	 * @param string $fileId
124
	 * @param string $access_token
125
	 * @return JSONResponse
126
	 * @throws InvalidPathException
127
	 * @throws NotFoundException
128
	 */
129
	public function checkFileInfo($fileId, $access_token) {
130
		try {
131
			list($fileId, , $version) = Helper::parseFileId($fileId);
132
133
			$wopi = $this->wopiMapper->getWopiForToken($access_token);
134
			if ($wopi->isTemplateToken()) {
135
				$this->templateManager->setUserId($wopi->getOwnerUid());
136
				$file = $this->templateManager->get($wopi->getFileid());
137
			} else {
138
				$file = $this->getFileForWopiToken($wopi);
139
			}
140
			if(!($file instanceof File)) {
141
				throw new NotFoundException('No valid file found for ' . $fileId);
142
			}
143
		} catch (NotFoundException $e) {
144
			$this->logger->debug($e->getMessage(), ['app' => 'richdocuments', '']);
145
			return new JSONResponse([], Http::STATUS_FORBIDDEN);
146
		} catch (\Exception $e) {
147
			$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...
148
			return new JSONResponse([], Http::STATUS_FORBIDDEN);
149
		}
150
151
		$isPublic = $wopi->getEditorUid() === null;
152
		$guestUserId = 'Guest-' . \OC::$server->getSecureRandom()->generate(8);
153
		$user = $this->userManager->get($wopi->getEditorUid());
154
		$userDisplayName = $user !== null && !$isPublic ? $user->getDisplayName() : $wopi->getGuestDisplayname();
155
		$response = [
156
			'BaseFileName' => $file->getName(),
157
			'Size' => $file->getSize(),
158
			'Version' => $version,
159
			'UserId' => !$isPublic ? $wopi->getEditorUid() : $guestUserId,
160
			'OwnerId' => $wopi->getOwnerUid(),
161
			'UserFriendlyName' => $userDisplayName,
162
			'UserExtraInfo' => [
163
			],
164
			'UserCanWrite' => $wopi->getCanwrite(),
165
			'UserCanNotWriteRelative' => \OC::$server->getEncryptionManager()->isEnabled() || $isPublic,
166
			'PostMessageOrigin' => $wopi->getServerHost(),
167
			'LastModifiedTime' => Helper::toISO8601($file->getMTime()),
168
			'SupportsRename' => true,
169
			'UserCanRename' => !$isPublic,
170
			'EnableInsertRemoteImage' => true,
171
			'EnableShare' => true,
172
			'HideUserList' => 'desktop',
173
			'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...
174
			'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...
175
			'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...
176
			'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...
177
			'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...
178
			'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...
179
		];
180
181
		if ($wopi->isTemplateToken()) {
182
			$userFolder = $this->rootFolder->getUserFolder($wopi->getOwnerUid());
183
			$file = $userFolder->getById($wopi->getTemplateDestination())[0];
184
			$response['TemplateSaveAs'] = $file->getName();
185
		}
186
187
		$user = $this->userManager->get($wopi->getEditorUid());
188
		if($user !== null && $user->getAvatarImage(32) !== null) {
189
			$response['UserExtraInfo']['avatar'] = $this->urlGenerator->linkToRouteAbsolute('core.avatar.getAvatar', ['userId' => $wopi->getEditorUid(), 'size' => 32]);
190
		}
191
192
		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...
193
			$response = $this->setFederationFileInfo($wopi, $response);
194
		}
195
196
		return new JSONResponse($response);
197
	}
198
199
	private function setFederationFileInfo($wopi, $response) {
200
		$remoteUserId = $wopi->getGuestDisplayname();
201
		$cloudID = \OC::$server->getCloudIdManager()->resolveCloudId($remoteUserId);
202
		$response['UserFriendlyName'] = $cloudID->getDisplayId();
203
		$response['UserExtraInfo']['avatar'] = $this->urlGenerator->linkToRouteAbsolute('core.avatar.getAvatar', ['userId' => explode('@', $remoteUserId)[0], 'size' => 32]);
204
		$cleanCloudId = str_replace(['http://', 'https://'], '', $cloudID->getId());
205
		$addressBookEntries = \OC::$server->getContactsManager()->search($cleanCloudId, ['CLOUD']);
206
		foreach ($addressBookEntries as $entry) {
207
			if (isset($entry['CLOUD'])) {
208
				foreach ($entry['CLOUD'] as $cloudID) {
209
					if ($cloudID === $cleanCloudId) {
210
						$response['UserFriendlyName'] = $entry['FN'];
211
						break;
212
					}
213
				}
214
			}
215
		}
216
		return $response;
217
	}
218
219
	/**
220
	 * Given an access token and a fileId, returns the contents of the file.
221
	 * Expects a valid token in access_token parameter.
222
	 *
223
	 * @PublicPage
224
	 * @NoCSRFRequired
225
	 *
226
	 * @param string $fileId
227
	 * @param string $access_token
228
	 * @return Http\Response
229
	 * @throws DoesNotExistException
230
	 * @throws NotFoundException
231
	 * @throws NotPermittedException
232
	 */
233
	public function getFile($fileId,
234
							$access_token) {
235
		list($fileId, , $version) = Helper::parseFileId($fileId);
236
237
		$wopi = $this->wopiMapper->getWopiForToken($access_token);
238
239
		if ((int)$fileId !== $wopi->getFileid()) {
240
			return new JSONResponse([], Http::STATUS_FORBIDDEN);
241
		}
242
243
		// Template is just returned as there is no version logic
244
		if ($wopi->isTemplateToken()) {
245
			$this->templateManager->setUserId($wopi->getOwnerUid());
246
			$file = $this->templateManager->get($wopi->getFileid());
247
			$response = new StreamResponse($file->fopen('rb'));
248
			$response->addHeader('Content-Disposition', 'attachment');
249
			$response->addHeader('Content-Type', 'application/octet-stream');
250
			return $response;
251
		}
252
253
		try {
254
			/** @var File $file */
255
			$userFolder = $this->rootFolder->getUserFolder($wopi->getOwnerUid());
256
			$file = $userFolder->getById($fileId)[0];
257
			\OC_User::setIncognitoMode(true);
258
			if ($version !== '0') {
259
				$view = new View('/' . $wopi->getOwnerUid() . '/files');
260
				$relPath = $view->getRelativePath($file->getPath());
261
				$versionPath = '/files_versions/' . $relPath . '.v' . $version;
262
				$view = new View('/' . $wopi->getOwnerUid());
263
				if ($view->file_exists($versionPath)){
264
					$response = new StreamResponse($view->fopen($versionPath, 'rb'));
265
				}
266
				else {
267
					return new JSONResponse([], Http::STATUS_NOT_FOUND);
268
				}
269
			}
270
			else
271
			{
272
				$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...
273
			}
274
			$response->addHeader('Content-Disposition', 'attachment');
275
			$response->addHeader('Content-Type', 'application/octet-stream');
276
			return $response;
277
		} catch (\Exception $e) {
278
			$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...
279
			return new JSONResponse([], Http::STATUS_FORBIDDEN);
280
		}
281
	}
282
283
	/**
284
	 * Given an access token and a fileId, replaces the files with the request body.
285
	 * Expects a valid token in access_token parameter.
286
	 *
287
	 * @PublicPage
288
	 * @NoCSRFRequired
289
	 *
290
	 * @param string $fileId
291
	 * @param string $access_token
292
	 * @return JSONResponse
293
	 * @throws DoesNotExistException
294
	 */
295
	public function putFile($fileId,
296
							$access_token) {
297
		list($fileId, ,) = Helper::parseFileId($fileId);
298
		$isPutRelative = ($this->request->getHeader('X-WOPI-Override') === 'PUT_RELATIVE');
299
		$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...
300
301
		$wopi = $this->wopiMapper->getWopiForToken($access_token);
302
		if (!$wopi->getCanwrite()) {
303
			return new JSONResponse([], Http::STATUS_FORBIDDEN);
304
		}
305
306
		try {
307
			if ($isPutRelative) {
308
				// the new file needs to be installed in the current user dir
309
				$userFolder = $this->rootFolder->getUserFolder($wopi->getEditorUid());
310
				$file = $userFolder->getById($fileId)[0];
311
312
				$suggested = $this->request->getHeader('X-WOPI-SuggestedTarget');
313
				$suggested = iconv('utf-7', 'utf-8', $suggested);
314
315 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...
316
					$path = dirname($file->getPath()) . '/New File' . $suggested;
317
				}
318
				else if ($suggested[0] !== '/') {
319
					$path = dirname($file->getPath()) . '/' . $suggested;
320
				}
321
				else {
322
					$path = $userFolder->getPath() . $suggested;
323
				}
324
325
				if ($path === '') {
326
					return new JSONResponse([
327
						'status' => 'error',
328
						'message' => 'Cannot create the file'
329
					]);
330
				}
331
332
				// create the folder first
333
				if (!$this->rootFolder->nodeExists(dirname($path))) {
334
					$this->rootFolder->newFolder(dirname($path));
335
				}
336
337
				// create a unique new file
338
				$path = $this->rootFolder->getNonExistingName($path);
339
				$this->rootFolder->newFile($path);
340
				$file = $this->rootFolder->get($path);
341
			} else {
342
				$file = $this->getFileForWopiToken($wopi);
343
				$wopiHeaderTime = $this->request->getHeader('X-LOOL-WOPI-Timestamp');
344
				if ($wopiHeaderTime !== null && $wopiHeaderTime !== Helper::toISO8601($file->getMTime())) {
345
					$this->logger->debug('Document timestamp mismatch ! WOPI client says mtime {headerTime} but storage says {storageTime}', [
346
						'headerTime' => $wopiHeaderTime,
347
						'storageTime' => Helper::toISO8601($file->getMTime())
348
					]);
349
					// Tell WOPI client about this conflict.
350
					return new JSONResponse(['LOOLStatusCode' => self::LOOL_STATUS_DOC_CHANGED], Http::STATUS_CONFLICT);
351
				}
352
			}
353
354
			$content = fopen('php://input', 'rb');
355
356
			// Set the user to register the change under his name
357
			$editor = $this->userManager->get($wopi->getEditorUid());
358
			if ($editor !== null) {
359
				$this->userSession->setUser($editor);
360
			}
361
362
			$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...
363
364
			if ($isPutRelative) {
365
				// generate a token for the new file (the user still has to be
366
				// logged in)
367
				list(, $wopiToken) = $this->tokenManager->getToken($file->getId(), null, $wopi->getEditorUid());
368
369
				$wopi = 'index.php/apps/richdocuments/wopi/files/' . $file->getId() . '_' . $this->config->getSystemValue('instanceid') . '?access_token=' . $wopiToken;
370
				$url = $this->urlGenerator->getAbsoluteURL($wopi);
371
372
				return new JSONResponse([ 'Name' => $file->getName(), 'Url' => $url ], Http::STATUS_OK);
373
			}
374
375
			return new JSONResponse(['LastModifiedTime' => Helper::toISO8601($file->getMTime())]);
376
		} catch (\Exception $e) {
377
			$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...
378
			return new JSONResponse([], Http::STATUS_INTERNAL_SERVER_ERROR);
379
		}
380
	}
381
382
	/**
383
	 * Given an access token and a fileId, replaces the files with the request body.
384
	 * Expects a valid token in access_token parameter.
385
	 * Just actually routes to the PutFile, the implementation of PutFile
386
	 * handles both saving and saving as.* Given an access token and a fileId, replaces the files with the request body.
387
	 *
388
	 * @PublicPage
389
	 * @NoCSRFRequired
390
	 *
391
	 * @param string $fileId
392
	 * @param string $access_token
393
	 * @return JSONResponse
394
	 * @throws DoesNotExistException
395
	 */
396
	public function putRelativeFile($fileId,
397
					$access_token) {
398
		list($fileId, ,) = Helper::parseFileId($fileId);
399
		$wopi = $this->wopiMapper->getWopiForToken($access_token);
400
		$isRenameFile = ($this->request->getHeader('X-WOPI-Override') === 'RENAME_FILE');
401
402
		if (!$wopi->getCanwrite()) {
403
			return new JSONResponse([], Http::STATUS_FORBIDDEN);
404
		}
405
406
		// Unless the editor is empty (public link) we modify the files as the current editor
407
		$editor = $wopi->getEditorUid();
408
		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...
409
			$editor = $wopi->getOwnerUid();
410
		}
411
412
		try {
413
			// the new file needs to be installed in the current user dir
414
			$userFolder = $this->rootFolder->getUserFolder($editor);
415
416
			if ($wopi->isTemplateToken()) {
417
				$this->templateManager->setUserId($wopi->getOwnerUid());
418
				$file = $userFolder->getById($wopi->getTemplateDestination())[0];
419
			} else if ($isRenameFile) {
420
				// the new file needs to be installed in the current user dir
421
				$userFolder = $this->rootFolder->getUserFolder($wopi->getEditorUid());
422
				$file = $userFolder->getById($fileId)[0];
423
424
				$suggested = $this->request->getHeader('X-WOPI-RequestedName');
425
426
				$suggested = iconv('utf-7', 'utf-8', $suggested) . '.' . $file->getExtension();
427
428
				if (strpos($suggested, '.') === 0) {
429
					$path = dirname($file->getPath()) . '/New File' . $suggested;
430
				}
431
				else if (strpos($suggested, '/') !== 0) {
432
					$path = dirname($file->getPath()) . '/' . $suggested;
433
				}
434
				else {
435
					$path = $userFolder->getPath() . $suggested;
436
				}
437
438
				if ($path === '') {
439
					return new JSONResponse([
440
						'status' => 'error',
441
						'message' => 'Cannot rename the file'
442
					]);
443
				}
444
445
				// create the folder first
446
				if (!$this->rootFolder->nodeExists(dirname($path))) {
447
					$this->rootFolder->newFolder(dirname($path));
448
				}
449
450
				// create a unique new file
451
				$path = $this->rootFolder->getNonExistingName($path);
452
				$file = $file->move($path);
453
			} else {
454
				$file = $userFolder->getById($fileId)[0];
455
456
				$suggested = $this->request->getHeader('X-WOPI-SuggestedTarget');
457
				$suggested = iconv('utf-7', 'utf-8', $suggested);
458
459 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...
460
					$path = dirname($file->getPath()) . '/New File' . $suggested;
461
				} else if ($suggested[0] !== '/') {
462
					$path = dirname($file->getPath()) . '/' . $suggested;
463
				} else {
464
					$path = $userFolder->getPath() . $suggested;
465
				}
466
467
				if ($path === '') {
468
					return new JSONResponse([
469
						'status' => 'error',
470
						'message' => 'Cannot create the file'
471
					]);
472
				}
473
474
				// create the folder first
475
				if (!$this->rootFolder->nodeExists(dirname($path))) {
476
					$this->rootFolder->newFolder(dirname($path));
477
				}
478
479
				// create a unique new file
480
				$path = $this->rootFolder->getNonExistingName($path);
481
				$file = $this->rootFolder->newFile($path);
482
			}
483
484
			$content = fopen('php://input', 'rb');
485
486
			// Set the user to register the change under his name
487
			$editor = $this->userManager->get($wopi->getEditorUid());
488
			if ($editor !== null) {
489
				$this->userSession->setUser($editor);
490
			}
491
492
			$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...
493
494
			// generate a token for the new file (the user still has to be
495
			// logged in)
496
			list(, $wopiToken) = $this->tokenManager->getToken($file->getId(), null, $wopi->getEditorUid());
497
498
			$wopi = 'index.php/apps/richdocuments/wopi/files/' . $file->getId() . '_' . $this->config->getSystemValue('instanceid') . '?access_token=' . $wopiToken;
499
			$url = $this->urlGenerator->getAbsoluteURL($wopi);
500
501
			return new JSONResponse([ 'Name' => $file->getName(), 'Url' => $url ], Http::STATUS_OK);
502
		} catch (\Exception $e) {
503
			$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...
504
			return new JSONResponse([], Http::STATUS_INTERNAL_SERVER_ERROR);
505
		}
506
	}
507
508
	/**
509
	 * @param Wopi $wopi
510
	 * @return File|Folder|Node|null
511
	 * @throws NotFoundException
512
	 * @throws ShareNotFound
513
	 */
514
	private function getFileForWopiToken(Wopi $wopi) {
515
		$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...
516
517
		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...
518
			$share = $this->shareManager->getShareByToken($wopi->getEditorUid());
519
			$node = $share->getNode();
520
			if ($node instanceof Folder) {
521
				$file = $node->getById($wopi->getFileid())[0];
522
			} else {
523
				$file = $node;
524
			}
525
		} else {
526
			// Unless the editor is empty (public link) we modify the files as the current editor
527
			// TODO: add related share token to the wopi table so we can obtain the
528
			$editor = $wopi->getEditorUid();
529
			if ($editor === null) {
530
				$editor = $wopi->getOwnerUid();
531
			}
532
533
			$userFolder = $this->rootFolder->getUserFolder($editor);
534
			$file = $userFolder->getById($wopi->getFileid())[0];
535
		}
536
		return $file;
537
	}
538
}
539