Completed
Push — master ( b3e4d4...6fd95a )
by Lukas
02:38
created

DocumentController::getWopiUrl()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 10
Code Lines 7

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 6

Importance

Changes 0
Metric Value
dl 0
loc 10
ccs 0
cts 7
cp 0
rs 9.4285
c 0
b 0
f 0
cc 2
eloc 7
nc 2
nop 1
crap 6
1
<?php
2
/**
3
 * ownCloud - Richdocuments App
4
 *
5
 * @author Victor Dubiniuk
6
 * @copyright 2014 Victor Dubiniuk [email protected]
7
 *
8
 * This file is licensed under the Affero General Public License version 3 or
9
 * later.
10
 */
11
12
namespace OCA\Richdocuments\Controller;
13
14
use \OCP\AppFramework\Controller;
15
use \OCP\IRequest;
16
use \OCP\IConfig;
17
use \OCP\IL10N;
18
use \OCP\AppFramework\Http\ContentSecurityPolicy;
19
use \OCP\AppFramework\Http\JSONResponse;
20
use \OCP\AppFramework\Http\TemplateResponse;
21
22
use \OCA\Richdocuments\AppConfig;
23
use \OCA\Richdocuments\Db;
24
use \OCA\Richdocuments\Helper;
25
use \OCA\Richdocuments\Storage;
26
use \OCA\Richdocuments\Download;
27
use \OCA\Richdocuments\DownloadResponse;
28
use \OCA\Richdocuments\File;
29
use \OCA\Richdocuments\Genesis;
30
use \OC\Files\View;
31
use \OCP\ICacheFactory;
32
use \OCP\ILogger;
33
34
class ResponseException extends \Exception {
35
	private $hint;
36
37
	public function __construct($description, $hint = '') {
38
		parent::__construct($description);
39
		$this->hint = $hint;
40
	}
41
42
	public function getHint() {
43
		return $this->hint;
44
	}
45
}
46
47
class DocumentController extends Controller {
48
49
	private $uid;
50
	private $l10n;
51
	private $settings;
52
	private $appConfig;
53
	private $cache;
54
	private $logger;
55
	const ODT_TEMPLATE_PATH = '/assets/odttemplate.odt';
56
57 1
	public function __construct($appName, IRequest $request, IConfig $settings, AppConfig $appConfig, IL10N $l10n, $uid, ICacheFactory $cache, ILogger $logger){
58 1
		parent::__construct($appName, $request);
59 1
		$this->uid = $uid;
60 1
		$this->l10n = $l10n;
61 1
		$this->settings = $settings;
62 1
		$this->appConfig = $appConfig;
63 1
		$this->cache = $cache->create($appName);
64 1
		$this->logger = $logger;
65 1
	}
66
67
	/**
68
	 * @param \SimpleXMLElement $discovery
0 ignored issues
show
Documentation introduced by
There is no parameter named $discovery. Did you maybe mean $discovery_parsed?

This check looks for PHPDoc comments describing methods or function parameters that do not exist on the corresponding method or function. It has, however, found a similar but not annotated parameter which might be a good fit.

Consider the following example. The parameter $ireland is not defined by the method finale(...).

/**
 * @param array $germany
 * @param array $ireland
 */
function finale($germany, $island) {
    return "2:1";
}

The most likely cause is that the parameter was changed, but the annotation was not.

Loading history...
69
	 * @param string $mimetype
70
	 */
71
	private function getWopiSrcUrl($discovery_parsed, $mimetype) {
72
		if(is_null($discovery_parsed) || $discovery_parsed == false) {
73
			return null;
74
		}
75
76
		$result = $discovery_parsed->xpath(sprintf('/wopi-discovery/net-zone/app[@name=\'%s\']/action', $mimetype));
77
		if ($result && count($result) > 0) {
78
			return array(
79
				'urlsrc' => (string)$result[0]['urlsrc'],
80
				'action' => (string)$result[0]['name']
81
			);
82
		}
83
84
		return null;
85
	}
86
87
	/**
88
	 * Log the user with given $userid.
89
	 * This function should only be used from public controller methods where no
90
	 * existing session exists, for example, when loolwsd is directly calling a
91
	 * public method with its own access token. After validating the access
92
	 * token, and retrieving the correct user with help of access token, it can
93
	 * be set as current user with help of this method.
94
	 *
95
	 * @param string $userid
96
	 */
97
	private function loginUser($userid) {
98
		\OC_Util::tearDownFS();
99
100
		$users = \OC::$server->getUserManager()->search($userid, 1, 0);
101
		if (count($users) > 0) {
102
			$user = array_shift($users);
103
			if (strcasecmp($user->getUID(), $userid) === 0) {
104
				// clear the existing sessions, if any
105
				\OC::$server->getSession()->close();
106
107
				// initialize a dummy memory session
108
				$session = new \OC\Session\Memory('');
109
				// wrap it
110
				$cryptoWrapper = \OC::$server->getSessionCryptoWrapper();
111
				$session = $cryptoWrapper->wrapSession($session);
112
				// set our session
113
				\OC::$server->setSession($session);
114
115
				\OC::$server->getUserSession()->setUser($user);
116
			}
117
		}
118
119
		\OC_Util::setupFS();
120
	}
121
122
	/**
123
	 * Log out the current user
124
	 * This is helpful when we are artifically logged in as someone
125
	 */
126
	private function logoutUser() {
127
		\OC_Util::tearDownFS();
128
129
		\OC::$server->getSession()->close();
130
	}
131
132
	private function responseError($message, $hint = ''){
133
		$errors = array('errors' => array(array('error' => $message, 'hint' => $hint)));
134
		$response = new TemplateResponse('', 'error', $errors, 'error');
135
		return $response;
136
	}
137
138
    /**
139
     * Return the original wopi url or test wopi url
140
     */
141
	private function getWopiUrl($tester) {
142
		$wopiurl = '';
0 ignored issues
show
Unused Code introduced by
$wopiurl 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...
143
		if ($tester) {
144
			$wopiurl = $this->appConfig->getAppValue('test_wopi_url');
145
		} else {
146
			$wopiurl = $this->appConfig->getAppValue('wopi_url');
147
		}
148
149
		return $wopiurl;
150
	}
151
152
	/**
153
     * Return true if the currently logged in user is a tester.
154
     * This depends on whether current user is the member of one of the groups
155
     * mentioned in settings (test_server_groups)
156
     */
157
     private function isTester() {
158
		 $tester = false;
159
160
         $user = \OC::$server->getUserSession()->getUser()->getUID();
161
         $testgroups = array_filter(explode('|', $this->appConfig->getAppValue('test_server_groups')));
162
         \OC::$server->getLogger()->debug('Testgroups are {testgroups}', [
163
             'app' => $this->appName,
164
             'testgroups' => $testgroups
165
         ]);
166 View Code Duplication
         foreach ($testgroups as $testgroup) {
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...
167
             $test = \OC::$server->getGroupManager()->get($testgroup);
168
             if ($test !== null && sizeof($test->searchUsers($user)) > 0) {
169
                 \OC::$server->getLogger()->debug('User {user} found in {group}', [
170
                     'app' => $this->appName,
171
                     'user' => $user,
172
                     'group' => $testgroup
173
                 ]);
174
175
				 $tester = true;
176
				 break;
177
             }
178
         }
179
180
         return $tester;
181
     }
182
183
	/** Return the content of discovery.xml - either from cache, or download it.
184
	 */
185
	private function getDiscovery(){
186
		\OC::$server->getLogger()->debug('getDiscovery(): Getting discovery.xml from the cache.');
187
188
		$tester = $this->isTester();
189
		$wopiRemote = $this->getWopiUrl($tester);
190
		$discoveryKey = 'discovery.xml';
191
		if ($tester) {
192
			$discoveryKey = 'discovery.xml_test';
193
		}
194
		// Provides access to information about the capabilities of a WOPI client
195
		// and the mechanisms for invoking those abilities through URIs.
196
		$wopiDiscovery = $wopiRemote . '/hosting/discovery';
197
198
		// Read the memcached value (if the memcache is installed)
199
		$discovery = $this->cache->get($discoveryKey);
200
201
		if (is_null($discovery)) {
202
			$contact_admin = $this->l10n->t('Please contact the "%s" administrator.', array($wopiRemote));
203
204
			try {
205
				$wopiClient = \OC::$server->getHTTPClientService()->newClient();
206
				$discovery = $wopiClient->get($wopiDiscovery)->getBody();
207
			}
208
			catch (\Exception $e) {
209
				$error_message = $e->getMessage();
210
				if (preg_match('/^cURL error ([0-9]*):/', $error_message, $matches)) {
211
					$admin_check = $this->l10n->t('Please ask your administrator to check the Collabora Online server setting. The exact error message was: ') . $error_message;
212
213
					$curl_error = $matches[1];
214
					switch ($curl_error) {
215
					case '1':
216
						throw new ResponseException($this->l10n->t('Collabora Online: The protocol specified in "%s" is not allowed.', array($wopiRemote)), $admin_check);
217
					case '3':
218
						throw new ResponseException($this->l10n->t('Collabora Online: Malformed URL "%s".', array($wopiRemote)), $admin_check);
219
					case '6':
220
						throw new ResponseException($this->l10n->t('Collabora Online: Cannot resolve the host "%s".', array($wopiRemote)), $admin_check);
221
					case '7':
222
						throw new ResponseException($this->l10n->t('Collabora Online: Cannot connect to the host "%s".', array($wopiRemote)), $admin_check);
223
					case '60':
224
						throw new ResponseException($this->l10n->t('Collabora Online: SSL certificate is not installed.'), $this->l10n->t('Please ask your administrator to add ca-chain.cert.pem to the ca-bundle.crt, for example "cat /etc/loolwsd/ca-chain.cert.pem >> <server-installation>/resources/config/ca-bundle.crt" . The exact error message was: ') . $error_message);
225
					}
226
				}
227
				throw new ResponseException($this->l10n->t('Collabora Online unknown error: ') . $error_message, $contact_admin);
228
			}
229
230
			if (!$discovery) {
231
				throw new ResponseException($this->l10n->t('Collabora Online: Unable to read discovery.xml from "%s".', array($wopiRemote)), $contact_admin);
232
			}
233
234
			\OC::$server->getLogger()->debug('Storing the discovery.xml under key ' . $discoveryKey . ' to the cache.');
235
			$this->cache->set($discoveryKey, $discovery, 3600);
236
		}
237
238
		return $discovery;
239
	}
240
241
	/** Prepare document(s) structure
242
	 */
243
	private function prepareDocuments($rawDocuments){
244
		$discovery_parsed = null;
245
		try {
246
			$discovery = $this->getDiscovery();
247
248
			$loadEntities = libxml_disable_entity_loader(true);
249
			$discovery_parsed = simplexml_load_string($discovery);
250
			libxml_disable_entity_loader($loadEntities);
251
252 View Code Duplication
			if ($discovery_parsed === false) {
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...
253
				$this->cache->remove('discovery.xml');
254
				$wopiRemote = $this->getWopiUrl($this->isTester());
255
256
				return array(
257
					'status' => 'error',
258
					'message' => $this->l10n->t('Collabora Online: discovery.xml from "%s" is not a well-formed XML string.', array($wopiRemote)),
259
					'hint' => $this->l10n->t('Please contact the "%s" administrator.', array($wopiRemote))
260
				);
261
			}
262
		}
263
		catch (ResponseException $e) {
264
			return array(
265
				'status' => 'error',
266
				'message' => $e->getMessage(),
267
				'hint' => $e->getHint()
268
			);
269
		}
270
271
		$fileIds = array();
272
		$documents = array();
273
		$lolang = strtolower(str_replace('_', '-', $this->settings->getUserValue($this->uid, 'core', 'lang', 'en')));
274
		foreach ($rawDocuments as $key=>$document) {
275
			if (is_object($document)){
276
				$documents[] = $document->getData();
277
			} else {
278
				$documents[$key] = $document;
279
			}
280
			$documents[$key]['icon'] = preg_replace('/\.png$/', '.svg', \OCP\Template::mimetype_icon($document['mimetype']));
281
			$documents[$key]['hasPreview'] = \OC::$server->getPreviewManager()->isMimeSupported($document['mimetype']);
282
			$ret = $this->getWopiSrcUrl($discovery_parsed, $document['mimetype']);
283
			$documents[$key]['urlsrc'] = $ret['urlsrc'];
284
			$documents[$key]['action'] = $ret['action'];
285
			$documents[$key]['lolang'] = $lolang;
286
			$fileIds[] = $document['fileid'];
287
		}
288
289
		usort($documents, function($a, $b){
290
			return @$b['mtime']-@$a['mtime'];
291
		});
292
293
		$session = new Db\Session();
294
		$sessions = $session->getCollectionBy('file_id', $fileIds);
295
296
		$members = array();
297
		$member = new Db\Member();
298
		foreach ($sessions as $session) {
299
			$members[$session['es_id']] = $member->getActiveCollection($session['es_id']);
300
		}
301
302
		return array(
303
			'status' => 'success', 'documents' => $documents,'sessions' => $sessions,'members' => $members
304
		);
305
	}
306
307
	/**
308
	 * @NoAdminRequired
309
	 * @NoCSRFRequired
310
	 */
311
	public function index(){
312
		$wopiRemote = $this->getWopiUrl($this->isTester());
313
		if (($parts = parse_url($wopiRemote)) && isset($parts['scheme']) && isset($parts['host'])) {
314
			$webSocketProtocol = "ws://";
315
			if ($parts['scheme'] == "https") {
316
				$webSocketProtocol = "wss://";
317
			}
318
			$webSocket = sprintf(
319
				"%s%s%s",
320
				$webSocketProtocol,
321
				$parts['host'],
322
				isset($parts['port']) ? ":" . $parts['port'] : "");
323
		}
324
		else {
325
			return $this->responseError($this->l10n->t('Collabora Online: Invalid URL "%s".', array($wopiRemote)), $this->l10n->t('Please ask your administrator to check the Collabora Online server setting.'));
326
		}
327
328
		$user = \OC::$server->getUserSession()->getUser();
329
		$usergroups = array_filter(\OC::$server->getGroupManager()->getUserGroupIds($user));
330
		$usergroups = join('|', $usergroups);
331
		\OC::$server->getLogger()->debug('User is in groups: {groups}', [ 'app' => $this->appName, 'groups' => $usergroups ]);
332
333
		\OC::$server->getNavigationManager()->setActiveEntry( 'richdocuments_index' );
334
		$maxUploadFilesize = \OCP\Util::maxUploadFilesize("/");
335
		$response = new TemplateResponse('richdocuments', 'documents', [
336
			'enable_previews' =>		$this->settings->getSystemValue('enable_previews', true),
337
			'uploadMaxFilesize' =>		$maxUploadFilesize,
338
			'uploadMaxHumanFilesize' =>	\OCP\Util::humanFileSize($maxUploadFilesize),
339
			'allowShareWithLink' =>		$this->settings->getAppValue('core', 'shareapi_allow_links', 'yes'),
340
			'wopi_url' =>			$webSocket,
341
			'doc_format' =>			$this->appConfig->getAppValue('doc_format')
342
		]);
343
344
		$policy = new ContentSecurityPolicy();
345
		$policy->addAllowedScriptDomain('\'self\' http://ajax.googleapis.com/ajax/libs/jquery/2.1.0/jquery.min.js http://cdnjs.cloudflare.com/ajax/libs/jquery-mousewheel/3.1.12/jquery.mousewheel.min.js \'unsafe-eval\' ' . $wopiRemote);
346
		/* frame-src is deprecated on Firefox, but Safari wants it! */
347
		$policy->addAllowedFrameDomain('\'self\' http://ajax.googleapis.com/ajax/libs/jquery/2.1.0/jquery.min.js http://cdnjs.cloudflare.com/ajax/libs/jquery-mousewheel/3.1.12/jquery.mousewheel.min.js \'unsafe-eval\' ' . $wopiRemote . ' blob:');
348
		$policy->addAllowedChildSrcDomain('\'self\' http://ajax.googleapis.com/ajax/libs/jquery/2.1.0/jquery.min.js http://cdnjs.cloudflare.com/ajax/libs/jquery-mousewheel/3.1.12/jquery.mousewheel.min.js \'unsafe-eval\' ' . $wopiRemote);
349
		$policy->addAllowedConnectDomain($webSocket);
350
		$policy->addAllowedImageDomain('*');
351
		$policy->allowInlineScript(true);
352
		$policy->addAllowedFontDomain('data:');
353
		$response->setContentSecurityPolicy($policy);
354
355
		return $response;
356
	}
357
358
	/**
359
	 * @NoAdminRequired
360
	 */
361
	public function create(){
362
		$mimetype = $this->request->post['mimetype'];
363
		$filename = $this->request->post['filename'];
364
		$dir = $this->request->post['dir'];
365
366
		$view = new View('/' . $this->uid . '/files');
367
		if (!$dir){
368
			$dir = '/';
369
		}
370
371
		$basename = $this->l10n->t('New Document.odt');
372
		switch ($mimetype) {
373
			case 'application/vnd.oasis.opendocument.spreadsheet':
374
				$basename = $this->l10n->t('New Spreadsheet.ods');
375
				break;
376
			case 'application/vnd.oasis.opendocument.presentation':
377
				$basename = $this->l10n->t('New Presentation.odp');
378
				break;
379
			case 'application/vnd.openxmlformats-officedocument.wordprocessingml.document':
380
				$basename = $this->l10n->t('New Document.docx');
381
				break;
382
			case 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet':
383
				$basename = $this->l10n->t('New Spreadsheet.xlsx');
384
				break;
385
			case 'application/vnd.openxmlformats-officedocument.presentationml.presentation':
386
				$basename = $this->l10n->t('New Presentation.pptx');
387
				break;
388
			default:
389
				// to be safe
390
				$mimetype = 'application/vnd.oasis.opendocument.text';
391
				break;
392
		}
393
394
		if (!$filename){
395
			$path = Helper::getNewFileName($view, $dir . '/' . $basename);
396
		} else {
397
			$path = $dir . '/' . $filename;
398
		}
399
400
		$content = '';
401
		if (class_exists('\OC\Files\Type\TemplateManager')){
402
			$manager = \OC_Helper::getFileTemplateManager();
403
			$content = $manager->getTemplate($mimetype);
404
		}
405
406
		if (!$content){
407
			$content = file_get_contents(dirname(__DIR__) . self::ODT_TEMPLATE_PATH);
408
		}
409
410
		$discovery_parsed = null;
411
		try {
412
			$discovery = $this->getDiscovery();
413
414
			$loadEntities = libxml_disable_entity_loader(true);
415
			$discovery_parsed = simplexml_load_string($discovery);
416
			libxml_disable_entity_loader($loadEntities);
417
418 View Code Duplication
			if ($discovery_parsed === false) {
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...
419
				$this->cache->remove('discovery.xml');
420
				$wopiRemote = $this->getWopiUrl($this->isTester());
421
422
				return array(
423
					'status' => 'error',
424
					'message' => $this->l10n->t('Collabora Online: discovery.xml from "%s" is not a well-formed XML string.', array($wopiRemote)),
425
					'hint' => $this->l10n->t('Please contact the "%s" administrator.', array($wopiRemote))
426
				);
427
			}
428
		}
429
		catch (ResponseException $e) {
430
			return array(
431
				'status' => 'error',
432
				'message' => $e->getMessage(),
433
				'hint' => $e->getHint()
434
			);
435
		}
436
437
		if ($content && $view->file_put_contents($path, $content)){
438
			$info = $view->getFileInfo($path);
439
			$ret = $this->getWopiSrcUrl($discovery_parsed, $mimetype);
440
			$response =  array(
441
				'status' => 'success',
442
				'fileid' => $info['fileid'],
443
				'urlsrc' => $ret['urlsrc'],
444
				'action' => $ret['action'],
445
				'lolang' => $this->settings->getUserValue($this->uid, 'core', 'lang', 'en'),
446
				'data' => \OCA\Files\Helper::formatFileInfo($info)
447
			);
448
		} else {
449
			$response =  array(
450
				'status' => 'error',
451
				'message' => (string) $this->l10n->t('Can\'t create document')
452
			);
453
		}
454
		return $response;
455
	}
456
457
	/**
458
	 * @NoAdminRequired
459
	 * Generates and returns an access token for a given fileId.
460
	 * Only for authenticated users!
461
	 */
462
	public function wopiGetToken($fileId){
463
		$arr = explode('_', $fileId, 2);
464
		$version = '0';
465 View Code Duplication
		if (count($arr) == 2) {
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...
466
			$fileId = $arr[0];
467
			$version = $arr[1];
468
		}
469
470
		\OC::$server->getLogger()->debug('Generating WOPI Token for file {fileId}, version {version}.', [ 'app' => $this->appName, 'fileId' => $fileId, 'version' => $version ]);
471
472
		$view = \OC\Files\Filesystem::getView();
473
		$path = $view->getPath($fileId);
474
		$updatable = (bool)$view->isUpdatable($path);
475
476
		// Check if the editor (user who is accessing) is in editable group
477
		// UserCanWrite only if
478
		// 1. No edit groups are set or
479
		// 2. if they are set, it is in one of the edit groups
480
		$editorUid = \OC::$server->getUserSession()->getUser()->getUID();
481
		$editGroups = array_filter(explode('|', $this->appConfig->getAppValue('edit_groups')));
482
		if ($updatable && count($editGroups) > 0) {
483
			$updatable = false;
484 View Code Duplication
			foreach($editGroups as $editGroup) {
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...
485
				$editorGroup = \OC::$server->getGroupManager()->get($editGroup);
486
				if ($editorGroup !== null && sizeof($editorGroup->searchUsers($editorUid)) > 0) {
487
					\OC::$server->getLogger()->debug("Editor {editor} is in edit group {group}", [
488
						'app' => $this->appName,
489
						'editor' => $editorUid,
490
						'group' => $editGroup
491
					]);
492
					$updatable = true;
493
					break;
494
				}
495
			}
496
		}
497
498
		// If token is for some versioned file
499
		if ($version !== '0') {
500
			\OC::$server->getLogger()->debug('setting updatable to false');
501
			$updatable = false;
502
		}
503
504
		\OC::$server->getLogger()->debug('File with {fileid} has updatable set to {updatable}', [ 'app' => $this->appName, 'fileid' => $fileId, 'updatable' => $updatable ]);
505
506
		$row = new Db\Wopi();
507
		$serverHost = $this->request->getServerProtocol() . '://' . $this->request->getServerHost();
508
		$token = $row->generateFileToken($fileId, $version, $updatable, $serverHost);
509
510
		// Return the token.
511
		return array(
512
			'status' => 'success',
513
			'token' => $token
514
		);
515
	}
516
517
	/**
518
	 * @NoAdminRequired
519
	 * @NoCSRFRequired
520
	 * @PublicPage
521
	 * Returns general info about a file.
522
	 */
523
	public function wopiCheckFileInfo($fileId){
524
		$token = $this->request->getParam('access_token');
525
526
		$arr = explode('_', $fileId, 2);
527
		$version = '0';
528 View Code Duplication
		if (count($arr) == 2) {
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...
529
			$fileId = $arr[0];
530
			$version = $arr[1];
531
		}
532
533
		\OC::$server->getLogger()->debug('Getting info about file {fileId}, version {version} by token {token}.', [ 'app' => $this->appName, 'fileId' => $fileId, 'version' => $version, 'token' => $token ]);
534
535
		$row = new Db\Wopi();
536
		$row->loadBy('token', $token);
537
538
		$res = $row->getPathForToken($fileId, $version, $token);
539
		if ($res == false || http_response_code() != 200)
540
		{
541
			return false;
542
		}
543
544
		// Login the user to see his mount locations
545
		$this->loginUser($res['owner']);
546
		$view = new \OC\Files\View('/' . $res['owner'] . '/files');
547
		$info = $view->getFileInfo($res['path']);
548
		$this->logoutUser();
549
550
		if (!$info) {
551
			http_response_code(404);
552
			return false;
553
		}
554
555
		$editorName = \OC::$server->getUserManager()->get($res['editor'])->getDisplayName();
556
		return array(
557
			'BaseFileName' => $info['name'],
558
			'Size' => $info['size'],
559
			'Version' => $version,
560
			'UserId' => $res['editor'],
561
			'UserFriendlyName' => $editorName,
562
			'UserCanWrite' => $res['canwrite'] ? true : false,
563
			'PostMessageOrigin' => $res['server_host']
564
		);
565
	}
566
567
	/**
568
	 * @NoAdminRequired
569
	 * @NoCSRFRequired
570
	 * @PublicPage
571
	 * Given an access token and a fileId, returns the contents of the file.
572
	 * Expects a valid token in access_token parameter.
573
	 */
574
	public function wopiGetFile($fileId){
575
		$token = $this->request->getParam('access_token');
576
577
		$arr = explode('_', $fileId, 2);
578
		$version = '0';
579 View Code Duplication
		if (count($arr) == 2) {
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...
580
			$fileId = $arr[0];
581
			$version = $arr[1];
582
		}
583
584
		\OC::$server->getLogger()->debug('Getting contents of file {fileId}, version {version} by token {token}.', [ 'app' => $this->appName, 'fileId' => $fileId, 'version' => $version, 'token' => $token ]);
585
586
		$row = new Db\Wopi();
587
		$row->loadBy('token', $token);
588
589
		//TODO: Support X-WOPIMaxExpectedSize header.
590
		$res = $row->getPathForToken($fileId, $version, $token);
591
		$ownerid = $res['owner'];
592
593
		// Login the user to see his mount locations
594
		$this->loginUser($ownerid);
595
		$view = new \OC\Files\View('/' . $res['owner'] . '/files');
596
		$info = $view->getFileInfo($res['path']);
597
598
		if (!$info) {
599
			http_response_code(404);
600
			return false;
601
		}
602
603
		$filename = '';
0 ignored issues
show
Unused Code introduced by
$filename 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...
604
		// If some previous version is requested, fetch it from Files_Version app
605
		if ($version !== '0') {
606
			\OCP\JSON::checkAppEnabled('files_versions');
607
608
			$filename = '/files_versions/' . $info['name'] . '.v' . $version;
609
		} else {
610
			$filename = '/files' . $res['path'];
611
		}
612
613
		$this->logoutUser();
614
615
		return new DownloadResponse($this->request, $ownerid, $filename);
616
	}
617
618
	/**
619
	 * @NoAdminRequired
620
	 * @NoCSRFRequired
621
	 * @PublicPage
622
	 * Given an access token and a fileId, replaces the files with the request body.
623
	 * Expects a valid token in access_token parameter.
624
	 */
625
	public function wopiPutFile($fileId){
626
		$token = $this->request->getParam('access_token');
627
628
		$arr = explode('_', $fileId, 2);
629
		$version = '0';
630 View Code Duplication
		if (count($arr) == 2) {
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...
631
			$fileId = $arr[0];
632
			$version = $arr[1];
633
		}
634
635
		\OC::$server->getLogger()->debug('Putting contents of file {fileId}, version {version} by token {token}.', [ 'app' => $this->appName, 'fileId' => $fileId, 'version' => $version, 'token' => $token ]);
636
637
		$row = new Db\Wopi();
638
		$row->loadBy('token', $token);
639
640
		$res = $row->getPathForToken($fileId, $version, $token);
641
		if (!$res['canwrite']) {
642
			return array(
643
				'status' => 'error',
644
				'message' => 'Permission denied'
645
			);
646
		}
647
648
		// Log-in as the user to regiser the change under her name.
649
		$editorid = $res['editor'];
650
		// This call is made from loolwsd, so we need to initialize the
651
		// session before we can make the user who opened the document
652
		// login. This is necessary to make activity app register the
653
		// change made to this file under this user's (editorid) name.
654
		$this->loginUser($editorid);
655
656
		// Set up the filesystem view for the owner (where the file actually is).
657
		$userid = $res['owner'];
658
		$root = '/' . $userid . '/files';
659
		$view = new \OC\Files\View($root);
660
661
		// Read the contents of the file from the POST body and store.
662
		$content = fopen('php://input', 'r');
663
		\OC::$server->getLogger()->debug('Storing file {fileId} by {editor} owned by {owner}.', [ 'app' => $this->appName, 'fileId' => $fileId, 'editor' => $editorid, 'owner' => $userid ]);
664
665
		// Setup the FS which is needed to emit hooks (versioning).
666
		\OC_Util::tearDownFS();
667
		\OC_Util::setupFS($userid);
668
669
		$view->file_put_contents($res['path'], $content);
670
671
		$this->logoutUser();
672
673
		return array(
674
			'status' => 'success'
675
		);
676
	}
677
678
	/**
679
	 * @NoAdminRequired
680
	 * @PublicPage
681
	 * Process partial/complete file download
682
	 */
683
	public function serve($esId){
684
		$session = new Db\Session();
685
		$session->load($esId);
686
687
		$filename = $session->getGenesisUrl() ? $session->getGenesisUrl() : '';
688
		return new DownloadResponse($this->request, $session->getOwner(), $filename);
689
	}
690
691
	/**
692
	 * @NoAdminRequired
693
	 */
694
	public function download($path){
695
		if (!$path){
696
			$response = new JSONResponse();
697
			$response->setStatus(Http::STATUS_BAD_REQUEST);
698
			return $response;
699
		}
700
701
		$fullPath = '/files' . $path;
702
		$fileInfo = \OC\Files\Filesystem::getFileInfo($path);
703
		if ($fileInfo){
704
			$file = new File($fileInfo->getId());
705
			$genesis = new Genesis($file);
706
			$fullPath = $genesis->getPath();
707
		}
708
		return new DownloadResponse($this->request, $this->uid, $fullPath);
709
	}
710
711
712
	/**
713
	 * @NoAdminRequired
714
	 */
715 1
	public function rename($fileId){
716 1
		$name = $this->request->post['name'];
717
718 1
		$view = \OC\Files\Filesystem::getView();
719 1
		$path = $view->getPath($fileId);
720
721
		if ($name && $view->is_file($path) && $view->isUpdatable($path)) {
722
			$newPath = dirname($path) . '/' . $name;
723
			if ($view->rename($path, $newPath)) {
724
						return array('status' => 'success');
725
			}
726
		}
727
		return array(
728
			'status' => 'error',
729
			'message' => (string) $this->l10n->t('You don\'t have permission to rename this document')
730
		);
731
	}
732
733
	/**
734
	 * @NoAdminRequired
735
	 * Get file information about single document with fileId
736
	 */
737
	public function get($fileId){
738
		$documents = array();
739
		$documents[0] = Storage::getDocumentById($fileId);
740
741
		return $this->prepareDocuments($documents);
742
	}
743
744
745
	/**
746
	 * @NoAdminRequired
747
	 * lists the documents the user has access to (including shared files, once the code in core has been fixed)
748
	 * also adds session and member info for these files
749
	 */
750
	public function listAll(){
751
		return $this->prepareDocuments(Storage::getDocuments());
752
	}
753
}
754