Current.php 6.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232
  1. <?php
  2. /**
  3. * This file is part of the ForkBB <https://github.com/forkbb>.
  4. *
  5. * @copyright (c) Visman <mio.visman@yandex.ru, https://github.com/MioVisman>
  6. * @license The MIT License (MIT)
  7. */
  8. declare(strict_types=1);
  9. namespace ForkBB\Models\User;
  10. use ForkBB\Models\Action;
  11. use ForkBB\Models\User\User;
  12. use RuntimeException;
  13. class Current extends Action
  14. {
  15. /**
  16. * Получает пользователя на основе куки авторизации
  17. * Обновляет куку аутентификации
  18. */
  19. public function current(): User
  20. {
  21. $ip = $this->getIp();
  22. $cookie = $this->c->Cookie;
  23. $user = $this->load((int) $cookie->uId, $ip);
  24. if (! $user->isGuest) {
  25. if (! $cookie->verifyUser($user)) {
  26. $user = $this->load(0, $ip);
  27. } elseif ($user->ip_check_type > 0) {
  28. $hexIp = \bin2hex(\inet_pton($ip));
  29. if (false === \strpos("|{$user->login_ip_cache}|", "|{$hexIp}|")) {
  30. $user = $this->load(0, $ip);
  31. }
  32. }
  33. }
  34. $user->__ip = $ip;
  35. $user->__userAgent = $this->getUserAgent();
  36. $cookie->setUser($user);
  37. if ($user->isGuest) {
  38. $user->__isBot = $this->isBot($user->userAgent);
  39. $user->__timezone = $this->c->config->o_default_timezone;
  40. $user->__dst = $this->c->config->b_default_dst;
  41. $user->__language = $this->getLangFromHTTP();
  42. } else {
  43. $user->__isBot = false;
  44. // Special case: We've timed out, but no other user has browsed the forums since we timed out
  45. if (
  46. $user->logged > 0
  47. && $user->logged < \time() - $this->c->config->i_timeout_visit
  48. ) {
  49. $this->manager->updateLastVisit($user);
  50. }
  51. $this->manager->set($user->id, $user);
  52. }
  53. return $user;
  54. }
  55. /**
  56. * Загружает данные из базы в модель пользователя
  57. */
  58. protected function load(int $id, string $ip): User
  59. {
  60. $data = null;
  61. if ($id > 0) {
  62. $vars = [
  63. ':id' => $id,
  64. ];
  65. $query = 'SELECT u.*, g.*, o.logged
  66. FROM ::users AS u
  67. INNER JOIN ::groups AS g ON u.group_id=g.g_id
  68. LEFT JOIN ::online AS o ON o.user_id=u.id
  69. WHERE u.id=?i:id';
  70. $data = $this->c->DB->query($query, $vars)->fetch();
  71. }
  72. if (empty($data['id'])) {
  73. $vars = [
  74. ':ip' => $ip,
  75. ];
  76. $query = 'SELECT o.logged, o.last_post, o.last_search
  77. FROM ::online AS o
  78. WHERE o.user_id=0 AND o.ident=?s:ip';
  79. $data = $this->c->DB->query($query, $vars)->fetch();
  80. return $this->manager->guest($data ?: []);
  81. } else {
  82. return $this->manager->create($data);
  83. }
  84. }
  85. /**
  86. * Возврат ip пользователя
  87. */
  88. protected function getIp(): string
  89. {
  90. return \filter_var($_SERVER['REMOTE_ADDR'], \FILTER_VALIDATE_IP) ?: '0.0.0.0';
  91. }
  92. /**
  93. * Возврат юзер агента браузера пользователя
  94. */
  95. protected function getUserAgent(): string
  96. {
  97. return \trim($this->c->Secury->replInvalidChars($_SERVER['HTTP_USER_AGENT'] ?? ''));
  98. }
  99. protected $defRegex = '%(?:^|[ ()])\b([\w .-]*{}[\w/.!-]*)%i';
  100. protected $botSearchList = [
  101. 'bot' => ['%(?<!cu)bot(?!tle)%'],
  102. 'crawl' => [''],
  103. 'spider' => ['%spider(?![\w ]*build/)%'],
  104. 'google' => ['%google(?:\w| |;|\-(?!tr))%'],
  105. 'wordpress' => ['', '%(wordpress)%i'],
  106. 'compatible' => ['%compatible(?!;\ msie)%', '%compatible[;) ]+([\w ./!-]+)%i']
  107. ];
  108. /**
  109. * Пытается по юзерагентуопределить робота
  110. * Если робот, то возвращает вычисленное имя
  111. */
  112. protected function isBot(string $agent) /* : string|false */
  113. {
  114. if ('' == $agent) {
  115. return false;
  116. }
  117. $agentL = \strtolower($agent);
  118. $status = 0;
  119. if (
  120. false !== ($pos = \strpos($agentL, 'http:'))
  121. || false !== ($pos = \strpos($agentL, 'https:'))
  122. || false !== ($pos = \strpos($agentL, 'www.'))
  123. ) {
  124. $status = 1;
  125. $agent = \substr($agent, 0, $pos);
  126. $agentL = \strtolower($agent);
  127. }
  128. foreach ($this->botSearchList as $needle => $regex) {
  129. if (
  130. false !== \strpos($agentL, $needle)
  131. && (
  132. '' == $regex[0]
  133. || \preg_match($regex[0], $agentL)
  134. )
  135. && \preg_match($regex[1] ?? \str_replace('{}', $needle, $this->defRegex), $agent, $match)
  136. ) {
  137. $status = 2;
  138. $agent = $match[1];
  139. break;
  140. }
  141. }
  142. if (
  143. 0 === $status
  144. && 0 !== \substr_compare('Mozilla/', $agent, 0, 8)
  145. && 0 !== \substr_compare('Opera/', $agent, 0, 6)
  146. ) {
  147. $status = 1;
  148. }
  149. if (0 === $status) {
  150. return false;
  151. }
  152. $reg = [
  153. '%[^\w/.-]+%',
  154. '%(?:_| |-|\b)bot(?:_| |-|\b)%i',
  155. '%(?<=^|\s)[^a-zA-Z\s]{1,2}(?:\s|$)%',
  156. ];
  157. $rep = [
  158. ' ',
  159. '',
  160. '',
  161. ];
  162. $agent = \trim(\preg_replace($reg, $rep, $agent));
  163. if (
  164. empty($agent)
  165. || isset($agent[28])
  166. ) {
  167. return 'Unknown';
  168. } else {
  169. return $agent;
  170. }
  171. }
  172. /**
  173. * Возвращает имеющийся в наличии язык из HTTP_ACCEPT_LANGUAGE
  174. * или язык по умолчанию
  175. */
  176. protected function getLangFromHTTP(): string
  177. {
  178. if (! empty($_SERVER['HTTP_ACCEPT_LANGUAGE'])) {
  179. $langs = $this->c->Func->getLangs();
  180. $main = [];
  181. foreach ($this->c->Func->langParse($_SERVER['HTTP_ACCEPT_LANGUAGE']) as $entry) {
  182. $arr = \explode('-', $entry, 2);
  183. if (isset($arr[1])) {
  184. $entry = $arr[0] . '_' . \strtoupper($arr[1]);
  185. $main[] = $arr[0];
  186. }
  187. if (isset($langs[$entry])) {
  188. return $langs[$entry];
  189. }
  190. }
  191. if (! empty($main)) {
  192. foreach ($main as $entry) {
  193. if (isset($langs[$entry])) {
  194. return $langs[$entry];
  195. }
  196. }
  197. }
  198. }
  199. return $this->c->config->o_default_lang;
  200. }
  201. }