Completed
Push — master ( 7c0cd4...d11609 )
by Roeland
15:43 queued 10s
created

ShareController::showAuthenticate()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 17

Duplication

Lines 17
Ratio 100 %

Importance

Changes 0
Metric Value
cc 2
nc 2
nop 0
dl 17
loc 17
rs 9.7
c 0
b 0
f 0
1
<?php
2
/**
3
 * @copyright Copyright (c) 2016, ownCloud, Inc.
4
 *
5
 * @author Arthur Schiwon <[email protected]>
6
 * @author Bjoern Schiessle <[email protected]>
7
 * @author Björn Schießle <[email protected]>
8
 * @author Georg Ehrke <[email protected]>
9
 * @author Joas Schilling <[email protected]>
10
 * @author Lukas Reschke <[email protected]>
11
 * @author Maxence Lange <[email protected]>
12
 * @author Morris Jobke <[email protected]>
13
 * @author Piotr Filiciak <[email protected]>
14
 * @author Robin Appelman <[email protected]>
15
 * @author Roeland Jago Douma <[email protected]>
16
 * @author Sascha Sambale <[email protected]>
17
 * @author Thomas Müller <[email protected]>
18
 * @author Vincent Petry <[email protected]>
19
 *
20
 * @license AGPL-3.0
21
 *
22
 * This code is free software: you can redistribute it and/or modify
23
 * it under the terms of the GNU Affero General Public License, version 3,
24
 * as published by the Free Software Foundation.
25
 *
26
 * This program is distributed in the hope that it will be useful,
27
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
28
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
29
 * GNU Affero General Public License for more details.
30
 *
31
 * You should have received a copy of the GNU Affero General Public License, version 3,
32
 * along with this program.  If not, see <http://www.gnu.org/licenses/>
33
 *
34
 */
35
36
namespace OCA\Files_Sharing\Controller;
37
38
use OC\Security\CSP\ContentSecurityPolicy;
39
use OC_Files;
40
use OC_Util;
41
use OCA\FederatedFileSharing\FederatedShareProvider;
42
use OCP\AppFramework\AuthPublicShareController;
43
use OCP\AppFramework\Http\Template\SimpleMenuAction;
44
use OCP\AppFramework\Http\Template\ExternalShareMenuAction;
45
use OCP\AppFramework\Http\Template\LinkMenuAction;
46
use OCP\AppFramework\Http\Template\PublicTemplateResponse;
47
use OCP\Defaults;
48
use OCP\IL10N;
49
use OCP\Template;
50
use OCP\Share;
51
use OCP\IRequest;
52
use OCP\AppFramework\Http\TemplateResponse;
53
use OCP\AppFramework\Http\NotFoundResponse;
54
use OCP\IURLGenerator;
55
use OCP\IConfig;
56
use OCP\ILogger;
57
use OCP\IUserManager;
58
use OCP\ISession;
59
use OCP\IPreview;
60
use OCA\Files_Sharing\Activity\Providers\Downloads;
61
use OCP\Files\NotFoundException;
62
use OCP\Files\IRootFolder;
63
use OCP\Share\Exceptions\ShareNotFound;
64
use OCP\Util;
65
use Symfony\Component\EventDispatcher\EventDispatcherInterface;
66
use Symfony\Component\EventDispatcher\GenericEvent;
67
use OCP\Share\IManager as ShareManager;
68
69
/**
70
 * Class ShareController
71
 *
72
 * @package OCA\Files_Sharing\Controllers
73
 */
74
class ShareController extends AuthPublicShareController {
75
76
	/** @var IConfig */
77
	protected $config;
78
	/** @var IUserManager */
79
	protected $userManager;
80
	/** @var ILogger */
81
	protected $logger;
82
	/** @var \OCP\Activity\IManager */
83
	protected $activityManager;
84
	/** @var IPreview */
85
	protected $previewManager;
86
	/** @var IRootFolder */
87
	protected $rootFolder;
88
	/** @var FederatedShareProvider */
89
	protected $federatedShareProvider;
90
	/** @var EventDispatcherInterface */
91
	protected $eventDispatcher;
92
	/** @var IL10N */
93
	protected $l10n;
94
	/** @var Defaults */
95
	protected $defaults;
96
	/** @var ShareManager */
97
	protected $shareManager;
98
99
	/** @var Share\IShare */
100
	protected $share;
101
102
	/**
103
	 * @param string $appName
104
	 * @param IRequest $request
105
	 * @param IConfig $config
106
	 * @param IURLGenerator $urlGenerator
107
	 * @param IUserManager $userManager
108
	 * @param ILogger $logger
109
	 * @param \OCP\Activity\IManager $activityManager
110
	 * @param \OCP\Share\IManager $shareManager
111
	 * @param ISession $session
112
	 * @param IPreview $previewManager
113
	 * @param IRootFolder $rootFolder
114
	 * @param FederatedShareProvider $federatedShareProvider
115
	 * @param EventDispatcherInterface $eventDispatcher
116
	 * @param IL10N $l10n
117
	 * @param Defaults $defaults
118
	 */
119
	public function __construct(string $appName,
120
								IRequest $request,
121
								IConfig $config,
122
								IURLGenerator $urlGenerator,
123
								IUserManager $userManager,
124
								ILogger $logger,
125
								\OCP\Activity\IManager $activityManager,
126
								ShareManager $shareManager,
127
								ISession $session,
128
								IPreview $previewManager,
129
								IRootFolder $rootFolder,
130
								FederatedShareProvider $federatedShareProvider,
131
								EventDispatcherInterface $eventDispatcher,
132
								IL10N $l10n,
133
								Defaults $defaults) {
134
		parent::__construct($appName, $request, $session, $urlGenerator);
135
136
		$this->config = $config;
137
		$this->userManager = $userManager;
138
		$this->logger = $logger;
139
		$this->activityManager = $activityManager;
140
		$this->previewManager = $previewManager;
141
		$this->rootFolder = $rootFolder;
142
		$this->federatedShareProvider = $federatedShareProvider;
143
		$this->eventDispatcher = $eventDispatcher;
144
		$this->l10n = $l10n;
145
		$this->defaults = $defaults;
146
		$this->shareManager = $shareManager;
147
	}
148
149
	/**
150
	 * @PublicPage
151
	 * @NoCSRFRequired
152
	 *
153
	 * Show the authentication page
154
	 * The form has to submit to the authenticate method route
155
	 */
156 View Code Duplication
	public function showAuthenticate(): TemplateResponse {
157
		$templateParameters = ['share' => $this->share];
158
159
		$event = new GenericEvent(null, $templateParameters);
160
		$this->eventDispatcher->dispatch('OCA\Files_Sharing::loadAdditionalScripts::publicShareAuth', $event);
161
162
		$response = new TemplateResponse('core', 'publicshareauth', $templateParameters, 'guest');
163
		if ($this->share->getSendPasswordByTalk()) {
164
			$csp = new ContentSecurityPolicy();
165
			$csp->addAllowedConnectDomain('*');
166
			$csp->addAllowedMediaDomain('blob:');
167
			$csp->allowEvalScript(true);
168
			$response->setContentSecurityPolicy($csp);
169
		}
170
171
		return $response;
172
	}
173
174
	/**
175
	 * The template to show when authentication failed
176
	 */
177 View Code Duplication
	protected function showAuthFailed(): TemplateResponse {
178
		$templateParameters = ['share' => $this->share, 'wrongpw' => true];
179
180
		$event = new GenericEvent(null, $templateParameters);
181
		$this->eventDispatcher->dispatch('OCA\Files_Sharing::loadAdditionalScripts::publicShareAuth', $event);
182
183
		$response = new TemplateResponse('core', 'publicshareauth', $templateParameters, 'guest');
184
		if ($this->share->getSendPasswordByTalk()) {
185
			$csp = new ContentSecurityPolicy();
186
			$csp->addAllowedConnectDomain('*');
187
			$csp->addAllowedMediaDomain('blob:');
188
			$csp->allowEvalScript(true);
189
			$response->setContentSecurityPolicy($csp);
190
		}
191
192
		return $response;
193
	}
194
195
	protected function verifyPassword(string $password): bool {
196
		return $this->shareManager->checkPassword($this->share, $password);
197
	}
198
199
	protected function getPasswordHash(): string {
200
		return $this->share->getPassword();
201
	}
202
203 View Code Duplication
	public function isValidToken(): bool {
204
		try {
205
			$this->share = $this->shareManager->getShareByToken($this->getToken());
206
		} catch (ShareNotFound $e) {
207
			return false;
208
		}
209
210
		return true;
211
	}
212
213
	protected function isPasswordProtected(): bool {
214
		return $this->share->getPassword() !== null;
215
	}
216
217
	protected function authSucceeded() {
218
		// For share this was always set so it is still used in other apps
219
		$this->session->set('public_link_authenticated', (string)$this->share->getId());
220
	}
221
222
	protected function authFailed() {
223
		$this->emitAccessShareHook($this->share, 403, 'Wrong password');
224
	}
225
226
	/**
227
	 * throws hooks when a share is attempted to be accessed
228
	 *
229
	 * @param \OCP\Share\IShare|string $share the Share instance if available,
230
	 * otherwise token
231
	 * @param int $errorCode
232
	 * @param string $errorMessage
233
	 * @throws \OC\HintException
234
	 * @throws \OC\ServerNotAvailableException
235
	 */
236
	protected function emitAccessShareHook($share, $errorCode = 200, $errorMessage = '') {
237
		$itemType = $itemSource = $uidOwner = '';
238
		$token = $share;
239
		$exception = null;
240
		if($share instanceof \OCP\Share\IShare) {
241
			try {
242
				$token = $share->getToken();
243
				$uidOwner = $share->getSharedBy();
244
				$itemType = $share->getNodeType();
245
				$itemSource = $share->getNodeId();
246
			} catch (\Exception $e) {
247
				// we log what we know and pass on the exception afterwards
248
				$exception = $e;
249
			}
250
		}
251
		\OC_Hook::emit(Share::class, 'share_link_access', [
252
			'itemType' => $itemType,
253
			'itemSource' => $itemSource,
254
			'uidOwner' => $uidOwner,
255
			'token' => $token,
256
			'errorCode' => $errorCode,
257
			'errorMessage' => $errorMessage,
258
		]);
259
		if(!is_null($exception)) {
260
			throw $exception;
261
		}
262
	}
263
264
	/**
265
	 * Validate the permissions of the share
266
	 *
267
	 * @param Share\IShare $share
268
	 * @return bool
269
	 */
270
	private function validateShare(\OCP\Share\IShare $share) {
271
		return $share->getNode()->isReadable() && $share->getNode()->isShareable();
272
	}
273
274
	/**
275
	 * @PublicPage
276
	 * @NoCSRFRequired
277
	 *
278
279
	 * @param string $path
280
	 * @return TemplateResponse
281
	 * @throws NotFoundException
282
	 * @throws \Exception
283
	 */
284
	public function showShare($path = ''): TemplateResponse {
285
		\OC_User::setIncognitoMode(true);
286
287
		// Check whether share exists
288
		try {
289
			$share = $this->shareManager->getShareByToken($this->getToken());
290
		} catch (ShareNotFound $e) {
291
			$this->emitAccessShareHook($this->getToken(), 404, 'Share not found');
292
			throw new NotFoundException();
293
		}
294
295
		if (!$this->validateShare($share)) {
296
			throw new NotFoundException();
297
		}
298
		// We can't get the path of a file share
299
		try {
300
			if ($share->getNode() instanceof \OCP\Files\File && $path !== '') {
301
				$this->emitAccessShareHook($share, 404, 'Share not found');
302
				throw new NotFoundException();
303
			}
304
		} catch (\Exception $e) {
305
			$this->emitAccessShareHook($share, 404, 'Share not found');
306
			throw $e;
307
		}
308
309
		$shareTmpl = [];
310
		$shareTmpl['displayName'] = $this->userManager->get($share->getShareOwner())->getDisplayName();
311
		$shareTmpl['owner'] = $share->getShareOwner();
312
		$shareTmpl['filename'] = $share->getNode()->getName();
313
		$shareTmpl['directory_path'] = $share->getTarget();
314
		$shareTmpl['note'] = $share->getNote();
315
		$shareTmpl['mimetype'] = $share->getNode()->getMimetype();
316
		$shareTmpl['previewSupported'] = $this->previewManager->isMimeSupported($share->getNode()->getMimetype());
317
		$shareTmpl['dirToken'] = $this->getToken();
318
		$shareTmpl['sharingToken'] = $this->getToken();
319
		$shareTmpl['server2serversharing'] = $this->federatedShareProvider->isOutgoingServer2serverShareEnabled();
320
		$shareTmpl['protected'] = $share->getPassword() !== null ? 'true' : 'false';
321
		$shareTmpl['dir'] = '';
322
		$shareTmpl['nonHumanFileSize'] = $share->getNode()->getSize();
323
		$shareTmpl['fileSize'] = \OCP\Util::humanFileSize($share->getNode()->getSize());
324
325
		// Show file list
326
		$hideFileList = false;
327
		if ($share->getNode() instanceof \OCP\Files\Folder) {
328
			/** @var \OCP\Files\Folder $rootFolder */
329
			$rootFolder = $share->getNode();
330
331
			try {
332
				$folderNode = $rootFolder->get($path);
333
			} catch (\OCP\Files\NotFoundException $e) {
334
				$this->emitAccessShareHook($share, 404, 'Share not found');
335
				throw new NotFoundException();
336
			}
337
338
			$shareTmpl['dir'] = $rootFolder->getRelativePath($folderNode->getPath());
339
340
			/*
341
			 * The OC_Util methods require a view. This just uses the node API
342
			 */
343
			$freeSpace = $share->getNode()->getStorage()->free_space($share->getNode()->getInternalPath());
344 View Code Duplication
			if ($freeSpace < \OCP\Files\FileInfo::SPACE_UNLIMITED) {
345
				$freeSpace = max($freeSpace, 0);
346
			} else {
347
				$freeSpace = (INF > 0) ? INF: PHP_INT_MAX; // work around https://bugs.php.net/bug.php?id=69188
348
			}
349
350
			$hideFileList = !($share->getPermissions() & \OCP\Constants::PERMISSION_READ);
351
			$maxUploadFilesize = $freeSpace;
352
353
			$folder = new Template('files', 'list', '');
354
			$folder->assign('dir', $rootFolder->getRelativePath($folderNode->getPath()));
355
			$folder->assign('dirToken', $this->getToken());
356
			$folder->assign('permissions', \OCP\Constants::PERMISSION_READ);
357
			$folder->assign('isPublic', true);
358
			$folder->assign('hideFileList', $hideFileList);
359
			$folder->assign('publicUploadEnabled', 'no');
360
			$folder->assign('uploadMaxFilesize', $maxUploadFilesize);
361
			$folder->assign('uploadMaxHumanFilesize', \OCP\Util::humanFileSize($maxUploadFilesize));
362
			$folder->assign('freeSpace', $freeSpace);
363
			$folder->assign('usedSpacePercent', 0);
364
			$folder->assign('trash', false);
365
			$shareTmpl['folder'] = $folder->fetchPage();
366
		}
367
368
		$shareTmpl['hideFileList'] = $hideFileList;
369
		$shareTmpl['shareOwner'] = $this->userManager->get($share->getShareOwner())->getDisplayName();
370
		$shareTmpl['downloadURL'] = $this->urlGenerator->linkToRouteAbsolute('files_sharing.sharecontroller.downloadShare', ['token' => $this->getToken()]);
371
		$shareTmpl['shareUrl'] = $this->urlGenerator->linkToRouteAbsolute('files_sharing.sharecontroller.showShare', ['token' => $this->getToken()]);
372
		$shareTmpl['maxSizeAnimateGif'] = $this->config->getSystemValue('max_filesize_animated_gifs_public_sharing', 10);
373
		$shareTmpl['previewEnabled'] = $this->config->getSystemValue('enable_previews', true);
374
		$shareTmpl['previewMaxX'] = $this->config->getSystemValue('preview_max_x', 1024);
375
		$shareTmpl['previewMaxY'] = $this->config->getSystemValue('preview_max_y', 1024);
376
		$shareTmpl['disclaimer'] = $this->config->getAppValue('core', 'shareapi_public_link_disclaimertext', null);
377
		$shareTmpl['previewURL'] = $shareTmpl['downloadURL'];
378
		$ogPreview = '';
0 ignored issues
show
Unused Code introduced by
$ogPreview 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...
379
		if ($shareTmpl['previewSupported']) {
380
			$shareTmpl['previewImage'] = $this->urlGenerator->linkToRouteAbsolute( 'files_sharing.PublicPreview.getPreview',
381
				['x' => 200, 'y' => 200, 'file' => $shareTmpl['directory_path'], 'token' => $shareTmpl['dirToken']]);
382
			$ogPreview = $shareTmpl['previewImage'];
383
384
			// We just have direct previews for image files
385
			if ($share->getNode()->getMimePart() === 'image') {
386
				$shareTmpl['previewURL'] = $this->urlGenerator->linkToRouteAbsolute('files_sharing.publicpreview.directLink', ['token' => $this->getToken()]);
387
388
				$ogPreview = $shareTmpl['previewURL'];
389
390
				//Whatapp is kind of picky about their size requirements
391
				if ($this->request->isUserAgent(['/^WhatsApp/'])) {
392
					$ogPreview = $this->urlGenerator->linkToRouteAbsolute('files_sharing.PublicPreview.getPreview', [
393
						'token' => $this->getToken(),
394
						'x' => 256,
395
						'y' => 256,
396
						'a' => true,
397
					]);
398
				}
399
			}
400
		} else {
401
			$shareTmpl['previewImage'] = $this->urlGenerator->getAbsoluteURL($this->urlGenerator->imagePath('core', 'favicon-fb.png'));
402
			$ogPreview = $shareTmpl['previewImage'];
403
		}
404
405
		// Load files we need
406
		\OCP\Util::addScript('files', 'file-upload');
407
		\OCP\Util::addStyle('files_sharing', 'publicView');
408
		\OCP\Util::addScript('files_sharing', 'public');
409
		\OCP\Util::addScript('files_sharing', 'templates');
410
		\OCP\Util::addScript('files_sharing', 'public_note');
411
		\OCP\Util::addScript('files', 'fileactions');
412
		\OCP\Util::addScript('files', 'fileactionsmenu');
413
		\OCP\Util::addScript('files', 'jquery.fileupload');
414
		\OCP\Util::addScript('files_sharing', 'files_drop');
415
416
		if (isset($shareTmpl['folder'])) {
417
			// JS required for folders
418
			\OCP\Util::addStyle('files', 'merged');
419
			\OCP\Util::addScript('files', 'filesummary');
420
			\OCP\Util::addScript('files', 'templates');
421
			\OCP\Util::addScript('files', 'breadcrumb');
422
			\OCP\Util::addScript('files', 'fileinfomodel');
423
			\OCP\Util::addScript('files', 'newfilemenu');
424
			\OCP\Util::addScript('files', 'files');
425
			\OCP\Util::addScript('files', 'filemultiselectmenu');
426
			\OCP\Util::addScript('files', 'filelist');
427
			\OCP\Util::addScript('files', 'keyboardshortcuts');
428
		}
429
430
		// OpenGraph Support: http://ogp.me/
431
		\OCP\Util::addHeader('meta', ['property' => "og:title", 'content' => $shareTmpl['filename']]);
432
		\OCP\Util::addHeader('meta', ['property' => "og:description", 'content' => $this->defaults->getName() . ($this->defaults->getSlogan() !== '' ? ' - ' . $this->defaults->getSlogan() : '')]);
433
		\OCP\Util::addHeader('meta', ['property' => "og:site_name", 'content' => $this->defaults->getName()]);
434
		\OCP\Util::addHeader('meta', ['property' => "og:url", 'content' => $shareTmpl['shareUrl']]);
435
		\OCP\Util::addHeader('meta', ['property' => "og:type", 'content' => "object"]);
436
		\OCP\Util::addHeader('meta', ['property' => "og:image", 'content' => $ogPreview]);
437
438
		$this->eventDispatcher->dispatch('OCA\Files_Sharing::loadAdditionalScripts');
439
440
		$csp = new \OCP\AppFramework\Http\ContentSecurityPolicy();
0 ignored issues
show
Deprecated Code introduced by
The class OCP\AppFramework\Http\ContentSecurityPolicy has been deprecated with message: 14.0.0 Use one of our stricter CSP policies

This class, trait or interface has been deprecated. The supplier of the file has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the type will be removed from the class and what other constant to use instead.

Loading history...
441
		$csp->addAllowedFrameDomain('\'self\'');
442
443
		$response = new PublicTemplateResponse($this->appName, 'public', $shareTmpl);
444
		$response->setHeaderTitle($shareTmpl['filename']);
445
		$response->setHeaderDetails($this->l10n->t('shared by %s', [$shareTmpl['displayName']]));
446
		$response->setHeaderActions([
447
			new SimpleMenuAction('download', $this->l10n->t('Download'), 'icon-download-white', $shareTmpl['downloadURL'], 0),
448
			new SimpleMenuAction('download', $this->l10n->t('Download'), 'icon-download', $shareTmpl['downloadURL'], 10, $shareTmpl['fileSize']),
449
			new LinkMenuAction($this->l10n->t('Direct link'), 'icon-public', $shareTmpl['previewURL']),
450
			new ExternalShareMenuAction($this->l10n->t('Add to your Nextcloud'), 'icon-external', $shareTmpl['owner'], $shareTmpl['displayName'], $shareTmpl['filename']),
451
		]);
452
453
		$response->setContentSecurityPolicy($csp);
454
455
		$this->emitAccessShareHook($share);
456
457
		return $response;
458
	}
459
460
	/**
461
	 * @PublicPage
462
	 * @NoCSRFRequired
463
	 *
464
	 * @param string $token
465
	 * @param string $files
466
	 * @param string $path
467
	 * @param string $downloadStartSecret
468
	 * @return void|\OCP\AppFramework\Http\Response
469
	 * @throws NotFoundException
470
	 */
471
	public function downloadShare($token, $files = null, $path = '', $downloadStartSecret = '') {
472
		\OC_User::setIncognitoMode(true);
473
474
		$share = $this->shareManager->getShareByToken($token);
475
476
		if(!($share->getPermissions() & \OCP\Constants::PERMISSION_READ)) {
477
			return new \OCP\AppFramework\Http\DataResponse('Share is read-only');
0 ignored issues
show
Documentation introduced by
'Share is read-only' is of type string, but the function expects a array|object.

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...
478
		}
479
480
		$files_list = null;
481
		if (!is_null($files)) { // download selected files
482
			$files_list = json_decode($files);
483
			// in case we get only a single file
484
			if ($files_list === null) {
485
				$files_list = [$files];
486
			}
487
			// Just in case $files is a single int like '1234'
488
			if (!is_array($files_list)) {
489
				$files_list = [$files_list];
490
			}
491
		}
492
493
494
		if (!$this->validateShare($share)) {
495
			throw new NotFoundException();
496
		}
497
498
		$userFolder = $this->rootFolder->getUserFolder($share->getShareOwner());
499
		$originalSharePath = $userFolder->getRelativePath($share->getNode()->getPath());
500
501
502
		// Single file share
503
		if ($share->getNode() instanceof \OCP\Files\File) {
504
			// Single file download
505
			$this->singleFileDownloaded($share, $share->getNode());
506
		}
507
		// Directory share
508
		else {
509
			/** @var \OCP\Files\Folder $node */
510
			$node = $share->getNode();
511
512
			// Try to get the path
513
			if ($path !== '') {
514
				try {
515
					$node = $node->get($path);
516
				} catch (NotFoundException $e) {
517
					$this->emitAccessShareHook($share, 404, 'Share not found');
518
					return new NotFoundResponse();
519
				}
520
			}
521
522
			$originalSharePath = $userFolder->getRelativePath($node->getPath());
523
524
			if ($node instanceof \OCP\Files\File) {
525
				// Single file download
526
				$this->singleFileDownloaded($share, $share->getNode());
527
			} else if (!empty($files_list)) {
528
				$this->fileListDownloaded($share, $files_list, $node);
0 ignored issues
show
Compatibility introduced by
$node of type object<OCP\Files\Node> is not a sub-type of object<OCP\Files\Folder>. It seems like you assume a child interface of the interface OCP\Files\Node to be always present.

This check looks for parameters that are defined as one type in their type hint or doc comment but seem to be used as a narrower type, i.e an implementation of an interface or a subclass.

Consider changing the type of the parameter or doing an instanceof check before assuming your parameter is of the expected type.

Loading history...
529
			} else {
530
				// The folder is downloaded
531
				$this->singleFileDownloaded($share, $share->getNode());
532
			}
533
		}
534
535
		/* FIXME: We should do this all nicely in OCP */
536
		OC_Util::tearDownFS();
537
		OC_Util::setupFS($share->getShareOwner());
538
539
		/**
540
		 * this sets a cookie to be able to recognize the start of the download
541
		 * the content must not be longer than 32 characters and must only contain
542
		 * alphanumeric characters
543
		 */
544 View Code Duplication
		if (!empty($downloadStartSecret)
545
			&& !isset($downloadStartSecret[32])
546
			&& preg_match('!^[a-zA-Z0-9]+$!', $downloadStartSecret) === 1) {
547
548
			// FIXME: set on the response once we use an actual app framework response
549
			setcookie('ocDownloadStarted', $downloadStartSecret, time() + 20, '/');
550
		}
551
552
		$this->emitAccessShareHook($share);
553
554
		$server_params = array( 'head' => $this->request->getMethod() === 'HEAD' );
555
556
		/**
557
		 * Http range requests support
558
		 */
559
		if (isset($_SERVER['HTTP_RANGE'])) {
560
			$server_params['range'] = $this->request->getHeader('Range');
561
		}
562
563
		// download selected files
564
		if (!is_null($files) && $files !== '') {
565
			// FIXME: The exit is required here because otherwise the AppFramework is trying to add headers as well
566
			// after dispatching the request which results in a "Cannot modify header information" notice.
567
			OC_Files::get($originalSharePath, $files_list, $server_params);
0 ignored issues
show
Documentation introduced by
$files_list is of type array|null, but the function expects a string.

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...
568
			exit();
569
		} else {
570
			// FIXME: The exit is required here because otherwise the AppFramework is trying to add headers as well
571
			// after dispatching the request which results in a "Cannot modify header information" notice.
572
			OC_Files::get(dirname($originalSharePath), basename($originalSharePath), $server_params);
573
			exit();
574
		}
575
	}
576
577
	/**
578
	 * create activity for every downloaded file
579
	 *
580
	 * @param Share\IShare $share
581
	 * @param array $files_list
582
	 * @param \OCP\Files\Folder $node
583
	 */
584
	protected function fileListDownloaded(Share\IShare $share, array $files_list, \OCP\Files\Folder $node) {
585
		foreach ($files_list as $file) {
586
			$subNode = $node->get($file);
587
			$this->singleFileDownloaded($share, $subNode);
588
		}
589
590
	}
591
592
	/**
593
	 * create activity if a single file was downloaded from a link share
594
	 *
595
	 * @param Share\IShare $share
596
	 */
597
	protected function singleFileDownloaded(Share\IShare $share, \OCP\Files\Node $node) {
598
599
		$fileId = $node->getId();
600
601
		$userFolder = $this->rootFolder->getUserFolder($share->getSharedBy());
602
		$userNodeList = $userFolder->getById($fileId);
603
		$userNode = $userNodeList[0];
604
		$ownerFolder = $this->rootFolder->getUserFolder($share->getShareOwner());
605
		$userPath = $userFolder->getRelativePath($userNode->getPath());
606
		$ownerPath = $ownerFolder->getRelativePath($node->getPath());
607
608
		$parameters = [$userPath];
609
610
		if ($share->getShareType() === \OCP\Share::SHARE_TYPE_EMAIL) {
611
			if ($node instanceof \OCP\Files\File) {
612
				$subject = Downloads::SUBJECT_SHARED_FILE_BY_EMAIL_DOWNLOADED;
613
			} else {
614
				$subject = Downloads::SUBJECT_SHARED_FOLDER_BY_EMAIL_DOWNLOADED;
615
			}
616
			$parameters[] = $share->getSharedWith();
617
		} else {
618
			if ($node instanceof \OCP\Files\File) {
619
				$subject = Downloads::SUBJECT_PUBLIC_SHARED_FILE_DOWNLOADED;
620
			} else {
621
				$subject = Downloads::SUBJECT_PUBLIC_SHARED_FOLDER_DOWNLOADED;
622
			}
623
		}
624
625
		$this->publishActivity($subject, $parameters, $share->getSharedBy(), $fileId, $userPath);
626
627
		if ($share->getShareOwner() !== $share->getSharedBy()) {
628
			$parameters[0] = $ownerPath;
629
			$this->publishActivity($subject, $parameters, $share->getShareOwner(), $fileId, $ownerPath);
630
		}
631
	}
632
633
	/**
634
	 * publish activity
635
	 *
636
	 * @param string $subject
637
	 * @param array $parameters
638
	 * @param string $affectedUser
639
	 * @param int $fileId
640
	 * @param string $filePath
641
	 */
642 View Code Duplication
	protected function publishActivity($subject,
643
										array $parameters,
644
										$affectedUser,
645
										$fileId,
646
										$filePath) {
647
648
		$event = $this->activityManager->generateEvent();
649
		$event->setApp('files_sharing')
650
			->setType('public_links')
651
			->setSubject($subject, $parameters)
652
			->setAffectedUser($affectedUser)
653
			->setObject('files', $fileId, $filePath);
654
		$this->activityManager->publish($event);
655
	}
656
657
658
}
659