Passed
Push — master ( 3eb5ac...514185 )
by Roeland
15:00
created

ShareController::isValidToken()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 8
Code Lines 5

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 2
eloc 5
nc 2
nop 0
dl 0
loc 8
rs 10
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
	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
	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
	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';
0 ignored issues
show
introduced by
The condition $share->getPassword() !== null is always true.
Loading history...
321
		$shareTmpl['dir'] = '';
322
		$shareTmpl['nonHumanFileSize'] = $share->getNode()->getSize();
323
		$shareTmpl['fileSize'] = \OCP\Util::humanFileSize($share->getNode()->getSize());
324
		$shareTmpl['hideDownload'] = $share->getHideDownload();
325
326
		// Show file list
327
		$hideFileList = false;
328
		if ($share->getNode() instanceof \OCP\Files\Folder) {
329
			/** @var \OCP\Files\Folder $rootFolder */
330
			$rootFolder = $share->getNode();
331
332
			try {
333
				$folderNode = $rootFolder->get($path);
334
			} catch (\OCP\Files\NotFoundException $e) {
335
				$this->emitAccessShareHook($share, 404, 'Share not found');
336
				throw new NotFoundException();
337
			}
338
339
			$shareTmpl['dir'] = $rootFolder->getRelativePath($folderNode->getPath());
340
341
			/*
342
			 * The OC_Util methods require a view. This just uses the node API
343
			 */
344
			$freeSpace = $share->getNode()->getStorage()->free_space($share->getNode()->getInternalPath());
345
			if ($freeSpace < \OCP\Files\FileInfo::SPACE_UNLIMITED) {
346
				$freeSpace = max($freeSpace, 0);
347
			} else {
348
				$freeSpace = (INF > 0) ? INF: PHP_INT_MAX; // work around https://bugs.php.net/bug.php?id=69188
349
			}
350
351
			$hideFileList = !($share->getPermissions() & \OCP\Constants::PERMISSION_READ);
352
			$maxUploadFilesize = $freeSpace;
353
354
			$folder = new Template('files', 'list', '');
355
			$folder->assign('dir', $rootFolder->getRelativePath($folderNode->getPath()));
356
			$folder->assign('dirToken', $this->getToken());
357
			$folder->assign('permissions', \OCP\Constants::PERMISSION_READ);
358
			$folder->assign('isPublic', true);
359
			$folder->assign('hideFileList', $hideFileList);
360
			$folder->assign('publicUploadEnabled', 'no');
361
			$folder->assign('showgridview', true);
362
			$folder->assign('uploadMaxFilesize', $maxUploadFilesize);
363
			$folder->assign('uploadMaxHumanFilesize', \OCP\Util::humanFileSize($maxUploadFilesize));
0 ignored issues
show
Bug introduced by
It seems like $maxUploadFilesize can also be of type double; however, parameter $bytes of OCP\Util::humanFileSize() does only seem to accept integer, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

363
			$folder->assign('uploadMaxHumanFilesize', \OCP\Util::humanFileSize(/** @scrutinizer ignore-type */ $maxUploadFilesize));
Loading history...
364
			$folder->assign('freeSpace', $freeSpace);
365
			$folder->assign('usedSpacePercent', 0);
366
			$folder->assign('trash', false);
367
			$shareTmpl['folder'] = $folder->fetchPage();
368
		}
369
370
		$shareTmpl['hideFileList'] = $hideFileList;
371
		$shareTmpl['shareOwner'] = $this->userManager->get($share->getShareOwner())->getDisplayName();
372
		$shareTmpl['downloadURL'] = $this->urlGenerator->linkToRouteAbsolute('files_sharing.sharecontroller.downloadShare', ['token' => $this->getToken()]);
373
		$shareTmpl['shareUrl'] = $this->urlGenerator->linkToRouteAbsolute('files_sharing.sharecontroller.showShare', ['token' => $this->getToken()]);
374
		$shareTmpl['maxSizeAnimateGif'] = $this->config->getSystemValue('max_filesize_animated_gifs_public_sharing', 10);
375
		$shareTmpl['previewEnabled'] = $this->config->getSystemValue('enable_previews', true);
376
		$shareTmpl['previewMaxX'] = $this->config->getSystemValue('preview_max_x', 1024);
377
		$shareTmpl['previewMaxY'] = $this->config->getSystemValue('preview_max_y', 1024);
378
		$shareTmpl['disclaimer'] = $this->config->getAppValue('core', 'shareapi_public_link_disclaimertext', null);
379
		$shareTmpl['previewURL'] = $shareTmpl['downloadURL'];
380
		$ogPreview = '';
381
		if ($shareTmpl['previewSupported']) {
382
			$shareTmpl['previewImage'] = $this->urlGenerator->linkToRouteAbsolute( 'files_sharing.PublicPreview.getPreview',
383
				['x' => 200, 'y' => 200, 'file' => $shareTmpl['directory_path'], 'token' => $shareTmpl['dirToken']]);
384
			$ogPreview = $shareTmpl['previewImage'];
385
386
			// We just have direct previews for image files
387
			if ($share->getNode()->getMimePart() === 'image') {
388
				$shareTmpl['previewURL'] = $this->urlGenerator->linkToRouteAbsolute('files_sharing.publicpreview.directLink', ['token' => $this->getToken()]);
389
390
				$ogPreview = $shareTmpl['previewURL'];
391
392
				//Whatapp is kind of picky about their size requirements
393
				if ($this->request->isUserAgent(['/^WhatsApp/'])) {
394
					$ogPreview = $this->urlGenerator->linkToRouteAbsolute('files_sharing.PublicPreview.getPreview', [
395
						'token' => $this->getToken(),
396
						'x' => 256,
397
						'y' => 256,
398
						'a' => true,
399
					]);
400
				}
401
			}
402
		} else {
403
			$shareTmpl['previewImage'] = $this->urlGenerator->getAbsoluteURL($this->urlGenerator->imagePath('core', 'favicon-fb.png'));
404
			$ogPreview = $shareTmpl['previewImage'];
405
		}
406
407
		// Load files we need
408
		\OCP\Util::addScript('files', 'file-upload');
409
		\OCP\Util::addStyle('files_sharing', 'publicView');
410
		\OCP\Util::addScript('files_sharing', 'public');
411
		\OCP\Util::addScript('files_sharing', 'templates');
412
		\OCP\Util::addScript('files_sharing', 'public_note');
413
		\OCP\Util::addScript('files', 'fileactions');
414
		\OCP\Util::addScript('files', 'fileactionsmenu');
415
		\OCP\Util::addScript('files', 'jquery.fileupload');
416
		\OCP\Util::addScript('files_sharing', 'files_drop');
417
418
		if (isset($shareTmpl['folder'])) {
419
			// JS required for folders
420
			\OCP\Util::addStyle('files', 'merged');
421
			\OCP\Util::addScript('files', 'filesummary');
422
			\OCP\Util::addScript('files', 'templates');
423
			\OCP\Util::addScript('files', 'breadcrumb');
424
			\OCP\Util::addScript('files', 'fileinfomodel');
425
			\OCP\Util::addScript('files', 'newfilemenu');
426
			\OCP\Util::addScript('files', 'files');
427
			\OCP\Util::addScript('files', 'filemultiselectmenu');
428
			\OCP\Util::addScript('files', 'filelist');
429
			\OCP\Util::addScript('files', 'keyboardshortcuts');
430
		}
431
432
		// OpenGraph Support: http://ogp.me/
433
		\OCP\Util::addHeader('meta', ['property' => "og:title", 'content' => $shareTmpl['filename']]);
434
		\OCP\Util::addHeader('meta', ['property' => "og:description", 'content' => $this->defaults->getName() . ($this->defaults->getSlogan() !== '' ? ' - ' . $this->defaults->getSlogan() : '')]);
435
		\OCP\Util::addHeader('meta', ['property' => "og:site_name", 'content' => $this->defaults->getName()]);
436
		\OCP\Util::addHeader('meta', ['property' => "og:url", 'content' => $shareTmpl['shareUrl']]);
437
		\OCP\Util::addHeader('meta', ['property' => "og:type", 'content' => "object"]);
438
		\OCP\Util::addHeader('meta', ['property' => "og:image", 'content' => $ogPreview]);
439
440
		$this->eventDispatcher->dispatch('OCA\Files_Sharing::loadAdditionalScripts');
441
442
		$csp = new \OCP\AppFramework\Http\ContentSecurityPolicy();
0 ignored issues
show
Deprecated Code introduced by
The class OCP\AppFramework\Http\ContentSecurityPolicy has been deprecated: 14.0.0 Use one of our stricter CSP policies ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-deprecated  annotation

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

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

481
			return new \OCP\AppFramework\Http\DataResponse(/** @scrutinizer ignore-type */ 'Share is read-only');
Loading history...
482
		}
483
484
		$files_list = null;
485
		if (!is_null($files)) { // download selected files
486
			$files_list = json_decode($files);
487
			// in case we get only a single file
488
			if ($files_list === null) {
489
				$files_list = [$files];
490
			}
491
			// Just in case $files is a single int like '1234'
492
			if (!is_array($files_list)) {
493
				$files_list = [$files_list];
494
			}
495
		}
496
497
498
		if (!$this->validateShare($share)) {
499
			throw new NotFoundException();
500
		}
501
502
		$userFolder = $this->rootFolder->getUserFolder($share->getShareOwner());
503
		$originalSharePath = $userFolder->getRelativePath($share->getNode()->getPath());
504
505
506
		// Single file share
507
		if ($share->getNode() instanceof \OCP\Files\File) {
508
			// Single file download
509
			$this->singleFileDownloaded($share, $share->getNode());
510
		}
511
		// Directory share
512
		else {
513
			/** @var \OCP\Files\Folder $node */
514
			$node = $share->getNode();
515
516
			// Try to get the path
517
			if ($path !== '') {
518
				try {
519
					$node = $node->get($path);
520
				} catch (NotFoundException $e) {
521
					$this->emitAccessShareHook($share, 404, 'Share not found');
522
					return new NotFoundResponse();
523
				}
524
			}
525
526
			$originalSharePath = $userFolder->getRelativePath($node->getPath());
527
528
			if ($node instanceof \OCP\Files\File) {
529
				// Single file download
530
				$this->singleFileDownloaded($share, $share->getNode());
531
			} else if (!empty($files_list)) {
532
				$this->fileListDownloaded($share, $files_list, $node);
533
			} else {
534
				// The folder is downloaded
535
				$this->singleFileDownloaded($share, $share->getNode());
536
			}
537
		}
538
539
		/* FIXME: We should do this all nicely in OCP */
540
		OC_Util::tearDownFS();
541
		OC_Util::setupFS($share->getShareOwner());
542
543
		/**
544
		 * this sets a cookie to be able to recognize the start of the download
545
		 * the content must not be longer than 32 characters and must only contain
546
		 * alphanumeric characters
547
		 */
548
		if (!empty($downloadStartSecret)
549
			&& !isset($downloadStartSecret[32])
550
			&& preg_match('!^[a-zA-Z0-9]+$!', $downloadStartSecret) === 1) {
551
552
			// FIXME: set on the response once we use an actual app framework response
553
			setcookie('ocDownloadStarted', $downloadStartSecret, time() + 20, '/');
554
		}
555
556
		$this->emitAccessShareHook($share);
557
558
		$server_params = array( 'head' => $this->request->getMethod() === 'HEAD' );
559
560
		/**
561
		 * Http range requests support
562
		 */
563
		if (isset($_SERVER['HTTP_RANGE'])) {
564
			$server_params['range'] = $this->request->getHeader('Range');
565
		}
566
567
		// download selected files
568
		if (!is_null($files) && $files !== '') {
569
			// FIXME: The exit is required here because otherwise the AppFramework is trying to add headers as well
570
			// after dispatching the request which results in a "Cannot modify header information" notice.
571
			OC_Files::get($originalSharePath, $files_list, $server_params);
0 ignored issues
show
Bug introduced by
It seems like $files_list can also be of type array<integer,mixed> and array<integer,string>; however, parameter $files of OC_Files::get() does only seem to accept string, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

571
			OC_Files::get($originalSharePath, /** @scrutinizer ignore-type */ $files_list, $server_params);
Loading history...
572
			exit();
0 ignored issues
show
Best Practice introduced by
Using exit here is not recommended.

In general, usage of exit should be done with care and only when running in a scripting context like a CLI script.

Loading history...
573
		} else {
574
			// FIXME: The exit is required here because otherwise the AppFramework is trying to add headers as well
575
			// after dispatching the request which results in a "Cannot modify header information" notice.
576
			OC_Files::get(dirname($originalSharePath), basename($originalSharePath), $server_params);
577
			exit();
0 ignored issues
show
Best Practice introduced by
Using exit here is not recommended.

In general, usage of exit should be done with care and only when running in a scripting context like a CLI script.

Loading history...
578
		}
579
	}
580
581
	/**
582
	 * create activity for every downloaded file
583
	 *
584
	 * @param Share\IShare $share
585
	 * @param array $files_list
586
	 * @param \OCP\Files\Folder $node
587
	 */
588
	protected function fileListDownloaded(Share\IShare $share, array $files_list, \OCP\Files\Folder $node) {
589
		foreach ($files_list as $file) {
590
			$subNode = $node->get($file);
591
			$this->singleFileDownloaded($share, $subNode);
592
		}
593
594
	}
595
596
	/**
597
	 * create activity if a single file was downloaded from a link share
598
	 *
599
	 * @param Share\IShare $share
600
	 */
601
	protected function singleFileDownloaded(Share\IShare $share, \OCP\Files\Node $node) {
602
603
		$fileId = $node->getId();
604
605
		$userFolder = $this->rootFolder->getUserFolder($share->getSharedBy());
606
		$userNodeList = $userFolder->getById($fileId);
607
		$userNode = $userNodeList[0];
608
		$ownerFolder = $this->rootFolder->getUserFolder($share->getShareOwner());
609
		$userPath = $userFolder->getRelativePath($userNode->getPath());
610
		$ownerPath = $ownerFolder->getRelativePath($node->getPath());
611
612
		$parameters = [$userPath];
613
614
		if ($share->getShareType() === \OCP\Share::SHARE_TYPE_EMAIL) {
615
			if ($node instanceof \OCP\Files\File) {
616
				$subject = Downloads::SUBJECT_SHARED_FILE_BY_EMAIL_DOWNLOADED;
617
			} else {
618
				$subject = Downloads::SUBJECT_SHARED_FOLDER_BY_EMAIL_DOWNLOADED;
619
			}
620
			$parameters[] = $share->getSharedWith();
621
		} else {
622
			if ($node instanceof \OCP\Files\File) {
623
				$subject = Downloads::SUBJECT_PUBLIC_SHARED_FILE_DOWNLOADED;
624
			} else {
625
				$subject = Downloads::SUBJECT_PUBLIC_SHARED_FOLDER_DOWNLOADED;
626
			}
627
		}
628
629
		$this->publishActivity($subject, $parameters, $share->getSharedBy(), $fileId, $userPath);
630
631
		if ($share->getShareOwner() !== $share->getSharedBy()) {
632
			$parameters[0] = $ownerPath;
633
			$this->publishActivity($subject, $parameters, $share->getShareOwner(), $fileId, $ownerPath);
634
		}
635
	}
636
637
	/**
638
	 * publish activity
639
	 *
640
	 * @param string $subject
641
	 * @param array $parameters
642
	 * @param string $affectedUser
643
	 * @param int $fileId
644
	 * @param string $filePath
645
	 */
646
	protected function publishActivity($subject,
647
										array $parameters,
648
										$affectedUser,
649
										$fileId,
650
										$filePath) {
651
652
		$event = $this->activityManager->generateEvent();
653
		$event->setApp('files_sharing')
654
			->setType('public_links')
655
			->setSubject($subject, $parameters)
656
			->setAffectedUser($affectedUser)
657
			->setObject('files', $fileId, $filePath);
658
		$this->activityManager->publish($event);
659
	}
660
661
662
}
663