Passed
Push — master ( e14ba5...031269 )
by Morris
12:48
created
apps/files_sharing/appinfo/routes.php 1 patch
Indentation   +157 added lines, -157 removed lines patch added patch discarded remove patch
@@ -26,170 +26,170 @@
 block discarded – undo
26 26
  */
27 27
 
28 28
 return [
29
-	'resources' => [
30
-		'ExternalShares' => ['url' => '/api/externalShares'],
31
-	],
32
-	'routes' => [
33
-		[
34
-			'name' => 'Share#showShare',
35
-			'url' => '/s/{token}',
36
-			'verb' => 'GET',
37
-			'root' => '',
38
-		],
39
-		[
40
-			'name' => 'Share#showAuthenticate',
41
-			'url' => '/s/{token}/authenticate/{redirect}',
42
-			'verb' => 'GET',
43
-			'root' => '',
44
-		],
45
-		[
46
-			'name' => 'Share#authenticate',
47
-			'url' => '/s/{token}/authenticate/{redirect}',
48
-			'verb' => 'POST',
49
-			'root' => '',
50
-		],
51
-		[
52
-			'name' => 'Share#downloadShare',
53
-			'url' => '/s/{token}/download/{filename}',
54
-			'verb' => 'GET',
55
-			'root' => '',
56
-			'defaults' => ['filename' => '']
57
-		],
58
-		[
59
-			'name' => 'PublicPreview#directLink',
60
-			'url' => '/s/{token}/preview',
61
-			'verb' => 'GET',
62
-			'root' => '',
63
-		],
29
+    'resources' => [
30
+        'ExternalShares' => ['url' => '/api/externalShares'],
31
+    ],
32
+    'routes' => [
33
+        [
34
+            'name' => 'Share#showShare',
35
+            'url' => '/s/{token}',
36
+            'verb' => 'GET',
37
+            'root' => '',
38
+        ],
39
+        [
40
+            'name' => 'Share#showAuthenticate',
41
+            'url' => '/s/{token}/authenticate/{redirect}',
42
+            'verb' => 'GET',
43
+            'root' => '',
44
+        ],
45
+        [
46
+            'name' => 'Share#authenticate',
47
+            'url' => '/s/{token}/authenticate/{redirect}',
48
+            'verb' => 'POST',
49
+            'root' => '',
50
+        ],
51
+        [
52
+            'name' => 'Share#downloadShare',
53
+            'url' => '/s/{token}/download/{filename}',
54
+            'verb' => 'GET',
55
+            'root' => '',
56
+            'defaults' => ['filename' => '']
57
+        ],
58
+        [
59
+            'name' => 'PublicPreview#directLink',
60
+            'url' => '/s/{token}/preview',
61
+            'verb' => 'GET',
62
+            'root' => '',
63
+        ],
64 64
 
65
-		[
66
-			'name' => 'externalShares#testRemote',
67
-			'url' => '/testremote',
68
-			'verb' => 'GET'
69
-		],
70
-		[
71
-			'name' => 'PublicPreview#getPreview',
72
-			'url' => '/publicpreview/{token}',
73
-			'verb' => 'GET',
74
-		],
75
-		[
76
-			'name' => 'ShareInfo#info',
77
-			'url' => '/shareinfo',
78
-			'verb' => 'POST',
79
-		],
80
-		[
81
-			'name' => 'Settings#setDefaultAccept',
82
-			'url' => '/settings/defaultAccept',
83
-			'verb' => 'PUT',
84
-		],
85
-		[
86
-			'name' => 'Accept#accept',
87
-			'url' => '/accept/{shareId}',
88
-			'verb' => 'GET',
89
-		],
90
-	],
91
-	'ocs' => [
92
-		/*
65
+        [
66
+            'name' => 'externalShares#testRemote',
67
+            'url' => '/testremote',
68
+            'verb' => 'GET'
69
+        ],
70
+        [
71
+            'name' => 'PublicPreview#getPreview',
72
+            'url' => '/publicpreview/{token}',
73
+            'verb' => 'GET',
74
+        ],
75
+        [
76
+            'name' => 'ShareInfo#info',
77
+            'url' => '/shareinfo',
78
+            'verb' => 'POST',
79
+        ],
80
+        [
81
+            'name' => 'Settings#setDefaultAccept',
82
+            'url' => '/settings/defaultAccept',
83
+            'verb' => 'PUT',
84
+        ],
85
+        [
86
+            'name' => 'Accept#accept',
87
+            'url' => '/accept/{shareId}',
88
+            'verb' => 'GET',
89
+        ],
90
+    ],
91
+    'ocs' => [
92
+        /*
93 93
 		 * OCS Share API
94 94
 		 */
95
-		[
96
-			'name' => 'ShareAPI#getShares',
97
-			'url'  => '/api/v1/shares',
98
-			'verb' => 'GET',
99
-		],
100
-		[
101
-			'name' => 'ShareAPI#getInheritedShares',
102
-			'url'  => '/api/v1/shares/inherited',
103
-			'verb' => 'GET',
104
-		],
105
-		[
106
-			'name' => 'ShareAPI#createShare',
107
-			'url'  => '/api/v1/shares',
108
-			'verb' => 'POST',
109
-		],
110
-		[
111
-			'name' => 'ShareAPI#pendingShares',
112
-			'url'  => '/api/v1/shares/pending',
113
-			'verb' => 'GET',
114
-		],
115
-		[
116
-			'name' => 'ShareAPI#getShare',
117
-			'url'  => '/api/v1/shares/{id}',
118
-			'verb' => 'GET',
119
-		],
120
-		[
121
-			'name' => 'ShareAPI#updateShare',
122
-			'url'  => '/api/v1/shares/{id}',
123
-			'verb' => 'PUT',
124
-		],
125
-		[
126
-			'name' => 'ShareAPI#deleteShare',
127
-			'url'  => '/api/v1/shares/{id}',
128
-			'verb' => 'DELETE',
129
-		],
130
-		[
131
-			'name' => 'ShareAPI#acceptShare',
132
-			'url'  => '/api/v1/shares/pending/{id}',
133
-			'verb' => 'POST',
134
-		],
135
-		/*
95
+        [
96
+            'name' => 'ShareAPI#getShares',
97
+            'url'  => '/api/v1/shares',
98
+            'verb' => 'GET',
99
+        ],
100
+        [
101
+            'name' => 'ShareAPI#getInheritedShares',
102
+            'url'  => '/api/v1/shares/inherited',
103
+            'verb' => 'GET',
104
+        ],
105
+        [
106
+            'name' => 'ShareAPI#createShare',
107
+            'url'  => '/api/v1/shares',
108
+            'verb' => 'POST',
109
+        ],
110
+        [
111
+            'name' => 'ShareAPI#pendingShares',
112
+            'url'  => '/api/v1/shares/pending',
113
+            'verb' => 'GET',
114
+        ],
115
+        [
116
+            'name' => 'ShareAPI#getShare',
117
+            'url'  => '/api/v1/shares/{id}',
118
+            'verb' => 'GET',
119
+        ],
120
+        [
121
+            'name' => 'ShareAPI#updateShare',
122
+            'url'  => '/api/v1/shares/{id}',
123
+            'verb' => 'PUT',
124
+        ],
125
+        [
126
+            'name' => 'ShareAPI#deleteShare',
127
+            'url'  => '/api/v1/shares/{id}',
128
+            'verb' => 'DELETE',
129
+        ],
130
+        [
131
+            'name' => 'ShareAPI#acceptShare',
132
+            'url'  => '/api/v1/shares/pending/{id}',
133
+            'verb' => 'POST',
134
+        ],
135
+        /*
136 136
 		 * Deleted Shares
137 137
 		 */
138
-		[
139
-			'name' => 'DeletedShareAPI#index',
140
-			'url'  => '/api/v1/deletedshares',
141
-			'verb' => 'GET',
142
-		],
143
-		[
144
-			'name' => 'DeletedShareAPI#undelete',
145
-			'url'  => '/api/v1/deletedshares/{id}',
146
-			'verb' => 'POST',
147
-		],
148
-		/*
138
+        [
139
+            'name' => 'DeletedShareAPI#index',
140
+            'url'  => '/api/v1/deletedshares',
141
+            'verb' => 'GET',
142
+        ],
143
+        [
144
+            'name' => 'DeletedShareAPI#undelete',
145
+            'url'  => '/api/v1/deletedshares/{id}',
146
+            'verb' => 'POST',
147
+        ],
148
+        /*
149 149
 		 * OCS Sharee API
150 150
 		 */
151
-		[
152
-			'name' => 'ShareesAPI#search',
153
-			'url' => '/api/v1/sharees',
154
-			'verb' => 'GET',
155
-		],
156
-		[
157
-			'name' => 'ShareesAPI#findRecommended',
158
-			'url' => '/api/v1/sharees_recommended',
159
-			'verb' => 'GET',
160
-		],
161
-		/*
151
+        [
152
+            'name' => 'ShareesAPI#search',
153
+            'url' => '/api/v1/sharees',
154
+            'verb' => 'GET',
155
+        ],
156
+        [
157
+            'name' => 'ShareesAPI#findRecommended',
158
+            'url' => '/api/v1/sharees_recommended',
159
+            'verb' => 'GET',
160
+        ],
161
+        /*
162 162
 		 * Remote Shares
163 163
 		 */
164
-		[
165
-			'name' => 'Remote#getShares',
166
-			'url' => '/api/v1/remote_shares',
167
-			'verb' => 'GET',
168
-		],
169
-		[
170
-			'name' => 'Remote#getOpenShares',
171
-			'url' => '/api/v1/remote_shares/pending',
172
-			'verb' => 'GET',
173
-		],
174
-		[
175
-			'name' => 'Remote#acceptShare',
176
-			'url' => '/api/v1/remote_shares/pending/{id}',
177
-			'verb' => 'POST',
178
-		],
179
-		[
180
-			'name' => 'Remote#declineShare',
181
-			'url' => '/api/v1/remote_shares/pending/{id}',
182
-			'verb' => 'DELETE',
183
-		],
184
-		[
185
-			'name' => 'Remote#getShare',
186
-			'url' => '/api/v1/remote_shares/{id}',
187
-			'verb' => 'GET',
188
-		],
189
-		[
190
-			'name' => 'Remote#unshare',
191
-			'url' => '/api/v1/remote_shares/{id}',
192
-			'verb' => 'DELETE',
193
-		],
194
-	],
164
+        [
165
+            'name' => 'Remote#getShares',
166
+            'url' => '/api/v1/remote_shares',
167
+            'verb' => 'GET',
168
+        ],
169
+        [
170
+            'name' => 'Remote#getOpenShares',
171
+            'url' => '/api/v1/remote_shares/pending',
172
+            'verb' => 'GET',
173
+        ],
174
+        [
175
+            'name' => 'Remote#acceptShare',
176
+            'url' => '/api/v1/remote_shares/pending/{id}',
177
+            'verb' => 'POST',
178
+        ],
179
+        [
180
+            'name' => 'Remote#declineShare',
181
+            'url' => '/api/v1/remote_shares/pending/{id}',
182
+            'verb' => 'DELETE',
183
+        ],
184
+        [
185
+            'name' => 'Remote#getShare',
186
+            'url' => '/api/v1/remote_shares/{id}',
187
+            'verb' => 'GET',
188
+        ],
189
+        [
190
+            'name' => 'Remote#unshare',
191
+            'url' => '/api/v1/remote_shares/{id}',
192
+            'verb' => 'DELETE',
193
+        ],
194
+    ],
195 195
 ];
Please login to merge, or discard this patch.
apps/files_sharing/lib/Controller/ShareController.php 1 patch
Indentation   +660 added lines, -660 removed lines patch added patch discarded remove patch
@@ -85,666 +85,666 @@
 block discarded – undo
85 85
  */
86 86
 class ShareController extends AuthPublicShareController {
87 87
 
88
-	/** @var IConfig */
89
-	protected $config;
90
-	/** @var IUserManager */
91
-	protected $userManager;
92
-	/** @var ILogger */
93
-	protected $logger;
94
-	/** @var \OCP\Activity\IManager */
95
-	protected $activityManager;
96
-	/** @var IPreview */
97
-	protected $previewManager;
98
-	/** @var IRootFolder */
99
-	protected $rootFolder;
100
-	/** @var FederatedShareProvider */
101
-	protected $federatedShareProvider;
102
-	/** @var IAccountManager */
103
-	protected $accountManager;
104
-	/** @var IEventDispatcher */
105
-	protected $eventDispatcher;
106
-	/** @var IL10N */
107
-	protected $l10n;
108
-	/** @var Defaults */
109
-	protected $defaults;
110
-	/** @var ShareManager */
111
-	protected $shareManager;
112
-
113
-	/** @var Share\IShare */
114
-	protected $share;
115
-
116
-	/**
117
-	 * @param string $appName
118
-	 * @param IRequest $request
119
-	 * @param IConfig $config
120
-	 * @param IURLGenerator $urlGenerator
121
-	 * @param IUserManager $userManager
122
-	 * @param ILogger $logger
123
-	 * @param \OCP\Activity\IManager $activityManager
124
-	 * @param \OCP\Share\IManager $shareManager
125
-	 * @param ISession $session
126
-	 * @param IPreview $previewManager
127
-	 * @param IRootFolder $rootFolder
128
-	 * @param FederatedShareProvider $federatedShareProvider
129
-	 * @param IAccountManager $accountManager
130
-	 * @param IEventDispatcher $eventDispatcher
131
-	 * @param IL10N $l10n
132
-	 * @param Defaults $defaults
133
-	 */
134
-	public function __construct(string $appName,
135
-								IRequest $request,
136
-								IConfig $config,
137
-								IURLGenerator $urlGenerator,
138
-								IUserManager $userManager,
139
-								ILogger $logger,
140
-								\OCP\Activity\IManager $activityManager,
141
-								ShareManager $shareManager,
142
-								ISession $session,
143
-								IPreview $previewManager,
144
-								IRootFolder $rootFolder,
145
-								FederatedShareProvider $federatedShareProvider,
146
-								IAccountManager $accountManager,
147
-								IEventDispatcher $eventDispatcher,
148
-								IL10N $l10n,
149
-								Defaults $defaults) {
150
-		parent::__construct($appName, $request, $session, $urlGenerator);
151
-
152
-		$this->config = $config;
153
-		$this->userManager = $userManager;
154
-		$this->logger = $logger;
155
-		$this->activityManager = $activityManager;
156
-		$this->previewManager = $previewManager;
157
-		$this->rootFolder = $rootFolder;
158
-		$this->federatedShareProvider = $federatedShareProvider;
159
-		$this->accountManager = $accountManager;
160
-		$this->eventDispatcher = $eventDispatcher;
161
-		$this->l10n = $l10n;
162
-		$this->defaults = $defaults;
163
-		$this->shareManager = $shareManager;
164
-	}
165
-
166
-	/**
167
-	 * @PublicPage
168
-	 * @NoCSRFRequired
169
-	 *
170
-	 * Show the authentication page
171
-	 * The form has to submit to the authenticate method route
172
-	 */
173
-	public function showAuthenticate(): TemplateResponse {
174
-		$templateParameters = ['share' => $this->share];
175
-
176
-		$this->eventDispatcher->dispatchTyped(new BeforeTemplateRenderedEvent($this->share, BeforeTemplateRenderedEvent::SCOPE_PUBLIC_SHARE_AUTH));
177
-
178
-		$response = new TemplateResponse('core', 'publicshareauth', $templateParameters, 'guest');
179
-		if ($this->share->getSendPasswordByTalk()) {
180
-			$csp = new ContentSecurityPolicy();
181
-			$csp->addAllowedConnectDomain('*');
182
-			$csp->addAllowedMediaDomain('blob:');
183
-			$response->setContentSecurityPolicy($csp);
184
-		}
185
-
186
-		return $response;
187
-	}
188
-
189
-	/**
190
-	 * The template to show when authentication failed
191
-	 */
192
-	protected function showAuthFailed(): TemplateResponse {
193
-		$templateParameters = ['share' => $this->share, 'wrongpw' => true];
194
-
195
-		$this->eventDispatcher->dispatchTyped(new BeforeTemplateRenderedEvent($this->share, BeforeTemplateRenderedEvent::SCOPE_PUBLIC_SHARE_AUTH));
196
-
197
-		$response = new TemplateResponse('core', 'publicshareauth', $templateParameters, 'guest');
198
-		if ($this->share->getSendPasswordByTalk()) {
199
-			$csp = new ContentSecurityPolicy();
200
-			$csp->addAllowedConnectDomain('*');
201
-			$csp->addAllowedMediaDomain('blob:');
202
-			$response->setContentSecurityPolicy($csp);
203
-		}
204
-
205
-		return $response;
206
-	}
207
-
208
-	protected function verifyPassword(string $password): bool {
209
-		return $this->shareManager->checkPassword($this->share, $password);
210
-	}
211
-
212
-	protected function getPasswordHash(): string {
213
-		return $this->share->getPassword();
214
-	}
215
-
216
-	public function isValidToken(): bool {
217
-		try {
218
-			$this->share = $this->shareManager->getShareByToken($this->getToken());
219
-		} catch (ShareNotFound $e) {
220
-			return false;
221
-		}
222
-
223
-		return true;
224
-	}
225
-
226
-	protected function isPasswordProtected(): bool {
227
-		return $this->share->getPassword() !== null;
228
-	}
229
-
230
-	protected function authSucceeded() {
231
-		// For share this was always set so it is still used in other apps
232
-		$this->session->set('public_link_authenticated', (string)$this->share->getId());
233
-	}
234
-
235
-	protected function authFailed() {
236
-		$this->emitAccessShareHook($this->share, 403, 'Wrong password');
237
-	}
238
-
239
-	/**
240
-	 * throws hooks when a share is attempted to be accessed
241
-	 *
242
-	 * @param \OCP\Share\IShare|string $share the Share instance if available,
243
-	 * otherwise token
244
-	 * @param int $errorCode
245
-	 * @param string $errorMessage
246
-	 * @throws \OC\HintException
247
-	 * @throws \OC\ServerNotAvailableException
248
-	 */
249
-	protected function emitAccessShareHook($share, $errorCode = 200, $errorMessage = '') {
250
-		$itemType = $itemSource = $uidOwner = '';
251
-		$token = $share;
252
-		$exception = null;
253
-		if ($share instanceof \OCP\Share\IShare) {
254
-			try {
255
-				$token = $share->getToken();
256
-				$uidOwner = $share->getSharedBy();
257
-				$itemType = $share->getNodeType();
258
-				$itemSource = $share->getNodeId();
259
-			} catch (\Exception $e) {
260
-				// we log what we know and pass on the exception afterwards
261
-				$exception = $e;
262
-			}
263
-		}
264
-		\OC_Hook::emit(Share::class, 'share_link_access', [
265
-			'itemType' => $itemType,
266
-			'itemSource' => $itemSource,
267
-			'uidOwner' => $uidOwner,
268
-			'token' => $token,
269
-			'errorCode' => $errorCode,
270
-			'errorMessage' => $errorMessage,
271
-		]);
272
-		if (!is_null($exception)) {
273
-			throw $exception;
274
-		}
275
-	}
276
-
277
-	/**
278
-	 * Validate the permissions of the share
279
-	 *
280
-	 * @param Share\IShare $share
281
-	 * @return bool
282
-	 */
283
-	private function validateShare(\OCP\Share\IShare $share) {
284
-		// If the owner is disabled no access to the linke is granted
285
-		$owner = $this->userManager->get($share->getShareOwner());
286
-		if ($owner === null || !$owner->isEnabled()) {
287
-			return false;
288
-		}
289
-
290
-		// If the initiator of the share is disabled no access is granted
291
-		$initiator = $this->userManager->get($share->getSharedBy());
292
-		if ($initiator === null || !$initiator->isEnabled()) {
293
-			return false;
294
-		}
295
-
296
-		return $share->getNode()->isReadable() && $share->getNode()->isShareable();
297
-	}
298
-
299
-	/**
300
-	 * @PublicPage
301
-	 * @NoCSRFRequired
302
-	 *
303
-	 *
304
-	 * @param string $path
305
-	 * @return TemplateResponse
306
-	 * @throws NotFoundException
307
-	 * @throws \Exception
308
-	 */
309
-	public function showShare($path = ''): TemplateResponse {
310
-		\OC_User::setIncognitoMode(true);
311
-
312
-		// Check whether share exists
313
-		try {
314
-			$share = $this->shareManager->getShareByToken($this->getToken());
315
-		} catch (ShareNotFound $e) {
316
-			$this->emitAccessShareHook($this->getToken(), 404, 'Share not found');
317
-			throw new NotFoundException();
318
-		}
319
-
320
-		if (!$this->validateShare($share)) {
321
-			throw new NotFoundException();
322
-		}
323
-
324
-		$shareNode = $share->getNode();
325
-
326
-		// We can't get the path of a file share
327
-		try {
328
-			if ($shareNode instanceof \OCP\Files\File && $path !== '') {
329
-				$this->emitAccessShareHook($share, 404, 'Share not found');
330
-				throw new NotFoundException();
331
-			}
332
-		} catch (\Exception $e) {
333
-			$this->emitAccessShareHook($share, 404, 'Share not found');
334
-			throw $e;
335
-		}
336
-
337
-		$shareTmpl = [];
338
-		$shareTmpl['owner'] = '';
339
-		$shareTmpl['shareOwner'] = '';
340
-
341
-		$owner = $this->userManager->get($share->getShareOwner());
342
-		if ($owner instanceof IUser) {
343
-			$ownerAccount = $this->accountManager->getAccount($owner);
344
-
345
-			$ownerName = $ownerAccount->getProperty(IAccountManager::PROPERTY_DISPLAYNAME);
346
-			if ($ownerName->getScope() === IAccountManager::VISIBILITY_PUBLIC) {
347
-				$shareTmpl['owner'] = $owner->getUID();
348
-				$shareTmpl['shareOwner'] = $owner->getDisplayName();
349
-			}
350
-		}
351
-
352
-		$shareTmpl['filename'] = $shareNode->getName();
353
-		$shareTmpl['directory_path'] = $share->getTarget();
354
-		$shareTmpl['note'] = $share->getNote();
355
-		$shareTmpl['mimetype'] = $shareNode->getMimetype();
356
-		$shareTmpl['previewSupported'] = $this->previewManager->isMimeSupported($shareNode->getMimetype());
357
-		$shareTmpl['dirToken'] = $this->getToken();
358
-		$shareTmpl['sharingToken'] = $this->getToken();
359
-		$shareTmpl['server2serversharing'] = $this->federatedShareProvider->isOutgoingServer2serverShareEnabled();
360
-		$shareTmpl['protected'] = $share->getPassword() !== null ? 'true' : 'false';
361
-		$shareTmpl['dir'] = '';
362
-		$shareTmpl['nonHumanFileSize'] = $shareNode->getSize();
363
-		$shareTmpl['fileSize'] = \OCP\Util::humanFileSize($shareNode->getSize());
364
-		$shareTmpl['hideDownload'] = $share->getHideDownload();
365
-
366
-		$hideFileList = false;
367
-
368
-		if ($shareNode instanceof \OCP\Files\Folder) {
369
-			$shareIsFolder = true;
370
-
371
-			try {
372
-				$folderNode = $shareNode->get($path);
373
-			} catch (\OCP\Files\NotFoundException $e) {
374
-				$this->emitAccessShareHook($share, 404, 'Share not found');
375
-				throw new NotFoundException();
376
-			}
377
-
378
-			$shareTmpl['dir'] = $shareNode->getRelativePath($folderNode->getPath());
379
-
380
-			/*
88
+    /** @var IConfig */
89
+    protected $config;
90
+    /** @var IUserManager */
91
+    protected $userManager;
92
+    /** @var ILogger */
93
+    protected $logger;
94
+    /** @var \OCP\Activity\IManager */
95
+    protected $activityManager;
96
+    /** @var IPreview */
97
+    protected $previewManager;
98
+    /** @var IRootFolder */
99
+    protected $rootFolder;
100
+    /** @var FederatedShareProvider */
101
+    protected $federatedShareProvider;
102
+    /** @var IAccountManager */
103
+    protected $accountManager;
104
+    /** @var IEventDispatcher */
105
+    protected $eventDispatcher;
106
+    /** @var IL10N */
107
+    protected $l10n;
108
+    /** @var Defaults */
109
+    protected $defaults;
110
+    /** @var ShareManager */
111
+    protected $shareManager;
112
+
113
+    /** @var Share\IShare */
114
+    protected $share;
115
+
116
+    /**
117
+     * @param string $appName
118
+     * @param IRequest $request
119
+     * @param IConfig $config
120
+     * @param IURLGenerator $urlGenerator
121
+     * @param IUserManager $userManager
122
+     * @param ILogger $logger
123
+     * @param \OCP\Activity\IManager $activityManager
124
+     * @param \OCP\Share\IManager $shareManager
125
+     * @param ISession $session
126
+     * @param IPreview $previewManager
127
+     * @param IRootFolder $rootFolder
128
+     * @param FederatedShareProvider $federatedShareProvider
129
+     * @param IAccountManager $accountManager
130
+     * @param IEventDispatcher $eventDispatcher
131
+     * @param IL10N $l10n
132
+     * @param Defaults $defaults
133
+     */
134
+    public function __construct(string $appName,
135
+                                IRequest $request,
136
+                                IConfig $config,
137
+                                IURLGenerator $urlGenerator,
138
+                                IUserManager $userManager,
139
+                                ILogger $logger,
140
+                                \OCP\Activity\IManager $activityManager,
141
+                                ShareManager $shareManager,
142
+                                ISession $session,
143
+                                IPreview $previewManager,
144
+                                IRootFolder $rootFolder,
145
+                                FederatedShareProvider $federatedShareProvider,
146
+                                IAccountManager $accountManager,
147
+                                IEventDispatcher $eventDispatcher,
148
+                                IL10N $l10n,
149
+                                Defaults $defaults) {
150
+        parent::__construct($appName, $request, $session, $urlGenerator);
151
+
152
+        $this->config = $config;
153
+        $this->userManager = $userManager;
154
+        $this->logger = $logger;
155
+        $this->activityManager = $activityManager;
156
+        $this->previewManager = $previewManager;
157
+        $this->rootFolder = $rootFolder;
158
+        $this->federatedShareProvider = $federatedShareProvider;
159
+        $this->accountManager = $accountManager;
160
+        $this->eventDispatcher = $eventDispatcher;
161
+        $this->l10n = $l10n;
162
+        $this->defaults = $defaults;
163
+        $this->shareManager = $shareManager;
164
+    }
165
+
166
+    /**
167
+     * @PublicPage
168
+     * @NoCSRFRequired
169
+     *
170
+     * Show the authentication page
171
+     * The form has to submit to the authenticate method route
172
+     */
173
+    public function showAuthenticate(): TemplateResponse {
174
+        $templateParameters = ['share' => $this->share];
175
+
176
+        $this->eventDispatcher->dispatchTyped(new BeforeTemplateRenderedEvent($this->share, BeforeTemplateRenderedEvent::SCOPE_PUBLIC_SHARE_AUTH));
177
+
178
+        $response = new TemplateResponse('core', 'publicshareauth', $templateParameters, 'guest');
179
+        if ($this->share->getSendPasswordByTalk()) {
180
+            $csp = new ContentSecurityPolicy();
181
+            $csp->addAllowedConnectDomain('*');
182
+            $csp->addAllowedMediaDomain('blob:');
183
+            $response->setContentSecurityPolicy($csp);
184
+        }
185
+
186
+        return $response;
187
+    }
188
+
189
+    /**
190
+     * The template to show when authentication failed
191
+     */
192
+    protected function showAuthFailed(): TemplateResponse {
193
+        $templateParameters = ['share' => $this->share, 'wrongpw' => true];
194
+
195
+        $this->eventDispatcher->dispatchTyped(new BeforeTemplateRenderedEvent($this->share, BeforeTemplateRenderedEvent::SCOPE_PUBLIC_SHARE_AUTH));
196
+
197
+        $response = new TemplateResponse('core', 'publicshareauth', $templateParameters, 'guest');
198
+        if ($this->share->getSendPasswordByTalk()) {
199
+            $csp = new ContentSecurityPolicy();
200
+            $csp->addAllowedConnectDomain('*');
201
+            $csp->addAllowedMediaDomain('blob:');
202
+            $response->setContentSecurityPolicy($csp);
203
+        }
204
+
205
+        return $response;
206
+    }
207
+
208
+    protected function verifyPassword(string $password): bool {
209
+        return $this->shareManager->checkPassword($this->share, $password);
210
+    }
211
+
212
+    protected function getPasswordHash(): string {
213
+        return $this->share->getPassword();
214
+    }
215
+
216
+    public function isValidToken(): bool {
217
+        try {
218
+            $this->share = $this->shareManager->getShareByToken($this->getToken());
219
+        } catch (ShareNotFound $e) {
220
+            return false;
221
+        }
222
+
223
+        return true;
224
+    }
225
+
226
+    protected function isPasswordProtected(): bool {
227
+        return $this->share->getPassword() !== null;
228
+    }
229
+
230
+    protected function authSucceeded() {
231
+        // For share this was always set so it is still used in other apps
232
+        $this->session->set('public_link_authenticated', (string)$this->share->getId());
233
+    }
234
+
235
+    protected function authFailed() {
236
+        $this->emitAccessShareHook($this->share, 403, 'Wrong password');
237
+    }
238
+
239
+    /**
240
+     * throws hooks when a share is attempted to be accessed
241
+     *
242
+     * @param \OCP\Share\IShare|string $share the Share instance if available,
243
+     * otherwise token
244
+     * @param int $errorCode
245
+     * @param string $errorMessage
246
+     * @throws \OC\HintException
247
+     * @throws \OC\ServerNotAvailableException
248
+     */
249
+    protected function emitAccessShareHook($share, $errorCode = 200, $errorMessage = '') {
250
+        $itemType = $itemSource = $uidOwner = '';
251
+        $token = $share;
252
+        $exception = null;
253
+        if ($share instanceof \OCP\Share\IShare) {
254
+            try {
255
+                $token = $share->getToken();
256
+                $uidOwner = $share->getSharedBy();
257
+                $itemType = $share->getNodeType();
258
+                $itemSource = $share->getNodeId();
259
+            } catch (\Exception $e) {
260
+                // we log what we know and pass on the exception afterwards
261
+                $exception = $e;
262
+            }
263
+        }
264
+        \OC_Hook::emit(Share::class, 'share_link_access', [
265
+            'itemType' => $itemType,
266
+            'itemSource' => $itemSource,
267
+            'uidOwner' => $uidOwner,
268
+            'token' => $token,
269
+            'errorCode' => $errorCode,
270
+            'errorMessage' => $errorMessage,
271
+        ]);
272
+        if (!is_null($exception)) {
273
+            throw $exception;
274
+        }
275
+    }
276
+
277
+    /**
278
+     * Validate the permissions of the share
279
+     *
280
+     * @param Share\IShare $share
281
+     * @return bool
282
+     */
283
+    private function validateShare(\OCP\Share\IShare $share) {
284
+        // If the owner is disabled no access to the linke is granted
285
+        $owner = $this->userManager->get($share->getShareOwner());
286
+        if ($owner === null || !$owner->isEnabled()) {
287
+            return false;
288
+        }
289
+
290
+        // If the initiator of the share is disabled no access is granted
291
+        $initiator = $this->userManager->get($share->getSharedBy());
292
+        if ($initiator === null || !$initiator->isEnabled()) {
293
+            return false;
294
+        }
295
+
296
+        return $share->getNode()->isReadable() && $share->getNode()->isShareable();
297
+    }
298
+
299
+    /**
300
+     * @PublicPage
301
+     * @NoCSRFRequired
302
+     *
303
+     *
304
+     * @param string $path
305
+     * @return TemplateResponse
306
+     * @throws NotFoundException
307
+     * @throws \Exception
308
+     */
309
+    public function showShare($path = ''): TemplateResponse {
310
+        \OC_User::setIncognitoMode(true);
311
+
312
+        // Check whether share exists
313
+        try {
314
+            $share = $this->shareManager->getShareByToken($this->getToken());
315
+        } catch (ShareNotFound $e) {
316
+            $this->emitAccessShareHook($this->getToken(), 404, 'Share not found');
317
+            throw new NotFoundException();
318
+        }
319
+
320
+        if (!$this->validateShare($share)) {
321
+            throw new NotFoundException();
322
+        }
323
+
324
+        $shareNode = $share->getNode();
325
+
326
+        // We can't get the path of a file share
327
+        try {
328
+            if ($shareNode instanceof \OCP\Files\File && $path !== '') {
329
+                $this->emitAccessShareHook($share, 404, 'Share not found');
330
+                throw new NotFoundException();
331
+            }
332
+        } catch (\Exception $e) {
333
+            $this->emitAccessShareHook($share, 404, 'Share not found');
334
+            throw $e;
335
+        }
336
+
337
+        $shareTmpl = [];
338
+        $shareTmpl['owner'] = '';
339
+        $shareTmpl['shareOwner'] = '';
340
+
341
+        $owner = $this->userManager->get($share->getShareOwner());
342
+        if ($owner instanceof IUser) {
343
+            $ownerAccount = $this->accountManager->getAccount($owner);
344
+
345
+            $ownerName = $ownerAccount->getProperty(IAccountManager::PROPERTY_DISPLAYNAME);
346
+            if ($ownerName->getScope() === IAccountManager::VISIBILITY_PUBLIC) {
347
+                $shareTmpl['owner'] = $owner->getUID();
348
+                $shareTmpl['shareOwner'] = $owner->getDisplayName();
349
+            }
350
+        }
351
+
352
+        $shareTmpl['filename'] = $shareNode->getName();
353
+        $shareTmpl['directory_path'] = $share->getTarget();
354
+        $shareTmpl['note'] = $share->getNote();
355
+        $shareTmpl['mimetype'] = $shareNode->getMimetype();
356
+        $shareTmpl['previewSupported'] = $this->previewManager->isMimeSupported($shareNode->getMimetype());
357
+        $shareTmpl['dirToken'] = $this->getToken();
358
+        $shareTmpl['sharingToken'] = $this->getToken();
359
+        $shareTmpl['server2serversharing'] = $this->federatedShareProvider->isOutgoingServer2serverShareEnabled();
360
+        $shareTmpl['protected'] = $share->getPassword() !== null ? 'true' : 'false';
361
+        $shareTmpl['dir'] = '';
362
+        $shareTmpl['nonHumanFileSize'] = $shareNode->getSize();
363
+        $shareTmpl['fileSize'] = \OCP\Util::humanFileSize($shareNode->getSize());
364
+        $shareTmpl['hideDownload'] = $share->getHideDownload();
365
+
366
+        $hideFileList = false;
367
+
368
+        if ($shareNode instanceof \OCP\Files\Folder) {
369
+            $shareIsFolder = true;
370
+
371
+            try {
372
+                $folderNode = $shareNode->get($path);
373
+            } catch (\OCP\Files\NotFoundException $e) {
374
+                $this->emitAccessShareHook($share, 404, 'Share not found');
375
+                throw new NotFoundException();
376
+            }
377
+
378
+            $shareTmpl['dir'] = $shareNode->getRelativePath($folderNode->getPath());
379
+
380
+            /*
381 381
 			 * The OC_Util methods require a view. This just uses the node API
382 382
 			 */
383
-			$freeSpace = $share->getNode()->getStorage()->free_space($share->getNode()->getInternalPath());
384
-			if ($freeSpace < \OCP\Files\FileInfo::SPACE_UNLIMITED) {
385
-				$freeSpace = max($freeSpace, 0);
386
-			} else {
387
-				$freeSpace = (INF > 0) ? INF: PHP_INT_MAX; // work around https://bugs.php.net/bug.php?id=69188
388
-			}
389
-
390
-			$hideFileList = !($share->getPermissions() & \OCP\Constants::PERMISSION_READ);
391
-			$maxUploadFilesize = $freeSpace;
392
-
393
-			$folder = new Template('files', 'list', '');
394
-
395
-			$folder->assign('dir', $shareNode->getRelativePath($folderNode->getPath()));
396
-			$folder->assign('dirToken', $this->getToken());
397
-			$folder->assign('permissions', \OCP\Constants::PERMISSION_READ);
398
-			$folder->assign('isPublic', true);
399
-			$folder->assign('hideFileList', $hideFileList);
400
-			$folder->assign('publicUploadEnabled', 'no');
401
-			// default to list view
402
-			$folder->assign('showgridview', false);
403
-			$folder->assign('uploadMaxFilesize', $maxUploadFilesize);
404
-			$folder->assign('uploadMaxHumanFilesize', \OCP\Util::humanFileSize($maxUploadFilesize));
405
-			$folder->assign('freeSpace', $freeSpace);
406
-			$folder->assign('usedSpacePercent', 0);
407
-			$folder->assign('trash', false);
408
-			$shareTmpl['folder'] = $folder->fetchPage();
409
-		} else {
410
-			$shareIsFolder = false;
411
-		}
412
-
413
-		// default to list view
414
-		$shareTmpl['showgridview'] = false;
415
-
416
-		$shareTmpl['hideFileList'] = $hideFileList;
417
-		$shareTmpl['downloadURL'] = $this->urlGenerator->linkToRouteAbsolute('files_sharing.sharecontroller.downloadShare', [
418
-			'token' => $this->getToken(),
419
-			'filename' => $shareIsFolder ? null : $shareNode->getName()
420
-		]);
421
-		$shareTmpl['shareUrl'] = $this->urlGenerator->linkToRouteAbsolute('files_sharing.sharecontroller.showShare', ['token' => $this->getToken()]);
422
-		$shareTmpl['maxSizeAnimateGif'] = $this->config->getSystemValue('max_filesize_animated_gifs_public_sharing', 10);
423
-		$shareTmpl['previewEnabled'] = $this->config->getSystemValue('enable_previews', true);
424
-		$shareTmpl['previewMaxX'] = $this->config->getSystemValue('preview_max_x', 1024);
425
-		$shareTmpl['previewMaxY'] = $this->config->getSystemValue('preview_max_y', 1024);
426
-		$shareTmpl['disclaimer'] = $this->config->getAppValue('core', 'shareapi_public_link_disclaimertext', null);
427
-		$shareTmpl['previewURL'] = $shareTmpl['downloadURL'];
428
-
429
-		if ($shareTmpl['previewSupported']) {
430
-			$shareTmpl['previewImage'] = $this->urlGenerator->linkToRouteAbsolute('files_sharing.PublicPreview.getPreview',
431
-				['x' => 200, 'y' => 200, 'file' => $shareTmpl['directory_path'], 'token' => $shareTmpl['dirToken']]);
432
-			$ogPreview = $shareTmpl['previewImage'];
433
-
434
-			// We just have direct previews for image files
435
-			if ($shareNode->getMimePart() === 'image') {
436
-				$shareTmpl['previewURL'] = $this->urlGenerator->linkToRouteAbsolute('files_sharing.publicpreview.directLink', ['token' => $this->getToken()]);
437
-
438
-				$ogPreview = $shareTmpl['previewURL'];
439
-
440
-				//Whatapp is kind of picky about their size requirements
441
-				if ($this->request->isUserAgent(['/^WhatsApp/'])) {
442
-					$ogPreview = $this->urlGenerator->linkToRouteAbsolute('files_sharing.PublicPreview.getPreview', [
443
-						'token' => $this->getToken(),
444
-						'x' => 256,
445
-						'y' => 256,
446
-						'a' => true,
447
-					]);
448
-				}
449
-			}
450
-		} else {
451
-			$shareTmpl['previewImage'] = $this->urlGenerator->getAbsoluteURL($this->urlGenerator->imagePath('core', 'favicon-fb.png'));
452
-			$ogPreview = $shareTmpl['previewImage'];
453
-		}
454
-
455
-		// Load files we need
456
-		\OCP\Util::addScript('files', 'semaphore');
457
-		\OCP\Util::addScript('files', 'file-upload');
458
-		\OCP\Util::addStyle('files_sharing', 'publicView');
459
-		\OCP\Util::addScript('files_sharing', 'public');
460
-		\OCP\Util::addScript('files_sharing', 'templates');
461
-		\OCP\Util::addScript('files', 'fileactions');
462
-		\OCP\Util::addScript('files', 'fileactionsmenu');
463
-		\OCP\Util::addScript('files', 'jquery.fileupload');
464
-		\OCP\Util::addScript('files_sharing', 'files_drop');
465
-
466
-		if (isset($shareTmpl['folder'])) {
467
-			// JS required for folders
468
-			\OCP\Util::addStyle('files', 'merged');
469
-			\OCP\Util::addScript('files', 'filesummary');
470
-			\OCP\Util::addScript('files', 'templates');
471
-			\OCP\Util::addScript('files', 'breadcrumb');
472
-			\OCP\Util::addScript('files', 'fileinfomodel');
473
-			\OCP\Util::addScript('files', 'newfilemenu');
474
-			\OCP\Util::addScript('files', 'files');
475
-			\OCP\Util::addScript('files', 'filemultiselectmenu');
476
-			\OCP\Util::addScript('files', 'filelist');
477
-			\OCP\Util::addScript('files', 'keyboardshortcuts');
478
-			\OCP\Util::addScript('files', 'operationprogressbar');
479
-
480
-			// Load Viewer scripts
481
-			if (class_exists(LoadViewer::class)) {
482
-				$this->eventDispatcher->dispatchTyped(new LoadViewer());
483
-			}
484
-		}
485
-
486
-		// OpenGraph Support: http://ogp.me/
487
-		\OCP\Util::addHeader('meta', ['property' => "og:title", 'content' => $shareTmpl['filename']]);
488
-		\OCP\Util::addHeader('meta', ['property' => "og:description", 'content' => $this->defaults->getName() . ($this->defaults->getSlogan() !== '' ? ' - ' . $this->defaults->getSlogan() : '')]);
489
-		\OCP\Util::addHeader('meta', ['property' => "og:site_name", 'content' => $this->defaults->getName()]);
490
-		\OCP\Util::addHeader('meta', ['property' => "og:url", 'content' => $shareTmpl['shareUrl']]);
491
-		\OCP\Util::addHeader('meta', ['property' => "og:type", 'content' => "object"]);
492
-		\OCP\Util::addHeader('meta', ['property' => "og:image", 'content' => $ogPreview]);
493
-
494
-		$this->eventDispatcher->dispatchTyped(new BeforeTemplateRenderedEvent($share));
495
-
496
-		$csp = new \OCP\AppFramework\Http\ContentSecurityPolicy();
497
-		$csp->addAllowedFrameDomain('\'self\'');
498
-
499
-		$response = new PublicTemplateResponse($this->appName, 'public', $shareTmpl);
500
-		$response->setHeaderTitle($shareTmpl['filename']);
501
-		if ($shareTmpl['shareOwner'] !== '') {
502
-			$response->setHeaderDetails($this->l10n->t('shared by %s', [$shareTmpl['shareOwner']]));
503
-		}
504
-
505
-		$isNoneFileDropFolder = $shareIsFolder === false || $share->getPermissions() !== \OCP\Constants::PERMISSION_CREATE;
506
-
507
-		if ($isNoneFileDropFolder && !$share->getHideDownload()) {
508
-			\OCP\Util::addScript('files_sharing', 'public_note');
509
-
510
-			$downloadWhite = new SimpleMenuAction('download', $this->l10n->t('Download'), 'icon-download-white', $shareTmpl['downloadURL'], 0);
511
-			$downloadAllWhite = new SimpleMenuAction('download', $this->l10n->t('Download all files'), 'icon-download-white', $shareTmpl['downloadURL'], 0);
512
-			$download = new SimpleMenuAction('download', $this->l10n->t('Download'), 'icon-download', $shareTmpl['downloadURL'], 10, $shareTmpl['fileSize']);
513
-			$downloadAll = new SimpleMenuAction('download', $this->l10n->t('Download all files'), 'icon-download', $shareTmpl['downloadURL'], 10, $shareTmpl['fileSize']);
514
-			$directLink = new LinkMenuAction($this->l10n->t('Direct link'), 'icon-public', $shareTmpl['previewURL']);
515
-			$externalShare = new ExternalShareMenuAction($this->l10n->t('Add to your Nextcloud'), 'icon-external', $shareTmpl['owner'], $shareTmpl['shareOwner'], $shareTmpl['filename']);
516
-
517
-			$responseComposer = [];
518
-
519
-			if ($shareIsFolder) {
520
-				$responseComposer[] = $downloadAllWhite;
521
-				$responseComposer[] = $downloadAll;
522
-			} else {
523
-				$responseComposer[] = $downloadWhite;
524
-				$responseComposer[] = $download;
525
-			}
526
-			$responseComposer[] = $directLink;
527
-			if ($this->federatedShareProvider->isOutgoingServer2serverShareEnabled()) {
528
-				$responseComposer[] = $externalShare;
529
-			}
530
-
531
-			$response->setHeaderActions($responseComposer);
532
-		}
533
-
534
-		$response->setContentSecurityPolicy($csp);
535
-
536
-		$this->emitAccessShareHook($share);
537
-
538
-		return $response;
539
-	}
540
-
541
-	/**
542
-	 * @PublicPage
543
-	 * @NoCSRFRequired
544
-	 * @NoSameSiteCookieRequired
545
-	 *
546
-	 * @param string $token
547
-	 * @param string $files
548
-	 * @param string $path
549
-	 * @param string $downloadStartSecret
550
-	 * @return void|\OCP\AppFramework\Http\Response
551
-	 * @throws NotFoundException
552
-	 */
553
-	public function downloadShare($token, $files = null, $path = '', $downloadStartSecret = '') {
554
-		\OC_User::setIncognitoMode(true);
555
-
556
-		$share = $this->shareManager->getShareByToken($token);
557
-
558
-		if (!($share->getPermissions() & \OCP\Constants::PERMISSION_READ)) {
559
-			return new \OCP\AppFramework\Http\DataResponse('Share is read-only');
560
-		}
561
-
562
-		$files_list = null;
563
-		if (!is_null($files)) { // download selected files
564
-			$files_list = json_decode($files);
565
-			// in case we get only a single file
566
-			if ($files_list === null) {
567
-				$files_list = [$files];
568
-			}
569
-			// Just in case $files is a single int like '1234'
570
-			if (!is_array($files_list)) {
571
-				$files_list = [$files_list];
572
-			}
573
-		}
574
-
575
-		if (!$this->validateShare($share)) {
576
-			throw new NotFoundException();
577
-		}
578
-
579
-		$userFolder = $this->rootFolder->getUserFolder($share->getShareOwner());
580
-		$originalSharePath = $userFolder->getRelativePath($share->getNode()->getPath());
581
-
582
-
583
-		// Single file share
584
-		if ($share->getNode() instanceof \OCP\Files\File) {
585
-			// Single file download
586
-			$this->singleFileDownloaded($share, $share->getNode());
587
-		}
588
-		// Directory share
589
-		else {
590
-			/** @var \OCP\Files\Folder $node */
591
-			$node = $share->getNode();
592
-
593
-			// Try to get the path
594
-			if ($path !== '') {
595
-				try {
596
-					$node = $node->get($path);
597
-				} catch (NotFoundException $e) {
598
-					$this->emitAccessShareHook($share, 404, 'Share not found');
599
-					return new NotFoundResponse();
600
-				}
601
-			}
602
-
603
-			$originalSharePath = $userFolder->getRelativePath($node->getPath());
604
-
605
-			if ($node instanceof \OCP\Files\File) {
606
-				// Single file download
607
-				$this->singleFileDownloaded($share, $share->getNode());
608
-			} else {
609
-				try {
610
-					if (!empty($files_list)) {
611
-						$this->fileListDownloaded($share, $files_list, $node);
612
-					} else {
613
-						// The folder is downloaded
614
-						$this->singleFileDownloaded($share, $share->getNode());
615
-					}
616
-				} catch (NotFoundException $e) {
617
-					return new NotFoundResponse();
618
-				}
619
-			}
620
-		}
621
-
622
-		/* FIXME: We should do this all nicely in OCP */
623
-		OC_Util::tearDownFS();
624
-		OC_Util::setupFS($share->getShareOwner());
625
-
626
-		/**
627
-		 * this sets a cookie to be able to recognize the start of the download
628
-		 * the content must not be longer than 32 characters and must only contain
629
-		 * alphanumeric characters
630
-		 */
631
-		if (!empty($downloadStartSecret)
632
-			&& !isset($downloadStartSecret[32])
633
-			&& preg_match('!^[a-zA-Z0-9]+$!', $downloadStartSecret) === 1) {
634
-
635
-			// FIXME: set on the response once we use an actual app framework response
636
-			setcookie('ocDownloadStarted', $downloadStartSecret, time() + 20, '/');
637
-		}
638
-
639
-		$this->emitAccessShareHook($share);
640
-
641
-		$server_params = [ 'head' => $this->request->getMethod() === 'HEAD' ];
642
-
643
-		/**
644
-		 * Http range requests support
645
-		 */
646
-		if (isset($_SERVER['HTTP_RANGE'])) {
647
-			$server_params['range'] = $this->request->getHeader('Range');
648
-		}
649
-
650
-		// download selected files
651
-		if (!is_null($files) && $files !== '') {
652
-			// FIXME: The exit is required here because otherwise the AppFramework is trying to add headers as well
653
-			// after dispatching the request which results in a "Cannot modify header information" notice.
654
-			OC_Files::get($originalSharePath, $files_list, $server_params);
655
-			exit();
656
-		} else {
657
-			// FIXME: The exit is required here because otherwise the AppFramework is trying to add headers as well
658
-			// after dispatching the request which results in a "Cannot modify header information" notice.
659
-			OC_Files::get(dirname($originalSharePath), basename($originalSharePath), $server_params);
660
-			exit();
661
-		}
662
-	}
663
-
664
-	/**
665
-	 * create activity for every downloaded file
666
-	 *
667
-	 * @param Share\IShare $share
668
-	 * @param array $files_list
669
-	 * @param \OCP\Files\Folder $node
670
-	 * @throws NotFoundException when trying to download a folder or multiple files of a "hide download" share
671
-	 */
672
-	protected function fileListDownloaded(Share\IShare $share, array $files_list, \OCP\Files\Folder $node) {
673
-		if ($share->getHideDownload() && count($files_list) > 1) {
674
-			throw new NotFoundException('Downloading more than 1 file');
675
-		}
676
-
677
-		foreach ($files_list as $file) {
678
-			$subNode = $node->get($file);
679
-			$this->singleFileDownloaded($share, $subNode);
680
-		}
681
-	}
682
-
683
-	/**
684
-	 * create activity if a single file was downloaded from a link share
685
-	 *
686
-	 * @param Share\IShare $share
687
-	 * @throws NotFoundException when trying to download a folder of a "hide download" share
688
-	 */
689
-	protected function singleFileDownloaded(Share\IShare $share, \OCP\Files\Node $node) {
690
-		if ($share->getHideDownload() && $node instanceof Folder) {
691
-			throw new NotFoundException('Downloading a folder');
692
-		}
693
-
694
-		$fileId = $node->getId();
695
-
696
-		$userFolder = $this->rootFolder->getUserFolder($share->getSharedBy());
697
-		$userNodeList = $userFolder->getById($fileId);
698
-		$userNode = $userNodeList[0];
699
-		$ownerFolder = $this->rootFolder->getUserFolder($share->getShareOwner());
700
-		$userPath = $userFolder->getRelativePath($userNode->getPath());
701
-		$ownerPath = $ownerFolder->getRelativePath($node->getPath());
702
-
703
-		$parameters = [$userPath];
704
-
705
-		if ($share->getShareType() === IShare::TYPE_EMAIL) {
706
-			if ($node instanceof \OCP\Files\File) {
707
-				$subject = Downloads::SUBJECT_SHARED_FILE_BY_EMAIL_DOWNLOADED;
708
-			} else {
709
-				$subject = Downloads::SUBJECT_SHARED_FOLDER_BY_EMAIL_DOWNLOADED;
710
-			}
711
-			$parameters[] = $share->getSharedWith();
712
-		} else {
713
-			if ($node instanceof \OCP\Files\File) {
714
-				$subject = Downloads::SUBJECT_PUBLIC_SHARED_FILE_DOWNLOADED;
715
-			} else {
716
-				$subject = Downloads::SUBJECT_PUBLIC_SHARED_FOLDER_DOWNLOADED;
717
-			}
718
-		}
719
-
720
-		$this->publishActivity($subject, $parameters, $share->getSharedBy(), $fileId, $userPath);
721
-
722
-		if ($share->getShareOwner() !== $share->getSharedBy()) {
723
-			$parameters[0] = $ownerPath;
724
-			$this->publishActivity($subject, $parameters, $share->getShareOwner(), $fileId, $ownerPath);
725
-		}
726
-	}
727
-
728
-	/**
729
-	 * publish activity
730
-	 *
731
-	 * @param string $subject
732
-	 * @param array $parameters
733
-	 * @param string $affectedUser
734
-	 * @param int $fileId
735
-	 * @param string $filePath
736
-	 */
737
-	protected function publishActivity($subject,
738
-										array $parameters,
739
-										$affectedUser,
740
-										$fileId,
741
-										$filePath) {
742
-		$event = $this->activityManager->generateEvent();
743
-		$event->setApp('files_sharing')
744
-			->setType('public_links')
745
-			->setSubject($subject, $parameters)
746
-			->setAffectedUser($affectedUser)
747
-			->setObject('files', $fileId, $filePath);
748
-		$this->activityManager->publish($event);
749
-	}
383
+            $freeSpace = $share->getNode()->getStorage()->free_space($share->getNode()->getInternalPath());
384
+            if ($freeSpace < \OCP\Files\FileInfo::SPACE_UNLIMITED) {
385
+                $freeSpace = max($freeSpace, 0);
386
+            } else {
387
+                $freeSpace = (INF > 0) ? INF: PHP_INT_MAX; // work around https://bugs.php.net/bug.php?id=69188
388
+            }
389
+
390
+            $hideFileList = !($share->getPermissions() & \OCP\Constants::PERMISSION_READ);
391
+            $maxUploadFilesize = $freeSpace;
392
+
393
+            $folder = new Template('files', 'list', '');
394
+
395
+            $folder->assign('dir', $shareNode->getRelativePath($folderNode->getPath()));
396
+            $folder->assign('dirToken', $this->getToken());
397
+            $folder->assign('permissions', \OCP\Constants::PERMISSION_READ);
398
+            $folder->assign('isPublic', true);
399
+            $folder->assign('hideFileList', $hideFileList);
400
+            $folder->assign('publicUploadEnabled', 'no');
401
+            // default to list view
402
+            $folder->assign('showgridview', false);
403
+            $folder->assign('uploadMaxFilesize', $maxUploadFilesize);
404
+            $folder->assign('uploadMaxHumanFilesize', \OCP\Util::humanFileSize($maxUploadFilesize));
405
+            $folder->assign('freeSpace', $freeSpace);
406
+            $folder->assign('usedSpacePercent', 0);
407
+            $folder->assign('trash', false);
408
+            $shareTmpl['folder'] = $folder->fetchPage();
409
+        } else {
410
+            $shareIsFolder = false;
411
+        }
412
+
413
+        // default to list view
414
+        $shareTmpl['showgridview'] = false;
415
+
416
+        $shareTmpl['hideFileList'] = $hideFileList;
417
+        $shareTmpl['downloadURL'] = $this->urlGenerator->linkToRouteAbsolute('files_sharing.sharecontroller.downloadShare', [
418
+            'token' => $this->getToken(),
419
+            'filename' => $shareIsFolder ? null : $shareNode->getName()
420
+        ]);
421
+        $shareTmpl['shareUrl'] = $this->urlGenerator->linkToRouteAbsolute('files_sharing.sharecontroller.showShare', ['token' => $this->getToken()]);
422
+        $shareTmpl['maxSizeAnimateGif'] = $this->config->getSystemValue('max_filesize_animated_gifs_public_sharing', 10);
423
+        $shareTmpl['previewEnabled'] = $this->config->getSystemValue('enable_previews', true);
424
+        $shareTmpl['previewMaxX'] = $this->config->getSystemValue('preview_max_x', 1024);
425
+        $shareTmpl['previewMaxY'] = $this->config->getSystemValue('preview_max_y', 1024);
426
+        $shareTmpl['disclaimer'] = $this->config->getAppValue('core', 'shareapi_public_link_disclaimertext', null);
427
+        $shareTmpl['previewURL'] = $shareTmpl['downloadURL'];
428
+
429
+        if ($shareTmpl['previewSupported']) {
430
+            $shareTmpl['previewImage'] = $this->urlGenerator->linkToRouteAbsolute('files_sharing.PublicPreview.getPreview',
431
+                ['x' => 200, 'y' => 200, 'file' => $shareTmpl['directory_path'], 'token' => $shareTmpl['dirToken']]);
432
+            $ogPreview = $shareTmpl['previewImage'];
433
+
434
+            // We just have direct previews for image files
435
+            if ($shareNode->getMimePart() === 'image') {
436
+                $shareTmpl['previewURL'] = $this->urlGenerator->linkToRouteAbsolute('files_sharing.publicpreview.directLink', ['token' => $this->getToken()]);
437
+
438
+                $ogPreview = $shareTmpl['previewURL'];
439
+
440
+                //Whatapp is kind of picky about their size requirements
441
+                if ($this->request->isUserAgent(['/^WhatsApp/'])) {
442
+                    $ogPreview = $this->urlGenerator->linkToRouteAbsolute('files_sharing.PublicPreview.getPreview', [
443
+                        'token' => $this->getToken(),
444
+                        'x' => 256,
445
+                        'y' => 256,
446
+                        'a' => true,
447
+                    ]);
448
+                }
449
+            }
450
+        } else {
451
+            $shareTmpl['previewImage'] = $this->urlGenerator->getAbsoluteURL($this->urlGenerator->imagePath('core', 'favicon-fb.png'));
452
+            $ogPreview = $shareTmpl['previewImage'];
453
+        }
454
+
455
+        // Load files we need
456
+        \OCP\Util::addScript('files', 'semaphore');
457
+        \OCP\Util::addScript('files', 'file-upload');
458
+        \OCP\Util::addStyle('files_sharing', 'publicView');
459
+        \OCP\Util::addScript('files_sharing', 'public');
460
+        \OCP\Util::addScript('files_sharing', 'templates');
461
+        \OCP\Util::addScript('files', 'fileactions');
462
+        \OCP\Util::addScript('files', 'fileactionsmenu');
463
+        \OCP\Util::addScript('files', 'jquery.fileupload');
464
+        \OCP\Util::addScript('files_sharing', 'files_drop');
465
+
466
+        if (isset($shareTmpl['folder'])) {
467
+            // JS required for folders
468
+            \OCP\Util::addStyle('files', 'merged');
469
+            \OCP\Util::addScript('files', 'filesummary');
470
+            \OCP\Util::addScript('files', 'templates');
471
+            \OCP\Util::addScript('files', 'breadcrumb');
472
+            \OCP\Util::addScript('files', 'fileinfomodel');
473
+            \OCP\Util::addScript('files', 'newfilemenu');
474
+            \OCP\Util::addScript('files', 'files');
475
+            \OCP\Util::addScript('files', 'filemultiselectmenu');
476
+            \OCP\Util::addScript('files', 'filelist');
477
+            \OCP\Util::addScript('files', 'keyboardshortcuts');
478
+            \OCP\Util::addScript('files', 'operationprogressbar');
479
+
480
+            // Load Viewer scripts
481
+            if (class_exists(LoadViewer::class)) {
482
+                $this->eventDispatcher->dispatchTyped(new LoadViewer());
483
+            }
484
+        }
485
+
486
+        // OpenGraph Support: http://ogp.me/
487
+        \OCP\Util::addHeader('meta', ['property' => "og:title", 'content' => $shareTmpl['filename']]);
488
+        \OCP\Util::addHeader('meta', ['property' => "og:description", 'content' => $this->defaults->getName() . ($this->defaults->getSlogan() !== '' ? ' - ' . $this->defaults->getSlogan() : '')]);
489
+        \OCP\Util::addHeader('meta', ['property' => "og:site_name", 'content' => $this->defaults->getName()]);
490
+        \OCP\Util::addHeader('meta', ['property' => "og:url", 'content' => $shareTmpl['shareUrl']]);
491
+        \OCP\Util::addHeader('meta', ['property' => "og:type", 'content' => "object"]);
492
+        \OCP\Util::addHeader('meta', ['property' => "og:image", 'content' => $ogPreview]);
493
+
494
+        $this->eventDispatcher->dispatchTyped(new BeforeTemplateRenderedEvent($share));
495
+
496
+        $csp = new \OCP\AppFramework\Http\ContentSecurityPolicy();
497
+        $csp->addAllowedFrameDomain('\'self\'');
498
+
499
+        $response = new PublicTemplateResponse($this->appName, 'public', $shareTmpl);
500
+        $response->setHeaderTitle($shareTmpl['filename']);
501
+        if ($shareTmpl['shareOwner'] !== '') {
502
+            $response->setHeaderDetails($this->l10n->t('shared by %s', [$shareTmpl['shareOwner']]));
503
+        }
504
+
505
+        $isNoneFileDropFolder = $shareIsFolder === false || $share->getPermissions() !== \OCP\Constants::PERMISSION_CREATE;
506
+
507
+        if ($isNoneFileDropFolder && !$share->getHideDownload()) {
508
+            \OCP\Util::addScript('files_sharing', 'public_note');
509
+
510
+            $downloadWhite = new SimpleMenuAction('download', $this->l10n->t('Download'), 'icon-download-white', $shareTmpl['downloadURL'], 0);
511
+            $downloadAllWhite = new SimpleMenuAction('download', $this->l10n->t('Download all files'), 'icon-download-white', $shareTmpl['downloadURL'], 0);
512
+            $download = new SimpleMenuAction('download', $this->l10n->t('Download'), 'icon-download', $shareTmpl['downloadURL'], 10, $shareTmpl['fileSize']);
513
+            $downloadAll = new SimpleMenuAction('download', $this->l10n->t('Download all files'), 'icon-download', $shareTmpl['downloadURL'], 10, $shareTmpl['fileSize']);
514
+            $directLink = new LinkMenuAction($this->l10n->t('Direct link'), 'icon-public', $shareTmpl['previewURL']);
515
+            $externalShare = new ExternalShareMenuAction($this->l10n->t('Add to your Nextcloud'), 'icon-external', $shareTmpl['owner'], $shareTmpl['shareOwner'], $shareTmpl['filename']);
516
+
517
+            $responseComposer = [];
518
+
519
+            if ($shareIsFolder) {
520
+                $responseComposer[] = $downloadAllWhite;
521
+                $responseComposer[] = $downloadAll;
522
+            } else {
523
+                $responseComposer[] = $downloadWhite;
524
+                $responseComposer[] = $download;
525
+            }
526
+            $responseComposer[] = $directLink;
527
+            if ($this->federatedShareProvider->isOutgoingServer2serverShareEnabled()) {
528
+                $responseComposer[] = $externalShare;
529
+            }
530
+
531
+            $response->setHeaderActions($responseComposer);
532
+        }
533
+
534
+        $response->setContentSecurityPolicy($csp);
535
+
536
+        $this->emitAccessShareHook($share);
537
+
538
+        return $response;
539
+    }
540
+
541
+    /**
542
+     * @PublicPage
543
+     * @NoCSRFRequired
544
+     * @NoSameSiteCookieRequired
545
+     *
546
+     * @param string $token
547
+     * @param string $files
548
+     * @param string $path
549
+     * @param string $downloadStartSecret
550
+     * @return void|\OCP\AppFramework\Http\Response
551
+     * @throws NotFoundException
552
+     */
553
+    public function downloadShare($token, $files = null, $path = '', $downloadStartSecret = '') {
554
+        \OC_User::setIncognitoMode(true);
555
+
556
+        $share = $this->shareManager->getShareByToken($token);
557
+
558
+        if (!($share->getPermissions() & \OCP\Constants::PERMISSION_READ)) {
559
+            return new \OCP\AppFramework\Http\DataResponse('Share is read-only');
560
+        }
561
+
562
+        $files_list = null;
563
+        if (!is_null($files)) { // download selected files
564
+            $files_list = json_decode($files);
565
+            // in case we get only a single file
566
+            if ($files_list === null) {
567
+                $files_list = [$files];
568
+            }
569
+            // Just in case $files is a single int like '1234'
570
+            if (!is_array($files_list)) {
571
+                $files_list = [$files_list];
572
+            }
573
+        }
574
+
575
+        if (!$this->validateShare($share)) {
576
+            throw new NotFoundException();
577
+        }
578
+
579
+        $userFolder = $this->rootFolder->getUserFolder($share->getShareOwner());
580
+        $originalSharePath = $userFolder->getRelativePath($share->getNode()->getPath());
581
+
582
+
583
+        // Single file share
584
+        if ($share->getNode() instanceof \OCP\Files\File) {
585
+            // Single file download
586
+            $this->singleFileDownloaded($share, $share->getNode());
587
+        }
588
+        // Directory share
589
+        else {
590
+            /** @var \OCP\Files\Folder $node */
591
+            $node = $share->getNode();
592
+
593
+            // Try to get the path
594
+            if ($path !== '') {
595
+                try {
596
+                    $node = $node->get($path);
597
+                } catch (NotFoundException $e) {
598
+                    $this->emitAccessShareHook($share, 404, 'Share not found');
599
+                    return new NotFoundResponse();
600
+                }
601
+            }
602
+
603
+            $originalSharePath = $userFolder->getRelativePath($node->getPath());
604
+
605
+            if ($node instanceof \OCP\Files\File) {
606
+                // Single file download
607
+                $this->singleFileDownloaded($share, $share->getNode());
608
+            } else {
609
+                try {
610
+                    if (!empty($files_list)) {
611
+                        $this->fileListDownloaded($share, $files_list, $node);
612
+                    } else {
613
+                        // The folder is downloaded
614
+                        $this->singleFileDownloaded($share, $share->getNode());
615
+                    }
616
+                } catch (NotFoundException $e) {
617
+                    return new NotFoundResponse();
618
+                }
619
+            }
620
+        }
621
+
622
+        /* FIXME: We should do this all nicely in OCP */
623
+        OC_Util::tearDownFS();
624
+        OC_Util::setupFS($share->getShareOwner());
625
+
626
+        /**
627
+         * this sets a cookie to be able to recognize the start of the download
628
+         * the content must not be longer than 32 characters and must only contain
629
+         * alphanumeric characters
630
+         */
631
+        if (!empty($downloadStartSecret)
632
+            && !isset($downloadStartSecret[32])
633
+            && preg_match('!^[a-zA-Z0-9]+$!', $downloadStartSecret) === 1) {
634
+
635
+            // FIXME: set on the response once we use an actual app framework response
636
+            setcookie('ocDownloadStarted', $downloadStartSecret, time() + 20, '/');
637
+        }
638
+
639
+        $this->emitAccessShareHook($share);
640
+
641
+        $server_params = [ 'head' => $this->request->getMethod() === 'HEAD' ];
642
+
643
+        /**
644
+         * Http range requests support
645
+         */
646
+        if (isset($_SERVER['HTTP_RANGE'])) {
647
+            $server_params['range'] = $this->request->getHeader('Range');
648
+        }
649
+
650
+        // download selected files
651
+        if (!is_null($files) && $files !== '') {
652
+            // FIXME: The exit is required here because otherwise the AppFramework is trying to add headers as well
653
+            // after dispatching the request which results in a "Cannot modify header information" notice.
654
+            OC_Files::get($originalSharePath, $files_list, $server_params);
655
+            exit();
656
+        } else {
657
+            // FIXME: The exit is required here because otherwise the AppFramework is trying to add headers as well
658
+            // after dispatching the request which results in a "Cannot modify header information" notice.
659
+            OC_Files::get(dirname($originalSharePath), basename($originalSharePath), $server_params);
660
+            exit();
661
+        }
662
+    }
663
+
664
+    /**
665
+     * create activity for every downloaded file
666
+     *
667
+     * @param Share\IShare $share
668
+     * @param array $files_list
669
+     * @param \OCP\Files\Folder $node
670
+     * @throws NotFoundException when trying to download a folder or multiple files of a "hide download" share
671
+     */
672
+    protected function fileListDownloaded(Share\IShare $share, array $files_list, \OCP\Files\Folder $node) {
673
+        if ($share->getHideDownload() && count($files_list) > 1) {
674
+            throw new NotFoundException('Downloading more than 1 file');
675
+        }
676
+
677
+        foreach ($files_list as $file) {
678
+            $subNode = $node->get($file);
679
+            $this->singleFileDownloaded($share, $subNode);
680
+        }
681
+    }
682
+
683
+    /**
684
+     * create activity if a single file was downloaded from a link share
685
+     *
686
+     * @param Share\IShare $share
687
+     * @throws NotFoundException when trying to download a folder of a "hide download" share
688
+     */
689
+    protected function singleFileDownloaded(Share\IShare $share, \OCP\Files\Node $node) {
690
+        if ($share->getHideDownload() && $node instanceof Folder) {
691
+            throw new NotFoundException('Downloading a folder');
692
+        }
693
+
694
+        $fileId = $node->getId();
695
+
696
+        $userFolder = $this->rootFolder->getUserFolder($share->getSharedBy());
697
+        $userNodeList = $userFolder->getById($fileId);
698
+        $userNode = $userNodeList[0];
699
+        $ownerFolder = $this->rootFolder->getUserFolder($share->getShareOwner());
700
+        $userPath = $userFolder->getRelativePath($userNode->getPath());
701
+        $ownerPath = $ownerFolder->getRelativePath($node->getPath());
702
+
703
+        $parameters = [$userPath];
704
+
705
+        if ($share->getShareType() === IShare::TYPE_EMAIL) {
706
+            if ($node instanceof \OCP\Files\File) {
707
+                $subject = Downloads::SUBJECT_SHARED_FILE_BY_EMAIL_DOWNLOADED;
708
+            } else {
709
+                $subject = Downloads::SUBJECT_SHARED_FOLDER_BY_EMAIL_DOWNLOADED;
710
+            }
711
+            $parameters[] = $share->getSharedWith();
712
+        } else {
713
+            if ($node instanceof \OCP\Files\File) {
714
+                $subject = Downloads::SUBJECT_PUBLIC_SHARED_FILE_DOWNLOADED;
715
+            } else {
716
+                $subject = Downloads::SUBJECT_PUBLIC_SHARED_FOLDER_DOWNLOADED;
717
+            }
718
+        }
719
+
720
+        $this->publishActivity($subject, $parameters, $share->getSharedBy(), $fileId, $userPath);
721
+
722
+        if ($share->getShareOwner() !== $share->getSharedBy()) {
723
+            $parameters[0] = $ownerPath;
724
+            $this->publishActivity($subject, $parameters, $share->getShareOwner(), $fileId, $ownerPath);
725
+        }
726
+    }
727
+
728
+    /**
729
+     * publish activity
730
+     *
731
+     * @param string $subject
732
+     * @param array $parameters
733
+     * @param string $affectedUser
734
+     * @param int $fileId
735
+     * @param string $filePath
736
+     */
737
+    protected function publishActivity($subject,
738
+                                        array $parameters,
739
+                                        $affectedUser,
740
+                                        $fileId,
741
+                                        $filePath) {
742
+        $event = $this->activityManager->generateEvent();
743
+        $event->setApp('files_sharing')
744
+            ->setType('public_links')
745
+            ->setSubject($subject, $parameters)
746
+            ->setAffectedUser($affectedUser)
747
+            ->setObject('files', $fileId, $filePath);
748
+        $this->activityManager->publish($event);
749
+    }
750 750
 }
Please login to merge, or discard this patch.