Completed
Push — master ( 0ad1f1...60adc9 )
by Blizzz
53:56 queued 32:56
created
lib/private/User/Session.php 1 patch
Indentation   +826 added lines, -826 removed lines patch added patch discarded remove patch
@@ -86,832 +86,832 @@
 block discarded – undo
86 86
  */
87 87
 class Session implements IUserSession, Emitter {
88 88
 
89
-	/** @var Manager|PublicEmitter $manager */
90
-	private $manager;
91
-
92
-	/** @var ISession $session */
93
-	private $session;
94
-
95
-	/** @var ITimeFactory */
96
-	private $timeFactory;
97
-
98
-	/** @var IProvider */
99
-	private $tokenProvider;
100
-
101
-	/** @var IConfig */
102
-	private $config;
103
-
104
-	/** @var User $activeUser */
105
-	protected $activeUser;
106
-
107
-	/** @var ISecureRandom */
108
-	private $random;
109
-
110
-	/** @var ILockdownManager  */
111
-	private $lockdownManager;
112
-
113
-	/** @var ILogger */
114
-	private $logger;
115
-
116
-	/**
117
-	 * @param Manager $manager
118
-	 * @param ISession $session
119
-	 * @param ITimeFactory $timeFactory
120
-	 * @param IProvider $tokenProvider
121
-	 * @param IConfig $config
122
-	 * @param ISecureRandom $random
123
-	 * @param ILockdownManager $lockdownManager
124
-	 * @param ILogger $logger
125
-	 */
126
-	public function __construct(Manager $manager,
127
-								ISession $session,
128
-								ITimeFactory $timeFactory,
129
-								$tokenProvider,
130
-								IConfig $config,
131
-								ISecureRandom $random,
132
-								ILockdownManager $lockdownManager,
133
-								ILogger $logger) {
134
-		$this->manager = $manager;
135
-		$this->session = $session;
136
-		$this->timeFactory = $timeFactory;
137
-		$this->tokenProvider = $tokenProvider;
138
-		$this->config = $config;
139
-		$this->random = $random;
140
-		$this->lockdownManager = $lockdownManager;
141
-		$this->logger = $logger;
142
-	}
143
-
144
-	/**
145
-	 * @param IProvider $provider
146
-	 */
147
-	public function setTokenProvider(IProvider $provider) {
148
-		$this->tokenProvider = $provider;
149
-	}
150
-
151
-	/**
152
-	 * @param string $scope
153
-	 * @param string $method
154
-	 * @param callable $callback
155
-	 */
156
-	public function listen($scope, $method, callable $callback) {
157
-		$this->manager->listen($scope, $method, $callback);
158
-	}
159
-
160
-	/**
161
-	 * @param string $scope optional
162
-	 * @param string $method optional
163
-	 * @param callable $callback optional
164
-	 */
165
-	public function removeListener($scope = null, $method = null, callable $callback = null) {
166
-		$this->manager->removeListener($scope, $method, $callback);
167
-	}
168
-
169
-	/**
170
-	 * get the manager object
171
-	 *
172
-	 * @return Manager|PublicEmitter
173
-	 */
174
-	public function getManager() {
175
-		return $this->manager;
176
-	}
177
-
178
-	/**
179
-	 * get the session object
180
-	 *
181
-	 * @return ISession
182
-	 */
183
-	public function getSession() {
184
-		return $this->session;
185
-	}
186
-
187
-	/**
188
-	 * set the session object
189
-	 *
190
-	 * @param ISession $session
191
-	 */
192
-	public function setSession(ISession $session) {
193
-		if ($this->session instanceof ISession) {
194
-			$this->session->close();
195
-		}
196
-		$this->session = $session;
197
-		$this->activeUser = null;
198
-	}
199
-
200
-	/**
201
-	 * set the currently active user
202
-	 *
203
-	 * @param IUser|null $user
204
-	 */
205
-	public function setUser($user) {
206
-		if (is_null($user)) {
207
-			$this->session->remove('user_id');
208
-		} else {
209
-			$this->session->set('user_id', $user->getUID());
210
-		}
211
-		$this->activeUser = $user;
212
-	}
213
-
214
-	/**
215
-	 * get the current active user
216
-	 *
217
-	 * @return IUser|null Current user, otherwise null
218
-	 */
219
-	public function getUser() {
220
-		// FIXME: This is a quick'n dirty work-around for the incognito mode as
221
-		// described at https://github.com/owncloud/core/pull/12912#issuecomment-67391155
222
-		if (OC_User::isIncognitoMode()) {
223
-			return null;
224
-		}
225
-		if (is_null($this->activeUser)) {
226
-			$uid = $this->session->get('user_id');
227
-			if (is_null($uid)) {
228
-				return null;
229
-			}
230
-			$this->activeUser = $this->manager->get($uid);
231
-			if (is_null($this->activeUser)) {
232
-				return null;
233
-			}
234
-			$this->validateSession();
235
-		}
236
-		return $this->activeUser;
237
-	}
238
-
239
-	/**
240
-	 * Validate whether the current session is valid
241
-	 *
242
-	 * - For token-authenticated clients, the token validity is checked
243
-	 * - For browsers, the session token validity is checked
244
-	 */
245
-	protected function validateSession() {
246
-		$token = null;
247
-		$appPassword = $this->session->get('app_password');
248
-
249
-		if (is_null($appPassword)) {
250
-			try {
251
-				$token = $this->session->getId();
252
-			} catch (SessionNotAvailableException $ex) {
253
-				return;
254
-			}
255
-		} else {
256
-			$token = $appPassword;
257
-		}
258
-
259
-		if (!$this->validateToken($token)) {
260
-			// Session was invalidated
261
-			$this->logout();
262
-		}
263
-	}
264
-
265
-	/**
266
-	 * Checks whether the user is logged in
267
-	 *
268
-	 * @return bool if logged in
269
-	 */
270
-	public function isLoggedIn() {
271
-		$user = $this->getUser();
272
-		if (is_null($user)) {
273
-			return false;
274
-		}
275
-
276
-		return $user->isEnabled();
277
-	}
278
-
279
-	/**
280
-	 * set the login name
281
-	 *
282
-	 * @param string|null $loginName for the logged in user
283
-	 */
284
-	public function setLoginName($loginName) {
285
-		if (is_null($loginName)) {
286
-			$this->session->remove('loginname');
287
-		} else {
288
-			$this->session->set('loginname', $loginName);
289
-		}
290
-	}
291
-
292
-	/**
293
-	 * get the login name of the current user
294
-	 *
295
-	 * @return string
296
-	 */
297
-	public function getLoginName() {
298
-		if ($this->activeUser) {
299
-			return $this->session->get('loginname');
300
-		}
301
-
302
-		$uid = $this->session->get('user_id');
303
-		if ($uid) {
304
-			$this->activeUser = $this->manager->get($uid);
305
-			return $this->session->get('loginname');
306
-		}
307
-
308
-		return null;
309
-	}
310
-
311
-	/**
312
-	 * set the token id
313
-	 *
314
-	 * @param int|null $token that was used to log in
315
-	 */
316
-	protected function setToken($token) {
317
-		if ($token === null) {
318
-			$this->session->remove('token-id');
319
-		} else {
320
-			$this->session->set('token-id', $token);
321
-		}
322
-	}
323
-
324
-	/**
325
-	 * try to log in with the provided credentials
326
-	 *
327
-	 * @param string $uid
328
-	 * @param string $password
329
-	 * @return boolean|null
330
-	 * @throws LoginException
331
-	 */
332
-	public function login($uid, $password) {
333
-		$this->session->regenerateId();
334
-		if ($this->validateToken($password, $uid)) {
335
-			return $this->loginWithToken($password);
336
-		}
337
-		return $this->loginWithPassword($uid, $password);
338
-	}
339
-
340
-	/**
341
-	 * @param IUser $user
342
-	 * @param array $loginDetails
343
-	 * @param bool $regenerateSessionId
344
-	 * @return true returns true if login successful or an exception otherwise
345
-	 * @throws LoginException
346
-	 */
347
-	public function completeLogin(IUser $user, array $loginDetails, $regenerateSessionId = true) {
348
-		if (!$user->isEnabled()) {
349
-			// disabled users can not log in
350
-			// injecting l10n does not work - there is a circular dependency between session and \OCP\L10N\IFactory
351
-			$message = \OC::$server->getL10N('lib')->t('User disabled');
352
-			throw new LoginException($message);
353
-		}
354
-
355
-		if($regenerateSessionId) {
356
-			$this->session->regenerateId();
357
-		}
358
-
359
-		$this->setUser($user);
360
-		$this->setLoginName($loginDetails['loginName']);
361
-
362
-		if(isset($loginDetails['token']) && $loginDetails['token'] instanceof IToken) {
363
-			$this->setToken($loginDetails['token']->getId());
364
-			$this->lockdownManager->setToken($loginDetails['token']);
365
-			$firstTimeLogin = false;
366
-		} else {
367
-			$this->setToken(null);
368
-			$firstTimeLogin = $user->updateLastLoginTimestamp();
369
-		}
370
-		$this->manager->emit('\OC\User', 'postLogin', [$user, $loginDetails['password']]);
371
-		if($this->isLoggedIn()) {
372
-			$this->prepareUserLogin($firstTimeLogin, $regenerateSessionId);
373
-			return true;
374
-		}
375
-
376
-		$message = \OC::$server->getL10N('lib')->t('Login canceled by app');
377
-		throw new LoginException($message);
378
-	}
379
-
380
-	/**
381
-	 * Tries to log in a client
382
-	 *
383
-	 * Checks token auth enforced
384
-	 * Checks 2FA enabled
385
-	 *
386
-	 * @param string $user
387
-	 * @param string $password
388
-	 * @param IRequest $request
389
-	 * @param OC\Security\Bruteforce\Throttler $throttler
390
-	 * @throws LoginException
391
-	 * @throws PasswordLoginForbiddenException
392
-	 * @return boolean
393
-	 */
394
-	public function logClientIn($user,
395
-								$password,
396
-								IRequest $request,
397
-								OC\Security\Bruteforce\Throttler $throttler) {
398
-		$currentDelay = $throttler->sleepDelay($request->getRemoteAddress(), 'login');
399
-
400
-		if ($this->manager instanceof PublicEmitter) {
401
-			$this->manager->emit('\OC\User', 'preLogin', array($user, $password));
402
-		}
403
-
404
-		$isTokenPassword = $this->isTokenPassword($password);
405
-		if (!$isTokenPassword && $this->isTokenAuthEnforced()) {
406
-			throw new PasswordLoginForbiddenException();
407
-		}
408
-		if (!$isTokenPassword && $this->isTwoFactorEnforced($user)) {
409
-			throw new PasswordLoginForbiddenException();
410
-		}
411
-
412
-		// Try to login with this username and password
413
-		if (!$this->login($user, $password) ) {
414
-
415
-			// Failed, maybe the user used their email address
416
-			$users = $this->manager->getByEmail($user);
417
-			if (!(\count($users) === 1 && $this->login($users[0]->getUID(), $password))) {
418
-
419
-				$this->logger->warning('Login failed: \'' . $user . '\' (Remote IP: \'' . \OC::$server->getRequest()->getRemoteAddress() . '\')', ['app' => 'core']);
420
-
421
-				$throttler->registerAttempt('login', $request->getRemoteAddress(), ['uid' => $user]);
422
-				if ($currentDelay === 0) {
423
-					$throttler->sleepDelay($request->getRemoteAddress(), 'login');
424
-				}
425
-				return false;
426
-			}
427
-		}
428
-
429
-		if ($isTokenPassword) {
430
-			$this->session->set('app_password', $password);
431
-		} else if($this->supportsCookies($request)) {
432
-			// Password login, but cookies supported -> create (browser) session token
433
-			$this->createSessionToken($request, $this->getUser()->getUID(), $user, $password);
434
-		}
435
-
436
-		return true;
437
-	}
438
-
439
-	protected function supportsCookies(IRequest $request) {
440
-		if (!is_null($request->getCookie('cookie_test'))) {
441
-			return true;
442
-		}
443
-		setcookie('cookie_test', 'test', $this->timeFactory->getTime() + 3600);
444
-		return false;
445
-	}
446
-
447
-	private function isTokenAuthEnforced() {
448
-		return $this->config->getSystemValue('token_auth_enforced', false);
449
-	}
450
-
451
-	protected function isTwoFactorEnforced($username) {
452
-		Util::emitHook(
453
-			'\OCA\Files_Sharing\API\Server2Server',
454
-			'preLoginNameUsedAsUserName',
455
-			array('uid' => &$username)
456
-		);
457
-		$user = $this->manager->get($username);
458
-		if (is_null($user)) {
459
-			$users = $this->manager->getByEmail($username);
460
-			if (empty($users)) {
461
-				return false;
462
-			}
463
-			if (count($users) !== 1) {
464
-				return true;
465
-			}
466
-			$user = $users[0];
467
-		}
468
-		// DI not possible due to cyclic dependencies :'-/
469
-		return OC::$server->getTwoFactorAuthManager()->isTwoFactorAuthenticated($user);
470
-	}
471
-
472
-	/**
473
-	 * Check if the given 'password' is actually a device token
474
-	 *
475
-	 * @param string $password
476
-	 * @return boolean
477
-	 */
478
-	public function isTokenPassword($password) {
479
-		try {
480
-			$this->tokenProvider->getToken($password);
481
-			return true;
482
-		} catch (InvalidTokenException $ex) {
483
-			return false;
484
-		}
485
-	}
486
-
487
-	protected function prepareUserLogin($firstTimeLogin, $refreshCsrfToken = true) {
488
-		if ($refreshCsrfToken) {
489
-			// TODO: mock/inject/use non-static
490
-			// Refresh the token
491
-			\OC::$server->getCsrfTokenManager()->refreshToken();
492
-		}
493
-
494
-		//we need to pass the user name, which may differ from login name
495
-		$user = $this->getUser()->getUID();
496
-		OC_Util::setupFS($user);
497
-
498
-		if ($firstTimeLogin) {
499
-			// TODO: lock necessary?
500
-			//trigger creation of user home and /files folder
501
-			$userFolder = \OC::$server->getUserFolder($user);
502
-
503
-			try {
504
-				// copy skeleton
505
-				\OC_Util::copySkeleton($user, $userFolder);
506
-			} catch (NotPermittedException $ex) {
507
-				// read only uses
508
-			}
509
-
510
-			// trigger any other initialization
511
-			\OC::$server->getEventDispatcher()->dispatch(IUser::class . '::firstLogin', new GenericEvent($this->getUser()));
512
-		}
513
-	}
514
-
515
-	/**
516
-	 * Tries to login the user with HTTP Basic Authentication
517
-	 *
518
-	 * @todo do not allow basic auth if the user is 2FA enforced
519
-	 * @param IRequest $request
520
-	 * @param OC\Security\Bruteforce\Throttler $throttler
521
-	 * @return boolean if the login was successful
522
-	 */
523
-	public function tryBasicAuthLogin(IRequest $request,
524
-									  OC\Security\Bruteforce\Throttler $throttler) {
525
-		if (!empty($request->server['PHP_AUTH_USER']) && !empty($request->server['PHP_AUTH_PW'])) {
526
-			try {
527
-				if ($this->logClientIn($request->server['PHP_AUTH_USER'], $request->server['PHP_AUTH_PW'], $request, $throttler)) {
528
-					/**
529
-					 * Add DAV authenticated. This should in an ideal world not be
530
-					 * necessary but the iOS App reads cookies from anywhere instead
531
-					 * only the DAV endpoint.
532
-					 * This makes sure that the cookies will be valid for the whole scope
533
-					 * @see https://github.com/owncloud/core/issues/22893
534
-					 */
535
-					$this->session->set(
536
-						Auth::DAV_AUTHENTICATED, $this->getUser()->getUID()
537
-					);
538
-
539
-					// Set the last-password-confirm session to make the sudo mode work
540
-					 $this->session->set('last-password-confirm', $this->timeFactory->getTime());
541
-
542
-					return true;
543
-				}
544
-			} catch (PasswordLoginForbiddenException $ex) {
545
-				// Nothing to do
546
-			}
547
-		}
548
-		return false;
549
-	}
550
-
551
-	/**
552
-	 * Log an user in via login name and password
553
-	 *
554
-	 * @param string $uid
555
-	 * @param string $password
556
-	 * @return boolean
557
-	 * @throws LoginException if an app canceld the login process or the user is not enabled
558
-	 */
559
-	private function loginWithPassword($uid, $password) {
560
-		$user = $this->manager->checkPasswordNoLogging($uid, $password);
561
-		if ($user === false) {
562
-			// Password check failed
563
-			return false;
564
-		}
565
-
566
-		return $this->completeLogin($user, ['loginName' => $uid, 'password' => $password], false);
567
-	}
568
-
569
-	/**
570
-	 * Log an user in with a given token (id)
571
-	 *
572
-	 * @param string $token
573
-	 * @return boolean
574
-	 * @throws LoginException if an app canceled the login process or the user is not enabled
575
-	 */
576
-	private function loginWithToken($token) {
577
-		try {
578
-			$dbToken = $this->tokenProvider->getToken($token);
579
-		} catch (InvalidTokenException $ex) {
580
-			return false;
581
-		}
582
-		$uid = $dbToken->getUID();
583
-
584
-		// When logging in with token, the password must be decrypted first before passing to login hook
585
-		$password = '';
586
-		try {
587
-			$password = $this->tokenProvider->getPassword($dbToken, $token);
588
-		} catch (PasswordlessTokenException $ex) {
589
-			// Ignore and use empty string instead
590
-		}
591
-
592
-		$this->manager->emit('\OC\User', 'preLogin', array($uid, $password));
593
-
594
-		$user = $this->manager->get($uid);
595
-		if (is_null($user)) {
596
-			// user does not exist
597
-			return false;
598
-		}
599
-
600
-		return $this->completeLogin(
601
-			$user,
602
-			[
603
-				'loginName' => $dbToken->getLoginName(),
604
-				'password' => $password,
605
-				'token' => $dbToken
606
-			],
607
-			false);
608
-	}
609
-
610
-	/**
611
-	 * Create a new session token for the given user credentials
612
-	 *
613
-	 * @param IRequest $request
614
-	 * @param string $uid user UID
615
-	 * @param string $loginName login name
616
-	 * @param string $password
617
-	 * @param int $remember
618
-	 * @return boolean
619
-	 */
620
-	public function createSessionToken(IRequest $request, $uid, $loginName, $password = null, $remember = IToken::DO_NOT_REMEMBER) {
621
-		if (is_null($this->manager->get($uid))) {
622
-			// User does not exist
623
-			return false;
624
-		}
625
-		$name = isset($request->server['HTTP_USER_AGENT']) ? $request->server['HTTP_USER_AGENT'] : 'unknown browser';
626
-		try {
627
-			$sessionId = $this->session->getId();
628
-			$pwd = $this->getPassword($password);
629
-			// Make sure the current sessionId has no leftover tokens
630
-			$this->tokenProvider->invalidateToken($sessionId);
631
-			$this->tokenProvider->generateToken($sessionId, $uid, $loginName, $pwd, $name, IToken::TEMPORARY_TOKEN, $remember);
632
-			return true;
633
-		} catch (SessionNotAvailableException $ex) {
634
-			// This can happen with OCC, where a memory session is used
635
-			// if a memory session is used, we shouldn't create a session token anyway
636
-			return false;
637
-		}
638
-	}
639
-
640
-	/**
641
-	 * Checks if the given password is a token.
642
-	 * If yes, the password is extracted from the token.
643
-	 * If no, the same password is returned.
644
-	 *
645
-	 * @param string $password either the login password or a device token
646
-	 * @return string|null the password or null if none was set in the token
647
-	 */
648
-	private function getPassword($password) {
649
-		if (is_null($password)) {
650
-			// This is surely no token ;-)
651
-			return null;
652
-		}
653
-		try {
654
-			$token = $this->tokenProvider->getToken($password);
655
-			try {
656
-				return $this->tokenProvider->getPassword($token, $password);
657
-			} catch (PasswordlessTokenException $ex) {
658
-				return null;
659
-			}
660
-		} catch (InvalidTokenException $ex) {
661
-			return $password;
662
-		}
663
-	}
664
-
665
-	/**
666
-	 * @param IToken $dbToken
667
-	 * @param string $token
668
-	 * @return boolean
669
-	 */
670
-	private function checkTokenCredentials(IToken $dbToken, $token) {
671
-		// Check whether login credentials are still valid and the user was not disabled
672
-		// This check is performed each 5 minutes
673
-		$lastCheck = $dbToken->getLastCheck() ? : 0;
674
-		$now = $this->timeFactory->getTime();
675
-		if ($lastCheck > ($now - 60 * 5)) {
676
-			// Checked performed recently, nothing to do now
677
-			return true;
678
-		}
679
-
680
-		try {
681
-			$pwd = $this->tokenProvider->getPassword($dbToken, $token);
682
-		} catch (InvalidTokenException $ex) {
683
-			// An invalid token password was used -> log user out
684
-			return false;
685
-		} catch (PasswordlessTokenException $ex) {
686
-			// Token has no password
687
-
688
-			if (!is_null($this->activeUser) && !$this->activeUser->isEnabled()) {
689
-				$this->tokenProvider->invalidateToken($token);
690
-				return false;
691
-			}
692
-
693
-			$dbToken->setLastCheck($now);
694
-			return true;
695
-		}
696
-
697
-		if ($this->manager->checkPassword($dbToken->getLoginName(), $pwd) === false
698
-			|| (!is_null($this->activeUser) && !$this->activeUser->isEnabled())) {
699
-			$this->tokenProvider->invalidateToken($token);
700
-			// Password has changed or user was disabled -> log user out
701
-			return false;
702
-		}
703
-		$dbToken->setLastCheck($now);
704
-		return true;
705
-	}
706
-
707
-	/**
708
-	 * Check if the given token exists and performs password/user-enabled checks
709
-	 *
710
-	 * Invalidates the token if checks fail
711
-	 *
712
-	 * @param string $token
713
-	 * @param string $user login name
714
-	 * @return boolean
715
-	 */
716
-	private function validateToken($token, $user = null) {
717
-		try {
718
-			$dbToken = $this->tokenProvider->getToken($token);
719
-		} catch (InvalidTokenException $ex) {
720
-			return false;
721
-		}
722
-
723
-		// Check if login names match
724
-		if (!is_null($user) && $dbToken->getLoginName() !== $user) {
725
-			// TODO: this makes it imposssible to use different login names on browser and client
726
-			// e.g. login by e-mail '[email protected]' on browser for generating the token will not
727
-			//      allow to use the client token with the login name 'user'.
728
-			return false;
729
-		}
730
-
731
-		if (!$this->checkTokenCredentials($dbToken, $token)) {
732
-			return false;
733
-		}
734
-
735
-		$this->tokenProvider->updateTokenActivity($dbToken);
736
-
737
-		return true;
738
-	}
739
-
740
-	/**
741
-	 * Tries to login the user with auth token header
742
-	 *
743
-	 * @param IRequest $request
744
-	 * @todo check remember me cookie
745
-	 * @return boolean
746
-	 */
747
-	public function tryTokenLogin(IRequest $request) {
748
-		$authHeader = $request->getHeader('Authorization');
749
-		if (strpos($authHeader, 'Bearer ') === false) {
750
-			// No auth header, let's try session id
751
-			try {
752
-				$token = $this->session->getId();
753
-			} catch (SessionNotAvailableException $ex) {
754
-				return false;
755
-			}
756
-		} else {
757
-			$token = substr($authHeader, 7);
758
-		}
759
-
760
-		if (!$this->loginWithToken($token)) {
761
-			return false;
762
-		}
763
-		if(!$this->validateToken($token)) {
764
-			return false;
765
-		}
766
-		return true;
767
-	}
768
-
769
-	/**
770
-	 * perform login using the magic cookie (remember login)
771
-	 *
772
-	 * @param string $uid the username
773
-	 * @param string $currentToken
774
-	 * @param string $oldSessionId
775
-	 * @return bool
776
-	 */
777
-	public function loginWithCookie($uid, $currentToken, $oldSessionId) {
778
-		$this->session->regenerateId();
779
-		$this->manager->emit('\OC\User', 'preRememberedLogin', array($uid));
780
-		$user = $this->manager->get($uid);
781
-		if (is_null($user)) {
782
-			// user does not exist
783
-			return false;
784
-		}
785
-
786
-		// get stored tokens
787
-		$tokens = $this->config->getUserKeys($uid, 'login_token');
788
-		// test cookies token against stored tokens
789
-		if (!in_array($currentToken, $tokens, true)) {
790
-			return false;
791
-		}
792
-		// replace successfully used token with a new one
793
-		$this->config->deleteUserValue($uid, 'login_token', $currentToken);
794
-		$newToken = $this->random->generate(32);
795
-		$this->config->setUserValue($uid, 'login_token', $newToken, $this->timeFactory->getTime());
796
-
797
-		try {
798
-			$sessionId = $this->session->getId();
799
-			$this->tokenProvider->renewSessionToken($oldSessionId, $sessionId);
800
-		} catch (SessionNotAvailableException $ex) {
801
-			return false;
802
-		} catch (InvalidTokenException $ex) {
803
-			\OC::$server->getLogger()->warning('Renewing session token failed', ['app' => 'core']);
804
-			return false;
805
-		}
806
-
807
-		$this->setMagicInCookie($user->getUID(), $newToken);
808
-		$token = $this->tokenProvider->getToken($sessionId);
809
-
810
-		//login
811
-		$this->setUser($user);
812
-		$this->setLoginName($token->getLoginName());
813
-		$this->setToken($token->getId());
814
-		$this->lockdownManager->setToken($token);
815
-		$user->updateLastLoginTimestamp();
816
-		$password = null;
817
-		try {
818
-			$password = $this->tokenProvider->getPassword($token, $sessionId);
819
-		} catch (PasswordlessTokenException $ex) {
820
-			// Ignore
821
-		}
822
-		$this->manager->emit('\OC\User', 'postRememberedLogin', [$user, $password]);
823
-		return true;
824
-	}
825
-
826
-	/**
827
-	 * @param IUser $user
828
-	 */
829
-	public function createRememberMeToken(IUser $user) {
830
-		$token = $this->random->generate(32);
831
-		$this->config->setUserValue($user->getUID(), 'login_token', $token, $this->timeFactory->getTime());
832
-		$this->setMagicInCookie($user->getUID(), $token);
833
-	}
834
-
835
-	/**
836
-	 * logout the user from the session
837
-	 */
838
-	public function logout() {
839
-		$this->manager->emit('\OC\User', 'logout');
840
-		$user = $this->getUser();
841
-		if (!is_null($user)) {
842
-			try {
843
-				$this->tokenProvider->invalidateToken($this->session->getId());
844
-			} catch (SessionNotAvailableException $ex) {
845
-
846
-			}
847
-		}
848
-		$this->setUser(null);
849
-		$this->setLoginName(null);
850
-		$this->setToken(null);
851
-		$this->unsetMagicInCookie();
852
-		$this->session->clear();
853
-		$this->manager->emit('\OC\User', 'postLogout');
854
-	}
855
-
856
-	/**
857
-	 * Set cookie value to use in next page load
858
-	 *
859
-	 * @param string $username username to be set
860
-	 * @param string $token
861
-	 */
862
-	public function setMagicInCookie($username, $token) {
863
-		$secureCookie = OC::$server->getRequest()->getServerProtocol() === 'https';
864
-		$webRoot = \OC::$WEBROOT;
865
-		if ($webRoot === '') {
866
-			$webRoot = '/';
867
-		}
868
-
869
-		$expires = $this->timeFactory->getTime() + $this->config->getSystemValue('remember_login_cookie_lifetime', 60 * 60 * 24 * 15);
870
-		setcookie('nc_username', $username, $expires, $webRoot, '', $secureCookie, true);
871
-		setcookie('nc_token', $token, $expires, $webRoot, '', $secureCookie, true);
872
-		try {
873
-			setcookie('nc_session_id', $this->session->getId(), $expires, $webRoot, '', $secureCookie, true);
874
-		} catch (SessionNotAvailableException $ex) {
875
-			// ignore
876
-		}
877
-	}
878
-
879
-	/**
880
-	 * Remove cookie for "remember username"
881
-	 */
882
-	public function unsetMagicInCookie() {
883
-		//TODO: DI for cookies and IRequest
884
-		$secureCookie = OC::$server->getRequest()->getServerProtocol() === 'https';
885
-
886
-		unset($_COOKIE['nc_username']); //TODO: DI
887
-		unset($_COOKIE['nc_token']);
888
-		unset($_COOKIE['nc_session_id']);
889
-		setcookie('nc_username', '', $this->timeFactory->getTime() - 3600, OC::$WEBROOT, '', $secureCookie, true);
890
-		setcookie('nc_token', '', $this->timeFactory->getTime() - 3600, OC::$WEBROOT, '', $secureCookie, true);
891
-		setcookie('nc_session_id', '', $this->timeFactory->getTime() - 3600, OC::$WEBROOT, '', $secureCookie, true);
892
-		// old cookies might be stored under /webroot/ instead of /webroot
893
-		// and Firefox doesn't like it!
894
-		setcookie('nc_username', '', $this->timeFactory->getTime() - 3600, OC::$WEBROOT . '/', '', $secureCookie, true);
895
-		setcookie('nc_token', '', $this->timeFactory->getTime() - 3600, OC::$WEBROOT . '/', '', $secureCookie, true);
896
-		setcookie('nc_session_id', '', $this->timeFactory->getTime() - 3600, OC::$WEBROOT . '/', '', $secureCookie, true);
897
-	}
898
-
899
-	/**
900
-	 * Update password of the browser session token if there is one
901
-	 *
902
-	 * @param string $password
903
-	 */
904
-	public function updateSessionTokenPassword($password) {
905
-		try {
906
-			$sessionId = $this->session->getId();
907
-			$token = $this->tokenProvider->getToken($sessionId);
908
-			$this->tokenProvider->setPassword($token, $sessionId, $password);
909
-		} catch (SessionNotAvailableException $ex) {
910
-			// Nothing to do
911
-		} catch (InvalidTokenException $ex) {
912
-			// Nothing to do
913
-		}
914
-	}
89
+    /** @var Manager|PublicEmitter $manager */
90
+    private $manager;
91
+
92
+    /** @var ISession $session */
93
+    private $session;
94
+
95
+    /** @var ITimeFactory */
96
+    private $timeFactory;
97
+
98
+    /** @var IProvider */
99
+    private $tokenProvider;
100
+
101
+    /** @var IConfig */
102
+    private $config;
103
+
104
+    /** @var User $activeUser */
105
+    protected $activeUser;
106
+
107
+    /** @var ISecureRandom */
108
+    private $random;
109
+
110
+    /** @var ILockdownManager  */
111
+    private $lockdownManager;
112
+
113
+    /** @var ILogger */
114
+    private $logger;
115
+
116
+    /**
117
+     * @param Manager $manager
118
+     * @param ISession $session
119
+     * @param ITimeFactory $timeFactory
120
+     * @param IProvider $tokenProvider
121
+     * @param IConfig $config
122
+     * @param ISecureRandom $random
123
+     * @param ILockdownManager $lockdownManager
124
+     * @param ILogger $logger
125
+     */
126
+    public function __construct(Manager $manager,
127
+                                ISession $session,
128
+                                ITimeFactory $timeFactory,
129
+                                $tokenProvider,
130
+                                IConfig $config,
131
+                                ISecureRandom $random,
132
+                                ILockdownManager $lockdownManager,
133
+                                ILogger $logger) {
134
+        $this->manager = $manager;
135
+        $this->session = $session;
136
+        $this->timeFactory = $timeFactory;
137
+        $this->tokenProvider = $tokenProvider;
138
+        $this->config = $config;
139
+        $this->random = $random;
140
+        $this->lockdownManager = $lockdownManager;
141
+        $this->logger = $logger;
142
+    }
143
+
144
+    /**
145
+     * @param IProvider $provider
146
+     */
147
+    public function setTokenProvider(IProvider $provider) {
148
+        $this->tokenProvider = $provider;
149
+    }
150
+
151
+    /**
152
+     * @param string $scope
153
+     * @param string $method
154
+     * @param callable $callback
155
+     */
156
+    public function listen($scope, $method, callable $callback) {
157
+        $this->manager->listen($scope, $method, $callback);
158
+    }
159
+
160
+    /**
161
+     * @param string $scope optional
162
+     * @param string $method optional
163
+     * @param callable $callback optional
164
+     */
165
+    public function removeListener($scope = null, $method = null, callable $callback = null) {
166
+        $this->manager->removeListener($scope, $method, $callback);
167
+    }
168
+
169
+    /**
170
+     * get the manager object
171
+     *
172
+     * @return Manager|PublicEmitter
173
+     */
174
+    public function getManager() {
175
+        return $this->manager;
176
+    }
177
+
178
+    /**
179
+     * get the session object
180
+     *
181
+     * @return ISession
182
+     */
183
+    public function getSession() {
184
+        return $this->session;
185
+    }
186
+
187
+    /**
188
+     * set the session object
189
+     *
190
+     * @param ISession $session
191
+     */
192
+    public function setSession(ISession $session) {
193
+        if ($this->session instanceof ISession) {
194
+            $this->session->close();
195
+        }
196
+        $this->session = $session;
197
+        $this->activeUser = null;
198
+    }
199
+
200
+    /**
201
+     * set the currently active user
202
+     *
203
+     * @param IUser|null $user
204
+     */
205
+    public function setUser($user) {
206
+        if (is_null($user)) {
207
+            $this->session->remove('user_id');
208
+        } else {
209
+            $this->session->set('user_id', $user->getUID());
210
+        }
211
+        $this->activeUser = $user;
212
+    }
213
+
214
+    /**
215
+     * get the current active user
216
+     *
217
+     * @return IUser|null Current user, otherwise null
218
+     */
219
+    public function getUser() {
220
+        // FIXME: This is a quick'n dirty work-around for the incognito mode as
221
+        // described at https://github.com/owncloud/core/pull/12912#issuecomment-67391155
222
+        if (OC_User::isIncognitoMode()) {
223
+            return null;
224
+        }
225
+        if (is_null($this->activeUser)) {
226
+            $uid = $this->session->get('user_id');
227
+            if (is_null($uid)) {
228
+                return null;
229
+            }
230
+            $this->activeUser = $this->manager->get($uid);
231
+            if (is_null($this->activeUser)) {
232
+                return null;
233
+            }
234
+            $this->validateSession();
235
+        }
236
+        return $this->activeUser;
237
+    }
238
+
239
+    /**
240
+     * Validate whether the current session is valid
241
+     *
242
+     * - For token-authenticated clients, the token validity is checked
243
+     * - For browsers, the session token validity is checked
244
+     */
245
+    protected function validateSession() {
246
+        $token = null;
247
+        $appPassword = $this->session->get('app_password');
248
+
249
+        if (is_null($appPassword)) {
250
+            try {
251
+                $token = $this->session->getId();
252
+            } catch (SessionNotAvailableException $ex) {
253
+                return;
254
+            }
255
+        } else {
256
+            $token = $appPassword;
257
+        }
258
+
259
+        if (!$this->validateToken($token)) {
260
+            // Session was invalidated
261
+            $this->logout();
262
+        }
263
+    }
264
+
265
+    /**
266
+     * Checks whether the user is logged in
267
+     *
268
+     * @return bool if logged in
269
+     */
270
+    public function isLoggedIn() {
271
+        $user = $this->getUser();
272
+        if (is_null($user)) {
273
+            return false;
274
+        }
275
+
276
+        return $user->isEnabled();
277
+    }
278
+
279
+    /**
280
+     * set the login name
281
+     *
282
+     * @param string|null $loginName for the logged in user
283
+     */
284
+    public function setLoginName($loginName) {
285
+        if (is_null($loginName)) {
286
+            $this->session->remove('loginname');
287
+        } else {
288
+            $this->session->set('loginname', $loginName);
289
+        }
290
+    }
291
+
292
+    /**
293
+     * get the login name of the current user
294
+     *
295
+     * @return string
296
+     */
297
+    public function getLoginName() {
298
+        if ($this->activeUser) {
299
+            return $this->session->get('loginname');
300
+        }
301
+
302
+        $uid = $this->session->get('user_id');
303
+        if ($uid) {
304
+            $this->activeUser = $this->manager->get($uid);
305
+            return $this->session->get('loginname');
306
+        }
307
+
308
+        return null;
309
+    }
310
+
311
+    /**
312
+     * set the token id
313
+     *
314
+     * @param int|null $token that was used to log in
315
+     */
316
+    protected function setToken($token) {
317
+        if ($token === null) {
318
+            $this->session->remove('token-id');
319
+        } else {
320
+            $this->session->set('token-id', $token);
321
+        }
322
+    }
323
+
324
+    /**
325
+     * try to log in with the provided credentials
326
+     *
327
+     * @param string $uid
328
+     * @param string $password
329
+     * @return boolean|null
330
+     * @throws LoginException
331
+     */
332
+    public function login($uid, $password) {
333
+        $this->session->regenerateId();
334
+        if ($this->validateToken($password, $uid)) {
335
+            return $this->loginWithToken($password);
336
+        }
337
+        return $this->loginWithPassword($uid, $password);
338
+    }
339
+
340
+    /**
341
+     * @param IUser $user
342
+     * @param array $loginDetails
343
+     * @param bool $regenerateSessionId
344
+     * @return true returns true if login successful or an exception otherwise
345
+     * @throws LoginException
346
+     */
347
+    public function completeLogin(IUser $user, array $loginDetails, $regenerateSessionId = true) {
348
+        if (!$user->isEnabled()) {
349
+            // disabled users can not log in
350
+            // injecting l10n does not work - there is a circular dependency between session and \OCP\L10N\IFactory
351
+            $message = \OC::$server->getL10N('lib')->t('User disabled');
352
+            throw new LoginException($message);
353
+        }
354
+
355
+        if($regenerateSessionId) {
356
+            $this->session->regenerateId();
357
+        }
358
+
359
+        $this->setUser($user);
360
+        $this->setLoginName($loginDetails['loginName']);
361
+
362
+        if(isset($loginDetails['token']) && $loginDetails['token'] instanceof IToken) {
363
+            $this->setToken($loginDetails['token']->getId());
364
+            $this->lockdownManager->setToken($loginDetails['token']);
365
+            $firstTimeLogin = false;
366
+        } else {
367
+            $this->setToken(null);
368
+            $firstTimeLogin = $user->updateLastLoginTimestamp();
369
+        }
370
+        $this->manager->emit('\OC\User', 'postLogin', [$user, $loginDetails['password']]);
371
+        if($this->isLoggedIn()) {
372
+            $this->prepareUserLogin($firstTimeLogin, $regenerateSessionId);
373
+            return true;
374
+        }
375
+
376
+        $message = \OC::$server->getL10N('lib')->t('Login canceled by app');
377
+        throw new LoginException($message);
378
+    }
379
+
380
+    /**
381
+     * Tries to log in a client
382
+     *
383
+     * Checks token auth enforced
384
+     * Checks 2FA enabled
385
+     *
386
+     * @param string $user
387
+     * @param string $password
388
+     * @param IRequest $request
389
+     * @param OC\Security\Bruteforce\Throttler $throttler
390
+     * @throws LoginException
391
+     * @throws PasswordLoginForbiddenException
392
+     * @return boolean
393
+     */
394
+    public function logClientIn($user,
395
+                                $password,
396
+                                IRequest $request,
397
+                                OC\Security\Bruteforce\Throttler $throttler) {
398
+        $currentDelay = $throttler->sleepDelay($request->getRemoteAddress(), 'login');
399
+
400
+        if ($this->manager instanceof PublicEmitter) {
401
+            $this->manager->emit('\OC\User', 'preLogin', array($user, $password));
402
+        }
403
+
404
+        $isTokenPassword = $this->isTokenPassword($password);
405
+        if (!$isTokenPassword && $this->isTokenAuthEnforced()) {
406
+            throw new PasswordLoginForbiddenException();
407
+        }
408
+        if (!$isTokenPassword && $this->isTwoFactorEnforced($user)) {
409
+            throw new PasswordLoginForbiddenException();
410
+        }
411
+
412
+        // Try to login with this username and password
413
+        if (!$this->login($user, $password) ) {
414
+
415
+            // Failed, maybe the user used their email address
416
+            $users = $this->manager->getByEmail($user);
417
+            if (!(\count($users) === 1 && $this->login($users[0]->getUID(), $password))) {
418
+
419
+                $this->logger->warning('Login failed: \'' . $user . '\' (Remote IP: \'' . \OC::$server->getRequest()->getRemoteAddress() . '\')', ['app' => 'core']);
420
+
421
+                $throttler->registerAttempt('login', $request->getRemoteAddress(), ['uid' => $user]);
422
+                if ($currentDelay === 0) {
423
+                    $throttler->sleepDelay($request->getRemoteAddress(), 'login');
424
+                }
425
+                return false;
426
+            }
427
+        }
428
+
429
+        if ($isTokenPassword) {
430
+            $this->session->set('app_password', $password);
431
+        } else if($this->supportsCookies($request)) {
432
+            // Password login, but cookies supported -> create (browser) session token
433
+            $this->createSessionToken($request, $this->getUser()->getUID(), $user, $password);
434
+        }
435
+
436
+        return true;
437
+    }
438
+
439
+    protected function supportsCookies(IRequest $request) {
440
+        if (!is_null($request->getCookie('cookie_test'))) {
441
+            return true;
442
+        }
443
+        setcookie('cookie_test', 'test', $this->timeFactory->getTime() + 3600);
444
+        return false;
445
+    }
446
+
447
+    private function isTokenAuthEnforced() {
448
+        return $this->config->getSystemValue('token_auth_enforced', false);
449
+    }
450
+
451
+    protected function isTwoFactorEnforced($username) {
452
+        Util::emitHook(
453
+            '\OCA\Files_Sharing\API\Server2Server',
454
+            'preLoginNameUsedAsUserName',
455
+            array('uid' => &$username)
456
+        );
457
+        $user = $this->manager->get($username);
458
+        if (is_null($user)) {
459
+            $users = $this->manager->getByEmail($username);
460
+            if (empty($users)) {
461
+                return false;
462
+            }
463
+            if (count($users) !== 1) {
464
+                return true;
465
+            }
466
+            $user = $users[0];
467
+        }
468
+        // DI not possible due to cyclic dependencies :'-/
469
+        return OC::$server->getTwoFactorAuthManager()->isTwoFactorAuthenticated($user);
470
+    }
471
+
472
+    /**
473
+     * Check if the given 'password' is actually a device token
474
+     *
475
+     * @param string $password
476
+     * @return boolean
477
+     */
478
+    public function isTokenPassword($password) {
479
+        try {
480
+            $this->tokenProvider->getToken($password);
481
+            return true;
482
+        } catch (InvalidTokenException $ex) {
483
+            return false;
484
+        }
485
+    }
486
+
487
+    protected function prepareUserLogin($firstTimeLogin, $refreshCsrfToken = true) {
488
+        if ($refreshCsrfToken) {
489
+            // TODO: mock/inject/use non-static
490
+            // Refresh the token
491
+            \OC::$server->getCsrfTokenManager()->refreshToken();
492
+        }
493
+
494
+        //we need to pass the user name, which may differ from login name
495
+        $user = $this->getUser()->getUID();
496
+        OC_Util::setupFS($user);
497
+
498
+        if ($firstTimeLogin) {
499
+            // TODO: lock necessary?
500
+            //trigger creation of user home and /files folder
501
+            $userFolder = \OC::$server->getUserFolder($user);
502
+
503
+            try {
504
+                // copy skeleton
505
+                \OC_Util::copySkeleton($user, $userFolder);
506
+            } catch (NotPermittedException $ex) {
507
+                // read only uses
508
+            }
509
+
510
+            // trigger any other initialization
511
+            \OC::$server->getEventDispatcher()->dispatch(IUser::class . '::firstLogin', new GenericEvent($this->getUser()));
512
+        }
513
+    }
514
+
515
+    /**
516
+     * Tries to login the user with HTTP Basic Authentication
517
+     *
518
+     * @todo do not allow basic auth if the user is 2FA enforced
519
+     * @param IRequest $request
520
+     * @param OC\Security\Bruteforce\Throttler $throttler
521
+     * @return boolean if the login was successful
522
+     */
523
+    public function tryBasicAuthLogin(IRequest $request,
524
+                                        OC\Security\Bruteforce\Throttler $throttler) {
525
+        if (!empty($request->server['PHP_AUTH_USER']) && !empty($request->server['PHP_AUTH_PW'])) {
526
+            try {
527
+                if ($this->logClientIn($request->server['PHP_AUTH_USER'], $request->server['PHP_AUTH_PW'], $request, $throttler)) {
528
+                    /**
529
+                     * Add DAV authenticated. This should in an ideal world not be
530
+                     * necessary but the iOS App reads cookies from anywhere instead
531
+                     * only the DAV endpoint.
532
+                     * This makes sure that the cookies will be valid for the whole scope
533
+                     * @see https://github.com/owncloud/core/issues/22893
534
+                     */
535
+                    $this->session->set(
536
+                        Auth::DAV_AUTHENTICATED, $this->getUser()->getUID()
537
+                    );
538
+
539
+                    // Set the last-password-confirm session to make the sudo mode work
540
+                        $this->session->set('last-password-confirm', $this->timeFactory->getTime());
541
+
542
+                    return true;
543
+                }
544
+            } catch (PasswordLoginForbiddenException $ex) {
545
+                // Nothing to do
546
+            }
547
+        }
548
+        return false;
549
+    }
550
+
551
+    /**
552
+     * Log an user in via login name and password
553
+     *
554
+     * @param string $uid
555
+     * @param string $password
556
+     * @return boolean
557
+     * @throws LoginException if an app canceld the login process or the user is not enabled
558
+     */
559
+    private function loginWithPassword($uid, $password) {
560
+        $user = $this->manager->checkPasswordNoLogging($uid, $password);
561
+        if ($user === false) {
562
+            // Password check failed
563
+            return false;
564
+        }
565
+
566
+        return $this->completeLogin($user, ['loginName' => $uid, 'password' => $password], false);
567
+    }
568
+
569
+    /**
570
+     * Log an user in with a given token (id)
571
+     *
572
+     * @param string $token
573
+     * @return boolean
574
+     * @throws LoginException if an app canceled the login process or the user is not enabled
575
+     */
576
+    private function loginWithToken($token) {
577
+        try {
578
+            $dbToken = $this->tokenProvider->getToken($token);
579
+        } catch (InvalidTokenException $ex) {
580
+            return false;
581
+        }
582
+        $uid = $dbToken->getUID();
583
+
584
+        // When logging in with token, the password must be decrypted first before passing to login hook
585
+        $password = '';
586
+        try {
587
+            $password = $this->tokenProvider->getPassword($dbToken, $token);
588
+        } catch (PasswordlessTokenException $ex) {
589
+            // Ignore and use empty string instead
590
+        }
591
+
592
+        $this->manager->emit('\OC\User', 'preLogin', array($uid, $password));
593
+
594
+        $user = $this->manager->get($uid);
595
+        if (is_null($user)) {
596
+            // user does not exist
597
+            return false;
598
+        }
599
+
600
+        return $this->completeLogin(
601
+            $user,
602
+            [
603
+                'loginName' => $dbToken->getLoginName(),
604
+                'password' => $password,
605
+                'token' => $dbToken
606
+            ],
607
+            false);
608
+    }
609
+
610
+    /**
611
+     * Create a new session token for the given user credentials
612
+     *
613
+     * @param IRequest $request
614
+     * @param string $uid user UID
615
+     * @param string $loginName login name
616
+     * @param string $password
617
+     * @param int $remember
618
+     * @return boolean
619
+     */
620
+    public function createSessionToken(IRequest $request, $uid, $loginName, $password = null, $remember = IToken::DO_NOT_REMEMBER) {
621
+        if (is_null($this->manager->get($uid))) {
622
+            // User does not exist
623
+            return false;
624
+        }
625
+        $name = isset($request->server['HTTP_USER_AGENT']) ? $request->server['HTTP_USER_AGENT'] : 'unknown browser';
626
+        try {
627
+            $sessionId = $this->session->getId();
628
+            $pwd = $this->getPassword($password);
629
+            // Make sure the current sessionId has no leftover tokens
630
+            $this->tokenProvider->invalidateToken($sessionId);
631
+            $this->tokenProvider->generateToken($sessionId, $uid, $loginName, $pwd, $name, IToken::TEMPORARY_TOKEN, $remember);
632
+            return true;
633
+        } catch (SessionNotAvailableException $ex) {
634
+            // This can happen with OCC, where a memory session is used
635
+            // if a memory session is used, we shouldn't create a session token anyway
636
+            return false;
637
+        }
638
+    }
639
+
640
+    /**
641
+     * Checks if the given password is a token.
642
+     * If yes, the password is extracted from the token.
643
+     * If no, the same password is returned.
644
+     *
645
+     * @param string $password either the login password or a device token
646
+     * @return string|null the password or null if none was set in the token
647
+     */
648
+    private function getPassword($password) {
649
+        if (is_null($password)) {
650
+            // This is surely no token ;-)
651
+            return null;
652
+        }
653
+        try {
654
+            $token = $this->tokenProvider->getToken($password);
655
+            try {
656
+                return $this->tokenProvider->getPassword($token, $password);
657
+            } catch (PasswordlessTokenException $ex) {
658
+                return null;
659
+            }
660
+        } catch (InvalidTokenException $ex) {
661
+            return $password;
662
+        }
663
+    }
664
+
665
+    /**
666
+     * @param IToken $dbToken
667
+     * @param string $token
668
+     * @return boolean
669
+     */
670
+    private function checkTokenCredentials(IToken $dbToken, $token) {
671
+        // Check whether login credentials are still valid and the user was not disabled
672
+        // This check is performed each 5 minutes
673
+        $lastCheck = $dbToken->getLastCheck() ? : 0;
674
+        $now = $this->timeFactory->getTime();
675
+        if ($lastCheck > ($now - 60 * 5)) {
676
+            // Checked performed recently, nothing to do now
677
+            return true;
678
+        }
679
+
680
+        try {
681
+            $pwd = $this->tokenProvider->getPassword($dbToken, $token);
682
+        } catch (InvalidTokenException $ex) {
683
+            // An invalid token password was used -> log user out
684
+            return false;
685
+        } catch (PasswordlessTokenException $ex) {
686
+            // Token has no password
687
+
688
+            if (!is_null($this->activeUser) && !$this->activeUser->isEnabled()) {
689
+                $this->tokenProvider->invalidateToken($token);
690
+                return false;
691
+            }
692
+
693
+            $dbToken->setLastCheck($now);
694
+            return true;
695
+        }
696
+
697
+        if ($this->manager->checkPassword($dbToken->getLoginName(), $pwd) === false
698
+            || (!is_null($this->activeUser) && !$this->activeUser->isEnabled())) {
699
+            $this->tokenProvider->invalidateToken($token);
700
+            // Password has changed or user was disabled -> log user out
701
+            return false;
702
+        }
703
+        $dbToken->setLastCheck($now);
704
+        return true;
705
+    }
706
+
707
+    /**
708
+     * Check if the given token exists and performs password/user-enabled checks
709
+     *
710
+     * Invalidates the token if checks fail
711
+     *
712
+     * @param string $token
713
+     * @param string $user login name
714
+     * @return boolean
715
+     */
716
+    private function validateToken($token, $user = null) {
717
+        try {
718
+            $dbToken = $this->tokenProvider->getToken($token);
719
+        } catch (InvalidTokenException $ex) {
720
+            return false;
721
+        }
722
+
723
+        // Check if login names match
724
+        if (!is_null($user) && $dbToken->getLoginName() !== $user) {
725
+            // TODO: this makes it imposssible to use different login names on browser and client
726
+            // e.g. login by e-mail '[email protected]' on browser for generating the token will not
727
+            //      allow to use the client token with the login name 'user'.
728
+            return false;
729
+        }
730
+
731
+        if (!$this->checkTokenCredentials($dbToken, $token)) {
732
+            return false;
733
+        }
734
+
735
+        $this->tokenProvider->updateTokenActivity($dbToken);
736
+
737
+        return true;
738
+    }
739
+
740
+    /**
741
+     * Tries to login the user with auth token header
742
+     *
743
+     * @param IRequest $request
744
+     * @todo check remember me cookie
745
+     * @return boolean
746
+     */
747
+    public function tryTokenLogin(IRequest $request) {
748
+        $authHeader = $request->getHeader('Authorization');
749
+        if (strpos($authHeader, 'Bearer ') === false) {
750
+            // No auth header, let's try session id
751
+            try {
752
+                $token = $this->session->getId();
753
+            } catch (SessionNotAvailableException $ex) {
754
+                return false;
755
+            }
756
+        } else {
757
+            $token = substr($authHeader, 7);
758
+        }
759
+
760
+        if (!$this->loginWithToken($token)) {
761
+            return false;
762
+        }
763
+        if(!$this->validateToken($token)) {
764
+            return false;
765
+        }
766
+        return true;
767
+    }
768
+
769
+    /**
770
+     * perform login using the magic cookie (remember login)
771
+     *
772
+     * @param string $uid the username
773
+     * @param string $currentToken
774
+     * @param string $oldSessionId
775
+     * @return bool
776
+     */
777
+    public function loginWithCookie($uid, $currentToken, $oldSessionId) {
778
+        $this->session->regenerateId();
779
+        $this->manager->emit('\OC\User', 'preRememberedLogin', array($uid));
780
+        $user = $this->manager->get($uid);
781
+        if (is_null($user)) {
782
+            // user does not exist
783
+            return false;
784
+        }
785
+
786
+        // get stored tokens
787
+        $tokens = $this->config->getUserKeys($uid, 'login_token');
788
+        // test cookies token against stored tokens
789
+        if (!in_array($currentToken, $tokens, true)) {
790
+            return false;
791
+        }
792
+        // replace successfully used token with a new one
793
+        $this->config->deleteUserValue($uid, 'login_token', $currentToken);
794
+        $newToken = $this->random->generate(32);
795
+        $this->config->setUserValue($uid, 'login_token', $newToken, $this->timeFactory->getTime());
796
+
797
+        try {
798
+            $sessionId = $this->session->getId();
799
+            $this->tokenProvider->renewSessionToken($oldSessionId, $sessionId);
800
+        } catch (SessionNotAvailableException $ex) {
801
+            return false;
802
+        } catch (InvalidTokenException $ex) {
803
+            \OC::$server->getLogger()->warning('Renewing session token failed', ['app' => 'core']);
804
+            return false;
805
+        }
806
+
807
+        $this->setMagicInCookie($user->getUID(), $newToken);
808
+        $token = $this->tokenProvider->getToken($sessionId);
809
+
810
+        //login
811
+        $this->setUser($user);
812
+        $this->setLoginName($token->getLoginName());
813
+        $this->setToken($token->getId());
814
+        $this->lockdownManager->setToken($token);
815
+        $user->updateLastLoginTimestamp();
816
+        $password = null;
817
+        try {
818
+            $password = $this->tokenProvider->getPassword($token, $sessionId);
819
+        } catch (PasswordlessTokenException $ex) {
820
+            // Ignore
821
+        }
822
+        $this->manager->emit('\OC\User', 'postRememberedLogin', [$user, $password]);
823
+        return true;
824
+    }
825
+
826
+    /**
827
+     * @param IUser $user
828
+     */
829
+    public function createRememberMeToken(IUser $user) {
830
+        $token = $this->random->generate(32);
831
+        $this->config->setUserValue($user->getUID(), 'login_token', $token, $this->timeFactory->getTime());
832
+        $this->setMagicInCookie($user->getUID(), $token);
833
+    }
834
+
835
+    /**
836
+     * logout the user from the session
837
+     */
838
+    public function logout() {
839
+        $this->manager->emit('\OC\User', 'logout');
840
+        $user = $this->getUser();
841
+        if (!is_null($user)) {
842
+            try {
843
+                $this->tokenProvider->invalidateToken($this->session->getId());
844
+            } catch (SessionNotAvailableException $ex) {
845
+
846
+            }
847
+        }
848
+        $this->setUser(null);
849
+        $this->setLoginName(null);
850
+        $this->setToken(null);
851
+        $this->unsetMagicInCookie();
852
+        $this->session->clear();
853
+        $this->manager->emit('\OC\User', 'postLogout');
854
+    }
855
+
856
+    /**
857
+     * Set cookie value to use in next page load
858
+     *
859
+     * @param string $username username to be set
860
+     * @param string $token
861
+     */
862
+    public function setMagicInCookie($username, $token) {
863
+        $secureCookie = OC::$server->getRequest()->getServerProtocol() === 'https';
864
+        $webRoot = \OC::$WEBROOT;
865
+        if ($webRoot === '') {
866
+            $webRoot = '/';
867
+        }
868
+
869
+        $expires = $this->timeFactory->getTime() + $this->config->getSystemValue('remember_login_cookie_lifetime', 60 * 60 * 24 * 15);
870
+        setcookie('nc_username', $username, $expires, $webRoot, '', $secureCookie, true);
871
+        setcookie('nc_token', $token, $expires, $webRoot, '', $secureCookie, true);
872
+        try {
873
+            setcookie('nc_session_id', $this->session->getId(), $expires, $webRoot, '', $secureCookie, true);
874
+        } catch (SessionNotAvailableException $ex) {
875
+            // ignore
876
+        }
877
+    }
878
+
879
+    /**
880
+     * Remove cookie for "remember username"
881
+     */
882
+    public function unsetMagicInCookie() {
883
+        //TODO: DI for cookies and IRequest
884
+        $secureCookie = OC::$server->getRequest()->getServerProtocol() === 'https';
885
+
886
+        unset($_COOKIE['nc_username']); //TODO: DI
887
+        unset($_COOKIE['nc_token']);
888
+        unset($_COOKIE['nc_session_id']);
889
+        setcookie('nc_username', '', $this->timeFactory->getTime() - 3600, OC::$WEBROOT, '', $secureCookie, true);
890
+        setcookie('nc_token', '', $this->timeFactory->getTime() - 3600, OC::$WEBROOT, '', $secureCookie, true);
891
+        setcookie('nc_session_id', '', $this->timeFactory->getTime() - 3600, OC::$WEBROOT, '', $secureCookie, true);
892
+        // old cookies might be stored under /webroot/ instead of /webroot
893
+        // and Firefox doesn't like it!
894
+        setcookie('nc_username', '', $this->timeFactory->getTime() - 3600, OC::$WEBROOT . '/', '', $secureCookie, true);
895
+        setcookie('nc_token', '', $this->timeFactory->getTime() - 3600, OC::$WEBROOT . '/', '', $secureCookie, true);
896
+        setcookie('nc_session_id', '', $this->timeFactory->getTime() - 3600, OC::$WEBROOT . '/', '', $secureCookie, true);
897
+    }
898
+
899
+    /**
900
+     * Update password of the browser session token if there is one
901
+     *
902
+     * @param string $password
903
+     */
904
+    public function updateSessionTokenPassword($password) {
905
+        try {
906
+            $sessionId = $this->session->getId();
907
+            $token = $this->tokenProvider->getToken($sessionId);
908
+            $this->tokenProvider->setPassword($token, $sessionId, $password);
909
+        } catch (SessionNotAvailableException $ex) {
910
+            // Nothing to do
911
+        } catch (InvalidTokenException $ex) {
912
+            // Nothing to do
913
+        }
914
+    }
915 915
 
916 916
 
917 917
 }
Please login to merge, or discard this patch.
lib/private/Session/Memory.php 1 patch
Indentation   +72 added lines, -72 removed lines patch added patch discarded remove patch
@@ -39,86 +39,86 @@
 block discarded – undo
39 39
  * @package OC\Session
40 40
  */
41 41
 class Memory extends Session {
42
-	protected $data;
42
+    protected $data;
43 43
 
44
-	public function __construct(string $name) {
45
-		//no need to use $name since all data is already scoped to this instance
46
-		$this->data = [];
47
-	}
44
+    public function __construct(string $name) {
45
+        //no need to use $name since all data is already scoped to this instance
46
+        $this->data = [];
47
+    }
48 48
 
49
-	/**
50
-	 * @param string $key
51
-	 * @param integer $value
52
-	 */
53
-	public function set(string $key, $value) {
54
-		$this->validateSession();
55
-		$this->data[$key] = $value;
56
-	}
49
+    /**
50
+     * @param string $key
51
+     * @param integer $value
52
+     */
53
+    public function set(string $key, $value) {
54
+        $this->validateSession();
55
+        $this->data[$key] = $value;
56
+    }
57 57
 
58
-	/**
59
-	 * @param string $key
60
-	 * @return mixed
61
-	 */
62
-	public function get(string $key) {
63
-		if (!$this->exists($key)) {
64
-			return null;
65
-		}
66
-		return $this->data[$key];
67
-	}
58
+    /**
59
+     * @param string $key
60
+     * @return mixed
61
+     */
62
+    public function get(string $key) {
63
+        if (!$this->exists($key)) {
64
+            return null;
65
+        }
66
+        return $this->data[$key];
67
+    }
68 68
 
69
-	/**
70
-	 * @param string $key
71
-	 * @return bool
72
-	 */
73
-	public function exists(string $key): bool {
74
-		return isset($this->data[$key]);
75
-	}
69
+    /**
70
+     * @param string $key
71
+     * @return bool
72
+     */
73
+    public function exists(string $key): bool {
74
+        return isset($this->data[$key]);
75
+    }
76 76
 
77
-	/**
78
-	 * @param string $key
79
-	 */
80
-	public function remove(string $key) {
81
-		$this->validateSession();
82
-		unset($this->data[$key]);
83
-	}
77
+    /**
78
+     * @param string $key
79
+     */
80
+    public function remove(string $key) {
81
+        $this->validateSession();
82
+        unset($this->data[$key]);
83
+    }
84 84
 
85
-	public function clear() {
86
-		$this->data = [];
87
-	}
85
+    public function clear() {
86
+        $this->data = [];
87
+    }
88 88
 
89
-	/**
90
-	 * Stub since the session ID does not need to get regenerated for the cache
91
-	 *
92
-	 * @param bool $deleteOldSession
93
-	 */
94
-	public function regenerateId(bool $deleteOldSession = true, bool $updateToken = false) {}
89
+    /**
90
+     * Stub since the session ID does not need to get regenerated for the cache
91
+     *
92
+     * @param bool $deleteOldSession
93
+     */
94
+    public function regenerateId(bool $deleteOldSession = true, bool $updateToken = false) {}
95 95
 
96
-	/**
97
-	 * Wrapper around session_id
98
-	 *
99
-	 * @return string
100
-	 * @throws SessionNotAvailableException
101
-	 * @since 9.1.0
102
-	 */
103
-	public function getId(): string {
104
-		throw new SessionNotAvailableException('Memory session does not have an ID');
105
-	}
96
+    /**
97
+     * Wrapper around session_id
98
+     *
99
+     * @return string
100
+     * @throws SessionNotAvailableException
101
+     * @since 9.1.0
102
+     */
103
+    public function getId(): string {
104
+        throw new SessionNotAvailableException('Memory session does not have an ID');
105
+    }
106 106
 
107
-	/**
108
-	 * Helper function for PHPUnit execution - don't use in non-test code
109
-	 */
110
-	public function reopen() {
111
-		$this->sessionClosed = false;
112
-	}
107
+    /**
108
+     * Helper function for PHPUnit execution - don't use in non-test code
109
+     */
110
+    public function reopen() {
111
+        $this->sessionClosed = false;
112
+    }
113 113
 
114
-	/**
115
-	 * In case the session has already been locked an exception will be thrown
116
-	 *
117
-	 * @throws Exception
118
-	 */
119
-	private function validateSession() {
120
-		if ($this->sessionClosed) {
121
-			throw new Exception('Session has been closed - no further changes to the session are allowed');
122
-		}
123
-	}
114
+    /**
115
+     * In case the session has already been locked an exception will be thrown
116
+     *
117
+     * @throws Exception
118
+     */
119
+    private function validateSession() {
120
+        if ($this->sessionClosed) {
121
+            throw new Exception('Session has been closed - no further changes to the session are allowed');
122
+        }
123
+    }
124 124
 }
Please login to merge, or discard this patch.
lib/private/Session/CryptoSessionData.php 1 patch
Indentation   +173 added lines, -173 removed lines patch added patch discarded remove patch
@@ -37,177 +37,177 @@
 block discarded – undo
37 37
  * @package OC\Session
38 38
  */
39 39
 class CryptoSessionData implements \ArrayAccess, ISession {
40
-	/** @var ISession */
41
-	protected $session;
42
-	/** @var \OCP\Security\ICrypto */
43
-	protected $crypto;
44
-	/** @var string */
45
-	protected $passphrase;
46
-	/** @var array */
47
-	protected $sessionValues;
48
-	/** @var bool */
49
-	protected $isModified = false;
50
-	CONST encryptedSessionName = 'encrypted_session_data';
51
-
52
-	/**
53
-	 * @param ISession $session
54
-	 * @param ICrypto $crypto
55
-	 * @param string $passphrase
56
-	 */
57
-	public function __construct(ISession $session,
58
-								ICrypto $crypto,
59
-								string $passphrase) {
60
-		$this->crypto = $crypto;
61
-		$this->session = $session;
62
-		$this->passphrase = $passphrase;
63
-		$this->initializeSession();
64
-	}
65
-
66
-	/**
67
-	 * Close session if class gets destructed
68
-	 */
69
-	public function __destruct() {
70
-		try {
71
-			$this->close();
72
-		} catch (SessionNotAvailableException $e){
73
-			// This exception can occur if session is already closed
74
-			// So it is safe to ignore it and let the garbage collector to proceed
75
-		}
76
-	}
77
-
78
-	protected function initializeSession() {
79
-		$encryptedSessionData = $this->session->get(self::encryptedSessionName) ?: '';
80
-		try {
81
-			$this->sessionValues = json_decode(
82
-				$this->crypto->decrypt($encryptedSessionData, $this->passphrase),
83
-				true
84
-			);
85
-		} catch (\Exception $e) {
86
-			$this->sessionValues = [];
87
-		}
88
-	}
89
-
90
-	/**
91
-	 * Set a value in the session
92
-	 *
93
-	 * @param string $key
94
-	 * @param mixed $value
95
-	 */
96
-	public function set(string $key, $value) {
97
-		$this->sessionValues[$key] = $value;
98
-		$this->isModified = true;
99
-	}
100
-
101
-	/**
102
-	 * Get a value from the session
103
-	 *
104
-	 * @param string $key
105
-	 * @return string|null Either the value or null
106
-	 */
107
-	public function get(string $key) {
108
-		if(isset($this->sessionValues[$key])) {
109
-			return $this->sessionValues[$key];
110
-		}
111
-
112
-		return null;
113
-	}
114
-
115
-	/**
116
-	 * Check if a named key exists in the session
117
-	 *
118
-	 * @param string $key
119
-	 * @return bool
120
-	 */
121
-	public function exists(string $key): bool {
122
-		return isset($this->sessionValues[$key]);
123
-	}
124
-
125
-	/**
126
-	 * Remove a $key/$value pair from the session
127
-	 *
128
-	 * @param string $key
129
-	 */
130
-	public function remove(string $key) {
131
-		$this->isModified = true;
132
-		unset($this->sessionValues[$key]);
133
-		$this->session->remove(self::encryptedSessionName);
134
-	}
135
-
136
-	/**
137
-	 * Reset and recreate the session
138
-	 */
139
-	public function clear() {
140
-		$requesttoken = $this->get('requesttoken');
141
-		$this->sessionValues = [];
142
-		if ($requesttoken !== null) {
143
-			$this->set('requesttoken', $requesttoken);
144
-		}
145
-		$this->isModified = true;
146
-		$this->session->clear();
147
-	}
148
-
149
-	/**
150
-	 * Wrapper around session_regenerate_id
151
-	 *
152
-	 * @param bool $deleteOldSession Whether to delete the old associated session file or not.
153
-	 * @param bool $updateToken Wheater to update the associated auth token
154
-	 * @return void
155
-	 */
156
-	public function regenerateId(bool $deleteOldSession = true, bool $updateToken = false) {
157
-		$this->session->regenerateId($deleteOldSession, $updateToken);
158
-	}
159
-
160
-	/**
161
-	 * Wrapper around session_id
162
-	 *
163
-	 * @return string
164
-	 * @throws SessionNotAvailableException
165
-	 * @since 9.1.0
166
-	 */
167
-	public function getId(): string {
168
-		return $this->session->getId();
169
-	}
170
-
171
-	/**
172
-	 * Close the session and release the lock, also writes all changed data in batch
173
-	 */
174
-	public function close() {
175
-		if($this->isModified) {
176
-			$encryptedValue = $this->crypto->encrypt(json_encode($this->sessionValues), $this->passphrase);
177
-			$this->session->set(self::encryptedSessionName, $encryptedValue);
178
-			$this->isModified = false;
179
-		}
180
-		$this->session->close();
181
-	}
182
-
183
-	/**
184
-	 * @param mixed $offset
185
-	 * @return bool
186
-	 */
187
-	public function offsetExists($offset): bool {
188
-		return $this->exists($offset);
189
-	}
190
-
191
-	/**
192
-	 * @param mixed $offset
193
-	 * @return mixed
194
-	 */
195
-	public function offsetGet($offset) {
196
-		return $this->get($offset);
197
-	}
198
-
199
-	/**
200
-	 * @param mixed $offset
201
-	 * @param mixed $value
202
-	 */
203
-	public function offsetSet($offset, $value) {
204
-		$this->set($offset, $value);
205
-	}
206
-
207
-	/**
208
-	 * @param mixed $offset
209
-	 */
210
-	public function offsetUnset($offset) {
211
-		$this->remove($offset);
212
-	}
40
+    /** @var ISession */
41
+    protected $session;
42
+    /** @var \OCP\Security\ICrypto */
43
+    protected $crypto;
44
+    /** @var string */
45
+    protected $passphrase;
46
+    /** @var array */
47
+    protected $sessionValues;
48
+    /** @var bool */
49
+    protected $isModified = false;
50
+    CONST encryptedSessionName = 'encrypted_session_data';
51
+
52
+    /**
53
+     * @param ISession $session
54
+     * @param ICrypto $crypto
55
+     * @param string $passphrase
56
+     */
57
+    public function __construct(ISession $session,
58
+                                ICrypto $crypto,
59
+                                string $passphrase) {
60
+        $this->crypto = $crypto;
61
+        $this->session = $session;
62
+        $this->passphrase = $passphrase;
63
+        $this->initializeSession();
64
+    }
65
+
66
+    /**
67
+     * Close session if class gets destructed
68
+     */
69
+    public function __destruct() {
70
+        try {
71
+            $this->close();
72
+        } catch (SessionNotAvailableException $e){
73
+            // This exception can occur if session is already closed
74
+            // So it is safe to ignore it and let the garbage collector to proceed
75
+        }
76
+    }
77
+
78
+    protected function initializeSession() {
79
+        $encryptedSessionData = $this->session->get(self::encryptedSessionName) ?: '';
80
+        try {
81
+            $this->sessionValues = json_decode(
82
+                $this->crypto->decrypt($encryptedSessionData, $this->passphrase),
83
+                true
84
+            );
85
+        } catch (\Exception $e) {
86
+            $this->sessionValues = [];
87
+        }
88
+    }
89
+
90
+    /**
91
+     * Set a value in the session
92
+     *
93
+     * @param string $key
94
+     * @param mixed $value
95
+     */
96
+    public function set(string $key, $value) {
97
+        $this->sessionValues[$key] = $value;
98
+        $this->isModified = true;
99
+    }
100
+
101
+    /**
102
+     * Get a value from the session
103
+     *
104
+     * @param string $key
105
+     * @return string|null Either the value or null
106
+     */
107
+    public function get(string $key) {
108
+        if(isset($this->sessionValues[$key])) {
109
+            return $this->sessionValues[$key];
110
+        }
111
+
112
+        return null;
113
+    }
114
+
115
+    /**
116
+     * Check if a named key exists in the session
117
+     *
118
+     * @param string $key
119
+     * @return bool
120
+     */
121
+    public function exists(string $key): bool {
122
+        return isset($this->sessionValues[$key]);
123
+    }
124
+
125
+    /**
126
+     * Remove a $key/$value pair from the session
127
+     *
128
+     * @param string $key
129
+     */
130
+    public function remove(string $key) {
131
+        $this->isModified = true;
132
+        unset($this->sessionValues[$key]);
133
+        $this->session->remove(self::encryptedSessionName);
134
+    }
135
+
136
+    /**
137
+     * Reset and recreate the session
138
+     */
139
+    public function clear() {
140
+        $requesttoken = $this->get('requesttoken');
141
+        $this->sessionValues = [];
142
+        if ($requesttoken !== null) {
143
+            $this->set('requesttoken', $requesttoken);
144
+        }
145
+        $this->isModified = true;
146
+        $this->session->clear();
147
+    }
148
+
149
+    /**
150
+     * Wrapper around session_regenerate_id
151
+     *
152
+     * @param bool $deleteOldSession Whether to delete the old associated session file or not.
153
+     * @param bool $updateToken Wheater to update the associated auth token
154
+     * @return void
155
+     */
156
+    public function regenerateId(bool $deleteOldSession = true, bool $updateToken = false) {
157
+        $this->session->regenerateId($deleteOldSession, $updateToken);
158
+    }
159
+
160
+    /**
161
+     * Wrapper around session_id
162
+     *
163
+     * @return string
164
+     * @throws SessionNotAvailableException
165
+     * @since 9.1.0
166
+     */
167
+    public function getId(): string {
168
+        return $this->session->getId();
169
+    }
170
+
171
+    /**
172
+     * Close the session and release the lock, also writes all changed data in batch
173
+     */
174
+    public function close() {
175
+        if($this->isModified) {
176
+            $encryptedValue = $this->crypto->encrypt(json_encode($this->sessionValues), $this->passphrase);
177
+            $this->session->set(self::encryptedSessionName, $encryptedValue);
178
+            $this->isModified = false;
179
+        }
180
+        $this->session->close();
181
+    }
182
+
183
+    /**
184
+     * @param mixed $offset
185
+     * @return bool
186
+     */
187
+    public function offsetExists($offset): bool {
188
+        return $this->exists($offset);
189
+    }
190
+
191
+    /**
192
+     * @param mixed $offset
193
+     * @return mixed
194
+     */
195
+    public function offsetGet($offset) {
196
+        return $this->get($offset);
197
+    }
198
+
199
+    /**
200
+     * @param mixed $offset
201
+     * @param mixed $value
202
+     */
203
+    public function offsetSet($offset, $value) {
204
+        $this->set($offset, $value);
205
+    }
206
+
207
+    /**
208
+     * @param mixed $offset
209
+     */
210
+    public function offsetUnset($offset) {
211
+        $this->remove($offset);
212
+    }
213 213
 }
Please login to merge, or discard this patch.
apps/files_sharing/lib/Controller/ShareController.php 2 patches
Indentation   +596 added lines, -596 removed lines patch added patch discarded remove patch
@@ -70,604 +70,604 @@
 block discarded – undo
70 70
  */
71 71
 class ShareController extends Controller {
72 72
 
73
-	/** @var IConfig */
74
-	protected $config;
75
-	/** @var IURLGenerator */
76
-	protected $urlGenerator;
77
-	/** @var IUserManager */
78
-	protected $userManager;
79
-	/** @var ILogger */
80
-	protected $logger;
81
-	/** @var \OCP\Activity\IManager */
82
-	protected $activityManager;
83
-	/** @var \OCP\Share\IManager */
84
-	protected $shareManager;
85
-	/** @var ISession */
86
-	protected $session;
87
-	/** @var IPreview */
88
-	protected $previewManager;
89
-	/** @var IRootFolder */
90
-	protected $rootFolder;
91
-	/** @var FederatedShareProvider */
92
-	protected $federatedShareProvider;
93
-	/** @var EventDispatcherInterface */
94
-	protected $eventDispatcher;
95
-	/** @var IL10N */
96
-	protected $l10n;
97
-	/** @var Defaults */
98
-	protected $defaults;
99
-
100
-	/**
101
-	 * @param string $appName
102
-	 * @param IRequest $request
103
-	 * @param IConfig $config
104
-	 * @param IURLGenerator $urlGenerator
105
-	 * @param IUserManager $userManager
106
-	 * @param ILogger $logger
107
-	 * @param \OCP\Activity\IManager $activityManager
108
-	 * @param \OCP\Share\IManager $shareManager
109
-	 * @param ISession $session
110
-	 * @param IPreview $previewManager
111
-	 * @param IRootFolder $rootFolder
112
-	 * @param FederatedShareProvider $federatedShareProvider
113
-	 * @param EventDispatcherInterface $eventDispatcher
114
-	 * @param IL10N $l10n
115
-	 * @param Defaults $defaults
116
-	 */
117
-	public function __construct($appName,
118
-								IRequest $request,
119
-								IConfig $config,
120
-								IURLGenerator $urlGenerator,
121
-								IUserManager $userManager,
122
-								ILogger $logger,
123
-								\OCP\Activity\IManager $activityManager,
124
-								\OCP\Share\IManager $shareManager,
125
-								ISession $session,
126
-								IPreview $previewManager,
127
-								IRootFolder $rootFolder,
128
-								FederatedShareProvider $federatedShareProvider,
129
-								EventDispatcherInterface $eventDispatcher,
130
-								IL10N $l10n,
131
-								Defaults $defaults) {
132
-		parent::__construct($appName, $request);
133
-
134
-		$this->config = $config;
135
-		$this->urlGenerator = $urlGenerator;
136
-		$this->userManager = $userManager;
137
-		$this->logger = $logger;
138
-		$this->activityManager = $activityManager;
139
-		$this->shareManager = $shareManager;
140
-		$this->session = $session;
141
-		$this->previewManager = $previewManager;
142
-		$this->rootFolder = $rootFolder;
143
-		$this->federatedShareProvider = $federatedShareProvider;
144
-		$this->eventDispatcher = $eventDispatcher;
145
-		$this->l10n = $l10n;
146
-		$this->defaults = $defaults;
147
-	}
148
-
149
-	/**
150
-	 * @PublicPage
151
-	 * @NoCSRFRequired
152
-	 *
153
-	 * @param string $token
154
-	 * @return TemplateResponse|RedirectResponse
155
-	 */
156
-	public function showAuthenticate($token) {
157
-		$share = $this->shareManager->getShareByToken($token);
158
-
159
-		if($this->linkShareAuth($share)) {
160
-			return new RedirectResponse($this->urlGenerator->linkToRoute('files_sharing.sharecontroller.showShare', array('token' => $token)));
161
-		}
162
-
163
-		return new TemplateResponse($this->appName, 'authenticate', array(), 'guest');
164
-	}
165
-
166
-	/**
167
-	 * @PublicPage
168
-	 * @UseSession
169
-	 * @BruteForceProtection(action=publicLinkAuth)
170
-	 *
171
-	 * Authenticates against password-protected shares
172
-	 * @param string $token
173
-	 * @param string $redirect
174
-	 * @param string $password
175
-	 * @return RedirectResponse|TemplateResponse|NotFoundResponse
176
-	 */
177
-	public function authenticate($token, $redirect, $password = '') {
178
-
179
-		// Check whether share exists
180
-		try {
181
-			$share = $this->shareManager->getShareByToken($token);
182
-		} catch (ShareNotFound $e) {
183
-			return new NotFoundResponse();
184
-		}
185
-
186
-		$authenticate = $this->linkShareAuth($share, $password);
187
-
188
-		// if download was requested before auth, redirect to download
189
-		if ($authenticate === true && $redirect === 'download') {
190
-			return new RedirectResponse($this->urlGenerator->linkToRoute(
191
-				'files_sharing.sharecontroller.downloadShare',
192
-				array('token' => $token))
193
-			);
194
-		} else if ($authenticate === true) {
195
-			return new RedirectResponse($this->urlGenerator->linkToRoute(
196
-				'files_sharing.sharecontroller.showShare',
197
-				array('token' => $token))
198
-			);
199
-		}
200
-
201
-		$response = new TemplateResponse($this->appName, 'authenticate', array('wrongpw' => true), 'guest');
202
-		$response->throttle();
203
-		return $response;
204
-	}
205
-
206
-	/**
207
-	 * Authenticate a link item with the given password.
208
-	 * Or use the session if no password is provided.
209
-	 *
210
-	 * This is a modified version of Helper::authenticate
211
-	 * TODO: Try to merge back eventually with Helper::authenticate
212
-	 *
213
-	 * @param \OCP\Share\IShare $share
214
-	 * @param string|null $password
215
-	 * @return bool
216
-	 */
217
-	private function linkShareAuth(\OCP\Share\IShare $share, $password = null) {
218
-		if ($password !== null) {
219
-			if ($this->shareManager->checkPassword($share, $password)) {
220
-				$this->session->regenerateId(true, true);
221
-				$this->session->set('public_link_authenticated', (string)$share->getId());
222
-			} else {
223
-				$this->emitAccessShareHook($share, 403, 'Wrong password');
224
-				return false;
225
-			}
226
-		} else {
227
-			// not authenticated ?
228
-			if ( ! $this->session->exists('public_link_authenticated')
229
-				|| $this->session->get('public_link_authenticated') !== (string)$share->getId()) {
230
-				return false;
231
-			}
232
-		}
233
-		return true;
234
-	}
235
-
236
-	/**
237
-	 * throws hooks when a share is attempted to be accessed
238
-	 *
239
-	 * @param \OCP\Share\IShare|string $share the Share instance if available,
240
-	 * otherwise token
241
-	 * @param int $errorCode
242
-	 * @param string $errorMessage
243
-	 * @throws \OC\HintException
244
-	 * @throws \OC\ServerNotAvailableException
245
-	 */
246
-	protected function emitAccessShareHook($share, $errorCode = 200, $errorMessage = '') {
247
-		$itemType = $itemSource = $uidOwner = '';
248
-		$token = $share;
249
-		$exception = null;
250
-		if($share instanceof \OCP\Share\IShare) {
251
-			try {
252
-				$token = $share->getToken();
253
-				$uidOwner = $share->getSharedBy();
254
-				$itemType = $share->getNodeType();
255
-				$itemSource = $share->getNodeId();
256
-			} catch (\Exception $e) {
257
-				// we log what we know and pass on the exception afterwards
258
-				$exception = $e;
259
-			}
260
-		}
261
-		\OC_Hook::emit(Share::class, 'share_link_access', [
262
-			'itemType' => $itemType,
263
-			'itemSource' => $itemSource,
264
-			'uidOwner' => $uidOwner,
265
-			'token' => $token,
266
-			'errorCode' => $errorCode,
267
-			'errorMessage' => $errorMessage,
268
-		]);
269
-		if(!is_null($exception)) {
270
-			throw $exception;
271
-		}
272
-	}
273
-
274
-	/**
275
-	 * Validate the permissions of the share
276
-	 *
277
-	 * @param Share\IShare $share
278
-	 * @return bool
279
-	 */
280
-	private function validateShare(\OCP\Share\IShare $share) {
281
-		return $share->getNode()->isReadable() && $share->getNode()->isShareable();
282
-	}
283
-
284
-	/**
285
-	 * @PublicPage
286
-	 * @NoCSRFRequired
287
-	 *
288
-	 * @param string $token
289
-	 * @param string $path
290
-	 * @return TemplateResponse|RedirectResponse|NotFoundResponse
291
-	 * @throws NotFoundException
292
-	 * @throws \Exception
293
-	 */
294
-	public function showShare($token, $path = '') {
295
-		\OC_User::setIncognitoMode(true);
296
-
297
-		// Check whether share exists
298
-		try {
299
-			$share = $this->shareManager->getShareByToken($token);
300
-		} catch (ShareNotFound $e) {
301
-			$this->emitAccessShareHook($token, 404, 'Share not found');
302
-			return new NotFoundResponse();
303
-		}
304
-
305
-		// Share is password protected - check whether the user is permitted to access the share
306
-		if ($share->getPassword() !== null && !$this->linkShareAuth($share)) {
307
-			return new RedirectResponse($this->urlGenerator->linkToRoute('files_sharing.sharecontroller.authenticate',
308
-				array('token' => $token, 'redirect' => 'preview')));
309
-		}
310
-
311
-		if (!$this->validateShare($share)) {
312
-			throw new NotFoundException();
313
-		}
314
-		// We can't get the path of a file share
315
-		try {
316
-			if ($share->getNode() instanceof \OCP\Files\File && $path !== '') {
317
-				$this->emitAccessShareHook($share, 404, 'Share not found');
318
-				throw new NotFoundException();
319
-			}
320
-		} catch (\Exception $e) {
321
-			$this->emitAccessShareHook($share, 404, 'Share not found');
322
-			throw $e;
323
-		}
324
-
325
-		$shareTmpl = [];
326
-		$shareTmpl['displayName'] = $this->userManager->get($share->getShareOwner())->getDisplayName();
327
-		$shareTmpl['owner'] = $share->getShareOwner();
328
-		$shareTmpl['filename'] = $share->getNode()->getName();
329
-		$shareTmpl['directory_path'] = $share->getTarget();
330
-		$shareTmpl['mimetype'] = $share->getNode()->getMimetype();
331
-		$shareTmpl['previewSupported'] = $this->previewManager->isMimeSupported($share->getNode()->getMimetype());
332
-		$shareTmpl['dirToken'] = $token;
333
-		$shareTmpl['sharingToken'] = $token;
334
-		$shareTmpl['server2serversharing'] = $this->federatedShareProvider->isOutgoingServer2serverShareEnabled();
335
-		$shareTmpl['protected'] = $share->getPassword() !== null ? 'true' : 'false';
336
-		$shareTmpl['dir'] = '';
337
-		$shareTmpl['nonHumanFileSize'] = $share->getNode()->getSize();
338
-		$shareTmpl['fileSize'] = \OCP\Util::humanFileSize($share->getNode()->getSize());
339
-
340
-		// Show file list
341
-		$hideFileList = false;
342
-		if ($share->getNode() instanceof \OCP\Files\Folder) {
343
-			/** @var \OCP\Files\Folder $rootFolder */
344
-			$rootFolder = $share->getNode();
345
-
346
-			try {
347
-				$folderNode = $rootFolder->get($path);
348
-			} catch (\OCP\Files\NotFoundException $e) {
349
-				$this->emitAccessShareHook($share, 404, 'Share not found');
350
-				throw new NotFoundException();
351
-			}
352
-
353
-			$shareTmpl['dir'] = $rootFolder->getRelativePath($folderNode->getPath());
354
-
355
-			/*
73
+    /** @var IConfig */
74
+    protected $config;
75
+    /** @var IURLGenerator */
76
+    protected $urlGenerator;
77
+    /** @var IUserManager */
78
+    protected $userManager;
79
+    /** @var ILogger */
80
+    protected $logger;
81
+    /** @var \OCP\Activity\IManager */
82
+    protected $activityManager;
83
+    /** @var \OCP\Share\IManager */
84
+    protected $shareManager;
85
+    /** @var ISession */
86
+    protected $session;
87
+    /** @var IPreview */
88
+    protected $previewManager;
89
+    /** @var IRootFolder */
90
+    protected $rootFolder;
91
+    /** @var FederatedShareProvider */
92
+    protected $federatedShareProvider;
93
+    /** @var EventDispatcherInterface */
94
+    protected $eventDispatcher;
95
+    /** @var IL10N */
96
+    protected $l10n;
97
+    /** @var Defaults */
98
+    protected $defaults;
99
+
100
+    /**
101
+     * @param string $appName
102
+     * @param IRequest $request
103
+     * @param IConfig $config
104
+     * @param IURLGenerator $urlGenerator
105
+     * @param IUserManager $userManager
106
+     * @param ILogger $logger
107
+     * @param \OCP\Activity\IManager $activityManager
108
+     * @param \OCP\Share\IManager $shareManager
109
+     * @param ISession $session
110
+     * @param IPreview $previewManager
111
+     * @param IRootFolder $rootFolder
112
+     * @param FederatedShareProvider $federatedShareProvider
113
+     * @param EventDispatcherInterface $eventDispatcher
114
+     * @param IL10N $l10n
115
+     * @param Defaults $defaults
116
+     */
117
+    public function __construct($appName,
118
+                                IRequest $request,
119
+                                IConfig $config,
120
+                                IURLGenerator $urlGenerator,
121
+                                IUserManager $userManager,
122
+                                ILogger $logger,
123
+                                \OCP\Activity\IManager $activityManager,
124
+                                \OCP\Share\IManager $shareManager,
125
+                                ISession $session,
126
+                                IPreview $previewManager,
127
+                                IRootFolder $rootFolder,
128
+                                FederatedShareProvider $federatedShareProvider,
129
+                                EventDispatcherInterface $eventDispatcher,
130
+                                IL10N $l10n,
131
+                                Defaults $defaults) {
132
+        parent::__construct($appName, $request);
133
+
134
+        $this->config = $config;
135
+        $this->urlGenerator = $urlGenerator;
136
+        $this->userManager = $userManager;
137
+        $this->logger = $logger;
138
+        $this->activityManager = $activityManager;
139
+        $this->shareManager = $shareManager;
140
+        $this->session = $session;
141
+        $this->previewManager = $previewManager;
142
+        $this->rootFolder = $rootFolder;
143
+        $this->federatedShareProvider = $federatedShareProvider;
144
+        $this->eventDispatcher = $eventDispatcher;
145
+        $this->l10n = $l10n;
146
+        $this->defaults = $defaults;
147
+    }
148
+
149
+    /**
150
+     * @PublicPage
151
+     * @NoCSRFRequired
152
+     *
153
+     * @param string $token
154
+     * @return TemplateResponse|RedirectResponse
155
+     */
156
+    public function showAuthenticate($token) {
157
+        $share = $this->shareManager->getShareByToken($token);
158
+
159
+        if($this->linkShareAuth($share)) {
160
+            return new RedirectResponse($this->urlGenerator->linkToRoute('files_sharing.sharecontroller.showShare', array('token' => $token)));
161
+        }
162
+
163
+        return new TemplateResponse($this->appName, 'authenticate', array(), 'guest');
164
+    }
165
+
166
+    /**
167
+     * @PublicPage
168
+     * @UseSession
169
+     * @BruteForceProtection(action=publicLinkAuth)
170
+     *
171
+     * Authenticates against password-protected shares
172
+     * @param string $token
173
+     * @param string $redirect
174
+     * @param string $password
175
+     * @return RedirectResponse|TemplateResponse|NotFoundResponse
176
+     */
177
+    public function authenticate($token, $redirect, $password = '') {
178
+
179
+        // Check whether share exists
180
+        try {
181
+            $share = $this->shareManager->getShareByToken($token);
182
+        } catch (ShareNotFound $e) {
183
+            return new NotFoundResponse();
184
+        }
185
+
186
+        $authenticate = $this->linkShareAuth($share, $password);
187
+
188
+        // if download was requested before auth, redirect to download
189
+        if ($authenticate === true && $redirect === 'download') {
190
+            return new RedirectResponse($this->urlGenerator->linkToRoute(
191
+                'files_sharing.sharecontroller.downloadShare',
192
+                array('token' => $token))
193
+            );
194
+        } else if ($authenticate === true) {
195
+            return new RedirectResponse($this->urlGenerator->linkToRoute(
196
+                'files_sharing.sharecontroller.showShare',
197
+                array('token' => $token))
198
+            );
199
+        }
200
+
201
+        $response = new TemplateResponse($this->appName, 'authenticate', array('wrongpw' => true), 'guest');
202
+        $response->throttle();
203
+        return $response;
204
+    }
205
+
206
+    /**
207
+     * Authenticate a link item with the given password.
208
+     * Or use the session if no password is provided.
209
+     *
210
+     * This is a modified version of Helper::authenticate
211
+     * TODO: Try to merge back eventually with Helper::authenticate
212
+     *
213
+     * @param \OCP\Share\IShare $share
214
+     * @param string|null $password
215
+     * @return bool
216
+     */
217
+    private function linkShareAuth(\OCP\Share\IShare $share, $password = null) {
218
+        if ($password !== null) {
219
+            if ($this->shareManager->checkPassword($share, $password)) {
220
+                $this->session->regenerateId(true, true);
221
+                $this->session->set('public_link_authenticated', (string)$share->getId());
222
+            } else {
223
+                $this->emitAccessShareHook($share, 403, 'Wrong password');
224
+                return false;
225
+            }
226
+        } else {
227
+            // not authenticated ?
228
+            if ( ! $this->session->exists('public_link_authenticated')
229
+                || $this->session->get('public_link_authenticated') !== (string)$share->getId()) {
230
+                return false;
231
+            }
232
+        }
233
+        return true;
234
+    }
235
+
236
+    /**
237
+     * throws hooks when a share is attempted to be accessed
238
+     *
239
+     * @param \OCP\Share\IShare|string $share the Share instance if available,
240
+     * otherwise token
241
+     * @param int $errorCode
242
+     * @param string $errorMessage
243
+     * @throws \OC\HintException
244
+     * @throws \OC\ServerNotAvailableException
245
+     */
246
+    protected function emitAccessShareHook($share, $errorCode = 200, $errorMessage = '') {
247
+        $itemType = $itemSource = $uidOwner = '';
248
+        $token = $share;
249
+        $exception = null;
250
+        if($share instanceof \OCP\Share\IShare) {
251
+            try {
252
+                $token = $share->getToken();
253
+                $uidOwner = $share->getSharedBy();
254
+                $itemType = $share->getNodeType();
255
+                $itemSource = $share->getNodeId();
256
+            } catch (\Exception $e) {
257
+                // we log what we know and pass on the exception afterwards
258
+                $exception = $e;
259
+            }
260
+        }
261
+        \OC_Hook::emit(Share::class, 'share_link_access', [
262
+            'itemType' => $itemType,
263
+            'itemSource' => $itemSource,
264
+            'uidOwner' => $uidOwner,
265
+            'token' => $token,
266
+            'errorCode' => $errorCode,
267
+            'errorMessage' => $errorMessage,
268
+        ]);
269
+        if(!is_null($exception)) {
270
+            throw $exception;
271
+        }
272
+    }
273
+
274
+    /**
275
+     * Validate the permissions of the share
276
+     *
277
+     * @param Share\IShare $share
278
+     * @return bool
279
+     */
280
+    private function validateShare(\OCP\Share\IShare $share) {
281
+        return $share->getNode()->isReadable() && $share->getNode()->isShareable();
282
+    }
283
+
284
+    /**
285
+     * @PublicPage
286
+     * @NoCSRFRequired
287
+     *
288
+     * @param string $token
289
+     * @param string $path
290
+     * @return TemplateResponse|RedirectResponse|NotFoundResponse
291
+     * @throws NotFoundException
292
+     * @throws \Exception
293
+     */
294
+    public function showShare($token, $path = '') {
295
+        \OC_User::setIncognitoMode(true);
296
+
297
+        // Check whether share exists
298
+        try {
299
+            $share = $this->shareManager->getShareByToken($token);
300
+        } catch (ShareNotFound $e) {
301
+            $this->emitAccessShareHook($token, 404, 'Share not found');
302
+            return new NotFoundResponse();
303
+        }
304
+
305
+        // Share is password protected - check whether the user is permitted to access the share
306
+        if ($share->getPassword() !== null && !$this->linkShareAuth($share)) {
307
+            return new RedirectResponse($this->urlGenerator->linkToRoute('files_sharing.sharecontroller.authenticate',
308
+                array('token' => $token, 'redirect' => 'preview')));
309
+        }
310
+
311
+        if (!$this->validateShare($share)) {
312
+            throw new NotFoundException();
313
+        }
314
+        // We can't get the path of a file share
315
+        try {
316
+            if ($share->getNode() instanceof \OCP\Files\File && $path !== '') {
317
+                $this->emitAccessShareHook($share, 404, 'Share not found');
318
+                throw new NotFoundException();
319
+            }
320
+        } catch (\Exception $e) {
321
+            $this->emitAccessShareHook($share, 404, 'Share not found');
322
+            throw $e;
323
+        }
324
+
325
+        $shareTmpl = [];
326
+        $shareTmpl['displayName'] = $this->userManager->get($share->getShareOwner())->getDisplayName();
327
+        $shareTmpl['owner'] = $share->getShareOwner();
328
+        $shareTmpl['filename'] = $share->getNode()->getName();
329
+        $shareTmpl['directory_path'] = $share->getTarget();
330
+        $shareTmpl['mimetype'] = $share->getNode()->getMimetype();
331
+        $shareTmpl['previewSupported'] = $this->previewManager->isMimeSupported($share->getNode()->getMimetype());
332
+        $shareTmpl['dirToken'] = $token;
333
+        $shareTmpl['sharingToken'] = $token;
334
+        $shareTmpl['server2serversharing'] = $this->federatedShareProvider->isOutgoingServer2serverShareEnabled();
335
+        $shareTmpl['protected'] = $share->getPassword() !== null ? 'true' : 'false';
336
+        $shareTmpl['dir'] = '';
337
+        $shareTmpl['nonHumanFileSize'] = $share->getNode()->getSize();
338
+        $shareTmpl['fileSize'] = \OCP\Util::humanFileSize($share->getNode()->getSize());
339
+
340
+        // Show file list
341
+        $hideFileList = false;
342
+        if ($share->getNode() instanceof \OCP\Files\Folder) {
343
+            /** @var \OCP\Files\Folder $rootFolder */
344
+            $rootFolder = $share->getNode();
345
+
346
+            try {
347
+                $folderNode = $rootFolder->get($path);
348
+            } catch (\OCP\Files\NotFoundException $e) {
349
+                $this->emitAccessShareHook($share, 404, 'Share not found');
350
+                throw new NotFoundException();
351
+            }
352
+
353
+            $shareTmpl['dir'] = $rootFolder->getRelativePath($folderNode->getPath());
354
+
355
+            /*
356 356
 			 * The OC_Util methods require a view. This just uses the node API
357 357
 			 */
358
-			$freeSpace = $share->getNode()->getStorage()->free_space($share->getNode()->getInternalPath());
359
-			if ($freeSpace < \OCP\Files\FileInfo::SPACE_UNLIMITED) {
360
-				$freeSpace = max($freeSpace, 0);
361
-			} else {
362
-				$freeSpace = (INF > 0) ? INF: PHP_INT_MAX; // work around https://bugs.php.net/bug.php?id=69188
363
-			}
364
-
365
-			$hideFileList = !($share->getPermissions() & \OCP\Constants::PERMISSION_READ);
366
-			$maxUploadFilesize = $freeSpace;
367
-
368
-			$folder = new Template('files', 'list', '');
369
-			$folder->assign('dir', $rootFolder->getRelativePath($folderNode->getPath()));
370
-			$folder->assign('dirToken', $token);
371
-			$folder->assign('permissions', \OCP\Constants::PERMISSION_READ);
372
-			$folder->assign('isPublic', true);
373
-			$folder->assign('hideFileList', $hideFileList);
374
-			$folder->assign('publicUploadEnabled', 'no');
375
-			$folder->assign('uploadMaxFilesize', $maxUploadFilesize);
376
-			$folder->assign('uploadMaxHumanFilesize', \OCP\Util::humanFileSize($maxUploadFilesize));
377
-			$folder->assign('freeSpace', $freeSpace);
378
-			$folder->assign('usedSpacePercent', 0);
379
-			$folder->assign('trash', false);
380
-			$shareTmpl['folder'] = $folder->fetchPage();
381
-		}
382
-
383
-		$shareTmpl['hideFileList'] = $hideFileList;
384
-		$shareTmpl['shareOwner'] = $this->userManager->get($share->getShareOwner())->getDisplayName();
385
-		$shareTmpl['downloadURL'] = $this->urlGenerator->linkToRouteAbsolute('files_sharing.sharecontroller.downloadShare', ['token' => $token]);
386
-		$shareTmpl['shareUrl'] = $this->urlGenerator->linkToRouteAbsolute('files_sharing.sharecontroller.showShare', ['token' => $token]);
387
-		$shareTmpl['maxSizeAnimateGif'] = $this->config->getSystemValue('max_filesize_animated_gifs_public_sharing', 10);
388
-		$shareTmpl['previewEnabled'] = $this->config->getSystemValue('enable_previews', true);
389
-		$shareTmpl['previewMaxX'] = $this->config->getSystemValue('preview_max_x', 1024);
390
-		$shareTmpl['previewMaxY'] = $this->config->getSystemValue('preview_max_y', 1024);
391
-		$shareTmpl['disclaimer'] = $this->config->getAppValue('core', 'shareapi_public_link_disclaimertext', null);
392
-		$shareTmpl['previewURL'] = $shareTmpl['downloadURL'];
393
-		$ogPreview = '';
394
-		if ($shareTmpl['previewSupported']) {
395
-			$shareTmpl['previewImage'] = $this->urlGenerator->linkToRouteAbsolute( 'files_sharing.PublicPreview.getPreview',
396
-				['x' => 200, 'y' => 200, 'file' => $shareTmpl['directory_path'], 't' => $shareTmpl['dirToken']]);
397
-			$ogPreview = $shareTmpl['previewImage'];
398
-
399
-			// We just have direct previews for image files
400
-			if ($share->getNode()->getMimePart() === 'image') {
401
-				$shareTmpl['previewURL'] = $this->urlGenerator->linkToRouteAbsolute('files_sharing.publicpreview.directLink', ['token' => $token]);
402
-
403
-				$ogPreview = $shareTmpl['previewURL'];
404
-
405
-				//Whatapp is kind of picky about their size requirements
406
-				if ($this->request->isUserAgent(['/^WhatsApp/'])) {
407
-					$ogPreview = $this->urlGenerator->linkToRouteAbsolute('files_sharing.PublicPreview.getPreview', [
408
-						't' => $token,
409
-						'x' => 256,
410
-						'y' => 256,
411
-						'a' => true,
412
-					]);
413
-				}
414
-			}
415
-		} else {
416
-			$shareTmpl['previewImage'] = $this->urlGenerator->getAbsoluteURL($this->urlGenerator->imagePath('core', 'favicon-fb.png'));
417
-			$ogPreview = $shareTmpl['previewImage'];
418
-		}
419
-
420
-		// Load files we need
421
-		\OCP\Util::addScript('files', 'file-upload');
422
-		\OCP\Util::addStyle('files_sharing', 'publicView');
423
-		\OCP\Util::addScript('files_sharing', 'public');
424
-		\OCP\Util::addScript('files', 'fileactions');
425
-		\OCP\Util::addScript('files', 'fileactionsmenu');
426
-		\OCP\Util::addScript('files', 'jquery.fileupload');
427
-		\OCP\Util::addScript('files_sharing', 'files_drop');
428
-
429
-		if (isset($shareTmpl['folder'])) {
430
-			// JS required for folders
431
-			\OCP\Util::addStyle('files', 'merged');
432
-			\OCP\Util::addScript('files', 'filesummary');
433
-			\OCP\Util::addScript('files', 'breadcrumb');
434
-			\OCP\Util::addScript('files', 'fileinfomodel');
435
-			\OCP\Util::addScript('files', 'newfilemenu');
436
-			\OCP\Util::addScript('files', 'files');
437
-			\OCP\Util::addScript('files', 'filelist');
438
-			\OCP\Util::addScript('files', 'keyboardshortcuts');
439
-		}
440
-
441
-		// OpenGraph Support: http://ogp.me/
442
-		\OCP\Util::addHeader('meta', ['property' => "og:title", 'content' => $shareTmpl['filename']]);
443
-		\OCP\Util::addHeader('meta', ['property' => "og:description", 'content' => $this->defaults->getName() . ($this->defaults->getSlogan() !== '' ? ' - ' . $this->defaults->getSlogan() : '')]);
444
-		\OCP\Util::addHeader('meta', ['property' => "og:site_name", 'content' => $this->defaults->getName()]);
445
-		\OCP\Util::addHeader('meta', ['property' => "og:url", 'content' => $shareTmpl['shareUrl']]);
446
-		\OCP\Util::addHeader('meta', ['property' => "og:type", 'content' => "object"]);
447
-		\OCP\Util::addHeader('meta', ['property' => "og:image", 'content' => $ogPreview]);
448
-
449
-		$this->eventDispatcher->dispatch('OCA\Files_Sharing::loadAdditionalScripts');
450
-
451
-		$csp = new \OCP\AppFramework\Http\ContentSecurityPolicy();
452
-		$csp->addAllowedFrameDomain('\'self\'');
453
-
454
-		$response = new PublicTemplateResponse($this->appName, 'public', $shareTmpl);
455
-		$response->setHeaderTitle($shareTmpl['filename']);
456
-		$response->setHeaderDetails($this->l10n->t('shared by %s', [$shareTmpl['displayName']]));
457
-		$response->setHeaderActions([
458
-			new SimpleMenuAction('download', $this->l10n->t('Download'), 'icon-download-white', $shareTmpl['downloadURL'], 0),
459
-			new SimpleMenuAction('download', $this->l10n->t('Download'), 'icon-download', $shareTmpl['downloadURL'], 10, $shareTmpl['fileSize']),
460
-			new LinkMenuAction($this->l10n->t('Direct link'), 'icon-public', $shareTmpl['previewURL']),
461
-			new ExternalShareMenuAction($this->l10n->t('Add to your Nextcloud'), 'icon-external', $shareTmpl['owner'], $shareTmpl['displayName'], $shareTmpl['filename']),
462
-		]);
463
-
464
-		$response->setContentSecurityPolicy($csp);
465
-
466
-		$this->emitAccessShareHook($share);
467
-
468
-		return $response;
469
-	}
470
-
471
-	/**
472
-	 * @PublicPage
473
-	 * @NoCSRFRequired
474
-	 *
475
-	 * @param string $token
476
-	 * @param string $files
477
-	 * @param string $path
478
-	 * @param string $downloadStartSecret
479
-	 * @return void|\OCP\AppFramework\Http\Response
480
-	 * @throws NotFoundException
481
-	 */
482
-	public function downloadShare($token, $files = null, $path = '', $downloadStartSecret = '') {
483
-		\OC_User::setIncognitoMode(true);
484
-
485
-		$share = $this->shareManager->getShareByToken($token);
486
-
487
-		if(!($share->getPermissions() & \OCP\Constants::PERMISSION_READ)) {
488
-			return new \OCP\AppFramework\Http\DataResponse('Share is read-only');
489
-		}
490
-
491
-		// Share is password protected - check whether the user is permitted to access the share
492
-		if ($share->getPassword() !== null && !$this->linkShareAuth($share)) {
493
-			return new RedirectResponse($this->urlGenerator->linkToRoute('files_sharing.sharecontroller.authenticate',
494
-				['token' => $token, 'redirect' => 'download']));
495
-		}
496
-
497
-		$files_list = null;
498
-		if (!is_null($files)) { // download selected files
499
-			$files_list = json_decode($files);
500
-			// in case we get only a single file
501
-			if ($files_list === null) {
502
-				$files_list = [$files];
503
-			}
504
-			// Just in case $files is a single int like '1234'
505
-			if (!is_array($files_list)) {
506
-				$files_list = [$files_list];
507
-			}
508
-		}
509
-
510
-		$userFolder = $this->rootFolder->getUserFolder($share->getShareOwner());
511
-		$originalSharePath = $userFolder->getRelativePath($share->getNode()->getPath());
512
-
513
-		if (!$this->validateShare($share)) {
514
-			throw new NotFoundException();
515
-		}
516
-
517
-		// Single file share
518
-		if ($share->getNode() instanceof \OCP\Files\File) {
519
-			// Single file download
520
-			$this->singleFileDownloaded($share, $share->getNode());
521
-		}
522
-		// Directory share
523
-		else {
524
-			/** @var \OCP\Files\Folder $node */
525
-			$node = $share->getNode();
526
-
527
-			// Try to get the path
528
-			if ($path !== '') {
529
-				try {
530
-					$node = $node->get($path);
531
-				} catch (NotFoundException $e) {
532
-					$this->emitAccessShareHook($share, 404, 'Share not found');
533
-					return new NotFoundResponse();
534
-				}
535
-			}
536
-
537
-			$originalSharePath = $userFolder->getRelativePath($node->getPath());
538
-
539
-			if ($node instanceof \OCP\Files\File) {
540
-				// Single file download
541
-				$this->singleFileDownloaded($share, $share->getNode());
542
-			} else if (!empty($files_list)) {
543
-				$this->fileListDownloaded($share, $files_list, $node);
544
-			} else {
545
-				// The folder is downloaded
546
-				$this->singleFileDownloaded($share, $share->getNode());
547
-			}
548
-		}
549
-
550
-		/* FIXME: We should do this all nicely in OCP */
551
-		OC_Util::tearDownFS();
552
-		OC_Util::setupFS($share->getShareOwner());
553
-
554
-		/**
555
-		 * this sets a cookie to be able to recognize the start of the download
556
-		 * the content must not be longer than 32 characters and must only contain
557
-		 * alphanumeric characters
558
-		 */
559
-		if (!empty($downloadStartSecret)
560
-			&& !isset($downloadStartSecret[32])
561
-			&& preg_match('!^[a-zA-Z0-9]+$!', $downloadStartSecret) === 1) {
562
-
563
-			// FIXME: set on the response once we use an actual app framework response
564
-			setcookie('ocDownloadStarted', $downloadStartSecret, time() + 20, '/');
565
-		}
566
-
567
-		$this->emitAccessShareHook($share);
568
-
569
-		$server_params = array( 'head' => $this->request->getMethod() === 'HEAD' );
570
-
571
-		/**
572
-		 * Http range requests support
573
-		 */
574
-		if (isset($_SERVER['HTTP_RANGE'])) {
575
-			$server_params['range'] = $this->request->getHeader('Range');
576
-		}
577
-
578
-		// download selected files
579
-		if (!is_null($files) && $files !== '') {
580
-			// FIXME: The exit is required here because otherwise the AppFramework is trying to add headers as well
581
-			// after dispatching the request which results in a "Cannot modify header information" notice.
582
-			OC_Files::get($originalSharePath, $files_list, $server_params);
583
-			exit();
584
-		} else {
585
-			// FIXME: The exit is required here because otherwise the AppFramework is trying to add headers as well
586
-			// after dispatching the request which results in a "Cannot modify header information" notice.
587
-			OC_Files::get(dirname($originalSharePath), basename($originalSharePath), $server_params);
588
-			exit();
589
-		}
590
-	}
591
-
592
-	/**
593
-	 * create activity for every downloaded file
594
-	 *
595
-	 * @param Share\IShare $share
596
-	 * @param array $files_list
597
-	 * @param \OCP\Files\Folder $node
598
-	 */
599
-	protected function fileListDownloaded(Share\IShare $share, array $files_list, \OCP\Files\Folder $node) {
600
-		foreach ($files_list as $file) {
601
-			$subNode = $node->get($file);
602
-			$this->singleFileDownloaded($share, $subNode);
603
-		}
604
-
605
-	}
606
-
607
-	/**
608
-	 * create activity if a single file was downloaded from a link share
609
-	 *
610
-	 * @param Share\IShare $share
611
-	 */
612
-	protected function singleFileDownloaded(Share\IShare $share, \OCP\Files\Node $node) {
613
-
614
-		$fileId = $node->getId();
615
-
616
-		$userFolder = $this->rootFolder->getUserFolder($share->getSharedBy());
617
-		$userNodeList = $userFolder->getById($fileId);
618
-		$userNode = $userNodeList[0];
619
-		$ownerFolder = $this->rootFolder->getUserFolder($share->getShareOwner());
620
-		$userPath = $userFolder->getRelativePath($userNode->getPath());
621
-		$ownerPath = $ownerFolder->getRelativePath($node->getPath());
622
-
623
-		$parameters = [$userPath];
624
-
625
-		if ($share->getShareType() === \OCP\Share::SHARE_TYPE_EMAIL) {
626
-			if ($node instanceof \OCP\Files\File) {
627
-				$subject = Downloads::SUBJECT_SHARED_FILE_BY_EMAIL_DOWNLOADED;
628
-			} else {
629
-				$subject = Downloads::SUBJECT_SHARED_FOLDER_BY_EMAIL_DOWNLOADED;
630
-			}
631
-			$parameters[] = $share->getSharedWith();
632
-		} else {
633
-			if ($node instanceof \OCP\Files\File) {
634
-				$subject = Downloads::SUBJECT_PUBLIC_SHARED_FILE_DOWNLOADED;
635
-			} else {
636
-				$subject = Downloads::SUBJECT_PUBLIC_SHARED_FOLDER_DOWNLOADED;
637
-			}
638
-		}
639
-
640
-		$this->publishActivity($subject, $parameters, $share->getSharedBy(), $fileId, $userPath);
641
-
642
-		if ($share->getShareOwner() !== $share->getSharedBy()) {
643
-			$parameters[0] = $ownerPath;
644
-			$this->publishActivity($subject, $parameters, $share->getShareOwner(), $fileId, $ownerPath);
645
-		}
646
-	}
647
-
648
-	/**
649
-	 * publish activity
650
-	 *
651
-	 * @param string $subject
652
-	 * @param array $parameters
653
-	 * @param string $affectedUser
654
-	 * @param int $fileId
655
-	 * @param string $filePath
656
-	 */
657
-	protected function publishActivity($subject,
658
-										array $parameters,
659
-										$affectedUser,
660
-										$fileId,
661
-										$filePath) {
662
-
663
-		$event = $this->activityManager->generateEvent();
664
-		$event->setApp('files_sharing')
665
-			->setType('public_links')
666
-			->setSubject($subject, $parameters)
667
-			->setAffectedUser($affectedUser)
668
-			->setObject('files', $fileId, $filePath);
669
-		$this->activityManager->publish($event);
670
-	}
358
+            $freeSpace = $share->getNode()->getStorage()->free_space($share->getNode()->getInternalPath());
359
+            if ($freeSpace < \OCP\Files\FileInfo::SPACE_UNLIMITED) {
360
+                $freeSpace = max($freeSpace, 0);
361
+            } else {
362
+                $freeSpace = (INF > 0) ? INF: PHP_INT_MAX; // work around https://bugs.php.net/bug.php?id=69188
363
+            }
364
+
365
+            $hideFileList = !($share->getPermissions() & \OCP\Constants::PERMISSION_READ);
366
+            $maxUploadFilesize = $freeSpace;
367
+
368
+            $folder = new Template('files', 'list', '');
369
+            $folder->assign('dir', $rootFolder->getRelativePath($folderNode->getPath()));
370
+            $folder->assign('dirToken', $token);
371
+            $folder->assign('permissions', \OCP\Constants::PERMISSION_READ);
372
+            $folder->assign('isPublic', true);
373
+            $folder->assign('hideFileList', $hideFileList);
374
+            $folder->assign('publicUploadEnabled', 'no');
375
+            $folder->assign('uploadMaxFilesize', $maxUploadFilesize);
376
+            $folder->assign('uploadMaxHumanFilesize', \OCP\Util::humanFileSize($maxUploadFilesize));
377
+            $folder->assign('freeSpace', $freeSpace);
378
+            $folder->assign('usedSpacePercent', 0);
379
+            $folder->assign('trash', false);
380
+            $shareTmpl['folder'] = $folder->fetchPage();
381
+        }
382
+
383
+        $shareTmpl['hideFileList'] = $hideFileList;
384
+        $shareTmpl['shareOwner'] = $this->userManager->get($share->getShareOwner())->getDisplayName();
385
+        $shareTmpl['downloadURL'] = $this->urlGenerator->linkToRouteAbsolute('files_sharing.sharecontroller.downloadShare', ['token' => $token]);
386
+        $shareTmpl['shareUrl'] = $this->urlGenerator->linkToRouteAbsolute('files_sharing.sharecontroller.showShare', ['token' => $token]);
387
+        $shareTmpl['maxSizeAnimateGif'] = $this->config->getSystemValue('max_filesize_animated_gifs_public_sharing', 10);
388
+        $shareTmpl['previewEnabled'] = $this->config->getSystemValue('enable_previews', true);
389
+        $shareTmpl['previewMaxX'] = $this->config->getSystemValue('preview_max_x', 1024);
390
+        $shareTmpl['previewMaxY'] = $this->config->getSystemValue('preview_max_y', 1024);
391
+        $shareTmpl['disclaimer'] = $this->config->getAppValue('core', 'shareapi_public_link_disclaimertext', null);
392
+        $shareTmpl['previewURL'] = $shareTmpl['downloadURL'];
393
+        $ogPreview = '';
394
+        if ($shareTmpl['previewSupported']) {
395
+            $shareTmpl['previewImage'] = $this->urlGenerator->linkToRouteAbsolute( 'files_sharing.PublicPreview.getPreview',
396
+                ['x' => 200, 'y' => 200, 'file' => $shareTmpl['directory_path'], 't' => $shareTmpl['dirToken']]);
397
+            $ogPreview = $shareTmpl['previewImage'];
398
+
399
+            // We just have direct previews for image files
400
+            if ($share->getNode()->getMimePart() === 'image') {
401
+                $shareTmpl['previewURL'] = $this->urlGenerator->linkToRouteAbsolute('files_sharing.publicpreview.directLink', ['token' => $token]);
402
+
403
+                $ogPreview = $shareTmpl['previewURL'];
404
+
405
+                //Whatapp is kind of picky about their size requirements
406
+                if ($this->request->isUserAgent(['/^WhatsApp/'])) {
407
+                    $ogPreview = $this->urlGenerator->linkToRouteAbsolute('files_sharing.PublicPreview.getPreview', [
408
+                        't' => $token,
409
+                        'x' => 256,
410
+                        'y' => 256,
411
+                        'a' => true,
412
+                    ]);
413
+                }
414
+            }
415
+        } else {
416
+            $shareTmpl['previewImage'] = $this->urlGenerator->getAbsoluteURL($this->urlGenerator->imagePath('core', 'favicon-fb.png'));
417
+            $ogPreview = $shareTmpl['previewImage'];
418
+        }
419
+
420
+        // Load files we need
421
+        \OCP\Util::addScript('files', 'file-upload');
422
+        \OCP\Util::addStyle('files_sharing', 'publicView');
423
+        \OCP\Util::addScript('files_sharing', 'public');
424
+        \OCP\Util::addScript('files', 'fileactions');
425
+        \OCP\Util::addScript('files', 'fileactionsmenu');
426
+        \OCP\Util::addScript('files', 'jquery.fileupload');
427
+        \OCP\Util::addScript('files_sharing', 'files_drop');
428
+
429
+        if (isset($shareTmpl['folder'])) {
430
+            // JS required for folders
431
+            \OCP\Util::addStyle('files', 'merged');
432
+            \OCP\Util::addScript('files', 'filesummary');
433
+            \OCP\Util::addScript('files', 'breadcrumb');
434
+            \OCP\Util::addScript('files', 'fileinfomodel');
435
+            \OCP\Util::addScript('files', 'newfilemenu');
436
+            \OCP\Util::addScript('files', 'files');
437
+            \OCP\Util::addScript('files', 'filelist');
438
+            \OCP\Util::addScript('files', 'keyboardshortcuts');
439
+        }
440
+
441
+        // OpenGraph Support: http://ogp.me/
442
+        \OCP\Util::addHeader('meta', ['property' => "og:title", 'content' => $shareTmpl['filename']]);
443
+        \OCP\Util::addHeader('meta', ['property' => "og:description", 'content' => $this->defaults->getName() . ($this->defaults->getSlogan() !== '' ? ' - ' . $this->defaults->getSlogan() : '')]);
444
+        \OCP\Util::addHeader('meta', ['property' => "og:site_name", 'content' => $this->defaults->getName()]);
445
+        \OCP\Util::addHeader('meta', ['property' => "og:url", 'content' => $shareTmpl['shareUrl']]);
446
+        \OCP\Util::addHeader('meta', ['property' => "og:type", 'content' => "object"]);
447
+        \OCP\Util::addHeader('meta', ['property' => "og:image", 'content' => $ogPreview]);
448
+
449
+        $this->eventDispatcher->dispatch('OCA\Files_Sharing::loadAdditionalScripts');
450
+
451
+        $csp = new \OCP\AppFramework\Http\ContentSecurityPolicy();
452
+        $csp->addAllowedFrameDomain('\'self\'');
453
+
454
+        $response = new PublicTemplateResponse($this->appName, 'public', $shareTmpl);
455
+        $response->setHeaderTitle($shareTmpl['filename']);
456
+        $response->setHeaderDetails($this->l10n->t('shared by %s', [$shareTmpl['displayName']]));
457
+        $response->setHeaderActions([
458
+            new SimpleMenuAction('download', $this->l10n->t('Download'), 'icon-download-white', $shareTmpl['downloadURL'], 0),
459
+            new SimpleMenuAction('download', $this->l10n->t('Download'), 'icon-download', $shareTmpl['downloadURL'], 10, $shareTmpl['fileSize']),
460
+            new LinkMenuAction($this->l10n->t('Direct link'), 'icon-public', $shareTmpl['previewURL']),
461
+            new ExternalShareMenuAction($this->l10n->t('Add to your Nextcloud'), 'icon-external', $shareTmpl['owner'], $shareTmpl['displayName'], $shareTmpl['filename']),
462
+        ]);
463
+
464
+        $response->setContentSecurityPolicy($csp);
465
+
466
+        $this->emitAccessShareHook($share);
467
+
468
+        return $response;
469
+    }
470
+
471
+    /**
472
+     * @PublicPage
473
+     * @NoCSRFRequired
474
+     *
475
+     * @param string $token
476
+     * @param string $files
477
+     * @param string $path
478
+     * @param string $downloadStartSecret
479
+     * @return void|\OCP\AppFramework\Http\Response
480
+     * @throws NotFoundException
481
+     */
482
+    public function downloadShare($token, $files = null, $path = '', $downloadStartSecret = '') {
483
+        \OC_User::setIncognitoMode(true);
484
+
485
+        $share = $this->shareManager->getShareByToken($token);
486
+
487
+        if(!($share->getPermissions() & \OCP\Constants::PERMISSION_READ)) {
488
+            return new \OCP\AppFramework\Http\DataResponse('Share is read-only');
489
+        }
490
+
491
+        // Share is password protected - check whether the user is permitted to access the share
492
+        if ($share->getPassword() !== null && !$this->linkShareAuth($share)) {
493
+            return new RedirectResponse($this->urlGenerator->linkToRoute('files_sharing.sharecontroller.authenticate',
494
+                ['token' => $token, 'redirect' => 'download']));
495
+        }
496
+
497
+        $files_list = null;
498
+        if (!is_null($files)) { // download selected files
499
+            $files_list = json_decode($files);
500
+            // in case we get only a single file
501
+            if ($files_list === null) {
502
+                $files_list = [$files];
503
+            }
504
+            // Just in case $files is a single int like '1234'
505
+            if (!is_array($files_list)) {
506
+                $files_list = [$files_list];
507
+            }
508
+        }
509
+
510
+        $userFolder = $this->rootFolder->getUserFolder($share->getShareOwner());
511
+        $originalSharePath = $userFolder->getRelativePath($share->getNode()->getPath());
512
+
513
+        if (!$this->validateShare($share)) {
514
+            throw new NotFoundException();
515
+        }
516
+
517
+        // Single file share
518
+        if ($share->getNode() instanceof \OCP\Files\File) {
519
+            // Single file download
520
+            $this->singleFileDownloaded($share, $share->getNode());
521
+        }
522
+        // Directory share
523
+        else {
524
+            /** @var \OCP\Files\Folder $node */
525
+            $node = $share->getNode();
526
+
527
+            // Try to get the path
528
+            if ($path !== '') {
529
+                try {
530
+                    $node = $node->get($path);
531
+                } catch (NotFoundException $e) {
532
+                    $this->emitAccessShareHook($share, 404, 'Share not found');
533
+                    return new NotFoundResponse();
534
+                }
535
+            }
536
+
537
+            $originalSharePath = $userFolder->getRelativePath($node->getPath());
538
+
539
+            if ($node instanceof \OCP\Files\File) {
540
+                // Single file download
541
+                $this->singleFileDownloaded($share, $share->getNode());
542
+            } else if (!empty($files_list)) {
543
+                $this->fileListDownloaded($share, $files_list, $node);
544
+            } else {
545
+                // The folder is downloaded
546
+                $this->singleFileDownloaded($share, $share->getNode());
547
+            }
548
+        }
549
+
550
+        /* FIXME: We should do this all nicely in OCP */
551
+        OC_Util::tearDownFS();
552
+        OC_Util::setupFS($share->getShareOwner());
553
+
554
+        /**
555
+         * this sets a cookie to be able to recognize the start of the download
556
+         * the content must not be longer than 32 characters and must only contain
557
+         * alphanumeric characters
558
+         */
559
+        if (!empty($downloadStartSecret)
560
+            && !isset($downloadStartSecret[32])
561
+            && preg_match('!^[a-zA-Z0-9]+$!', $downloadStartSecret) === 1) {
562
+
563
+            // FIXME: set on the response once we use an actual app framework response
564
+            setcookie('ocDownloadStarted', $downloadStartSecret, time() + 20, '/');
565
+        }
566
+
567
+        $this->emitAccessShareHook($share);
568
+
569
+        $server_params = array( 'head' => $this->request->getMethod() === 'HEAD' );
570
+
571
+        /**
572
+         * Http range requests support
573
+         */
574
+        if (isset($_SERVER['HTTP_RANGE'])) {
575
+            $server_params['range'] = $this->request->getHeader('Range');
576
+        }
577
+
578
+        // download selected files
579
+        if (!is_null($files) && $files !== '') {
580
+            // FIXME: The exit is required here because otherwise the AppFramework is trying to add headers as well
581
+            // after dispatching the request which results in a "Cannot modify header information" notice.
582
+            OC_Files::get($originalSharePath, $files_list, $server_params);
583
+            exit();
584
+        } else {
585
+            // FIXME: The exit is required here because otherwise the AppFramework is trying to add headers as well
586
+            // after dispatching the request which results in a "Cannot modify header information" notice.
587
+            OC_Files::get(dirname($originalSharePath), basename($originalSharePath), $server_params);
588
+            exit();
589
+        }
590
+    }
591
+
592
+    /**
593
+     * create activity for every downloaded file
594
+     *
595
+     * @param Share\IShare $share
596
+     * @param array $files_list
597
+     * @param \OCP\Files\Folder $node
598
+     */
599
+    protected function fileListDownloaded(Share\IShare $share, array $files_list, \OCP\Files\Folder $node) {
600
+        foreach ($files_list as $file) {
601
+            $subNode = $node->get($file);
602
+            $this->singleFileDownloaded($share, $subNode);
603
+        }
604
+
605
+    }
606
+
607
+    /**
608
+     * create activity if a single file was downloaded from a link share
609
+     *
610
+     * @param Share\IShare $share
611
+     */
612
+    protected function singleFileDownloaded(Share\IShare $share, \OCP\Files\Node $node) {
613
+
614
+        $fileId = $node->getId();
615
+
616
+        $userFolder = $this->rootFolder->getUserFolder($share->getSharedBy());
617
+        $userNodeList = $userFolder->getById($fileId);
618
+        $userNode = $userNodeList[0];
619
+        $ownerFolder = $this->rootFolder->getUserFolder($share->getShareOwner());
620
+        $userPath = $userFolder->getRelativePath($userNode->getPath());
621
+        $ownerPath = $ownerFolder->getRelativePath($node->getPath());
622
+
623
+        $parameters = [$userPath];
624
+
625
+        if ($share->getShareType() === \OCP\Share::SHARE_TYPE_EMAIL) {
626
+            if ($node instanceof \OCP\Files\File) {
627
+                $subject = Downloads::SUBJECT_SHARED_FILE_BY_EMAIL_DOWNLOADED;
628
+            } else {
629
+                $subject = Downloads::SUBJECT_SHARED_FOLDER_BY_EMAIL_DOWNLOADED;
630
+            }
631
+            $parameters[] = $share->getSharedWith();
632
+        } else {
633
+            if ($node instanceof \OCP\Files\File) {
634
+                $subject = Downloads::SUBJECT_PUBLIC_SHARED_FILE_DOWNLOADED;
635
+            } else {
636
+                $subject = Downloads::SUBJECT_PUBLIC_SHARED_FOLDER_DOWNLOADED;
637
+            }
638
+        }
639
+
640
+        $this->publishActivity($subject, $parameters, $share->getSharedBy(), $fileId, $userPath);
641
+
642
+        if ($share->getShareOwner() !== $share->getSharedBy()) {
643
+            $parameters[0] = $ownerPath;
644
+            $this->publishActivity($subject, $parameters, $share->getShareOwner(), $fileId, $ownerPath);
645
+        }
646
+    }
647
+
648
+    /**
649
+     * publish activity
650
+     *
651
+     * @param string $subject
652
+     * @param array $parameters
653
+     * @param string $affectedUser
654
+     * @param int $fileId
655
+     * @param string $filePath
656
+     */
657
+    protected function publishActivity($subject,
658
+                                        array $parameters,
659
+                                        $affectedUser,
660
+                                        $fileId,
661
+                                        $filePath) {
662
+
663
+        $event = $this->activityManager->generateEvent();
664
+        $event->setApp('files_sharing')
665
+            ->setType('public_links')
666
+            ->setSubject($subject, $parameters)
667
+            ->setAffectedUser($affectedUser)
668
+            ->setObject('files', $fileId, $filePath);
669
+        $this->activityManager->publish($event);
670
+    }
671 671
 
672 672
 
673 673
 }
Please login to merge, or discard this patch.
Spacing   +10 added lines, -10 removed lines patch added patch discarded remove patch
@@ -156,7 +156,7 @@  discard block
 block discarded – undo
156 156
 	public function showAuthenticate($token) {
157 157
 		$share = $this->shareManager->getShareByToken($token);
158 158
 
159
-		if($this->linkShareAuth($share)) {
159
+		if ($this->linkShareAuth($share)) {
160 160
 			return new RedirectResponse($this->urlGenerator->linkToRoute('files_sharing.sharecontroller.showShare', array('token' => $token)));
161 161
 		}
162 162
 
@@ -218,15 +218,15 @@  discard block
 block discarded – undo
218 218
 		if ($password !== null) {
219 219
 			if ($this->shareManager->checkPassword($share, $password)) {
220 220
 				$this->session->regenerateId(true, true);
221
-				$this->session->set('public_link_authenticated', (string)$share->getId());
221
+				$this->session->set('public_link_authenticated', (string) $share->getId());
222 222
 			} else {
223 223
 				$this->emitAccessShareHook($share, 403, 'Wrong password');
224 224
 				return false;
225 225
 			}
226 226
 		} else {
227 227
 			// not authenticated ?
228
-			if ( ! $this->session->exists('public_link_authenticated')
229
-				|| $this->session->get('public_link_authenticated') !== (string)$share->getId()) {
228
+			if (!$this->session->exists('public_link_authenticated')
229
+				|| $this->session->get('public_link_authenticated') !== (string) $share->getId()) {
230 230
 				return false;
231 231
 			}
232 232
 		}
@@ -247,7 +247,7 @@  discard block
 block discarded – undo
247 247
 		$itemType = $itemSource = $uidOwner = '';
248 248
 		$token = $share;
249 249
 		$exception = null;
250
-		if($share instanceof \OCP\Share\IShare) {
250
+		if ($share instanceof \OCP\Share\IShare) {
251 251
 			try {
252 252
 				$token = $share->getToken();
253 253
 				$uidOwner = $share->getSharedBy();
@@ -266,7 +266,7 @@  discard block
 block discarded – undo
266 266
 			'errorCode' => $errorCode,
267 267
 			'errorMessage' => $errorMessage,
268 268
 		]);
269
-		if(!is_null($exception)) {
269
+		if (!is_null($exception)) {
270 270
 			throw $exception;
271 271
 		}
272 272
 	}
@@ -392,7 +392,7 @@  discard block
 block discarded – undo
392 392
 		$shareTmpl['previewURL'] = $shareTmpl['downloadURL'];
393 393
 		$ogPreview = '';
394 394
 		if ($shareTmpl['previewSupported']) {
395
-			$shareTmpl['previewImage'] = $this->urlGenerator->linkToRouteAbsolute( 'files_sharing.PublicPreview.getPreview',
395
+			$shareTmpl['previewImage'] = $this->urlGenerator->linkToRouteAbsolute('files_sharing.PublicPreview.getPreview',
396 396
 				['x' => 200, 'y' => 200, 'file' => $shareTmpl['directory_path'], 't' => $shareTmpl['dirToken']]);
397 397
 			$ogPreview = $shareTmpl['previewImage'];
398 398
 
@@ -440,7 +440,7 @@  discard block
 block discarded – undo
440 440
 
441 441
 		// OpenGraph Support: http://ogp.me/
442 442
 		\OCP\Util::addHeader('meta', ['property' => "og:title", 'content' => $shareTmpl['filename']]);
443
-		\OCP\Util::addHeader('meta', ['property' => "og:description", 'content' => $this->defaults->getName() . ($this->defaults->getSlogan() !== '' ? ' - ' . $this->defaults->getSlogan() : '')]);
443
+		\OCP\Util::addHeader('meta', ['property' => "og:description", 'content' => $this->defaults->getName().($this->defaults->getSlogan() !== '' ? ' - '.$this->defaults->getSlogan() : '')]);
444 444
 		\OCP\Util::addHeader('meta', ['property' => "og:site_name", 'content' => $this->defaults->getName()]);
445 445
 		\OCP\Util::addHeader('meta', ['property' => "og:url", 'content' => $shareTmpl['shareUrl']]);
446 446
 		\OCP\Util::addHeader('meta', ['property' => "og:type", 'content' => "object"]);
@@ -484,7 +484,7 @@  discard block
 block discarded – undo
484 484
 
485 485
 		$share = $this->shareManager->getShareByToken($token);
486 486
 
487
-		if(!($share->getPermissions() & \OCP\Constants::PERMISSION_READ)) {
487
+		if (!($share->getPermissions() & \OCP\Constants::PERMISSION_READ)) {
488 488
 			return new \OCP\AppFramework\Http\DataResponse('Share is read-only');
489 489
 		}
490 490
 
@@ -566,7 +566,7 @@  discard block
 block discarded – undo
566 566
 
567 567
 		$this->emitAccessShareHook($share);
568 568
 
569
-		$server_params = array( 'head' => $this->request->getMethod() === 'HEAD' );
569
+		$server_params = array('head' => $this->request->getMethod() === 'HEAD');
570 570
 
571 571
 		/**
572 572
 		 * Http range requests support
Please login to merge, or discard this patch.
lib/private/Session/Internal.php 2 patches
Unused Use Statements   -2 removed lines patch added patch discarded remove patch
@@ -32,8 +32,6 @@
 block discarded – undo
32 32
 
33 33
 use OC\Authentication\Exceptions\InvalidTokenException;
34 34
 use OC\Authentication\Token\IProvider;
35
-use OC\SystemConfig;
36
-use OCP\IConfig;
37 35
 use OCP\Session\Exceptions\SessionNotAvailableException;
38 36
 
39 37
 /**
Please login to merge, or discard this patch.
Indentation   +166 added lines, -166 removed lines patch added patch discarded remove patch
@@ -44,170 +44,170 @@
 block discarded – undo
44 44
  * @package OC\Session
45 45
  */
46 46
 class Internal extends Session {
47
-	/**
48
-	 * @param string $name
49
-	 * @throws \Exception
50
-	 */
51
-	public function __construct(string $name) {
52
-		set_error_handler([$this, 'trapError']);
53
-		$this->invoke('session_name', [$name]);
54
-		try {
55
-			$this->invoke('session_start');
56
-		} catch (\Exception $e) {
57
-			setcookie($this->invoke('session_name'), null, -1, \OC::$WEBROOT ?: '/');
58
-		}
59
-		restore_error_handler();
60
-		if (!isset($_SESSION)) {
61
-			throw new \Exception('Failed to start session');
62
-		}
63
-	}
64
-
65
-	/**
66
-	 * @param string $key
67
-	 * @param integer $value
68
-	 */
69
-	public function set(string $key, $value) {
70
-		$this->validateSession();
71
-		$_SESSION[$key] = $value;
72
-	}
73
-
74
-	/**
75
-	 * @param string $key
76
-	 * @return mixed
77
-	 */
78
-	public function get(string $key) {
79
-		if (!$this->exists($key)) {
80
-			return null;
81
-		}
82
-		return $_SESSION[$key];
83
-	}
84
-
85
-	/**
86
-	 * @param string $key
87
-	 * @return bool
88
-	 */
89
-	public function exists(string $key): bool {
90
-		return isset($_SESSION[$key]);
91
-	}
92
-
93
-	/**
94
-	 * @param string $key
95
-	 */
96
-	public function remove(string $key) {
97
-		if (isset($_SESSION[$key])) {
98
-			unset($_SESSION[$key]);
99
-		}
100
-	}
101
-
102
-	public function clear() {
103
-		$this->invoke('session_unset');
104
-		$this->regenerateId();
105
-		$this->invoke('session_start', [], true);
106
-		$_SESSION = [];
107
-	}
108
-
109
-	public function close() {
110
-		$this->invoke('session_write_close');
111
-		parent::close();
112
-	}
113
-
114
-	/**
115
-	 * Wrapper around session_regenerate_id
116
-	 *
117
-	 * @param bool $deleteOldSession Whether to delete the old associated session file or not.
118
-	 * @param bool $updateToken Wheater to update the associated auth token
119
-	 * @return void
120
-	 */
121
-	public function regenerateId(bool $deleteOldSession = true, bool $updateToken = false) {
122
-		$oldId = null;
123
-
124
-		if ($updateToken) {
125
-			// Get the old id to update the token
126
-			try {
127
-				$oldId = $this->getId();
128
-			} catch (SessionNotAvailableException $e) {
129
-				// We can't update a token if there is no previous id
130
-				$updateToken = false;
131
-			}
132
-		}
133
-
134
-		try {
135
-			@session_regenerate_id($deleteOldSession);
136
-		} catch (\Error $e) {
137
-			$this->trapError($e->getCode(), $e->getMessage());
138
-		}
139
-
140
-		if ($updateToken) {
141
-			// Get the new id to update the token
142
-			$newId = $this->getId();
143
-
144
-			/** @var IProvider $tokenProvider */
145
-			$tokenProvider = \OC::$server->query(IProvider::class);
146
-
147
-			try {
148
-				$tokenProvider->renewSessionToken($oldId, $newId);
149
-			} catch (InvalidTokenException $e) {
150
-				// Just ignore
151
-			}
152
-		}
153
-	}
154
-
155
-	/**
156
-	 * Wrapper around session_id
157
-	 *
158
-	 * @return string
159
-	 * @throws SessionNotAvailableException
160
-	 * @since 9.1.0
161
-	 */
162
-	public function getId(): string {
163
-		$id = $this->invoke('session_id', [], true);
164
-		if ($id === '') {
165
-			throw new SessionNotAvailableException();
166
-		}
167
-		return $id;
168
-	}
169
-
170
-	/**
171
-	 * @throws \Exception
172
-	 */
173
-	public function reopen() {
174
-		throw new \Exception('The session cannot be reopened - reopen() is ony to be used in unit testing.');
175
-	}
176
-
177
-	/**
178
-	 * @param int $errorNumber
179
-	 * @param string $errorString
180
-	 * @throws \ErrorException
181
-	 */
182
-	public function trapError(int $errorNumber, string $errorString) {
183
-		throw new \ErrorException($errorString);
184
-	}
185
-
186
-	/**
187
-	 * @throws \Exception
188
-	 */
189
-	private function validateSession() {
190
-		if ($this->sessionClosed) {
191
-			throw new SessionNotAvailableException('Session has been closed - no further changes to the session are allowed');
192
-		}
193
-	}
194
-
195
-	/**
196
-	 * @param string $functionName the full session_* function name
197
-	 * @param array $parameters
198
-	 * @param bool $silence whether to suppress warnings
199
-	 * @throws \ErrorException via trapError
200
-	 * @return mixed
201
-	 */
202
-	private function invoke(string $functionName, array $parameters = [], bool $silence = false) {
203
-		try {
204
-			if($silence) {
205
-				return @call_user_func_array($functionName, $parameters);
206
-			} else {
207
-				return call_user_func_array($functionName, $parameters);
208
-			}
209
-		} catch(\Error $e) {
210
-			$this->trapError($e->getCode(), $e->getMessage());
211
-		}
212
-	}
47
+    /**
48
+     * @param string $name
49
+     * @throws \Exception
50
+     */
51
+    public function __construct(string $name) {
52
+        set_error_handler([$this, 'trapError']);
53
+        $this->invoke('session_name', [$name]);
54
+        try {
55
+            $this->invoke('session_start');
56
+        } catch (\Exception $e) {
57
+            setcookie($this->invoke('session_name'), null, -1, \OC::$WEBROOT ?: '/');
58
+        }
59
+        restore_error_handler();
60
+        if (!isset($_SESSION)) {
61
+            throw new \Exception('Failed to start session');
62
+        }
63
+    }
64
+
65
+    /**
66
+     * @param string $key
67
+     * @param integer $value
68
+     */
69
+    public function set(string $key, $value) {
70
+        $this->validateSession();
71
+        $_SESSION[$key] = $value;
72
+    }
73
+
74
+    /**
75
+     * @param string $key
76
+     * @return mixed
77
+     */
78
+    public function get(string $key) {
79
+        if (!$this->exists($key)) {
80
+            return null;
81
+        }
82
+        return $_SESSION[$key];
83
+    }
84
+
85
+    /**
86
+     * @param string $key
87
+     * @return bool
88
+     */
89
+    public function exists(string $key): bool {
90
+        return isset($_SESSION[$key]);
91
+    }
92
+
93
+    /**
94
+     * @param string $key
95
+     */
96
+    public function remove(string $key) {
97
+        if (isset($_SESSION[$key])) {
98
+            unset($_SESSION[$key]);
99
+        }
100
+    }
101
+
102
+    public function clear() {
103
+        $this->invoke('session_unset');
104
+        $this->regenerateId();
105
+        $this->invoke('session_start', [], true);
106
+        $_SESSION = [];
107
+    }
108
+
109
+    public function close() {
110
+        $this->invoke('session_write_close');
111
+        parent::close();
112
+    }
113
+
114
+    /**
115
+     * Wrapper around session_regenerate_id
116
+     *
117
+     * @param bool $deleteOldSession Whether to delete the old associated session file or not.
118
+     * @param bool $updateToken Wheater to update the associated auth token
119
+     * @return void
120
+     */
121
+    public function regenerateId(bool $deleteOldSession = true, bool $updateToken = false) {
122
+        $oldId = null;
123
+
124
+        if ($updateToken) {
125
+            // Get the old id to update the token
126
+            try {
127
+                $oldId = $this->getId();
128
+            } catch (SessionNotAvailableException $e) {
129
+                // We can't update a token if there is no previous id
130
+                $updateToken = false;
131
+            }
132
+        }
133
+
134
+        try {
135
+            @session_regenerate_id($deleteOldSession);
136
+        } catch (\Error $e) {
137
+            $this->trapError($e->getCode(), $e->getMessage());
138
+        }
139
+
140
+        if ($updateToken) {
141
+            // Get the new id to update the token
142
+            $newId = $this->getId();
143
+
144
+            /** @var IProvider $tokenProvider */
145
+            $tokenProvider = \OC::$server->query(IProvider::class);
146
+
147
+            try {
148
+                $tokenProvider->renewSessionToken($oldId, $newId);
149
+            } catch (InvalidTokenException $e) {
150
+                // Just ignore
151
+            }
152
+        }
153
+    }
154
+
155
+    /**
156
+     * Wrapper around session_id
157
+     *
158
+     * @return string
159
+     * @throws SessionNotAvailableException
160
+     * @since 9.1.0
161
+     */
162
+    public function getId(): string {
163
+        $id = $this->invoke('session_id', [], true);
164
+        if ($id === '') {
165
+            throw new SessionNotAvailableException();
166
+        }
167
+        return $id;
168
+    }
169
+
170
+    /**
171
+     * @throws \Exception
172
+     */
173
+    public function reopen() {
174
+        throw new \Exception('The session cannot be reopened - reopen() is ony to be used in unit testing.');
175
+    }
176
+
177
+    /**
178
+     * @param int $errorNumber
179
+     * @param string $errorString
180
+     * @throws \ErrorException
181
+     */
182
+    public function trapError(int $errorNumber, string $errorString) {
183
+        throw new \ErrorException($errorString);
184
+    }
185
+
186
+    /**
187
+     * @throws \Exception
188
+     */
189
+    private function validateSession() {
190
+        if ($this->sessionClosed) {
191
+            throw new SessionNotAvailableException('Session has been closed - no further changes to the session are allowed');
192
+        }
193
+    }
194
+
195
+    /**
196
+     * @param string $functionName the full session_* function name
197
+     * @param array $parameters
198
+     * @param bool $silence whether to suppress warnings
199
+     * @throws \ErrorException via trapError
200
+     * @return mixed
201
+     */
202
+    private function invoke(string $functionName, array $parameters = [], bool $silence = false) {
203
+        try {
204
+            if($silence) {
205
+                return @call_user_func_array($functionName, $parameters);
206
+            } else {
207
+                return call_user_func_array($functionName, $parameters);
208
+            }
209
+        } catch(\Error $e) {
210
+            $this->trapError($e->getCode(), $e->getMessage());
211
+        }
212
+    }
213 213
 }
Please login to merge, or discard this patch.
lib/public/ISession.php 1 patch
Indentation   +58 added lines, -58 removed lines patch added patch discarded remove patch
@@ -45,69 +45,69 @@
 block discarded – undo
45 45
  */
46 46
 interface ISession {
47 47
 
48
-	/**
49
-	 * Set a value in the session
50
-	 *
51
-	 * @param string $key
52
-	 * @param mixed $value
53
-	 * @since 6.0.0
54
-	 */
55
-	public function set(string $key, $value);
48
+    /**
49
+     * Set a value in the session
50
+     *
51
+     * @param string $key
52
+     * @param mixed $value
53
+     * @since 6.0.0
54
+     */
55
+    public function set(string $key, $value);
56 56
 
57
-	/**
58
-	 * Get a value from the session
59
-	 *
60
-	 * @param string $key
61
-	 * @return mixed should return null if $key does not exist
62
-	 * @since 6.0.0
63
-	 */
64
-	public function get(string $key);
57
+    /**
58
+     * Get a value from the session
59
+     *
60
+     * @param string $key
61
+     * @return mixed should return null if $key does not exist
62
+     * @since 6.0.0
63
+     */
64
+    public function get(string $key);
65 65
 
66
-	/**
67
-	 * Check if a named key exists in the session
68
-	 *
69
-	 * @param string $key
70
-	 * @return bool
71
-	 * @since 6.0.0
72
-	 */
73
-	public function exists(string $key): bool;
66
+    /**
67
+     * Check if a named key exists in the session
68
+     *
69
+     * @param string $key
70
+     * @return bool
71
+     * @since 6.0.0
72
+     */
73
+    public function exists(string $key): bool;
74 74
 
75
-	/**
76
-	 * Remove a $key/$value pair from the session
77
-	 *
78
-	 * @param string $key
79
-	 * @since 6.0.0
80
-	 */
81
-	public function remove(string $key);
75
+    /**
76
+     * Remove a $key/$value pair from the session
77
+     *
78
+     * @param string $key
79
+     * @since 6.0.0
80
+     */
81
+    public function remove(string $key);
82 82
 
83
-	/**
84
-	 * Reset and recreate the session
85
-	 * @since 6.0.0
86
-	 */
87
-	public function clear();
83
+    /**
84
+     * Reset and recreate the session
85
+     * @since 6.0.0
86
+     */
87
+    public function clear();
88 88
 
89
-	/**
90
-	 * Close the session and release the lock
91
-	 * @since 7.0.0
92
-	 */
93
-	public function close();
89
+    /**
90
+     * Close the session and release the lock
91
+     * @since 7.0.0
92
+     */
93
+    public function close();
94 94
 
95
-	/**
96
-	 * Wrapper around session_regenerate_id
97
-	 *
98
-	 * @param bool $deleteOldSession Whether to delete the old associated session file or not.
99
-	 * @param bool $updateToken Wheater to update the associated auth token
100
-	 * @return void
101
-	 * @since 9.0.0, $updateToken added in 14.0.0
102
-	 */
103
-	public function regenerateId(bool $deleteOldSession = true, bool $updateToken = false);
95
+    /**
96
+     * Wrapper around session_regenerate_id
97
+     *
98
+     * @param bool $deleteOldSession Whether to delete the old associated session file or not.
99
+     * @param bool $updateToken Wheater to update the associated auth token
100
+     * @return void
101
+     * @since 9.0.0, $updateToken added in 14.0.0
102
+     */
103
+    public function regenerateId(bool $deleteOldSession = true, bool $updateToken = false);
104 104
 
105
-	/**
106
-	 * Wrapper around session_id
107
-	 *
108
-	 * @return string
109
-	 * @throws SessionNotAvailableException
110
-	 * @since 9.1.0
111
-	 */
112
-	public function getId(): string;
105
+    /**
106
+     * Wrapper around session_id
107
+     *
108
+     * @return string
109
+     * @throws SessionNotAvailableException
110
+     * @since 9.1.0
111
+     */
112
+    public function getId(): string;
113 113
 }
Please login to merge, or discard this patch.