2013-05-29 01:46:57 +04:00
|
|
|
<?php
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Copyright (c) 2013 Robin Appelman <icewind@owncloud.com>
|
|
|
|
* This file is licensed under the Affero General Public License version 3 or
|
|
|
|
* later.
|
|
|
|
* See the COPYING-README file.
|
|
|
|
*/
|
|
|
|
|
|
|
|
namespace Test\User;
|
|
|
|
|
2014-10-13 18:31:26 +04:00
|
|
|
use OC\Session\Memory;
|
|
|
|
use OC\User\User;
|
|
|
|
|
2014-11-11 01:30:38 +03:00
|
|
|
class Session extends \Test\TestCase {
|
2013-05-29 01:46:57 +04:00
|
|
|
public function testGetUser() {
|
|
|
|
$session = $this->getMock('\OC\Session\Memory', array(), array(''));
|
|
|
|
$session->expects($this->once())
|
|
|
|
->method('get')
|
|
|
|
->with('user_id')
|
|
|
|
->will($this->returnValue('foo'));
|
|
|
|
|
2015-09-22 01:56:36 +03:00
|
|
|
$backend = $this->getMock('\Test\Util\User\Dummy');
|
2013-05-29 01:46:57 +04:00
|
|
|
$backend->expects($this->once())
|
|
|
|
->method('userExists')
|
|
|
|
->with('foo')
|
|
|
|
->will($this->returnValue(true));
|
|
|
|
|
|
|
|
$manager = new \OC\User\Manager();
|
|
|
|
$manager->registerBackend($backend);
|
|
|
|
|
|
|
|
$userSession = new \OC\User\Session($manager, $session);
|
|
|
|
$user = $userSession->getUser();
|
|
|
|
$this->assertEquals('foo', $user->getUID());
|
|
|
|
}
|
|
|
|
|
2014-12-16 21:07:14 +03:00
|
|
|
public function testIsLoggedIn() {
|
|
|
|
$session = $this->getMock('\OC\Session\Memory', array(), array(''));
|
|
|
|
$session->expects($this->once())
|
|
|
|
->method('get')
|
|
|
|
->with('user_id')
|
2014-12-16 22:01:49 +03:00
|
|
|
->will($this->returnValue('foo'));
|
2014-12-16 21:07:14 +03:00
|
|
|
|
2015-09-22 01:56:36 +03:00
|
|
|
$backend = $this->getMock('\Test\Util\User\Dummy');
|
2014-12-16 21:07:14 +03:00
|
|
|
$backend->expects($this->once())
|
|
|
|
->method('userExists')
|
|
|
|
->with('foo')
|
|
|
|
->will($this->returnValue(true));
|
|
|
|
|
|
|
|
$manager = new \OC\User\Manager();
|
|
|
|
$manager->registerBackend($backend);
|
|
|
|
|
|
|
|
$userSession = new \OC\User\Session($manager, $session);
|
|
|
|
$isLoggedIn = $userSession->isLoggedIn();
|
2014-12-16 22:01:49 +03:00
|
|
|
$this->assertTrue($isLoggedIn);
|
|
|
|
}
|
2014-12-16 21:07:14 +03:00
|
|
|
|
2014-12-16 22:01:49 +03:00
|
|
|
public function testNotLoggedIn() {
|
|
|
|
$session = $this->getMock('\OC\Session\Memory', array(), array(''));
|
2014-12-16 21:07:14 +03:00
|
|
|
$session->expects($this->once())
|
|
|
|
->method('get')
|
|
|
|
->with('user_id')
|
2014-12-16 22:01:49 +03:00
|
|
|
->will($this->returnValue(null));
|
|
|
|
|
2015-09-22 01:56:36 +03:00
|
|
|
$backend = $this->getMock('\Test\Util\User\Dummy');
|
2014-12-16 22:02:30 +03:00
|
|
|
$backend->expects($this->never())
|
|
|
|
->method('userExists');
|
2014-12-16 22:01:49 +03:00
|
|
|
|
|
|
|
$manager = new \OC\User\Manager();
|
|
|
|
$manager->registerBackend($backend);
|
|
|
|
|
|
|
|
$userSession = new \OC\User\Session($manager, $session);
|
2014-12-16 21:07:14 +03:00
|
|
|
$isLoggedIn = $userSession->isLoggedIn();
|
2014-12-16 22:01:49 +03:00
|
|
|
$this->assertFalse($isLoggedIn);
|
2014-12-16 21:07:14 +03:00
|
|
|
}
|
|
|
|
|
2013-05-29 01:46:57 +04:00
|
|
|
public function testSetUser() {
|
|
|
|
$session = $this->getMock('\OC\Session\Memory', array(), array(''));
|
|
|
|
$session->expects($this->once())
|
|
|
|
->method('set')
|
|
|
|
->with('user_id', 'foo');
|
|
|
|
|
|
|
|
$manager = $this->getMock('\OC\User\Manager');
|
|
|
|
|
2015-09-22 01:56:36 +03:00
|
|
|
$backend = $this->getMock('\Test\Util\User\Dummy');
|
2013-05-29 01:46:57 +04:00
|
|
|
|
|
|
|
$user = $this->getMock('\OC\User\User', array(), array('foo', $backend));
|
|
|
|
$user->expects($this->once())
|
|
|
|
->method('getUID')
|
|
|
|
->will($this->returnValue('foo'));
|
|
|
|
|
|
|
|
$userSession = new \OC\User\Session($manager, $session);
|
|
|
|
$userSession->setUser($user);
|
|
|
|
}
|
|
|
|
|
|
|
|
public function testLoginValidPasswordEnabled() {
|
|
|
|
$session = $this->getMock('\OC\Session\Memory', array(), array(''));
|
2016-01-04 17:00:58 +03:00
|
|
|
$session->expects($this->once())
|
|
|
|
->method('regenerateId');
|
2013-12-13 15:56:06 +04:00
|
|
|
$session->expects($this->exactly(2))
|
2013-05-29 01:46:57 +04:00
|
|
|
->method('set')
|
2014-10-13 18:31:26 +04:00
|
|
|
->with($this->callback(function ($key) {
|
|
|
|
switch ($key) {
|
|
|
|
case 'user_id':
|
|
|
|
case 'loginname':
|
|
|
|
return true;
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
return false;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
},
|
|
|
|
'foo'));
|
2013-05-29 01:46:57 +04:00
|
|
|
|
2014-05-23 02:59:26 +04:00
|
|
|
$managerMethods = get_class_methods('\OC\User\Manager');
|
|
|
|
//keep following methods intact in order to ensure hooks are
|
|
|
|
//working
|
|
|
|
$doNotMock = array('__construct', 'emit', 'listen');
|
2014-10-13 18:31:26 +04:00
|
|
|
foreach ($doNotMock as $methodName) {
|
2014-05-23 02:59:26 +04:00
|
|
|
$i = array_search($methodName, $managerMethods, true);
|
2014-10-13 18:31:26 +04:00
|
|
|
if ($i !== false) {
|
2014-05-23 02:59:26 +04:00
|
|
|
unset($managerMethods[$i]);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
$manager = $this->getMock('\OC\User\Manager', $managerMethods, array());
|
2013-05-29 01:46:57 +04:00
|
|
|
|
2015-09-22 01:56:36 +03:00
|
|
|
$backend = $this->getMock('\Test\Util\User\Dummy');
|
2013-05-29 01:46:57 +04:00
|
|
|
|
|
|
|
$user = $this->getMock('\OC\User\User', array(), array('foo', $backend));
|
|
|
|
$user->expects($this->once())
|
|
|
|
->method('isEnabled')
|
|
|
|
->will($this->returnValue(true));
|
|
|
|
$user->expects($this->any())
|
|
|
|
->method('getUID')
|
|
|
|
->will($this->returnValue('foo'));
|
2014-05-23 02:59:26 +04:00
|
|
|
$user->expects($this->once())
|
|
|
|
->method('updateLastLoginTimestamp');
|
2013-05-29 01:46:57 +04:00
|
|
|
|
|
|
|
$manager->expects($this->once())
|
2013-09-16 16:15:35 +04:00
|
|
|
->method('checkPassword')
|
|
|
|
->with('foo', 'bar')
|
2013-05-29 01:46:57 +04:00
|
|
|
->will($this->returnValue($user));
|
|
|
|
|
|
|
|
$userSession = new \OC\User\Session($manager, $session);
|
|
|
|
$userSession->login('foo', 'bar');
|
|
|
|
$this->assertEquals($user, $userSession->getUser());
|
|
|
|
}
|
|
|
|
|
|
|
|
public function testLoginValidPasswordDisabled() {
|
|
|
|
$session = $this->getMock('\OC\Session\Memory', array(), array(''));
|
|
|
|
$session->expects($this->never())
|
|
|
|
->method('set');
|
2016-01-04 17:00:58 +03:00
|
|
|
$session->expects($this->once())
|
|
|
|
->method('regenerateId');
|
2013-05-29 01:46:57 +04:00
|
|
|
|
2014-05-23 02:59:26 +04:00
|
|
|
$managerMethods = get_class_methods('\OC\User\Manager');
|
|
|
|
//keep following methods intact in order to ensure hooks are
|
|
|
|
//working
|
|
|
|
$doNotMock = array('__construct', 'emit', 'listen');
|
2014-10-13 18:31:26 +04:00
|
|
|
foreach ($doNotMock as $methodName) {
|
2014-05-23 02:59:26 +04:00
|
|
|
$i = array_search($methodName, $managerMethods, true);
|
2014-10-13 18:31:26 +04:00
|
|
|
if ($i !== false) {
|
2014-05-23 02:59:26 +04:00
|
|
|
unset($managerMethods[$i]);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
$manager = $this->getMock('\OC\User\Manager', $managerMethods, array());
|
2013-05-29 01:46:57 +04:00
|
|
|
|
2015-09-22 01:56:36 +03:00
|
|
|
$backend = $this->getMock('\Test\Util\User\Dummy');
|
2013-05-29 01:46:57 +04:00
|
|
|
|
|
|
|
$user = $this->getMock('\OC\User\User', array(), array('foo', $backend));
|
|
|
|
$user->expects($this->once())
|
|
|
|
->method('isEnabled')
|
|
|
|
->will($this->returnValue(false));
|
2014-05-23 02:59:26 +04:00
|
|
|
$user->expects($this->never())
|
|
|
|
->method('updateLastLoginTimestamp');
|
2013-05-29 01:46:57 +04:00
|
|
|
|
|
|
|
$manager->expects($this->once())
|
2013-09-16 16:15:35 +04:00
|
|
|
->method('checkPassword')
|
|
|
|
->with('foo', 'bar')
|
2013-05-29 01:46:57 +04:00
|
|
|
->will($this->returnValue($user));
|
|
|
|
|
|
|
|
$userSession = new \OC\User\Session($manager, $session);
|
|
|
|
$userSession->login('foo', 'bar');
|
|
|
|
}
|
|
|
|
|
2016-01-04 17:00:58 +03:00
|
|
|
public function testLoginInvalidPassword() {
|
2013-05-29 01:46:57 +04:00
|
|
|
$session = $this->getMock('\OC\Session\Memory', array(), array(''));
|
|
|
|
$session->expects($this->never())
|
|
|
|
->method('set');
|
2016-01-04 17:00:58 +03:00
|
|
|
$session->expects($this->once())
|
|
|
|
->method('regenerateId');
|
2013-05-29 01:46:57 +04:00
|
|
|
|
2014-05-23 02:59:26 +04:00
|
|
|
$managerMethods = get_class_methods('\OC\User\Manager');
|
|
|
|
//keep following methods intact in order to ensure hooks are
|
|
|
|
//working
|
|
|
|
$doNotMock = array('__construct', 'emit', 'listen');
|
2014-10-13 18:31:26 +04:00
|
|
|
foreach ($doNotMock as $methodName) {
|
2014-05-23 02:59:26 +04:00
|
|
|
$i = array_search($methodName, $managerMethods, true);
|
2014-10-13 18:31:26 +04:00
|
|
|
if ($i !== false) {
|
2014-05-23 02:59:26 +04:00
|
|
|
unset($managerMethods[$i]);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
$manager = $this->getMock('\OC\User\Manager', $managerMethods, array());
|
2013-05-29 01:46:57 +04:00
|
|
|
|
2015-09-22 01:56:36 +03:00
|
|
|
$backend = $this->getMock('\Test\Util\User\Dummy');
|
2013-05-29 01:46:57 +04:00
|
|
|
|
|
|
|
$user = $this->getMock('\OC\User\User', array(), array('foo', $backend));
|
|
|
|
$user->expects($this->never())
|
|
|
|
->method('isEnabled');
|
2014-05-23 02:59:26 +04:00
|
|
|
$user->expects($this->never())
|
|
|
|
->method('updateLastLoginTimestamp');
|
2013-05-29 01:46:57 +04:00
|
|
|
|
|
|
|
$manager->expects($this->once())
|
2013-09-16 16:15:35 +04:00
|
|
|
->method('checkPassword')
|
|
|
|
->with('foo', 'bar')
|
|
|
|
->will($this->returnValue(false));
|
2013-05-29 01:46:57 +04:00
|
|
|
|
|
|
|
$userSession = new \OC\User\Session($manager, $session);
|
|
|
|
$userSession->login('foo', 'bar');
|
|
|
|
}
|
|
|
|
|
|
|
|
public function testLoginNonExisting() {
|
|
|
|
$session = $this->getMock('\OC\Session\Memory', array(), array(''));
|
|
|
|
$session->expects($this->never())
|
|
|
|
->method('set');
|
2016-01-04 17:00:58 +03:00
|
|
|
$session->expects($this->once())
|
|
|
|
->method('regenerateId');
|
2013-05-29 01:46:57 +04:00
|
|
|
|
|
|
|
$manager = $this->getMock('\OC\User\Manager');
|
|
|
|
|
2015-09-22 01:56:36 +03:00
|
|
|
$backend = $this->getMock('\Test\Util\User\Dummy');
|
2013-05-29 01:46:57 +04:00
|
|
|
|
|
|
|
$manager->expects($this->once())
|
2013-09-16 16:15:35 +04:00
|
|
|
->method('checkPassword')
|
|
|
|
->with('foo', 'bar')
|
|
|
|
->will($this->returnValue(false));
|
2013-05-29 01:46:57 +04:00
|
|
|
|
|
|
|
$userSession = new \OC\User\Session($manager, $session);
|
|
|
|
$userSession->login('foo', 'bar');
|
|
|
|
}
|
2014-05-23 02:18:07 +04:00
|
|
|
|
|
|
|
public function testRememberLoginValidToken() {
|
|
|
|
$session = $this->getMock('\OC\Session\Memory', array(), array(''));
|
|
|
|
$session->expects($this->exactly(1))
|
|
|
|
->method('set')
|
2014-10-13 18:31:26 +04:00
|
|
|
->with($this->callback(function ($key) {
|
|
|
|
switch ($key) {
|
|
|
|
case 'user_id':
|
|
|
|
return true;
|
|
|
|
default:
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
},
|
|
|
|
'foo'));
|
2016-01-04 17:00:58 +03:00
|
|
|
$session->expects($this->once())
|
|
|
|
->method('regenerateId');
|
2014-05-23 02:18:07 +04:00
|
|
|
|
2014-05-23 02:54:17 +04:00
|
|
|
$managerMethods = get_class_methods('\OC\User\Manager');
|
|
|
|
//keep following methods intact in order to ensure hooks are
|
|
|
|
//working
|
|
|
|
$doNotMock = array('__construct', 'emit', 'listen');
|
2014-10-13 18:31:26 +04:00
|
|
|
foreach ($doNotMock as $methodName) {
|
2014-05-23 02:54:17 +04:00
|
|
|
$i = array_search($methodName, $managerMethods, true);
|
2014-10-13 18:31:26 +04:00
|
|
|
if ($i !== false) {
|
2014-05-23 02:54:17 +04:00
|
|
|
unset($managerMethods[$i]);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
$manager = $this->getMock('\OC\User\Manager', $managerMethods, array());
|
2014-05-23 02:18:07 +04:00
|
|
|
|
2015-09-22 01:56:36 +03:00
|
|
|
$backend = $this->getMock('\Test\Util\User\Dummy');
|
2014-05-23 02:18:07 +04:00
|
|
|
|
|
|
|
$user = $this->getMock('\OC\User\User', array(), array('foo', $backend));
|
|
|
|
|
|
|
|
$user->expects($this->any())
|
|
|
|
->method('getUID')
|
|
|
|
->will($this->returnValue('foo'));
|
2014-05-23 02:54:17 +04:00
|
|
|
$user->expects($this->once())
|
|
|
|
->method('updateLastLoginTimestamp');
|
2014-05-23 02:18:07 +04:00
|
|
|
|
|
|
|
$manager->expects($this->once())
|
|
|
|
->method('get')
|
|
|
|
->with('foo')
|
|
|
|
->will($this->returnValue($user));
|
|
|
|
|
|
|
|
//prepare login token
|
|
|
|
$token = 'goodToken';
|
2014-12-04 18:48:07 +03:00
|
|
|
\OC::$server->getConfig()->setUserValue('foo', 'login_token', $token, time());
|
2014-05-23 02:18:07 +04:00
|
|
|
|
|
|
|
$userSession = $this->getMock(
|
|
|
|
'\OC\User\Session',
|
|
|
|
//override, otherwise tests will fail because of setcookie()
|
|
|
|
array('setMagicInCookie'),
|
|
|
|
//there are passed as parameters to the constructor
|
|
|
|
array($manager, $session));
|
|
|
|
|
|
|
|
$granted = $userSession->loginWithCookie('foo', $token);
|
|
|
|
|
|
|
|
$this->assertSame($granted, true);
|
|
|
|
}
|
|
|
|
|
|
|
|
public function testRememberLoginInvalidToken() {
|
|
|
|
$session = $this->getMock('\OC\Session\Memory', array(), array(''));
|
|
|
|
$session->expects($this->never())
|
|
|
|
->method('set');
|
2016-01-04 17:00:58 +03:00
|
|
|
$session->expects($this->once())
|
|
|
|
->method('regenerateId');
|
2014-05-23 02:18:07 +04:00
|
|
|
|
2014-05-23 02:54:17 +04:00
|
|
|
$managerMethods = get_class_methods('\OC\User\Manager');
|
|
|
|
//keep following methods intact in order to ensure hooks are
|
|
|
|
//working
|
|
|
|
$doNotMock = array('__construct', 'emit', 'listen');
|
2014-10-13 18:31:26 +04:00
|
|
|
foreach ($doNotMock as $methodName) {
|
2014-05-23 02:54:17 +04:00
|
|
|
$i = array_search($methodName, $managerMethods, true);
|
2014-10-13 18:31:26 +04:00
|
|
|
if ($i !== false) {
|
2014-05-23 02:54:17 +04:00
|
|
|
unset($managerMethods[$i]);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
$manager = $this->getMock('\OC\User\Manager', $managerMethods, array());
|
2014-05-23 02:18:07 +04:00
|
|
|
|
2015-09-22 01:56:36 +03:00
|
|
|
$backend = $this->getMock('\Test\Util\User\Dummy');
|
2014-05-23 02:18:07 +04:00
|
|
|
|
|
|
|
$user = $this->getMock('\OC\User\User', array(), array('foo', $backend));
|
|
|
|
|
|
|
|
$user->expects($this->any())
|
|
|
|
->method('getUID')
|
|
|
|
->will($this->returnValue('foo'));
|
2014-05-23 02:54:17 +04:00
|
|
|
$user->expects($this->never())
|
|
|
|
->method('updateLastLoginTimestamp');
|
2014-05-23 02:18:07 +04:00
|
|
|
|
|
|
|
$manager->expects($this->once())
|
|
|
|
->method('get')
|
|
|
|
->with('foo')
|
|
|
|
->will($this->returnValue($user));
|
|
|
|
|
|
|
|
//prepare login token
|
|
|
|
$token = 'goodToken';
|
2014-12-04 18:48:07 +03:00
|
|
|
\OC::$server->getConfig()->setUserValue('foo', 'login_token', $token, time());
|
2014-05-23 02:18:07 +04:00
|
|
|
|
|
|
|
$userSession = new \OC\User\Session($manager, $session);
|
|
|
|
$granted = $userSession->loginWithCookie('foo', 'badToken');
|
|
|
|
|
|
|
|
$this->assertSame($granted, false);
|
|
|
|
}
|
|
|
|
|
|
|
|
public function testRememberLoginInvalidUser() {
|
|
|
|
$session = $this->getMock('\OC\Session\Memory', array(), array(''));
|
|
|
|
$session->expects($this->never())
|
|
|
|
->method('set');
|
2016-01-04 17:00:58 +03:00
|
|
|
$session->expects($this->once())
|
|
|
|
->method('regenerateId');
|
2014-05-23 02:18:07 +04:00
|
|
|
|
2014-05-23 02:54:17 +04:00
|
|
|
$managerMethods = get_class_methods('\OC\User\Manager');
|
|
|
|
//keep following methods intact in order to ensure hooks are
|
|
|
|
//working
|
|
|
|
$doNotMock = array('__construct', 'emit', 'listen');
|
2014-10-13 18:31:26 +04:00
|
|
|
foreach ($doNotMock as $methodName) {
|
2014-05-23 02:54:17 +04:00
|
|
|
$i = array_search($methodName, $managerMethods, true);
|
2014-10-13 18:31:26 +04:00
|
|
|
if ($i !== false) {
|
2014-05-23 02:54:17 +04:00
|
|
|
unset($managerMethods[$i]);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
$manager = $this->getMock('\OC\User\Manager', $managerMethods, array());
|
2014-05-23 02:18:07 +04:00
|
|
|
|
2015-09-22 01:56:36 +03:00
|
|
|
$backend = $this->getMock('\Test\Util\User\Dummy');
|
2014-05-23 02:18:07 +04:00
|
|
|
|
|
|
|
$user = $this->getMock('\OC\User\User', array(), array('foo', $backend));
|
|
|
|
|
|
|
|
$user->expects($this->never())
|
|
|
|
->method('getUID');
|
2014-05-23 02:54:17 +04:00
|
|
|
$user->expects($this->never())
|
|
|
|
->method('updateLastLoginTimestamp');
|
2014-05-23 02:18:07 +04:00
|
|
|
|
|
|
|
$manager->expects($this->once())
|
|
|
|
->method('get')
|
|
|
|
->with('foo')
|
|
|
|
->will($this->returnValue(null));
|
|
|
|
|
|
|
|
//prepare login token
|
|
|
|
$token = 'goodToken';
|
2014-12-04 18:48:07 +03:00
|
|
|
\OC::$server->getConfig()->setUserValue('foo', 'login_token', $token, time());
|
2014-05-23 02:18:07 +04:00
|
|
|
|
|
|
|
$userSession = new \OC\User\Session($manager, $session);
|
|
|
|
$granted = $userSession->loginWithCookie('foo', $token);
|
|
|
|
|
|
|
|
$this->assertSame($granted, false);
|
|
|
|
}
|
2014-10-13 18:31:26 +04:00
|
|
|
|
|
|
|
public function testActiveUserAfterSetSession() {
|
|
|
|
$users = array(
|
|
|
|
'foo' => new User('foo', null),
|
|
|
|
'bar' => new User('bar', null)
|
|
|
|
);
|
|
|
|
|
|
|
|
$manager = $this->getMockBuilder('\OC\User\Manager')
|
|
|
|
->disableOriginalConstructor()
|
|
|
|
->getMock();
|
|
|
|
|
|
|
|
$manager->expects($this->any())
|
|
|
|
->method('get')
|
|
|
|
->will($this->returnCallback(function ($uid) use ($users) {
|
|
|
|
return $users[$uid];
|
|
|
|
}));
|
|
|
|
|
|
|
|
$session = new Memory('');
|
|
|
|
$session->set('user_id', 'foo');
|
|
|
|
$userSession = new \OC\User\Session($manager, $session);
|
|
|
|
$this->assertEquals($users['foo'], $userSession->getUser());
|
|
|
|
|
|
|
|
$session2 = new Memory('');
|
|
|
|
$session2->set('user_id', 'bar');
|
|
|
|
$userSession->setSession($session2);
|
|
|
|
$this->assertEquals($users['bar'], $userSession->getUser());
|
|
|
|
}
|
2013-05-29 01:46:57 +04:00
|
|
|
}
|