new model for user

This commit is contained in:
Visman 2017-02-16 20:26:15 +07:00
parent 52f0b53e5c
commit a3426e3380
25 changed files with 795 additions and 471 deletions

View file

@ -32,11 +32,12 @@ class Routing
$r = $this->c->get('Router');
// регистрация/вход/выход
if ($user['is_guest']) {
if ($user->isGuest) {
// вход
$r->add('GET', '/login', 'Auth:login', 'Login');
$r->add('POST', '/login', 'Auth:loginPost');
$r->add('GET', '/login/forget', 'Auth:forget', 'Forget');
$r->add('POST', '/login/forget', 'Auth:forgetPost');
// регистрация
if ($config['o_regs_allow'] == '1') {
$r->add('GET', '/registration', 'Registration:reg', 'Registration'); //????
@ -46,19 +47,19 @@ class Routing
$r->add('GET', '/logout/{token}', 'Auth:logout', 'Logout');
}
// просмотр разрешен
if ($user['g_read_board'] == '1') {
if ($user->gReadBoard == '1') {
// главная
$r->add('GET', '/', 'Index:view', 'Index');
// правила
if ($config['o_rules'] == '1' && (! $user['is_guest'] || $config['o_regs_allow'] == '1')) {
if ($config['o_rules'] == '1' && (! $user->isGuest || $config['o_regs_allow'] == '1')) {
$r->add('GET', '/rules', 'Rules:view', 'Rules');
}
// поиск
if ($user['g_search'] == '1') {
if ($user->gSearch == '1') {
$r->add('GET', '/search', 'Search:view', 'Search');
}
// юзеры
if ($user['g_view_users'] == '1') {
if ($user->gViewUsers == '1') {
// список пользователей
$r->add('GET', '/userlist[/page/{page}]', 'Userlist:view', 'Userlist');
// юзеры
@ -72,12 +73,12 @@ class Routing
}
// админ и модератор
if ($user['is_admmod']) {
if ($user->isAdmMod) {
$r->add('GET', '/admin/', 'AdminIndex:index', 'Admin');
$r->add('GET', '/admin/statistics', 'AdminStatistics:statistics', 'AdminStatistics');
}
// только админ
if ($user['g_id'] == PUN_ADMIN) {
if ($user->isAdmin) {
$r->add('GET', '/admin/statistics/info', 'AdminStatistics:info', 'AdminInfo');
}
@ -97,7 +98,7 @@ class Routing
break;
case $r::NOT_FOUND:
// ... 404 Not Found
if ($user['g_read_board'] != '1' && $user['is_guest']) {
if ($user->gReadBoard != '1' && $user->isGuest) {
$page = $this->c->get('Redirect')->setPage('Login');
} else {
// $page = $this->c->get('Message')->message('Bad request');

120
app/Core/AbstractModel.php Normal file
View file

@ -0,0 +1,120 @@
<?php
namespace ForkBB\Core;
abstract class AbstractModel
{
/**
* Данные модели
* @var array
*/
protected $data;
abstract protected function beforeConstruct(array $data);
/**
* Конструктор
* @param array $data
*/
public function __construct(array $data = [])
{
$data = $this->beforeConstruct($data);
foreach ($data as $key => $val) {
if (is_string($key)) {
$this->data[$this->camelCase($key)] = $val;
}
}
}
/**
* Проверяет наличие свойства
* @param mixed $key
* @return bool
*/
public function __isset($key)
{
return is_string($key) && isset($this->data[$key]);
}
/**
* Удаляет свойство
* @param mixed $key
*/
public function __unset($key)
{
if (is_string($key)) {
unset($this->data[$key]);
}
}
/**
* Устанавливает значение для свойства
* При $key = __attrs ожидаем массив в $val
* @param mixed $key
* @param mixed $vak
*/
public function __set($key, $val)
{
if ('__attrs' === $key) {
if (is_array($val)) {
foreach ($val as $x => $y) {
$x = $this->camelCase($x);
$this->$x = $y;
}
}
} elseif (is_string($key)) {
$method = 'set' . ucfirst($key);
if (method_exists($this, $method)) {
$this->$method($val);
} else {
$this->data[$key] = $val;
}
}
}
/**
* Возвращает значение свойства
* При $key = __attrs возвращает все свойства
* @param mixed $key
* @return mixed
*/
public function __get($key)
{
if ('__attrs' === $key) {
$data = [];
foreach ($this->data as $x => $y) {
$data[$this->underScore($x)] = $y; //????
}
return $data;
} elseif (is_string($key)) {
$method = 'get' . ucfirst($key);
if (method_exists($this, $method)) {
return $this->$method();
} else {
return isset($this->data[$key]) ? $this->data[$key] : null;
}
}
}
/**
* Преобразует строку в camelCase
* @param string $str
* @return string
*/
protected function camelCase($str)
{
return lcfirst(str_replace('_', '', ucwords($str, '_')));
}
/**
* Преобразует строку в under_score
* @param string $str
* @return string
*/
protected function underScore($str)
{
return preg_replace('%([A-Z])%', function($match) {
return '_' . strtolower($match[1]);
}, $str);
}
}

View file

@ -2,6 +2,8 @@
namespace ForkBB\Core;
use ForkBB\Core\Secury;
class Cookie
{
/**
@ -39,7 +41,7 @@ class Cookie
* @param Secury $secury
* @param array $options
*/
public function __construct($secury, array $options)
public function __construct(Secury $secury, array $options)
{
$this->secury = $secury;

View file

@ -71,8 +71,8 @@ class Lang
} elseif (isset($this->loaded[$name])) {
return;
}
$lang = $lang ?: $this->c->get('user')['language'];
$path = $path ?: $this->c->getParameter('DIR_TRANSL');
$lang = $lang ?: $this->c->get('user')->language;
$path = $path ?: $this->c->getParameter('DIR_LANG');
do {
$flag = true;
$fullPath = $path . '/'. $lang . '/' . $name . '.po';

18
app/Core/Mail.php Normal file
View file

@ -0,0 +1,18 @@
<?php
namespace ForkBB\Core;
class Mail
{
/**
* Валидация email
* @param mixed $email
* @return bool
*/
public function valid($email)
{
return is_string($email)
&& strlen($email) < 255
&& preg_match('%^.+@.+$%D', $email);
}
}

View file

@ -132,7 +132,7 @@ class CacheGenerator
*/
public function forums(User $user)
{
$groupId = $user['g_id'];
$groupId = $user->gId;
$result = $this->db->query('SELECT g_read_board FROM '.$this->db->prefix.'groups WHERE g_id='.$groupId) or error('Unable to fetch user group read permission', __FILE__, __LINE__, $this->db->error());
$read = $this->db->result($result);

View file

@ -61,7 +61,7 @@ class CacheLoader
public function loadForums()
{
$mark = $this->cache->get('forums_mark');
$key = 'forums_' . $this->c->get('user')['g_id'];
$key = 'forums_' . $this->c->get('user')->gId;
if (empty($mark)) {
$this->cache->set('forums_mark', time());

View file

@ -2,7 +2,6 @@
namespace ForkBB\Models\Actions;
use ForkBB\Models\User;
use R2\DependencyInjection\ContainerInterface;
class CheckBans
@ -13,6 +12,10 @@ class CheckBans
*/
protected $c;
/**
* Конструктор
* @param ContainerInterface $container
*/
public function __construct(ContainerInterface $container)
{
$this->c = $container;
@ -20,26 +23,25 @@ class CheckBans
/**
* Возвращает массив с описанием бана или null
* @param User $user
*
* @return null|array
*/
public function check(User $user) //????
public function check()
{
$bans = $this->c->get('bans');
$user = $this->c->get('user');
// Для админов и при отсутствии банов прекращаем проверку
if ($user['g_id'] == PUN_ADMIN || empty($bans)) {
if ($user->isAdmin || empty($bans)) {
return null;
}
// Add a dot or a colon (depending on IPv4/IPv6) at the end of the IP address to prevent banned address
// 192.168.0.5 from matching e.g. 192.168.0.50
$user_ip = get_remote_address();
$add = strpos($user_ip, '.') !== false ? '.' : ':';
$user_ip .= $add;
$userIp = $user->ip;
$add = strpos($userIp, '.') !== false ? '.' : ':';
$userIp .= $add;
$username = mb_strtolower($user['username']);
$username = mb_strtolower($user->username);
$banned = false;
$remove = [];
@ -55,11 +57,11 @@ class CheckBans
continue;
}
if (! $user['is_guest']) {
if (! $user->isGuest) {
if ($cur['username'] != '' && $username == mb_strtolower($cur['username'])) {
$banned = $cur;
continue;
} elseif ($cur['email'] != '' && $user['email'] == $cur['email']) {
} elseif ($cur['email'] != '' && $user->email == $cur['email']) {
$banned = $cur;
continue;
}
@ -70,7 +72,7 @@ class CheckBans
$ips = explode(' ', $cur['ip']);
foreach ($ips as $ip) {
$ip .= $add;
if (substr($user_ip, 0, strlen($ip)) == $ip) {
if (substr($userIp, 0, strlen($ip)) == $ip) {
$banned = $cur;
break;
}

View file

@ -0,0 +1,215 @@
<?php
namespace ForkBB\Models\Actions;
use ForkBB\Models\UserCookie;
use ForkBB\Models\UserMapper;
use RuntimeException;
class LoadUserFromCookie
{
protected $mapper;
protected $cookie;
protected $config;
/**
* Конструктор
*
* @param UserMapper $mapper
* @param UserCookie $cookie
* @param array $config
*/
public function __construct(UserMapper $mapper, UserCookie $cookie, array $config)
{
$this->mapper = $mapper;
$this->cookie = $cookie;
$this->config = $config;
}
/**
* Получение юзера на основе куки авторизации
* Обновление куки аутентификации
*
* @return User
*/
public function load()
{
$id = $this->cookie->id() ?: 1;
$user = $this->mapper->getCurrent($id);
if (! $user->isGuest) {
if (! $this->cookie->verifyHash($user->id, $user->password)) {
$user = $this->mapper->getCurrent(1);
} elseif ($this->config['o_check_ip'] == '1'
&& $user->isAdmMod
&& $user->registrationIp != $user->ip
) {
$user = $this->mapper->getCurrent(1);
}
}
$this->cookie->setUserCookie($user->id, $user->password);
if ($user->isGuest) {
$user->isBot = $this->isBot();
$user->dispTopics = $this->config['o_disp_topics_default'];
$user->dispPosts = $this->config['o_disp_posts_default'];
$user->timezone = $this->config['o_default_timezone'];
$user->dst = $this->config['o_default_dst'];
$user->language = $this->config['o_default_lang'];
$user->style = $this->config['o_default_style'];
// быстрое переключение языка - Visman
$language = $this->cookie->get('glang');
if (null !== $language) {
$language = preg_replace('%[^\w]%', '', $language);
$languages = forum_list_langs();
if (in_array($language, $languages)) {
$user->language = $language;
}
}
} else {
$user->isBot = false;
if (! $user->dispTopics) {
$user->dispTopics = $this->config['o_disp_topics_default'];
}
if (! $user->dispPosts) {
$user->dispPosts = $this->config['o_disp_posts_default'];
}
// Special case: We've timed out, but no other user has browsed the forums since we timed out
if ($user->isLogged && $user->logged < time() - $this->config['o_timeout_visit']) {
$this->mapper->updateLastVisit($user->id, $user->logged);
$user->lastVisit = $user->logged;
}
}
return $user;
}
/**
* Проверка на робота
* Если робот, то возврат имени
* @return false|string
*/
protected function isBot()
{
$agent = trim($_SERVER['HTTP_USER_AGENT']);
if ($agent == '') {
return false;
}
$agentL = strtolower($agent);
if (strpos($agentL, 'bot') !== false
|| strpos($agentL, 'spider') !== false
|| strpos($agentL, 'crawler') !== false
|| strpos($agentL, 'http') !== false
) {
return $this->nameBot($agent, $agentL);
}
if (strpos($agent, 'Mozilla/') !== false
&& (strpos($agent, 'Gecko') !== false
|| (strpos($agent, '(compatible; MSIE ') !== false
&& strpos($agent, 'Windows') !== false
)
)
) {
return false;
} elseif (strpos($agent, 'Opera/') !== false
&& strpos($agent, 'Presto/') !== false
) {
return false;
}
return $this->nameBot($agent, $agentL);
}
/**
* Выделяет имя робота из юзерагента
* @param string $agent
* @param string $agentL
* @retrun string
*/
protected function nameBot($agent, $agentL)
{
if (strpos($agentL, 'mozilla') !== false) {
$agent = preg_replace('%Mozilla.*?compatible%i', ' ', $agent);
}
if (strpos($agentL, 'http') !== false || strpos($agentL, 'www.') !== false) {
$agent = preg_replace('%(?:https?://|www\.)[^\)]*(\)[^/]+$)?%i', ' ', $agent);
}
if (strpos($agent, '@') !== false) {
$agent = preg_replace('%\b[\w\.-]+@[^\)]+%', ' ', $agent);
}
$agentL = strtolower($agent);
if (strpos($agentL, 'bot') !== false
|| strpos($agentL, 'spider') !== false
|| strpos($agentL, 'crawler') !== false
|| strpos($agentL, 'engine') !== false
) {
$f = true;
$p = '%(?<=[^a-z\d\.-])(?:robot|bot|spider|crawler)\b.*%i';
} else {
$f = false;
$p = '%^$%';
}
if ($f && preg_match('%\b(([a-z\d\.! _-]+)?(?:robot|(?<!ro)bot|spider|crawler|engine)(?(2)[a-z\d\.! _-]*|[a-z\d\.! _-]+))%i', $agent, $matches))
{
$agent = $matches[1];
$pat = [
$p,
'%[^a-z\d\.!-]+%i',
'%(?<=^|\s|-)v?\d+\.\d[^\s]*\s*%i',
'%(?<=^|\s)\S{1,2}(?:\s|$)%',
];
$rep = [
'',
' ',
'',
'',
];
} else {
$pat = [
'%\((?:KHTML|Linux|Mac|Windows|X11)[^\)]*\)?%i',
$p,
'%\b(?:AppleWebKit|Chrom|compatible|Firefox|Gecko|Mobile(?=[/ ])|Moz|Opera|OPR|Presto|Safari|Version)[^\s]*%i',
'%\b(?:InfoP|Intel|Linux|Mac|MRA|MRS|MSIE|SV|Trident|Win|WOW|X11)[^;\)]*%i',
'%\.NET[^;\)]*%i',
'%/.*%',
'%[^a-z\d\.!-]+%i',
'%(?<=^|\s|-)v?\d+\.\d[^\s]*\s*%i',
'%(?<=^|\s)\S{1,2}(?:\s|$)%',
];
$rep = [
' ',
'',
'',
'',
'',
'',
' ',
'',
'',
];
}
$agent = trim(preg_replace($pat, $rep, $agent), ' -');
if (empty($agent)) {
return 'Unknown';
}
$a = explode(' ', $agent);
$agent = $a[0];
if (strlen($agent) < 20
&& ! empty($a[1])
&& strlen($agent . ' ' . $a[1]) < 26
) {
$agent .= ' ' . $a[1];
} elseif (strlen($agent) > 25) {
$agent = 'Unknown';
}
return $agent;
}
}

View file

@ -26,7 +26,7 @@ class Csrf
public function __construct(Secury $secury, User $user)
{
$this->secury = $secury;
$this->key = sha1($user['password'] . $user['ip'] . $user['id']);
$this->key = sha1($user->password . $user->ip . $user->id);
}
/**
@ -46,14 +46,15 @@ class Csrf
/**
* Проверка токена
* @param string $token
* @param mixed $token
* @param string $marker
* @param array $args
* @return bool
*/
public function check($token, $marker, array $args = [])
{
return preg_match('%f(\d+)$%D', $token, $matches)
return is_string($token)
&& preg_match('%f(\d+)$%D', $token, $matches)
&& $matches[1] < time()
&& $matches[1] + 1800 > time()
&& hash_equals($this->create($marker, $args, $matches[1]), $token);

View file

@ -181,11 +181,13 @@ for ($i=0;$i<100;++$i) {
{
$now = time();
// гость
if ($this->user['is_guest']) {
$oname = (string) $this->user['is_bot'];
if ($this->user->isGuest) {
$oname = (string) $this->user->isBot;
if ($this->user['is_logged']) {
$this->db->query('INSERT INTO '.$this->db->prefix.'online (user_id, ident, logged, o_position, o_name) SELECT 1, \''.$this->db->escape($this->user['ip']).'\', '.$now.', \''.$this->db->escape($position).'\', \''.$this->db->escape($oname).'\' FROM '.$this->db->prefix.'groups WHERE NOT EXISTS (SELECT 1 FROM '.$this->db->prefix.'online WHERE user_id=1 AND ident=\''.$this->db->escape($this->user['ip']).'\') LIMIT 1') or error('Unable to insert into online list', __FILE__, __LINE__, $this->db->error());
if ($this->user->isLogged) {
$this->db->query('UPDATE '.$this->db->prefix.'online SET logged='.$now.', o_position=\''.$this->db->escape($position).'\', o_name=\''.$this->db->escape($oname).'\' WHERE user_id=1 AND ident=\''.$this->db->escape($this->user->ip).'\'') or error('Unable to update online list', __FILE__, __LINE__, $this->db->error());
} else {
$this->db->query('INSERT INTO '.$this->db->prefix.'online (user_id, ident, logged, o_position, o_name) SELECT 1, \''.$this->db->escape($this->user->ip).'\', '.$now.', \''.$this->db->escape($position).'\', \''.$this->db->escape($oname).'\' FROM '.$this->db->prefix.'groups WHERE NOT EXISTS (SELECT 1 FROM '.$this->db->prefix.'online WHERE user_id=1 AND ident=\''.$this->db->escape($this->user->ip).'\') LIMIT 1') or error('Unable to insert into online list', __FILE__, __LINE__, $this->db->error());
// With MySQL/MySQLi/SQLite, REPLACE INTO avoids a user having two rows in the online table
/* switch ($this->c->getParameter('DB_TYPE')) {
@ -194,21 +196,22 @@ for ($i=0;$i<100;++$i) {
case 'mysql_innodb':
case 'mysqli_innodb':
case 'sqlite':
$this->db->query('REPLACE INTO '.$this->db->prefix.'online (user_id, ident, logged, o_position, o_name) VALUES(1, \''.$this->db->escape($this->user['ip']).'\', '.$now.', \''.$this->db->escape($position).'\', \''.$this->db->escape($oname).'\')') or error('Unable to insert into online list', __FILE__, __LINE__, $this->db->error());
$this->db->query('REPLACE INTO '.$this->db->prefix.'online (user_id, ident, logged, o_position, o_name) VALUES(1, \''.$this->db->escape($this->user->ip).'\', '.$now.', \''.$this->db->escape($position).'\', \''.$this->db->escape($oname).'\')') or error('Unable to insert into online list', __FILE__, __LINE__, $this->db->error());
break;
default:
$this->db->query('INSERT INTO '.$this->db->prefix.'online (user_id, ident, logged, o_position, o_name) SELECT 1, \''.$this->db->escape($this->user['ip']).'\', '.$now.', \''.$this->db->escape($position).'\', \''.$this->db->escape($oname).'\' WHERE NOT EXISTS (SELECT 1 FROM '.$this->db->prefix.'online WHERE user_id=1 AND ident=\''.$this->db->escape($this->user['ip']).'\')') or error('Unable to insert into online list', __FILE__, __LINE__, $this->db->error());
$this->db->query('INSERT INTO '.$this->db->prefix.'online (user_id, ident, logged, o_position, o_name) SELECT 1, \''.$this->db->escape($this->user->ip).'\', '.$now.', \''.$this->db->escape($position).'\', \''.$this->db->escape($oname).'\' WHERE NOT EXISTS (SELECT 1 FROM '.$this->db->prefix.'online WHERE user_id=1 AND ident=\''.$this->db->escape($this->user->ip).'\')') or error('Unable to insert into online list', __FILE__, __LINE__, $this->db->error());
break;
}
*/
} else {
$this->db->query('UPDATE '.$this->db->prefix.'online SET logged='.$now.', o_position=\''.$this->db->escape($position).'\', o_name=\''.$this->db->escape($oname).'\' WHERE user_id=1 AND ident=\''.$this->db->escape($this->user['ip']).'\'') or error('Unable to update online list', __FILE__, __LINE__, $this->db->error());
}
} else {
// пользователь
if ($this->user['is_logged']) {
$this->db->query('INSERT INTO '.$this->db->prefix.'online (user_id, ident, logged, o_position) SELECT '.$this->user['id'].', \''.$this->db->escape($this->user['username']).'\', '.$now.', \''.$this->db->escape($position).'\' FROM '.$this->db->prefix.'groups WHERE NOT EXISTS (SELECT 1 FROM '.$this->db->prefix.'online WHERE user_id='.$this->user['id'].') LIMIT 1') or error('Unable to insert into online list', __FILE__, __LINE__, $this->db->error());
if ($this->user->isLogged) {
$idle_sql = ($this->user->idle == '1') ? ', idle=0' : '';
$this->db->query('UPDATE '.$this->db->prefix.'online SET logged='.$now.$idle_sql.', o_position=\''.$this->db->escape($position).'\' WHERE user_id='.$this->user->id) or error('Unable to update online list', __FILE__, __LINE__, $this->db->error());
} else {
$this->db->query('INSERT INTO '.$this->db->prefix.'online (user_id, ident, logged, o_position) SELECT '.$this->user->id.', \''.$this->db->escape($this->user->username).'\', '.$now.', \''.$this->db->escape($position).'\' FROM '.$this->db->prefix.'groups WHERE NOT EXISTS (SELECT 1 FROM '.$this->db->prefix.'online WHERE user_id='.$this->user->id.') LIMIT 1') or error('Unable to insert into online list', __FILE__, __LINE__, $this->db->error());
// With MySQL/MySQLi/SQLite, REPLACE INTO avoids a user having two rows in the online table
/* switch ($this->c->getParameter('DB_TYPE')) {
case 'mysql':
@ -216,17 +219,14 @@ for ($i=0;$i<100;++$i) {
case 'mysql_innodb':
case 'mysqli_innodb':
case 'sqlite':
$this->db->query('REPLACE INTO '.$this->db->prefix.'online (user_id, ident, logged, o_position) VALUES('.$this->user['id'].', \''.$this->db->escape($this->user['username']).'\', '.$now.', \''.$this->db->escape($position).'\')') or error('Unable to insert into online list', __FILE__, __LINE__, $this->db->error());
$this->db->query('REPLACE INTO '.$this->db->prefix.'online (user_id, ident, logged, o_position) VALUES('.$this->user->id.', \''.$this->db->escape($this->user->username).'\', '.$now.', \''.$this->db->escape($position).'\')') or error('Unable to insert into online list', __FILE__, __LINE__, $this->db->error());
break;
default:
$this->db->query('INSERT INTO '.$this->db->prefix.'online (user_id, ident, logged, o_position) SELECT '.$this->user['id'].', \''.$this->db->escape($this->user['username']).'\', '.$now.', \''.$this->db->escape($position).'\' WHERE NOT EXISTS (SELECT 1 FROM '.$this->db->prefix.'online WHERE user_id='.$this->user['id'].')') or error('Unable to insert into online list', __FILE__, __LINE__, $this->db->error());
$this->db->query('INSERT INTO '.$this->db->prefix.'online (user_id, ident, logged, o_position) SELECT '.$this->user->id.', \''.$this->db->escape($this->user->username).'\', '.$now.', \''.$this->db->escape($position).'\' WHERE NOT EXISTS (SELECT 1 FROM '.$this->db->prefix.'online WHERE user_id='.$this->user->id.')') or error('Unable to insert into online list', __FILE__, __LINE__, $this->db->error());
break;
}
*/
} else {
$idle_sql = ($this->user['idle'] == '1') ? ', idle=0' : '';
$this->db->query('UPDATE '.$this->db->prefix.'online SET logged='.$now.$idle_sql.', o_position=\''.$this->db->escape($position).'\' WHERE user_id='.$this->user['id']) or error('Unable to update online list', __FILE__, __LINE__, $this->db->error());
}
}
}
@ -236,10 +236,10 @@ for ($i=0;$i<100;++$i) {
*/
public function delete(User $user)
{
if ($user['is_guest']) {
$this->db->query('DELETE FROM '.$this->db->prefix.'online WHERE user_id=1 AND ident=\''.$this->db->escape($user['ip']).'\'') or error('Unable to delete from online list', __FILE__, __LINE__, $this->db->error());
if ($user->isGuest) {
$this->db->query('DELETE FROM '.$this->db->prefix.'online WHERE user_id=1 AND ident=\''.$this->db->escape($user->ip).'\'') or error('Unable to delete from online list', __FILE__, __LINE__, $this->db->error());
} else {
$this->db->query('DELETE FROM '.$this->db->prefix.'online WHERE user_id='.$user['id']) or error('Unable to delete from online list', __FILE__, __LINE__, $this->db->error());
$this->db->query('DELETE FROM '.$this->db->prefix.'online WHERE user_id='.$user->id) or error('Unable to delete from online list', __FILE__, __LINE__, $this->db->error());
}
}
}

View file

@ -57,7 +57,6 @@ abstract class Admin extends Page
protected function aNavigation()
{
$user = $this->c->get('user');
$is_admin = $user['g_id'] == PUN_ADMIN;
$r = $this->c->get('Router');
$nav = [
@ -66,14 +65,14 @@ abstract class Admin extends Page
'users' => ['admin_users.php', __('Users')],
],
];
if ($is_admin || $user['g_mod_ban_users'] == '1') {
if ($user->isAdmin || $user->gModBanUsers == '1') {
$nav['Moderator menu']['bans'] = ['admin_bans.php', __('Bans')];
}
if ($is_admin || $this->config['o_report_method'] == '0' || $this->config['o_report_method'] == '2') {
if ($user->isAdmin || $this->config['o_report_method'] == '0' || $this->config['o_report_method'] == '2') {
$nav['Moderator menu']['reports'] = ['admin_reports.php', __('Reports')];
}
if ($is_admin) {
if ($user->isAdmin) {
$nav['Admin menu'] = [
'options' => ['admin_options.php', __('Admin options')],
'permissions' => ['admin_permissions.php', __('Permissions')],

View file

@ -10,7 +10,7 @@ class Auth extends Page
* Имя шаблона
* @var string
*/
protected $nameTpl = 'auth';
protected $nameTpl = 'login';
/**
* Позиция для таблицы онлайн текущего пользователя
@ -100,7 +100,7 @@ class Auth extends Page
'_save' => $save,
];
if (empty($token) || ! $this->c->get('Csrf')->check($token, 'Login')) {
if (! $this->c->get('Csrf')->check($token, 'Login')) {
$this->iswev['e'][] = __('Bad token');
return $this->login($args);
}
@ -119,4 +119,62 @@ class Auth extends Page
return $this->c->get('Redirect')->setUrl($redirect)->setMessage(__('Login redirect'));
}
/**
* Подготовка данных для страницы восстановления пароля
* @param array $args
* @return Page
*/
public function forget($args)
{
$this->c->get('Lang')->load('login');
$this->nameTpl = 'forget';
$this->onlinePos = 'forget';
if (! isset($args['_email'])) {
$args['_email'] = '';
}
$this->titles = [
__('Request pass'),
];
$this->data = [
'email' => $args['_email'],
'formAction' => $this->c->get('Router')->link('Forget'),
'formToken' => $this->c->get('Csrf')->create('Forget'),
];
return $this;
}
/**
* Отправка письма для восстановления пароля
* @return Page
*/
public function forgetPost()
{
$this->c->get('Lang')->load('login');
$token = $this->c->get('Request')->postStr('token');
$email = $this->c->get('Request')->postStr('email');
$args = [
'_email' => $email,
];
if (! $this->c->get('Csrf')->check($token, 'Forget')) {
$this->iswev['e'][] = __('Bad token');
return $this->forget($args);
}
$mail = $this->c->get('Mail');
if (! $mail->valid($email)) {
$this->iswev['v'][] = __('Invalid email');
return $this->forget($args);
}
}
}

View file

@ -50,7 +50,7 @@ class Index extends Page
$stats['total_users'] = $this->number($stats['total_users']);
if ($user['g_view_users'] == '1') {
if ($user->gViewUsers == '1') {
$stats['newest_user'] = [
$r->link('User', [
'id' => $stats['last_user']['id'],
@ -73,7 +73,7 @@ class Index extends Page
list($users, $guests, $bots) = $this->c->get('Online')->handle($this);
$list = [];
if ($user['g_view_users'] == '1') {
if ($user->gViewUsers == '1') {
foreach ($users as $id => $cur) {
$list[] = [
$r->link('User', [
@ -131,10 +131,10 @@ class Index extends Page
// текущие данные по подразделам
$forums = array_slice($fAsc[$root], 1);
if ($user['is_guest']) {
if ($user->isGuest) {
$result = $db->query('SELECT id, forum_desc, moderators, num_topics, num_posts, last_post, last_post_id, last_poster, last_topic FROM '.$db->prefix.'forums WHERE id IN ('.implode(',', $forums).')', true) or error('Unable to fetch forum list', __FILE__, __LINE__, $db->error());
} else {
$result = $db->query('SELECT f.id, f.forum_desc, f.moderators, f.num_topics, f.num_posts, f.last_post, f.last_post_id, f.last_poster, f.last_topic, mof.mf_upper FROM '.$db->prefix.'forums AS f LEFT JOIN '.$db->prefix.'mark_of_forum AS mof ON (mof.uid='.$user['id'].' AND f.id=mof.fid) WHERE f.id IN ('.implode(',', $forums).')', true) or error('Unable to fetch forum list', __FILE__, __LINE__, $db->error());
$result = $db->query('SELECT f.id, f.forum_desc, f.moderators, f.num_topics, f.num_posts, f.last_post, f.last_post_id, f.last_poster, f.last_topic, mof.mf_upper FROM '.$db->prefix.'forums AS f LEFT JOIN '.$db->prefix.'mark_of_forum AS mof ON (mof.uid='.$user->id.' AND f.id=mof.fid) WHERE f.id IN ('.implode(',', $forums).')', true) or error('Unable to fetch forum list', __FILE__, __LINE__, $db->error());
}
$forums = [];
@ -145,9 +145,9 @@ class Index extends Page
// поиск новых
$new = [];
if (! $user['is_guest']) {
if (! $user->isGuest) {
// предварительная проверка разделов
$max = max((int) $user['last_visit'], (int) $user['u_mark_all_read']);
$max = max((int) $user->lastVisit, (int) $user->uMarkAllRead);
foreach ($forums as $id => $cur) {
$t = max($max, (int) $cur['mf_upper']);
if ($cur['last_post'] > $t) {
@ -156,7 +156,7 @@ class Index extends Page
}
// проверка по темам
if (! empty($new)) {
$result = $db->query('SELECT t.forum_id, t.id, t.last_post FROM '.$db->prefix.'topics AS t LEFT JOIN '.$db->prefix.'mark_of_topic AS mot ON (mot.uid='.$user['id'].' AND mot.tid=t.id) WHERE t.forum_id IN('.implode(',', array_keys($new)).') AND t.last_post>'.$max.' AND t.moved_to IS NULL AND (mot.mt_upper IS NULL OR t.last_post>mot.mt_upper)') or error('Unable to fetch new topics', __FILE__, __LINE__, $db->error());
$result = $db->query('SELECT t.forum_id, t.id, t.last_post FROM '.$db->prefix.'topics AS t LEFT JOIN '.$db->prefix.'mark_of_topic AS mot ON (mot.uid='.$user->id.' AND mot.tid=t.id) WHERE t.forum_id IN('.implode(',', array_keys($new)).') AND t.last_post>'.$max.' AND t.moved_to IS NULL AND (mot.mt_upper IS NULL OR t.last_post>mot.mt_upper)') or error('Unable to fetch new topics', __FILE__, __LINE__, $db->error());
$tmp = [];
while ($cur = $db->fetch_assoc($result)) {
if ($cur['last_post']>$new[$cur['forum_id']]) {
@ -191,7 +191,7 @@ class Index extends Page
if (!empty($forums[$fId]['moderators'])) {
$mods = unserialize($forums[$fId]['moderators']);
foreach ($mods as $name => $id) {
if ($user['g_view_users'] == '1') {
if ($user->gViewUsers == '1') {
$moderators[] = [
$r->link('User', [
'id' => $id,

View file

@ -183,7 +183,7 @@ abstract class Page
{
if ($this->config['o_maintenance'] == '1') {
$user = $this->c->get('user');
if ($user['is_admmod']) {
if ($user->isAdmMod) {
$this->iswev['w'][] = '<a href="' . $this->c->get('Router')->link('AdminOptions', ['#' => 'maintenance']). '">' . __('Maintenance mode enabled') . '</a>';
}
}
@ -232,33 +232,33 @@ abstract class Page
'index' => [$r->link('Index'), __('Index')]
];
if ($user['g_read_board'] == '1' && $user['g_view_users'] == '1') {
if ($user->gReadBoard == '1' && $user->gViewUsers == '1') {
$nav['userlist'] = [$r->link('Userlist'), __('User list')];
}
if ($this->config['o_rules'] == '1' && (! $user['is_guest'] || $user['g_read_board'] == '1' || $this->config['o_regs_allow'] == '1')) {
if ($this->config['o_rules'] == '1' && (! $user->isGuest || $user->gReadBoard == '1' || $this->config['o_regs_allow'] == '1')) {
$nav['rules'] = [$r->link('Rules'), __('Rules')];
}
if ($user['g_read_board'] == '1' && $user['g_search'] == '1') {
if ($user->gReadBoard == '1' && $user->gSearch == '1') {
$nav['search'] = [$r->link('Search'), __('Search')];
}
if ($user['is_guest']) {
if ($user->isGuest) {
$nav['register'] = ['register.php', __('Register')];
$nav['login'] = [$r->link('Login'), __('Login')];
} else {
$nav['profile'] = [$r->link('User', [
'id' => $user['id'],
'name' => $user['username']
'id' => $user->id,
'name' => $user->username,
]), __('Profile')];
// New PMS
if ($this->config['o_pms_enabled'] == '1' && ($user['g_pm'] == 1 || $user['messages_new'] > 0)) { //????
if ($this->config['o_pms_enabled'] == '1' && ($user->isAdmin || $user->messagesNew > 0)) { //????
$nav['pmsnew'] = ['pmsnew.php', __('PM')]; //'<li id="nav"'.((PUN_ACTIVE_PAGE == 'pms_new' || $user['messages_new'] > 0) ? ' class="isactive"' : '').'><a href="pmsnew.php">'.__('PM').(($user['messages_new'] > 0) ? ' (<span'.((empty($this->config['o_pms_flasher']) || PUN_ACTIVE_PAGE == 'pms_new') ? '' : ' class="remflasher"' ).'>'.$user['messages_new'].'</span>)' : '').'</a></li>';
}
// New PMS
if ($user['is_admmod']) {
if ($user->isAdmMod) {
$nav['admin'] = [$r->link('Admin'), __('Admin')];
}
@ -267,7 +267,7 @@ abstract class Page
]), __('Logout')];
}
if ($user['g_read_board'] == '1' && $this->config['o_additional_navlinks'] != '') {
if ($user->gReadBoard == '1' && $this->config['o_additional_navlinks'] != '') {
// position|name|link[|id]\n
if (preg_match_all('%^(\d+)\|([^\|\n\r]+)\|([^\|\n\r]+)(?:\|([^\|\n\r]+))?$%m', $this->config['o_additional_navlinks']."\n", $matches)) {
$k = count($matches[0]);
@ -359,14 +359,14 @@ abstract class Page
$user = $this->c->get('user');
$diff = ($user['timezone'] + $user['dst']) * 3600;
$diff = ($user->timezone + $user->dst) * 3600;
$timestamp += $diff;
if (null === $dateFormat) {
$dateFormat = $this->c->getParameter('date_formats')[$user['date_format']];
$dateFormat = $this->c->getParameter('date_formats')[$user->dateFormat];
}
if(null === $timeFormat) {
$timeFormat = $this->c->getParameter('time_formats')[$user['time_format']];
$timeFormat = $this->c->getParameter('time_formats')[$user->timeFormat];
}
$date = gmdate($dateFormat, $timestamp);

View file

@ -0,0 +1,127 @@
<?php
namespace ForkBB\Models;
use ForkBB\Core\Model; //????
use R2\DependencyInjection\ContainerInterface;
use RuntimeException;
class User extends Model
{
/**
* Контейнер
* @var ContainerInterface
*/
protected $c;
/**
* @var array
*/
protected $config;
/**
* @var UserCookie
*/
protected $userCookie;
/**
* @var DB
*/
protected $db;
/**
* Конструктор
*/
public function __construct(array $config, $cookie, $db, ContainerInterface $container)
{
$this->config = $config;
$this->userCookie = $cookie;
$this->db = $db;
$this->c = $container;
}
/**
* @return User
*/
public function init()
{
$this->current = $this->c->get('LoadCurrentUser')->load();
return $this;
}
/**
* Выход
*/
public function logout()
{
if ($this->current['is_guest']) {
return;
}
$this->userCookie->deleteUserCookie();
$this->c->get('Online')->delete($this);
// Update last_visit (make sure there's something to update it with)
if (isset($this->current['logged'])) {
$this->db->query('UPDATE '.$this->db->prefix.'users SET last_visit='.$this->current['logged'].' WHERE id='.$this->current['id']) or error('Unable to update user visit data', __FILE__, __LINE__, $this->db->error());
}
}
/**
* Вход
* @param string $name
* @param string $password
* @param bool $save
* @return mixed
*/
public function login($name, $password, $save)
{
$result = $this->db->query('SELECT u.id, u.group_id, u.username, u.password, u.registration_ip, g.g_moderator FROM '.$this->db->prefix.'users AS u LEFT JOIN '.$this->db->prefix.'groups AS g ON u.group_id=g.g_id WHERE u.username=\''.$this->db->escape($name).'\'') or error('Unable to fetch user info', __FILE__, __LINE__, $this->db->error());
$user = $this->db->fetch_assoc($result);
$this->db->free_result($result);
if (empty($user['id'])) {
return false;
}
$authorized = false;
// For FluxBB by Visman 1.5.10.74 and above
if (strlen($user['password']) == 40) {
if (hash_equals($user['password'], sha1($password . $this->c->getParameter('SALT1')))) {
$authorized = true;
$user['password'] = password_hash($password, PASSWORD_DEFAULT);
$this->db->query('UPDATE '.$this->db->prefix.'users SET password=\''.$this->db->escape($user['password']).'\' WHERE id='.$user['id']) or error('Unable to update user password', __FILE__, __LINE__, $this->db->error());
}
} else {
$authorized = password_verify($password, $user['password']);
}
if (! $authorized) {
return false;
}
// Update the status if this is the first time the user logged in
if ($user['group_id'] == PUN_UNVERIFIED)
{
$this->db->query('UPDATE '.$this->db->prefix.'users SET group_id='.$this->config['o_default_user_group'].' WHERE id='.$user['id']) or error('Unable to update user status', __FILE__, __LINE__, $this->db->error());
$this->c->get('users_info update');
}
// перезаписываем ip админа и модератора - Visman
if ($this->config['o_check_ip'] == '1' && $user['registration_ip'] != $this->current['ip'])
{
if ($user['g_id'] == PUN_ADMIN || $user['g_moderator'] == '1')
$this->db->query('UPDATE '.$this->db->prefix.'users SET registration_ip=\''.$this->db->escape($this->current['ip']).'\' WHERE id='.$user['id']) or error('Unable to update user IP', __FILE__, __LINE__, $this->db->error());
}
$this->c->get('Online')->delete($this);
$this->c->get('UserCookie')->setUserCookie($user['id'], $user['password'], $save);
return $user['id'];
}
}

View file

@ -2,11 +2,11 @@
namespace ForkBB\Models;
use ForkBB\Core\Model; //????
use ForkBB\Core\AbstractModel;
use R2\DependencyInjection\ContainerInterface;
use RuntimeException;
class User extends Model
class User extends AbstractModel
{
/**
* Контейнер
@ -30,281 +30,77 @@ class User extends Model
protected $db;
/**
* Адрес пользователя
* @var string
* Время
* @var int
*/
protected $ip;
protected $now;
/**
* Конструктор
*/
public function __construct(array $config, $cookie, $db, ContainerInterface $container)
public function __construct(array $data, ContainerInterface $container)
{
$this->config = $config;
$this->userCookie = $cookie;
$this->db = $db;
$this->now = time();
$this->c = $container;
$this->ip = $this->getIpAddress();
$this->config = $container->get('config');
$this->userCookie = $container->get('UserCookie');
$this->db = $container->get('DB');
parent::__construct($data);
}
/**
* @return User
* Выполняется до конструктора родителя
*/
public function init()
protected function beforeConstruct(array $data)
{
if (($userId = $this->userCookie->id()) === false) {
return $this->initGuest();
}
return $data;
}
$result = $this->db->query('SELECT u.*, g.*, o.logged, o.idle FROM '.$this->db->prefix.'users AS u INNER JOIN '.$this->db->prefix.'groups AS g ON u.group_id=g.g_id LEFT JOIN '.$this->db->prefix.'online AS o ON o.user_id=u.id WHERE u.id='.$userId) or error('Unable to fetch user information', __FILE__, __LINE__, $this->db->error());
$user = $this->db->fetch_assoc($result);
$this->db->free_result($result);
protected function getIsGuest()
{
return $this->id < 2 || empty($this->gId) || $this->gId == PUN_GUEST;
}
if (empty($user['id']) || ! $this->userCookie->verifyHash($user['id'], $user['password'])) {
return $this->initGuest();
}
protected function getIsAdmin()
{
return $this->gId == PUN_ADMIN;
}
// проверка ip админа и модератора - Visman
if ($this->config['o_check_ip'] == '1' && ($user['g_id'] == PUN_ADMIN || $user['g_moderator'] == '1') && $user['registration_ip'] != $this->ip) {
return $this->initGuest();
}
protected function getIsAdmMod()
{
return $this->gId == PUN_ADMIN || $this->gModerator == '1';
}
$this->userCookie->setUserCookie($user['id'], $user['password']);
protected function getLogged()
{
return empty($this->data['logged']) ? $this->now : $this->data['logged'];
}
// Set a default language if the user selected language no longer exists
if (!file_exists(PUN_ROOT.'lang/'.$user['language'])) {
$user['language'] = $this->config['o_default_lang'];
}
protected function getIsLogged()
{
return ! empty($this->data['logged']);
}
// Set a default style if the user selected style no longer exists
if (!file_exists(PUN_ROOT.'style/'.$user['style'].'.css')) {
$user['style'] = $this->config['o_default_style'];
}
if (!$user['disp_topics']) {
$user['disp_topics'] = $this->config['o_disp_topics_default'];
}
if (!$user['disp_posts']) {
$user['disp_posts'] = $this->config['o_disp_posts_default'];
}
$now = time();
if (! $user['logged']) {
$user['logged'] = $now;
$user['is_logged'] = true;
// Reset tracked topics
set_tracked_topics(null);
protected function getLanguage()
{
if ($this->isGuest
|| ! file_exists($this->c->getParameter('DIR_LANG') . '/' . $this->data['language'] . '/common.po')
) {
return $this->config['o_default_lang'];
} else {
$user['is_logged'] = false;
// Special case: We've timed out, but no other user has browsed the forums since we timed out
if ($user['logged'] < ($now - $this->config['o_timeout_visit']))
{
$this->db->query('UPDATE '.$this->db->prefix.'users SET last_visit='.$user['logged'].' WHERE id='.$user['id']) or error('Unable to update user visit data', __FILE__, __LINE__, $this->db->error());
$user['last_visit'] = $user['logged'];
}
$cookie = $this->c->get('Cookie');
$track = $cookie->get('track');
// Update tracked topics with the current expire time
if (isset($track)) {
$cookie->set('track', $track, $now + $this->config['o_timeout_visit']);
}
return $this->data['language'];
}
$user['is_guest'] = false;
$user['is_admmod'] = $user['g_id'] == PUN_ADMIN || $user['g_moderator'] == '1';
$user['is_bot'] = false;
$user['ip'] = $this->ip;
$this->current = $user;
return $this;
}
/**
* @throws \RuntimeException
* @return User
*/
protected function initGuest()
protected function getStyle()
{
$result = $this->db->query('SELECT u.*, g.*, o.logged, o.last_post, o.last_search FROM '.$this->db->prefix.'users AS u INNER JOIN '.$this->db->prefix.'groups AS g ON u.group_id=g.g_id LEFT JOIN '.$this->db->prefix.'online AS o ON (o.user_id=1 AND o.ident=\''.$this->db->escape($this->ip).'\') WHERE u.id=1') or error('Unable to fetch guest information', __FILE__, __LINE__, $this->db->error());
$user = $this->db->fetch_assoc($result);
$this->db->free_result($result);
if (empty($user['id'])) {
throw new RuntimeException('Unable to fetch guest information. Your database must contain both a guest user and a guest user group.');
}
$this->userCookie->deleteUserCookie();
// этого гостя нет в таблице online
if (! $user['logged']) {
$user['logged'] = time();
$user['is_logged'] = true;
if ($this->isGuest
//??? || ! file_exists($this->c->getParameter('DIR_LANG') . '/' . $this->data['language'])
) {
return $this->config['o_default_style'];
} else {
$user['is_logged'] = false;
return $this->data['style'];
}
$user['disp_topics'] = $this->config['o_disp_topics_default'];
$user['disp_posts'] = $this->config['o_disp_posts_default'];
$user['timezone'] = $this->config['o_default_timezone'];
$user['dst'] = $this->config['o_default_dst'];
$user['language'] = $this->config['o_default_lang'];
$user['style'] = $this->config['o_default_style'];
$user['is_guest'] = true;
$user['is_admmod'] = false;
$user['is_bot'] = $this->isBot();
$user['ip'] = $this->ip;
// быстрое переключение языка - Visman
$language = $this->c->get('Cookie')->get('glang');
if (null !== $language)
{
$language = preg_replace('%[^\w]%', '', $language);
$languages = forum_list_langs();
if (in_array($language, $languages))
$user['language'] = $language;
}
$this->current = $user;
return $this;
}
/**
* Возврат адреса пользователя
* @return string
*/
protected function getIpAddress()
{
return filter_var($_SERVER['REMOTE_ADDR'], FILTER_VALIDATE_IP) ?: 'unknow';
}
/**
* Проверка на робота
* Если робот, то возврат имени
* @return false|string
*/
protected function isBot()
{
$agent = trim($_SERVER['HTTP_USER_AGENT']);
if ($agent == '') {
return false;
}
$agentL = strtolower($agent);
if (strpos($agentL, 'bot') !== false
|| strpos($agentL, 'spider') !== false
|| strpos($agentL, 'crawler') !== false
|| strpos($agentL, 'http') !== false
) {
return $this->nameBot($agent, $agentL);
}
if (strpos($agent, 'Mozilla/') !== false
&& (strpos($agent, 'Gecko') !== false
|| (strpos($agent, '(compatible; MSIE ') !== false
&& strpos($agent, 'Windows') !== false
)
)
) {
return false;
} elseif (strpos($agent, 'Opera/') !== false
&& strpos($agent, 'Presto/') !== false
) {
return false;
}
return $this->nameBot($agent, $agentL);
}
/**
* Выделяет имя робота из юзерагента
* @param string $agent
* @param string $agentL
* @retrun string
*/
protected function nameBot($agent, $agentL)
{
if (strpos($agentL, 'mozilla') !== false) {
$agent = preg_replace('%Mozilla.*?compatible%i', ' ', $agent);
}
if (strpos($agentL, 'http') !== false || strpos($agentL, 'www.') !== false) {
$agent = preg_replace('%(?:https?://|www\.)[^\)]*(\)[^/]+$)?%i', ' ', $agent);
}
if (strpos($agent, '@') !== false) {
$agent = preg_replace('%\b[\w\.-]+@[^\)]+%', ' ', $agent);
}
$agentL = strtolower($agent);
if (strpos($agentL, 'bot') !== false
|| strpos($agentL, 'spider') !== false
|| strpos($agentL, 'crawler') !== false
|| strpos($agentL, 'engine') !== false
) {
$f = true;
$p = '%(?<=[^a-z\d\.-])(?:robot|bot|spider|crawler)\b.*%i';
} else {
$f = false;
$p = '%^$%';
}
if ($f && preg_match('%\b(([a-z\d\.! _-]+)?(?:robot|(?<!ro)bot|spider|crawler|engine)(?(2)[a-z\d\.! _-]*|[a-z\d\.! _-]+))%i', $agent, $matches))
{
$agent = $matches[1];
$pat = [
$p,
'%[^a-z\d\.!-]+%i',
'%(?<=^|\s|-)v?\d+\.\d[^\s]*\s*%i',
'%(?<=^|\s)\S{1,2}(?:\s|$)%',
];
$rep = [
'',
' ',
'',
'',
];
} else {
$pat = [
'%\((?:KHTML|Linux|Mac|Windows|X11)[^\)]*\)?%i',
$p,
'%\b(?:AppleWebKit|Chrom|compatible|Firefox|Gecko|Mobile(?=[/ ])|Moz|Opera|OPR|Presto|Safari|Version)[^\s]*%i',
'%\b(?:InfoP|Intel|Linux|Mac|MRA|MRS|MSIE|SV|Trident|Win|WOW|X11)[^;\)]*%i',
'%\.NET[^;\)]*%i',
'%/.*%',
'%[^a-z\d\.!-]+%i',
'%(?<=^|\s|-)v?\d+\.\d[^\s]*\s*%i',
'%(?<=^|\s)\S{1,2}(?:\s|$)%',
];
$rep = [
' ',
'',
'',
'',
'',
'',
' ',
'',
'',
];
}
$agent = trim(preg_replace($pat, $rep, $agent), ' -');
if (empty($agent)) {
return 'Unknown';
}
$a = explode(' ', $agent);
$agent = $a[0];
if (strlen($agent) < 20
&& ! empty($a[1])
&& strlen($agent . ' ' . $a[1]) < 26
) {
$agent .= ' ' . $a[1];
} elseif (strlen($agent) > 25) {
$agent = 'Unknown';
}
return $agent;
}
/**
@ -312,15 +108,15 @@ class User extends Model
*/
public function logout()
{
if ($this->current['is_guest']) {
if ($this->isGuest) {
return;
}
$this->userCookie->deleteUserCookie();
$this->c->get('Online')->delete($this);
// Update last_visit (make sure there's something to update it with)
if (isset($this->current['logged'])) {
$this->db->query('UPDATE '.$this->db->prefix.'users SET last_visit='.$this->current['logged'].' WHERE id='.$this->current['id']) or error('Unable to update user visit data', __FILE__, __LINE__, $this->db->error());
if ($this->isLogged) {
$this->db->query('UPDATE '.$this->db->prefix.'users SET last_visit='.$this->logged.' WHERE id='.$this->id) or error('Unable to update user visit data', __FILE__, __LINE__, $this->db->error());
}
}
@ -367,10 +163,10 @@ class User extends Model
}
// перезаписываем ip админа и модератора - Visman
if ($this->config['o_check_ip'] == '1' && $user['registration_ip'] != $this->current['ip'])
if ($this->config['o_check_ip'] == '1' && $user['registration_ip'] != $this->ip)
{
if ($user['g_id'] == PUN_ADMIN || $user['g_moderator'] == '1')
$this->db->query('UPDATE '.$this->db->prefix.'users SET registration_ip=\''.$this->db->escape($this->current['ip']).'\' WHERE id='.$user['id']) or error('Unable to update user IP', __FILE__, __LINE__, $this->db->error());
$this->db->query('UPDATE '.$this->db->prefix.'users SET registration_ip=\''.$this->db->escape($this->ip).'\' WHERE id='.$user['id']) or error('Unable to update user IP', __FILE__, __LINE__, $this->db->error());
}
$this->c->get('Online')->delete($this);
@ -379,5 +175,4 @@ class User extends Model
return $user['id'];
}
}

View file

@ -1,22 +1,22 @@
<?php
namespace ForkBB\Core\Cookie;
namespace ForkBB\Models;
class UserCookie
use ForkBB\Core\Cookie;
use ForkBB\Core\Secury;
use R2\DependencyInjection\ContainerInterface;
class UserCookie extends Cookie
{
const NAME = 'user';
const KEY1 = 'key1';
const KEY2 = 'key2';
/**
* @var Secury
* Контейнер
* @var ContainerInterface
*/
protected $secury;
/**
* @var Cookie
*/
protected $cookie;
protected $c;
/**
* Флаг указывающий на режим "запомнить меня"
@ -42,33 +42,15 @@ class UserCookie
*/
protected $passHash;
/**
* Период действия куки аутентификации в секундах для режима "не запоминать меня"
* @var int
*/
protected $timeMin;
/**
* Период действия куки аутентификации в секундах для режима "запомнить меня"
* @var int
*/
protected $timeMax;
/**
* Конструктор
*
* @param Secury $secury
* @param Cookie $cookie
* @param int $timeMin
* @param int $timeMax
* @param ContainerInterface $container
*/
public function __construct($secury, $cookie, $timeMin, $timeMax)
public function __construct(Secury $secury, array $options, ContainerInterface $container)
{
$this->secury = $secury;
$this->cookie = $cookie;
$this->timeMin = $timeMin;
$this->timeMax = $timeMax;
parent::__construct($secury, $options);
$this->c = $container;
$this->init();
}
@ -77,7 +59,7 @@ class UserCookie
*/
protected function init()
{
$ckUser = $this->cookie->get(self::NAME);
$ckUser = $this->get(self::NAME);
if (null === $ckUser
|| ! preg_match('%^(\-)?(\d{1,10})_(\d{10})_([a-f\d]{32,})_([a-f\d]{32,})$%Di', $ckUser, $ms)
@ -143,18 +125,18 @@ class UserCookie
&& $this->remember
)
) {
$expTime = time() + $this->timeMax;
$expTime = time() + $this->c->getParameter('TIME_REMEMBER');
$expire = $expTime;
$pfx = '';
} else {
$expTime = time() + $this->timeMin;
$expTime = time() + $this->c->get('config')['o_timeout_visit'];
$expire = 0;
$pfx = '-';
}
$passHash = $this->secury->hmac($hash . $expTime, self::KEY2);
$ckHash = $this->secury->hmac($pfx . $id . $expTime . $passHash, self::KEY1);
return $this->cookie->set(self::NAME, $pfx . $id . '_' . $expTime . '_' . $passHash . '_' . $ckHash, $expire);
return $this->set(self::NAME, $pfx . $id . '_' . $expTime . '_' . $passHash . '_' . $ckHash, $expire);
}
/**
@ -164,10 +146,10 @@ class UserCookie
*/
public function deleteUserCookie()
{
if (null === $this->cookie->get(self::NAME)) {
if (null === $this->get(self::NAME)) {
return true;
} else {
return $this->cookie->delete(self::NAME);
return $this->delete(self::NAME);
}
}
}

View file

@ -3,98 +3,88 @@
namespace ForkBB\Models;
use ForkBB\Models\User;
use R2\DependencyInjection\ContainerInterface;
use RuntimeException;
use InvalidArgumentException;
class UserMapper
{
/**
* Контейнер
* @var ContainerInterface
*/
protected $c;
/**
* @var array
*/
protected $config;
/**
* @var UserCookie
*/
protected $cookie;
/**
* @var DB
*/
protected $db
protected $db;
/**
* Конструктор
* @param array $config
* @param UserCookie $cookie
* @param DB $db
* @param ContainerInterface $container
*/
public function __construct(array $config, $cookie, $db)
public function __construct(ContainerInterface $container)
{
$this->config = $config;
$this->cookie = $cookie;
$this->db = $db;
$this->c = $container;
$this->config = $container->get('config');
$this->db = $container->get('DB');
}
/**
* Возврат адреса пользователя
* @return string
*/
protected function getIpAddress()
{
return filter_var($_SERVER['REMOTE_ADDR'], FILTER_VALIDATE_IP) ?: 'unknow';
}
/**
* Получение данных для текущего пользователя/гостя
* @param int $id
*
* @throws \InvalidArgumentException
* @retrun User
*/
public function load($id = null)
{
if (null === $id) {
$user = $this->loadCurrent();
return new User($user, $this->config, $this->cookie);
} elseif ($id < 2) {
throw new InvalidArgumentException('User id can not be less than 2');
}
}
/**
* @retrun array
*/
protected function loadCurrent()
{
if (($userId = $this->cookie->id()) === false) {
return $this->loadGuest();
}
$result = $this->db->query('SELECT u.*, g.*, o.logged, o.idle, o.witt_data FROM '.$this->db->prefix.'users AS u INNER JOIN '.$this->db->prefix.'groups AS g ON u.group_id=g.g_id LEFT JOIN '.$this->db->prefix.'online AS o ON o.user_id=u.id WHERE u.id='.$userId) or error('Unable to fetch user information', __FILE__, __LINE__, $this->db->error());
$user = $this->db->fetch_assoc($result);
$this->db->free_result($result);
if (empty($user['id']) || ! $this->cookie->verifyHash($user['id'], $user['password'])) {
return $this->loadGuest();
}
// проверка ip админа и модератора - Visman
if ($this->config['o_check_ip'] == '1' && ($user['g_id'] == PUN_ADMIN || $user['g_moderator'] == '1') && $user['registration_ip'] != get_remote_address())
{
return $this->loadGuest();
}
return $user;
}
/**
* @return User
* @throws \RuntimeException
* @retrun array
*/
protected function loadGuest()
public function getCurrent($id = 1)
{
$remote_addr = get_remote_address();
$result = $this->db->query('SELECT u.*, g.*, o.logged, o.last_post, o.last_search, o.witt_data FROM '.$this->db->prefix.'users AS u INNER JOIN '.$this->db->prefix.'groups AS g ON u.group_id=g.g_id LEFT JOIN '.$this->db->prefix.'online AS o ON o.ident=\''.$this->db->escape($remote_addr).'\' WHERE u.id=1') or error('Unable to fetch guest information', __FILE__, __LINE__, $this->db->error());
$user = $this->db->fetch_assoc($result);
$this->db->free_result($result);
$ip = $this->getIpAddress();
$id = (int) $id;
if (empty($user['id']) {
if ($id > 1) {
$result = $this->db->query('SELECT u.*, g.*, o.logged, o.idle FROM '.$this->db->prefix.'users AS u INNER JOIN '.$this->db->prefix.'groups AS g ON u.group_id=g.g_id LEFT JOIN '.$this->db->prefix.'online AS o ON o.user_id=u.id WHERE u.id='.$id) or error('Unable to fetch user information', __FILE__, __LINE__, $this->db->error());
$user = $this->db->fetch_assoc($result);
$this->db->free_result($result);
}
if (empty($user['id'])) {
$result = $this->db->query('SELECT u.*, g.*, o.logged, o.last_post, o.last_search FROM '.$this->db->prefix.'users AS u INNER JOIN '.$this->db->prefix.'groups AS g ON u.group_id=g.g_id LEFT JOIN '.$this->db->prefix.'online AS o ON (o.user_id=1 AND o.ident=\''.$this->db->escape($ip).'\') WHERE u.id=1') or error('Unable to fetch guest information', __FILE__, __LINE__, $this->db->error());
$user = $this->db->fetch_assoc($result);
$this->db->free_result($result);
}
if (empty($user['id'])) {
throw new RuntimeException('Unable to fetch guest information. Your database must contain both a guest user and a guest user group.');
}
return $user;
$user['ip'] = $ip;
return new User($user, $this->c);
}
/**
* Обновляет время последнего визита для конкретного пользователя
* @param int $id
* @param int $time
*/
public function updateLastVisit($id, $time)
{
$id = (int) $id;
$time = (int) $time;
$this->db->query('UPDATE '.$this->db->prefix.'users SET last_visit='.$time.' WHERE id='.$id) or error('Unable to update user visit data', __FILE__, __LINE__, $this->db->error());
}
}

View file

@ -50,7 +50,7 @@ define('PUN', 1);
$container->setParameter('DIR_CONFIG', __DIR__ . '/config');
$container->setParameter('DIR_CACHE', __DIR__ . '/cache');
$container->setParameter('DIR_VIEWS', __DIR__ . '/templates');
$container->setParameter('DIR_TRANSL', __DIR__ . '/lang');
$container->setParameter('DIR_LANG', __DIR__ . '/lang');
$container->setParameter('START', $pun_start);
$config = $container->get('config');

View file

@ -1,25 +0,0 @@
@extends('layouts/main')
<section class="f-main f-login">
<div class="f-wrapdiv">
<h2>{!! __('Login') !!}</h2>
<form id="id-aform" method="post" action="{!! $formAction !!}">
<input type="hidden" name="token" value="{!! $formToken !!}">
<input type="hidden" name="redirect" value="{{ $formRedirect }}">
<label id="id-label1" for="id-fname">{!! __('Username') !!}</label>
<input required id="id-fname" type="text" name="name" value="{{ $name }}" maxlength="25" autofocus="autofocus" spellcheck="false" tabindex="1">
<label id="id-label2" for="id-fpassword">{!! __('Password') !!}<a class="f-forgetlink" href="{!! $forgetLink !!}" tabindex="5">{!! __('Forgotten pass') !!}</a></label>
<input required id="id-fpassword" type="password" name="password" tabindex="2">
@if($formSave)
<label id="id-label3"><input type="checkbox" name="save" value="1" tabindex="3" checked="checked">{!! __('Remember me') !!}</label>
@else
<label id="id-label3"><input type="checkbox" name="save" value="1" tabindex="3">{!! __('Remember me') !!}</label>
@endif
<input class="f-btn" type="submit" name="login" value="{!! __('Login') !!}" tabindex="4">
</form>
</div>
@if($regLink)
<div class="f-wrapdiv">
<p><a href="{!! $regLink !!}" tabindex="6">{!! __('Not registered') !!}</a></p>
</div>
@endif
</section>

13
app/templates/forget.tpl Normal file
View file

@ -0,0 +1,13 @@
@extends('layouts/main')
<section class="f-main f-login">
<div class="f-wdiv">
<h2>{!! __('Request pass') !!}</h2>
<form class="f-form" method="post" action="{!! $formAction !!}">
<input type="hidden" name="token" value="{!! $formToken !!}">
<label class="f-child1" for="id-email">{!! __('Email') !!}</label>
<input required id="id-email" type="text" name="email" value="{{ $email }}" maxlength="80" autofocus="autofocus" spellcheck="false" tabindex="1">
<label class="f-child2">{!! __('Request pass info') !!}</label>
<input class="f-btn" type="submit" name="submit" value="{!! __('Submit') !!}" tabindex="2">
</form>
</div>
</section>

25
app/templates/login.tpl Normal file
View file

@ -0,0 +1,25 @@
@extends('layouts/main')
<section class="f-main f-login">
<div class="f-wdiv">
<h2>{!! __('Login') !!}</h2>
<form class="f-form" method="post" action="{!! $formAction !!}">
<input type="hidden" name="token" value="{!! $formToken !!}">
<input type="hidden" name="redirect" value="{{ $formRedirect }}">
<label class="f-child1" for="id-name">{!! __('Username') !!}</label>
<input required id="id-name" type="text" name="name" value="{{ $name }}" maxlength="25" autofocus="autofocus" spellcheck="false" tabindex="1">
<label class="f-child1" for="id-password">{!! __('Password') !!}<a class="f-forgetlink" href="{!! $forgetLink !!}" tabindex="5">{!! __('Forgotten pass') !!}</a></label>
<input required id="id-password" type="password" name="password" tabindex="2">
@if($formSave)
<label class="f-child2"><input type="checkbox" name="save" value="1" tabindex="3" checked="checked">{!! __('Remember me') !!}</label>
@else
<label class="f-child2"><input type="checkbox" name="save" value="1" tabindex="3">{!! __('Remember me') !!}</label>
@endif
<input class="f-btn" type="submit" name="login" value="{!! __('Login') !!}" tabindex="4">
</form>
</div>
@if($regLink)
<div class="f-wdiv">
<p class="f-child3"><a href="{!! $regLink !!}" tabindex="6">{!! __('Not registered') !!}</a></p>
</div>
@endif
</section>

View file

@ -2041,7 +2041,7 @@ function __($data, ...$args)
if (isset($loaded[$args[0]])) {
return;
}
$dir = $data->getParameter('DIR_TRANSL');
$dir = $data->getParameter('DIR_LANG');
$lang = isset($args[1]) ? $args[1] : $data->get('user')['language'];
if (file_exists($dir . $lang . '/' . $args[0] . '.php')) {
$tr = (include $dir . $lang . '/' . $args[0] . '.php') + $tr;

View file

@ -468,6 +468,7 @@ select {
.f-main {
/* border-top: 0.0625rem solid #AA7939; */
margin: 1rem 0;
}
.f-main > h2 {
@ -511,6 +512,8 @@ select {
.f-rules > div {
padding: 0.625rem;
border: 0.0625rem solid #AA7939;
background-color: #F8F4E3;
}
/************/
@ -992,7 +995,7 @@ li + li .f-btn {
/*********/
/* Логин */
/*********/
.f-login .f-wrapdiv {
.f-login .f-wdiv {
margin: 1rem auto;
max-width: 20rem;
border: 0.0625rem solid #AA7939;
@ -1004,11 +1007,11 @@ li + li .f-btn {
text-align: center;
}
#id-aform {
.f-login .f-form {
padding: 0.625rem;
}
#id-aform > input {
.f-login .f-form > input {
padding: 0.5rem;
font-size: 1rem;
display: block;
@ -1017,7 +1020,7 @@ li + li .f-btn {
box-sizing: border-box;
}
#id-aform > label {
.f-login .f-form > label {
display: block;
width: 100%;
}
@ -1028,29 +1031,27 @@ li + li .f-btn {
font-weight: normal;
}
.f-login #id-label1,
.f-login #id-label2 {
.f-login .f-child1 {
font-weight: bold;
}
.f-login #id-label1:after,
.f-login #id-label2:after {
.f-login .f-child1:after {
content: ":";
}
.f-login #id-label3 {
.f-login .f-child2 {
font-size: 0.875rem;
}
.f-login #id-label3 > input {
.f-login .f-child2 > input {
margin: 0 0.625rem 0 0;
}
#id-aform .f-btn {
.f-login .f-btn {
margin: 1rem 0 0.375rem 0;
}
.f-login .f-wrapdiv > p {
.f-login .f-child3 {
padding: 0.625rem;
text-align: center;
font-size: 0.875rem;