PageRenderTime 55ms CodeModel.GetById 17ms RepoModel.GetById 0ms app.codeStats 1ms

/includes/sessions.php

https://github.com/MightyGorgon/icy_phoenix
PHP | 2213 lines | 1458 code | 280 blank | 475 comment | 316 complexity | eb1c16d33718246d398888cce95be578 MD5 | raw file
Possible License(s): AGPL-1.0
  1. <?php
  2. /**
  3. *
  4. * @package Icy Phoenix
  5. * @version $Id$
  6. * @copyright (c) 2008 Icy Phoenix
  7. * @license http://opensource.org/licenses/gpl-license.php GNU Public License
  8. *
  9. */
  10. /**
  11. * @ignore
  12. */
  13. if (!defined('IN_ICYPHOENIX'))
  14. {
  15. exit;
  16. }
  17. /**
  18. * Session class
  19. * @package phpBB3 / Icy Phoenix
  20. */
  21. class session
  22. {
  23. var $cookie_data = array();
  24. var $cookie_expire = 0;
  25. var $page = array();
  26. var $data = array();
  27. var $browser = '';
  28. var $forwarded_for = '';
  29. var $host = '';
  30. var $session_id = '';
  31. var $ip = '';
  32. var $load = 0;
  33. var $time_now = 0;
  34. var $update_session_page = true;
  35. /**
  36. * Start session management
  37. *
  38. * This is where all session activity begins. We gather various pieces of
  39. * information from the client and server. We test to see if a session already
  40. * exists. If it does, fine and dandy. If it doesn't we'll go on to create a
  41. * new one ... pretty logical heh? We also examine the system load (if we're
  42. * running on a system which makes such information readily available) and
  43. * halt if it's above an admin definable limit.
  44. *
  45. * @param bool $update_session_page if true the session page gets updated.
  46. * This can be set to circumvent certain scripts to update the users last visited page.
  47. */
  48. function session_begin($update_session_page = true)
  49. {
  50. global $SID, $_SID, $_EXTRA_URL, $db, $config;
  51. // ICY PHOENIX - BEGIN
  52. global $lang;
  53. // ICY PHOENIX - END
  54. // Give us some basic information
  55. $this->time_now = time();
  56. $this->cookie_data = array('u' => 0, 'k' => '');
  57. $this->cookie_expire = $this->time_now + (($config['max_autologin_time']) ? 86400 * (int) $config['max_autologin_time'] : 31536000);
  58. $this->update_session_page = (empty($update_session_page) || defined('IMG_THUMB')) ? false : true;
  59. //$this->browser = (!empty($_SERVER['HTTP_USER_AGENT'])) ? htmlspecialchars((string) $_SERVER['HTTP_USER_AGENT']) : '';
  60. $this->browser = (!empty($_SERVER['HTTP_USER_AGENT'])) ? (string) $_SERVER['HTTP_USER_AGENT'] : '';
  61. $this->referer = (!empty($_SERVER['HTTP_REFERER'])) ? htmlspecialchars((string) $_SERVER['HTTP_REFERER']) : '';
  62. $this->forwarded_for = (!empty($_SERVER['HTTP_X_FORWARDED_FOR'])) ? htmlspecialchars((string) $_SERVER['HTTP_X_FORWARDED_FOR']) : '';
  63. $this->host = extract_current_hostname();
  64. $this->page = extract_current_page(IP_ROOT_PATH);
  65. $session_cookie_empty = empty($_COOKIE[$config['cookie_name'] . '_sid']) ? true : false;
  66. $session_get_empty = empty($_GET['sid']) ? true : false;
  67. $session_empty = true;
  68. if (isset($_COOKIE[$config['cookie_name'] . '_sid']) || isset($_COOKIE[$config['cookie_name'] . '_u']))
  69. {
  70. $this->cookie_data['u'] = request_var($config['cookie_name'] . '_u', 0, false, true);
  71. $this->cookie_data['k'] = request_var($config['cookie_name'] . '_k', '', false, true);
  72. $this->session_id = request_var($config['cookie_name'] . '_sid', '', false, true);
  73. // Mighty Gorgon: I'm still not sure if I want to keep 'sid=' in Icy Phoenix as well... maybe better removing it!!!
  74. //$SID = (defined('NEED_SID')) ? ('sid=' . $this->session_id) : 'sid=';
  75. $SID = (defined('NEED_SID')) ? ('sid=' . $this->session_id) : '';
  76. $_SID = (defined('NEED_SID')) ? $this->session_id : '';
  77. $session_empty = empty($this->session_id) ? true : false;
  78. }
  79. // Mighty Gorgon: moved here this IF block... why it was so down in the code???
  80. // if no session id is set, redirect to index.php
  81. //if (defined('NEED_SID') && ($cookie_empty || (!isset($_GET['sid']) || ($this->session_id !== $_GET['sid']))))
  82. if (defined('NEED_SID') && !defined('IN_LOGIN') && ($session_cookie_empty || $session_empty || !isset($_GET['sid']) || ((isset($_GET['sid']) && ($this->session_id !== $_GET['sid'])))))
  83. {
  84. // Mighty Gorgon: I don't know why it isn't working properly, returning blank page!!!
  85. //send_status_line(401, 'Not authorized');
  86. // Mighty Gorgon: removed append_sid as it seems the user doesn't have a valid SID!
  87. redirect(IP_ROOT_PATH . 'index.' . PHP_EXT);
  88. }
  89. if ($session_empty)
  90. {
  91. $this->session_id = request_var('sid', '');
  92. $_SID = $this->session_id;
  93. $SID = 'sid=' . $this->session_id;
  94. $this->cookie_data = array('u' => 0, 'k' => '');
  95. }
  96. $_EXTRA_URL = array();
  97. // Why no forwarded_for et al? Well, too easily spoofed. With the results of my recent requests
  98. // it's pretty clear that in the majority of cases you'll at least be left with a proxy/cache ip.
  99. $this->ip = (!empty($_SERVER['REMOTE_ADDR'])) ? $_SERVER['REMOTE_ADDR'] : ((!empty($_ENV['REMOTE_ADDR'])) ? $_ENV['REMOTE_ADDR'] : getenv('REMOTE_ADDR'));
  100. $this->ip = preg_replace('#[ ]{2,}#', ' ', str_replace(array(',', ' '), ' ', $this->ip));
  101. // split the list of IPs
  102. $ips = explode(' ', $this->ip);
  103. // Default IP if REMOTE_ADDR is invalid
  104. $this->ip = '127.0.0.1';
  105. $format_ipv4 = get_preg_expression('ipv4');
  106. $format_ipv6 = get_preg_expression('ipv6');
  107. foreach ($ips as $ip)
  108. {
  109. if (preg_match($format_ipv4, $ip))
  110. {
  111. $this->ip = $ip;
  112. }
  113. elseif (preg_match($format_ipv6, $ip))
  114. {
  115. // Quick check for IPv4-mapped address in IPv6
  116. if (stripos($ip, '::ffff:') === 0)
  117. {
  118. $ipv4 = substr($ip, 7);
  119. if (preg_match($format_ipv4, $ipv4))
  120. {
  121. $ip = $ipv4;
  122. }
  123. }
  124. $this->ip = $ip;
  125. }
  126. else
  127. {
  128. // We want to use the last valid address in the chain
  129. // Leave foreach loop when address is invalid
  130. break;
  131. }
  132. }
  133. $this->load = false;
  134. // Load limit check (if applicable)
  135. if ($config['limit_load'] || $config['limit_search_load'])
  136. {
  137. if ((function_exists('sys_getloadavg') && ($load = sys_getloadavg())) || ($load = explode(' ', @file_get_contents('/proc/loadavg'))))
  138. {
  139. $this->load = array_slice($load, 0, 1);
  140. $this->load = floatval($this->load[0]);
  141. }
  142. else
  143. {
  144. set_config('limit_load', '0');
  145. set_config('limit_search_load', '0');
  146. }
  147. }
  148. // if session id is set
  149. if (!empty($this->session_id))
  150. {
  151. $sql = "SELECT u.*, s.*
  152. FROM " . SESSIONS_TABLE . " s, " . USERS_TABLE . " u
  153. WHERE s.session_id = '" . $db->sql_escape($this->session_id) . "'
  154. AND u.user_id = s.session_user_id";
  155. $result = $db->sql_query($sql);
  156. $this->data = $db->sql_fetchrow($result);
  157. $db->sql_freeresult($result);
  158. // Did the session exist in the DB?
  159. if (isset($this->data['user_id']))
  160. {
  161. if ((strpos($this->ip, ':') !== false) && (strpos($this->data['session_ip'], ':') !== false))
  162. {
  163. $s_ip = short_ipv6($this->data['session_ip'], $config['ip_check']);
  164. $u_ip = short_ipv6($this->ip, $config['ip_check']);
  165. }
  166. else
  167. {
  168. $s_ip = implode('.', array_slice(explode('.', $this->data['session_ip']), 0, $config['ip_check']));
  169. $u_ip = implode('.', array_slice(explode('.', $this->ip), 0, $config['ip_check']));
  170. }
  171. $s_browser = ($config['browser_check']) ? trim(strtolower(substr($this->data['session_browser'], 0, 254))) : '';
  172. $u_browser = ($config['browser_check']) ? trim(strtolower(substr($this->browser, 0, 254))) : '';
  173. // referer checks
  174. // The @ before $config['referer_validation'] suppresses notices present while running the updater
  175. $check_referer_path = (@$config['referer_validation'] == REFERER_VALIDATE_PATH);
  176. $referer_valid = true;
  177. // we assume HEAD and TRACE to be foul play and thus only whitelist GET
  178. if (@$config['referer_validation'] && isset($_SERVER['REQUEST_METHOD']) && strtolower($_SERVER['REQUEST_METHOD']) !== 'get')
  179. {
  180. $referer_valid = $this->validate_referer($check_referer_path);
  181. }
  182. if (($u_ip === $s_ip) && ($s_browser === $u_browser) && $referer_valid)
  183. {
  184. // Some useful boolean checks... defined here for future easy of use
  185. $session_expired = false;
  186. $session_refresh_time = (int) SESSION_REFRESH;
  187. $autologin_expired = (!empty($config['max_autologin_time']) && ($this->data['session_time'] < ($this->time_now - (86400 * (int) $config['max_autologin_time']) + $session_refresh_time))) ? true : false;
  188. $session_time_expired = ($this->data['session_time'] < ($this->time_now - ((int) $config['session_length'] + $session_refresh_time))) ? true : false;
  189. $session_refresh = ($this->data['session_time'] < ($this->time_now - $session_refresh_time)) ? true : false;
  190. if (!$session_expired)
  191. {
  192. // Check the session length timeframe if autologin is not enabled.
  193. // Else check the autologin length... and also removing those having autologin enabled but no longer allowed site-wide.
  194. if (empty($this->data['session_autologin']))
  195. {
  196. if ($session_time_expired)
  197. {
  198. $session_expired = true;
  199. }
  200. }
  201. elseif (empty($config['allow_autologin']) || $autologin_expired)
  202. {
  203. $session_expired = true;
  204. }
  205. }
  206. // ICY PHOENIX - BEGIN
  207. // This portion of code needs to stay here (after isset($this->data['user_id']) )... otherwise we are potentially going to instantiate some $user->data even if $user->data is still empty
  208. $this->bots_process();
  209. if (isset($this->data['user_id']) && ($this->data['user_id'] != ANONYMOUS) && isset($this->data['user_level']) && ($this->data['user_level'] == JUNIOR_ADMIN))
  210. {
  211. define('IS_JUNIOR_ADMIN', true);
  212. $this->data['user_level'] = (!defined('IN_ADMIN') && !defined('IN_CMS')) ? ADMIN : MOD;
  213. }
  214. // Refresh last visit time for those users having autologin enabled or those users with session time expired (only if config for this has been set)
  215. if (($this->data['user_id'] != ANONYMOUS) && ((!empty($config['session_last_visit_reset']) && $session_time_expired) || (!empty($config['allow_autologin']) && $autologin_expired) || empty($this->data['user_lastvisit'])))
  216. {
  217. $sql = "UPDATE " . USERS_TABLE . "
  218. SET user_lastvisit = " . (int) $this->data['session_time'] . "
  219. WHERE user_id = " . (int) $this->data['user_id'];
  220. $db->sql_query($sql);
  221. }
  222. // ICY PHOENIX - END
  223. if (!$session_expired)
  224. {
  225. // Only update session DB a minute or so after last update or if page changes
  226. // Mighty Gorgon: in Icy Phoenix we give maximum priority to $this->update_session_page, because we don't want the session to be updated for thumbnails or other special features!
  227. if ($this->update_session_page && ($session_refresh || ($this->data['session_page'] != $this->page['page'])) && empty($_REQUEST['explain']))
  228. {
  229. $sql_ary = array();
  230. // ICY PHOENIX - BEGIN
  231. // Update $user->data
  232. $this->data['user_session_time'] = $this->time_now;
  233. $this->data['user_session_page'] = (string) substr($this->page['page'], 0, 254);
  234. $this->data['user_browser'] = (string) substr($this->browser, 0, 254);
  235. $this->data['user_totalpages'] = (int) $this->data['user_totalpages'] + 1;
  236. $this->data['user_totaltime'] = (int) $this->data['user_totaltime'] + $this->time_now - $this->data['session_time'];
  237. // ICY PHOENIX - END
  238. // A little trick to reset session_admin on session re-usage
  239. if (!defined('IN_ADMIN') && !defined('IN_CMS') && $session_time_expired)
  240. {
  241. $sql_ary['session_admin'] = 0;
  242. }
  243. $sql_ary['session_time'] = $this->time_now;
  244. $sql_ary['session_page'] = $this->data['user_session_page'];
  245. $sql_ary['session_browser'] = $this->data['user_browser'];
  246. $sql_ary['session_forum_id'] = $this->page['forum'];
  247. $sql_ary['session_topic_id'] = $this->page['topic'];
  248. $db->sql_return_on_error(true);
  249. $sql = "UPDATE " . SESSIONS_TABLE . " SET " . $db->sql_build_array('UPDATE', $sql_ary) . "
  250. WHERE session_id = '" . $db->sql_escape($this->session_id) . "'";
  251. $result = $db->sql_query($sql);
  252. // ICY PHOENIX - BEGIN
  253. if ($this->data['user_id'] != ANONYMOUS)
  254. {
  255. $sql_ary = array();
  256. $sql_ary['user_ip'] = $this->ip;
  257. $sql_ary['user_session_time'] = $this->data['user_session_time'];
  258. $sql_ary['user_session_page'] = $this->data['user_session_page'];
  259. $sql_ary['user_browser'] = $this->data['user_browser'];
  260. $sql_ary['user_totalpages'] = $this->data['user_totalpages'];
  261. $sql_ary['user_totaltime'] = $this->data['user_totaltime'];
  262. $sql = "UPDATE " . USERS_TABLE . " SET " . $db->sql_build_array('UPDATE', $sql_ary) . "
  263. WHERE user_id = " . $this->data['user_id'];
  264. $result = $db->sql_query($sql);
  265. }
  266. // ICY PHOENIX - END
  267. $db->sql_return_on_error(false);
  268. }
  269. $this->data['is_registered'] = (empty($this->data['is_bot']) && ($this->data['user_id'] != ANONYMOUS) && !empty($this->data['user_active'])) ? true : false;
  270. $this->data['session_logged_in'] = $this->data['is_registered'];
  271. $this->data['user_lang'] = basename($this->data['user_lang']);
  272. $this->upi2db();
  273. return true;
  274. }
  275. }
  276. else
  277. {
  278. // Added logging temporarily to help debug bugs...
  279. if (defined('DEBUG_EXTRA') && DEBUG_EXTRA && ($this->data['user_id'] != ANONYMOUS))
  280. {
  281. if ($referer_valid)
  282. {
  283. add_log('critical', 'LOG_IP_BROWSER_FORWARDED_CHECK', $u_ip, $s_ip, $u_browser, $s_browser);
  284. }
  285. else
  286. {
  287. add_log('critical', 'LOG_REFERER_INVALID', $this->referer);
  288. }
  289. }
  290. }
  291. }
  292. }
  293. // If we reach here then no (valid) session exists. So we'll create a new one
  294. return $this->session_create();
  295. }
  296. /**
  297. * Create a new session
  298. *
  299. * If upon trying to start a session we discover there is nothing existing we
  300. * jump here. Additionally this method is called directly during login to regenerate
  301. * the session for the specific user. In this method we carry out a number of tasks;
  302. * garbage collection, (search)bot checking, banned user comparison. Basically
  303. * though this method will result in a new session for a specific user.
  304. */
  305. function session_create($user_id = false, $set_admin = false, $persist_login = false, $viewonline = true)
  306. {
  307. global $SID, $_SID, $db, $config, $cache;
  308. $this->data = array();
  309. $config['session_gc'] = (int) $config['cron_sessions_interval'];
  310. $config['session_last_gc'] = (int) $config['cron_sessions_last_run'];
  311. // Garbage collection ... remove old sessions updating user information if necessary. It means (potentially) 11 queries but only infrequently
  312. if ($this->time_now > ($config['session_last_gc'] + $config['session_gc']))
  313. {
  314. $this->session_gc();
  315. }
  316. // Do we allow autologin on this site? No? Then override anything that may be requested here
  317. if (!$config['allow_autologin'])
  318. {
  319. $this->cookie_data['k'] = false;
  320. $persist_login = false;
  321. }
  322. $user_logged_in = false;
  323. // If we're presented with an autologin key we'll join against it.
  324. // Else if we've been passed a user_id we'll grab data based on that
  325. if (isset($this->cookie_data['k']) && $this->cookie_data['k'] && $this->cookie_data['u'] && !sizeof($this->data))
  326. {
  327. $sql = "SELECT u.*
  328. FROM " . USERS_TABLE . " u, " . SESSIONS_KEYS_TABLE . " k
  329. WHERE u.user_id = " . (int) $this->cookie_data['u'] . "
  330. AND u.user_active = 1
  331. AND k.user_id = u.user_id
  332. AND k.key_id = '" . $db->sql_escape(md5($this->cookie_data['k'])) . "'";
  333. $result = $db->sql_query($sql);
  334. $this->data = $db->sql_fetchrow($result);
  335. $db->sql_freeresult($result);
  336. $user_logged_in = true;
  337. }
  338. elseif (($user_id !== false) && !sizeof($this->data))
  339. {
  340. $this->cookie_data['k'] = '';
  341. $this->cookie_data['u'] = $user_id;
  342. $sql = "SELECT *
  343. FROM " . USERS_TABLE . "
  344. WHERE user_id = " . (int) $this->cookie_data['u'] . "
  345. AND user_active = 1";
  346. $result = $db->sql_query($sql);
  347. $this->data = $db->sql_fetchrow($result);
  348. $db->sql_freeresult($result);
  349. $user_logged_in = true;
  350. }
  351. // If no data was returned one or more of the following occurred:
  352. // Key didn't match one in the DB
  353. // User does not exist
  354. // User is inactive
  355. if (!sizeof($this->data) || !is_array($this->data))
  356. {
  357. $this->cookie_data['k'] = '';
  358. $this->cookie_data['u'] = ANONYMOUS;
  359. $sql = "SELECT *
  360. FROM " . USERS_TABLE . "
  361. WHERE user_id = " . (int) $this->cookie_data['u'];
  362. $result = $db->sql_query($sql);
  363. $this->data = $db->sql_fetchrow($result);
  364. $db->sql_freeresult($result);
  365. }
  366. // ICY PHOENIX - BEGIN
  367. $this->bots_process();
  368. // ICY PHOENIX - END
  369. if ($this->data['user_id'] != ANONYMOUS)
  370. {
  371. $this->data['session_last_visit'] = !empty($this->data['user_lastvisit']) ? $this->data['user_lastvisit'] : $this->time_now;
  372. }
  373. else
  374. {
  375. // Bot user, if they have a SID in the Request URI we need to get rid of it otherwise they'll index this page with the SID, duplicate content oh my!
  376. if (isset($_GET['sid']) && !empty($this->data['is_bot']))
  377. {
  378. send_status_line(301, 'Moved Permanently');
  379. redirect(build_url(array('sid')));
  380. }
  381. $this->data['session_last_visit'] = $this->time_now;
  382. }
  383. // Force user id to be integer...
  384. $this->data['user_id'] = (int) $this->data['user_id'];
  385. // At this stage we should have a filled data array, defined cookie u and k data.
  386. // data array should contain recent session info if we have a real user and a recent session exists in which case session_id will also be set
  387. // Is user banned? Are they excluded? Won't return on ban, exists within method
  388. if ($this->data['user_level'] != ADMIN)
  389. {
  390. $ban_email = (($this->data['user_id'] != ANONYMOUS) && !empty($this->data['user_email'])) ? $this->data['user_email'] : false;
  391. $this->check_ban($this->data['user_id'], $this->ip, $ban_email);
  392. }
  393. // Mighty Gorgon: add to referers only if the user doesn't have a session... this is why this code is in session_create and not in session_begin
  394. if (empty($config['disable_referers']) && !empty($this->referer))
  395. {
  396. $this->process_referer();
  397. }
  398. $this->data['is_registered'] = (empty($this->data['is_bot']) && ($this->data['user_id'] != ANONYMOUS) && !empty($this->data['user_active'])) ? true : false;
  399. $this->data['session_logged_in'] = $this->data['is_registered'];
  400. // If our friend is a bot, we re-assign a previously assigned session
  401. if ($this->data['is_bot'])
  402. {
  403. // ICY PHOENIX - BEGIN
  404. // We give bots always the same session if it is not yet expired.
  405. $sql_fields = array();
  406. $sql_extra = '';
  407. if (!empty($this->browser))
  408. {
  409. $u_browser = trim(strtolower(substr($this->browser, 0, 254)));
  410. $sql_fields[] = "s.session_browser = '" . $db->sql_escape($u_browser) . "'";
  411. }
  412. if (!empty($this->ip))
  413. {
  414. $u_ip = $this->ip;
  415. $sql_fields[] = "s.session_ip = '" . $db->sql_escape($u_ip) . "'";
  416. }
  417. if (!empty($sql_fields))
  418. {
  419. foreach ($sql_fields as $sql_field)
  420. {
  421. $sql_extra .= (empty($sql_extra) ? " WHERE " : " AND ") . $sql_field;
  422. }
  423. if (!empty($sql_extra))
  424. {
  425. $bot_data = array();
  426. $sql = "SELECT s.* FROM " . SESSIONS_TABLE . " s " . $sql_extra . " AND s.session_time > " . ($this->time_now - ((int) $config['session_length'] + (int) SESSION_REFRESH));
  427. $result = $db->sql_query($sql);
  428. $bot_data = $db->sql_fetchrow($result);
  429. $db->sql_freeresult($result);
  430. if (!empty($bot_data))
  431. {
  432. $this->data = array_merge($this->data, $bot_data);
  433. }
  434. }
  435. }
  436. // ICY PHOENIX - END
  437. if (!empty($this->data['session_id']))
  438. {
  439. $this->session_id = $this->data['session_id'];
  440. // Only update session DB a minute or so after last update or if page changes
  441. if ((($this->time_now - $this->data['session_time']) > SESSION_REFRESH) || ($this->update_session_page && ($this->data['session_page'] != $this->page['page'])))
  442. {
  443. $this->data['session_time'] = $this->time_now;
  444. $this->data['session_last_visit'] = $this->time_now;
  445. $this->data['is_registered'] = false;
  446. $this->data['session_logged_in'] = $this->data['is_registered'];
  447. $this->bots_session_gc(false);
  448. }
  449. // Mighty Gorgon: I'm still not sure if I want to keep 'sid=' in Icy Phoenix as well... maybe better removing it!!!
  450. //$SID = 'sid=';
  451. $SID = '';
  452. $_SID = '';
  453. return true;
  454. }
  455. else
  456. {
  457. $this->bots_session_gc(true);
  458. }
  459. }
  460. $session_autologin = (($this->cookie_data['k'] || $persist_login) && $this->data['is_registered']) ? true : false;
  461. $set_admin = ($set_admin && $this->data['is_registered']) ? true : false;
  462. // Create or update the session
  463. $sql_ary = array(
  464. 'session_user_id' => (int) $this->data['user_id'],
  465. 'session_logged_in' => ($this->data['session_logged_in']) ? 1 : 0,
  466. 'session_start' => (int) $this->time_now,
  467. 'session_last_visit' => (int) $this->data['session_last_visit'],
  468. 'session_time' => (int) $this->time_now,
  469. 'session_browser' => (string) trim(substr($this->browser, 0, 254)),
  470. 'session_forwarded_for' => (string) $this->forwarded_for,
  471. 'session_ip' => (string) $this->ip,
  472. 'session_autologin' => ($session_autologin) ? 1 : 0,
  473. 'session_admin' => ($set_admin) ? 1 : 0,
  474. 'session_viewonline' => ($viewonline) ? 1 : 0,
  475. );
  476. if ($this->update_session_page)
  477. {
  478. $sql_ary['session_page'] = (string) substr($this->page['page'], 0, 254);
  479. $sql_ary['session_forum_id'] = $this->page['forum'];
  480. $sql_ary['session_topic_id'] = $this->page['topic'];
  481. }
  482. $db->sql_return_on_error(true);
  483. $sql = "DELETE
  484. FROM " . SESSIONS_TABLE . "
  485. WHERE session_id = '" . $db->sql_escape($this->session_id) . "'
  486. AND session_user_id = " . ANONYMOUS;
  487. if (!defined('IN_ERROR_HANDLER') && (!$this->session_id || !$db->sql_query($sql) || !$db->sql_affectedrows()))
  488. {
  489. // Limit new sessions in 1 minute period (if required)
  490. if (empty($this->data['session_time']) && !empty($config['active_sessions']))
  491. {
  492. //$db->sql_return_on_error(false);
  493. $sessions_limit = (int) $config['active_sessions'];
  494. $sessions_limit = ($sessions_limit < 100) ? 100 : $sessions_limit;
  495. $sql = "SELECT COUNT(session_id) AS sessions
  496. FROM " . SESSIONS_TABLE . "
  497. WHERE session_time >= " . ($this->time_now - SESSION_REFRESH);
  498. $result = $db->sql_query($sql);
  499. $row = $db->sql_fetchrow($result);
  500. $db->sql_freeresult($result);
  501. if ((int) $row['sessions'] > $sessions_limit)
  502. {
  503. send_status_line(503, 'Service Unavailable');
  504. trigger_error('Service Unavailable');
  505. }
  506. }
  507. }
  508. // Since we re-create the session id here, the inserted row must be unique. Therefore, we display potential errors.
  509. // Commented out because it will not allow forums to update correctly
  510. // $db->sql_return_on_error(false);
  511. // Something quite important: session_page always holds the *last* page visited, except for the *first* visit.
  512. // We are not able to simply have an empty session_page btw, therefore we need to detect this special case.
  513. // If the session id is empty, we have a completely new one and will set an "identifier" that we can check later if needed.
  514. if (empty($this->data['session_id']))
  515. {
  516. // This is a temporary variable, only set for the very first visit
  517. $this->data['session_created'] = true;
  518. }
  519. $this->session_id = md5(unique_id());
  520. $this->data['session_id'] = $this->session_id;
  521. $sql_ary['session_id'] = (string) $this->session_id;
  522. $sql_ary['session_page'] = (string) substr($this->page['page'], 0, 254);
  523. $sql_ary['session_browser'] = (string) substr($this->browser, 0, 254);
  524. $sql_ary['session_forum_id'] = $this->page['forum'];
  525. $sql_ary['session_topic_id'] = $this->page['topic'];
  526. $sql = "INSERT INTO " . SESSIONS_TABLE . " " . $db->sql_build_array('INSERT', $sql_ary);
  527. $db->sql_query($sql);
  528. $db->sql_return_on_error(false);
  529. // Regenerate autologin/persistent login key
  530. if ($session_autologin)
  531. {
  532. $this->set_login_key();
  533. }
  534. // refresh data
  535. $SID = 'sid=' . $this->session_id;
  536. $_SID = $this->session_id;
  537. $this->data = array_merge($this->data, $sql_ary);
  538. if (empty($this->data['is_bot']))
  539. {
  540. $this->set_cookie('u', $this->cookie_data['u'], $this->cookie_expire);
  541. $this->set_cookie('k', $this->cookie_data['k'], $this->cookie_expire);
  542. $this->set_cookie('sid', $this->session_id, $this->cookie_expire);
  543. $sql = "SELECT COUNT(session_id) AS sessions
  544. FROM " . SESSIONS_TABLE . "
  545. WHERE session_user_id = " . (int) $this->data['user_id'] . "
  546. AND session_time >= " . (int) ($this->time_now - (max($config['session_length'], $config['form_token_lifetime'])));
  547. $result = $db->sql_query($sql);
  548. $row = $db->sql_fetchrow($result);
  549. $db->sql_freeresult($result);
  550. // ICY PHOENIX - BEGIN
  551. $sql_ary = array();
  552. if ($this->data['user_id'] != ANONYMOUS)
  553. {
  554. $this->data['user_totallogon'] = (int) $this->data['user_totallogon'] + 1;
  555. $sql_ary['user_totallogon'] = $this->data['user_totallogon'];
  556. }
  557. if (((int) $row['sessions'] <= 1) || empty($this->data['user_form_salt']))
  558. {
  559. $this->data['user_form_salt'] = unique_id();
  560. $sql_ary['user_form_salt'] = $this->data['user_form_salt'];
  561. }
  562. if (sizeof($sql_ary))
  563. {
  564. $sql = "UPDATE " . USERS_TABLE . " SET " . $db->sql_build_array('UPDATE', $sql_ary) . "
  565. WHERE user_id = " . $this->data['user_id'];
  566. $result = $db->sql_query($sql);
  567. }
  568. // ICY PHOENIX - END
  569. // Start Advanced IP Tools Pack MOD
  570. if (empty($config['disable_logins']) && !empty($this->data['session_logged_in']))
  571. {
  572. $sql_logins_ary = array(
  573. 'login_userid' => $this->data['user_id'],
  574. 'login_ip' => $this->ip,
  575. 'login_user_agent' => substr($this->browser, 0, 254),
  576. 'login_time' => $this->time_now,
  577. );
  578. $db->sql_return_on_error(true);
  579. $sql = "INSERT INTO " . LOGINS_TABLE . " " . $db->sql_build_insert_update($sql_logins_ary, true);
  580. $db->sql_query($sql);
  581. $db->sql_return_on_error(false);
  582. $max_logins = (int) $config['last_logins_n'];
  583. $limit_sql = (!empty($max_logins) && ($max_logins > 0)) ? (" LIMIT 0, " . $max_logins . " ") : "";
  584. $sql = "SELECT login_id FROM " . LOGINS_TABLE . "
  585. WHERE login_userid = " . $this->data['user_id'] . "
  586. ORDER BY login_id DESC" .
  587. $limit_sql;
  588. $result = $db->sql_query($sql);
  589. $user_logins = $db->sql_numrows($result);
  590. $last_logins = $db->sql_fetchrowset($result);
  591. $db->sql_freeresult($result);
  592. if (!empty($user_logins) && ($user_logins > $max_logins))
  593. {
  594. $logins_to_keep = array();
  595. foreach ($last_logins as $login_row)
  596. {
  597. $logins_to_keep[] = $login_row['login_id'];
  598. }
  599. $db->sql_return_on_error(true);
  600. $sql = "DELETE FROM " . LOGINS_TABLE . "
  601. WHERE login_id NOT IN (" . implode(',', $logins_to_keep) . ")
  602. AND login_userid = " . $this->data['user_id'];
  603. $db->sql_query($sql);
  604. $db->sql_return_on_error(false);
  605. }
  606. }
  607. // End Advanced IP Tools Pack MOD
  608. }
  609. else
  610. {
  611. $this->data['session_time'] = $this->time_now;
  612. $this->data['session_last_visit'] = $this->time_now;
  613. // Mighty Gorgon: I'm still not sure if I want to keep 'sid=' in Icy Phoenix as well... maybe better removing it!!!
  614. //$SID = 'sid=';
  615. $SID = '';
  616. $_SID = '';
  617. }
  618. return true;
  619. }
  620. /**
  621. * Kills a session
  622. *
  623. * This method does what it says on the tin. It will delete a pre-existing session.
  624. * It resets cookie information (destroying any autologin key within that cookie data)
  625. * and update the users information from the relevant session data. It will then
  626. * grab guest user information.
  627. */
  628. function session_kill($new_session = true)
  629. {
  630. global $SID, $_SID, $db, $config;
  631. $sql = "DELETE FROM " . SESSIONS_TABLE . "
  632. WHERE session_id = '" . $db->sql_escape($this->session_id) . "'
  633. AND session_user_id = " . (int) $this->data['user_id'];
  634. $db->sql_query($sql);
  635. if ($this->data['user_id'] != ANONYMOUS)
  636. {
  637. // Delete existing session, update last visit info first!
  638. if (!isset($this->data['session_time']))
  639. {
  640. $this->data['session_time'] = time();
  641. }
  642. $sql = "UPDATE " . USERS_TABLE . "
  643. SET user_lastvisit = " . (int) $this->data['session_time'] . ", user_private_chat_alert = ''
  644. WHERE user_id = " . (int) $this->data['user_id'];
  645. $db->sql_query($sql);
  646. if ($this->cookie_data['k'])
  647. {
  648. $sql = "DELETE FROM " . SESSIONS_KEYS_TABLE . "
  649. WHERE user_id = " . (int) $this->data['user_id'] . "
  650. AND key_id = '" . $db->sql_escape(md5($this->cookie_data['k'])) . "'";
  651. $db->sql_query($sql);
  652. }
  653. // Reset the data array
  654. $this->data = array();
  655. $sql = "SELECT *
  656. FROM " . USERS_TABLE . "
  657. WHERE user_id = " . ANONYMOUS;
  658. $result = $db->sql_query($sql);
  659. $this->data = $db->sql_fetchrow($result);
  660. $db->sql_freeresult($result);
  661. }
  662. $cookie_expire = $this->time_now - 31536000;
  663. $this->set_cookie('u', '', $cookie_expire);
  664. $this->set_cookie('k', '', $cookie_expire);
  665. $this->set_cookie('sid', '', $cookie_expire);
  666. unset($cookie_expire);
  667. // Mighty Gorgon: I'm still not sure if I want to keep 'sid=' in Icy Phoenix as well... maybe better removing it!!!
  668. //$SID = 'sid=';
  669. $SID = '';
  670. $_SID = '';
  671. $this->session_id = '';
  672. // To make sure a valid session is created we create one for the anonymous user
  673. if ($new_session)
  674. {
  675. $this->session_create(ANONYMOUS);
  676. }
  677. return true;
  678. }
  679. /**
  680. * Session garbage collection
  681. *
  682. * This looks a lot more complex than it really is. Effectively we are
  683. * deleting any sessions older than an admin definable limit. Due to the
  684. * way in which we maintain session data we have to ensure we update user
  685. * data before those sessions are destroyed. In addition this method
  686. * removes autologin key information that is older than an admin defined
  687. * limit.
  688. */
  689. function session_gc()
  690. {
  691. global $db, $cache, $config;
  692. $batch_size = 10;
  693. if (!$this->time_now)
  694. {
  695. $this->time_now = time();
  696. }
  697. // Set here the desired time you would like to keep sessions...
  698. $session_remove_limit = 86400 * 2;
  699. $session_length = (int) $config['session_length'];
  700. // Remove old keys for users not logging in so frequently... 30 days should be fine!!!
  701. $max_autologin_time = !empty($config['max_autologin_time']) ? $config['max_autologin_time'] : 30;
  702. $sql = "DELETE FROM " . SESSIONS_KEYS_TABLE . "
  703. WHERE last_login < " . ($this->time_now - (86400 * (int) $max_autologin_time));
  704. $db->sql_query($sql);
  705. // Remove all sessions which are X days old from AJAX Chat table
  706. $sql = "DELETE FROM " . AJAX_SHOUTBOX_SESSIONS_TABLE . "
  707. WHERE session_time < " . (int) ($this->time_now - $session_remove_limit);
  708. $db->sql_query($sql);
  709. // Remove all sessions which are X days old from search table
  710. $sql = "DELETE FROM " . SEARCH_TABLE . "
  711. WHERE search_time < " . (int) ($this->time_now - $session_remove_limit);
  712. $db->sql_query($sql);
  713. // Delete Guest sessions which are at least X days old from sessions table (at least one day is needed for statistics)
  714. $sql = "DELETE FROM " . SESSIONS_TABLE . "
  715. WHERE session_user_id = " . ANONYMOUS . "
  716. AND session_time < " . (int) ($this->time_now - $session_length - $session_remove_limit);
  717. $db->sql_query($sql);
  718. // Get expired sessions, only most recent for each user
  719. $sql = "SELECT session_user_id, session_page, MAX(session_time) AS recent_time
  720. FROM " . SESSIONS_TABLE . "
  721. WHERE session_time < " . (int) ($this->time_now - $session_length) . "
  722. GROUP BY session_user_id, session_page";
  723. $result = $db->sql_query_limit($sql, $batch_size);
  724. $del_user_id = array();
  725. $del_sessions = 0;
  726. while ($row = $db->sql_fetchrow($result))
  727. {
  728. $sql = "UPDATE " . USERS_TABLE . "
  729. SET user_lastvisit = " . (int) $row['recent_time'] . ", user_session_page = '" . $db->sql_escape($row['session_page']) . "'
  730. WHERE user_id = " . (int) $row['session_user_id'];
  731. $db->sql_query($sql);
  732. $del_user_id[] = (int) $row['session_user_id'];
  733. $del_sessions++;
  734. }
  735. $db->sql_freeresult($result);
  736. if (sizeof($del_user_id))
  737. {
  738. // Delete expired sessions from more than 2 days (at least one day is needed for statistics)
  739. $sql = "DELETE FROM " . SESSIONS_TABLE . "
  740. WHERE " . $db->sql_in_set('session_user_id', $del_user_id) . "
  741. AND session_time < " . (int) ($this->time_now - $session_length - $session_remove_limit);
  742. $db->sql_query($sql);
  743. }
  744. if ($del_sessions < $batch_size)
  745. {
  746. // Less than 10 users, update gc timer ... else we want gc called again to delete other sessions
  747. set_config('session_last_gc', $this->time_now, true);
  748. set_config('cron_sessions_last_run', $this->time_now, true);
  749. if ($config['max_autologin_time'])
  750. {
  751. $sql = "DELETE FROM " . SESSIONS_KEYS_TABLE . "
  752. WHERE last_login < " . (time() - (86400 * (int) $config['max_autologin_time']));
  753. $db->sql_query($sql);
  754. }
  755. // only called from CRON; should be a safe workaround until the infrastructure gets going
  756. /*
  757. if (!class_exists('phpbb_captcha_factory'))
  758. {
  759. include(IP_ROOT_PATH . 'includes/captcha/captcha_factory.' . PHP_EXT);
  760. }
  761. phpbb_captcha_factory::garbage_collect($config['captcha_plugin']);
  762. */
  763. }
  764. return true;
  765. }
  766. /**
  767. * Bots session garbage collection
  768. *
  769. * This is needed to avoid bots filling up the whole sessions table due to SID removal... this is needed because in Icy Phoenix bots don't have USER_ID but are guests!
  770. */
  771. function bots_session_gc($clear_all = false)
  772. {
  773. global $db, $cache, $config;
  774. if (!$this->time_now)
  775. {
  776. $this->time_now = time();
  777. }
  778. $sql_extra = empty($clear_all) ? (" AND session_id <> '" . $db->sql_escape($this->session_id) . "' ") : '';
  779. if (!empty($this->browser))
  780. {
  781. $u_browser = trim(strtolower(substr($this->browser, 0, 254)));
  782. $sql = "DELETE FROM " . SESSIONS_TABLE . "
  783. WHERE session_browser = '" . $db->sql_escape($u_browser) . "'"
  784. . $sql_extra . "
  785. AND session_user_id = " . ANONYMOUS . "
  786. AND session_time < " . ($this->time_now - ONLINE_REFRESH);
  787. $db->sql_query($sql);
  788. }
  789. if (!empty($this->ip))
  790. {
  791. $sql = "DELETE FROM " . SESSIONS_TABLE . "
  792. WHERE session_ip = '" . $db->sql_escape($this->ip) . "'"
  793. . $sql_extra . "
  794. AND session_user_id = " . ANONYMOUS . "
  795. AND session_time < " . ($this->time_now - ONLINE_REFRESH);
  796. $db->sql_query($sql);
  797. }
  798. }
  799. /**
  800. * Confirm table garbage collection
  801. */
  802. function confirm_gc()
  803. {
  804. global $db, $cache, $config;
  805. // Clean some old sessions first!
  806. $this->session_gc();
  807. // We need to limit this SQL or we may have issue when sessions table has many records
  808. $limit = 2000;
  809. // We also query only those sessions in the last two hours... if a user didn't use its code, maybe he didn't need anymore... ;-)
  810. $sql = "SELECT session_id FROM " . SESSIONS_TABLE . " WHERE session_time > " . (int) (time() - 7200) . " ORDER BY session_time DESC LIMIT " . (int) $limit;
  811. $result = $db->sql_query($sql);
  812. $sessions_ids = $db->sql_fetchrowset($result);
  813. $db->sql_freeresult($result);
  814. if (!empty($sessions_ids))
  815. {
  816. $confirm_sql = '';
  817. foreach ($sessions_ids as $session_id)
  818. {
  819. $confirm_sql .= (!empty($confirm_sql) ? ', ' : '') . "'" . $session_id['session_id'] . "'";
  820. }
  821. $sql = "DELETE FROM " . CONFIRM_TABLE . "
  822. WHERE session_id NOT IN (" . $confirm_sql . ")";
  823. $db->sql_query($sql);
  824. }
  825. return true;
  826. }
  827. /**
  828. * Sets a cookie
  829. *
  830. * Sets a cookie of the given name with the specified data for the given length of time. If no time is specified, a session cookie will be set.
  831. *
  832. * @param string $name Name of the cookie, will be automatically prefixed with the phpBB cookie name. track becomes [cookie_name]_track then.
  833. * @param string $cookiedata The data to hold within the cookie
  834. * @param int $cookietime The expiration time as UNIX timestamp. If 0 is provided, a session cookie is set.
  835. */
  836. function set_cookie($name, $cookiedata, $cookietime)
  837. {
  838. global $config;
  839. // Old setcookie version...
  840. //setcookie($config['cookie_name'] . '_' . $name, $cookiedata, $cookietime, $config['cookie_path'], $config['cookie_domain'], $config['cookie_secure']);
  841. $name_data = rawurlencode($config['cookie_name'] . '_' . $name) . '=' . rawurlencode($cookiedata);
  842. $expire = gmdate('D, d-M-Y H:i:s \\G\\M\\T', $cookietime);
  843. $domain = (!$config['cookie_domain'] || ($config['cookie_domain'] == 'localhost') || ($config['cookie_domain'] == '127.0.0.1')) ? '' : '; domain=' . $config['cookie_domain'];
  844. header('Set-Cookie: ' . $name_data . (($cookietime) ? '; expires=' . $expire : '') . '; path=' . $config['cookie_path'] . $domain . ((!$config['cookie_secure']) ? '' : '; secure') . '; HttpOnly', false);
  845. }
  846. /**
  847. * Check for banned user
  848. *
  849. * Checks whether the supplied user is banned by id, ip or email. If no parameters
  850. * are passed to the method pre-existing session data is used. If $return is false
  851. * this routine does not return on finding a banned user, it outputs a relevant
  852. * message and stops execution.
  853. *
  854. * @param string|array $user_ips Can contain a string with one IP or an array of multiple IPs
  855. */
  856. function check_ban($user_id = false, $user_ips = false, $user_email = false, $return = false)
  857. {
  858. global $config, $cache, $db, $lang;
  859. if (defined('IN_CHECK_BAN'))
  860. {
  861. return;
  862. }
  863. $banned = false;
  864. $cache_ttl = 0;
  865. $where_sql = array();
  866. $sql = "SELECT *
  867. FROM " . BANLIST_TABLE . "
  868. WHERE ";
  869. // Determine which entries to check, only return those
  870. if ($user_email === false)
  871. {
  872. $where_sql[] = "(ban_email = '')";
  873. }
  874. if ($user_ips === false)
  875. {
  876. $where_sql[] = "(ban_ip = '')";
  877. }
  878. if ($user_id === false)
  879. {
  880. $where_sql[] = "(ban_userid = 0)";
  881. }
  882. else
  883. {
  884. $cache_ttl = ($user_id == ANONYMOUS) ? 86400 : 0;
  885. $_sql = "(ban_userid = " . $user_id;
  886. if ($user_email !== false)
  887. {
  888. $_sql .= " OR ban_email <> ''";
  889. }
  890. if ($user_ips !== false)
  891. {
  892. $_sql .= " OR ban_ip <> ''";
  893. }
  894. $_sql .= ")";
  895. $where_sql[] = $_sql;
  896. }
  897. $sql .= (sizeof($where_sql)) ? implode(" AND ", $where_sql) : "";
  898. $result = ((defined('CACHE_BAN_INFO') && CACHE_BAN_INFO) || !empty($cache_ttl)) ? $db->sql_query($sql, $cache_ttl, 'ban_', USERS_CACHE_FOLDER) : $db->sql_query($sql);
  899. $ban_triggered_by = 'user';
  900. while ($row = $db->sql_fetchrow($result))
  901. {
  902. if (($row['ban_userid'] == ANONYMOUS) && ($row['ban_ip'] == '') && ($row['ban_email'] == null))
  903. {
  904. $sql = "DELETE FROM " . BANLIST_TABLE . " WHERE ban_userid = '" . ANONYMOUS . "'";
  905. $db->sql_query($sql);
  906. $db->clear_cache('ban_', USERS_CACHE_FOLDER);
  907. continue;
  908. }
  909. if (!empty($row['ban_end']) && ($row['ban_end'] <= time()))
  910. {
  911. $sql = "DELETE FROM " . BANLIST_TABLE . " WHERE ban_id = '" . $row['ban_id'] . "'";
  912. $db->sql_query($sql);
  913. $db->clear_cache('ban_', USERS_CACHE_FOLDER);
  914. continue;
  915. }
  916. $ip_banned = false;
  917. if (!empty($row['ban_ip']))
  918. {
  919. if (!is_array($user_ips))
  920. {
  921. $ip_banned = preg_match('#^' . str_replace('\*', '.*?', preg_quote($row['ban_ip'], '#')) . '$#i', $user_ips);
  922. }
  923. else
  924. {
  925. foreach ($user_ips as $user_ip)
  926. {
  927. if (preg_match('#^' . str_replace('\*', '.*?', preg_quote($row['ban_ip'], '#')) . '$#i', $user_ip))
  928. {
  929. $ip_banned = true;
  930. break;
  931. }
  932. }
  933. }
  934. }
  935. if ((!empty($row['ban_userid']) && (intval($row['ban_userid']) == $user_id)) || $ip_banned || (!empty($row['ban_email']) && preg_match('#^' . str_replace('\*', '.*?', preg_quote($row['ban_email'], '#')) . '$#i', $user_email)))
  936. {
  937. $banned = true;
  938. $ban_row = $row;
  939. if (!empty($row['ban_userid']) && (intval($row['ban_userid']) == $user_id))
  940. {
  941. $ban_triggered_by = 'user';
  942. }
  943. elseif ($ip_banned)
  944. {
  945. $ban_triggered_by = 'ip';
  946. }
  947. else
  948. {
  949. $ban_triggered_by = 'email';
  950. }
  951. break;
  952. }
  953. }
  954. $db->sql_freeresult($result);
  955. if ($banned && !$return)
  956. {
  957. global $template;
  958. // The false here is needed, else the user is able to circumvent the ban.
  959. $this->session_kill(false);
  960. // We need to make sure we have at least the basic lang files included...
  961. if (empty($lang))
  962. {
  963. setup_basic_lang();
  964. }
  965. // A very special case... we are within the cron script which is not supposed to print out the ban message... show blank page
  966. if (defined('IN_CRON'))
  967. {
  968. garbage_collection();
  969. exit_handler();
  970. exit;
  971. }
  972. if (($ban_info['ban_pub_reason_mode'] == '0') || !isset($ban_info['ban_pub_reason_mode']))
  973. {
  974. $reason = $lang['You_been_banned'];
  975. }
  976. elseif ($ban_info['ban_pub_reason_mode'] == '1')
  977. {
  978. $reason = str_replace("\n", '<br />', $ban_info['ban_priv_reason']);
  979. }
  980. elseif ($ban_info['ban_pub_reason_mode'] == '2')
  981. {
  982. $reason = str_replace("\n", '<br />', $ban_info['ban_pub_reason']);
  983. }
  984. $reason = empty($reason) ? $lang['You_been_banned'] : $reason;
  985. message_die(CRITICAL_MESSAGE, $reason);
  986. }
  987. return ($banned && !empty($reason)) ? $reason : $banned;
  988. }
  989. /**
  990. * Check if ip is blacklisted
  991. * This should be called only where absolutly necessary
  992. *
  993. * Only IPv4 (rbldns does not support AAAA records/IPv6 lookups)
  994. *
  995. * @author satmd (from the php manual)
  996. * @param string $mode register/post - spamcop for example is ommitted for posting
  997. * @return false if ip is not blacklisted, else an array([checked server], [lookup])
  998. */
  999. function check_dnsbl($mode, $ip = false)
  1000. {
  1001. global $config;
  1002. if ($ip === false)
  1003. {
  1004. $ip = $this->ip;
  1005. }
  1006. $dnsbl_check = array(
  1007. 'sbl.spamhaus.org' => 'http://www.spamhaus.org/query/bl?ip=',
  1008. );
  1009. if ($mode == 'register')
  1010. {
  1011. $dnsbl_check['bl.spamcop.net'] = 'http://spamcop.net/bl.shtml?';
  1012. }
  1013. if ($ip)
  1014. {
  1015. $quads = explode('.', $ip);
  1016. $reverse_ip = $quads[3] . '.' . $quads[2] . '.' . $quads[1] . '.' . $quads[0];
  1017. // Need to be listed on all servers...
  1018. $listed = true;
  1019. $info = array();
  1020. foreach ($dnsbl_check as $dnsbl => $lookup)
  1021. {
  1022. if (phpbb_checkdnsrr($reverse_ip . '.' . $dnsbl . '.', 'A') === true)
  1023. {
  1024. $info = array($dnsbl, $lookup . $ip);
  1025. }
  1026. else
  1027. {
  1028. $listed = false;
  1029. }
  1030. }
  1031. if ($listed)
  1032. {
  1033. return $info;
  1034. }
  1035. }
  1036. return false;
  1037. }
  1038. /**
  1039. * Set/Update a persistent login key
  1040. *
  1041. * This method creates or updates a persistent session key. When a user makes
  1042. * use of persistent (formerly auto-) logins a key is generated and stored in the
  1043. * DB. When they revisit with the same key it's automatically updated in both the
  1044. * DB and cookie. Multiple keys may exist for each user representing different
  1045. * browsers or locations. As with _any_ non-secure-socket no passphrase login this
  1046. * remains vulnerable to exploit.
  1047. */
  1048. function set_login_key($user_id = false, $key = false, $user_ip = false)
  1049. {
  1050. global $config, $db;
  1051. $user_id = ($user_id === false) ? $this->data['user_id'] : $user_id;
  1052. $user_ip = ($user_ip === false) ? $this->ip : $user_ip;
  1053. $key = ($key === false) ? (($this->cookie_data['k']) ? $this->cookie_data['k'] : false) : $key;
  1054. $key_id = unique_id(hexdec(substr($this->session_id, 0, 8)));
  1055. $sql_ary = array(
  1056. 'key_id' => (string) md5($key_id),
  1057. 'last_ip' => (string) $this->ip,
  1058. 'last_login' => (int) time()
  1059. );
  1060. if (!$key)
  1061. {
  1062. $sql_ary += array(
  1063. 'user_id' => (int) $user_id
  1064. );
  1065. }
  1066. if ($key)
  1067. {
  1068. $sql = "UPDATE " . SESSIONS_KEYS_TABLE . "
  1069. SET " . $db->sql_build_array('UPDATE', $sql_ary) . "
  1070. WHERE user_id = " . (int) $user_id . "
  1071. AND key_id = '" . $db->sql_escape(md5($key)) . "'";
  1072. }
  1073. else
  1074. {
  1075. $sql = "INSERT INTO " . SESSIONS_KEYS_TABLE . " " . $db->sql_build_array('INSERT', $sql_ary);
  1076. }
  1077. $db->sql_query($sql);
  1078. $this->cookie_data['k'] = $key_id;
  1079. return false;
  1080. }
  1081. /**
  1082. * Reset all login keys for the specified user
  1083. *
  1084. * This method removes all current login keys for a specified (or the current)
  1085. * user. It will be called on password change to render old keys unusable
  1086. */
  1087. function reset_login_keys($user_id = false)
  1088. {
  1089. global $config, $db;
  1090. $user_id = ($user_id === false) ? (int) $this->data['user_id'] : (int) $user_id;
  1091. $sql = "DELETE FROM " . SESSIONS_KEYS_TABLE . "
  1092. WHERE user_id = " . (int) $user_id;
  1093. $db->sql_query($sql);
  1094. // If the user is logged in, update last visit info first before deleting sessions
  1095. $sql = "SELECT session_time, session_page
  1096. FROM " . SESSIONS_TABLE . "
  1097. WHERE session_user_id = " . (int) $user_id . "
  1098. ORDER BY session_time DESC";
  1099. $result = $db->sql_query_limit($sql, 1);
  1100. $row = $db->sql_fetchrow($result);
  1101. $db->sql_freeresult($result);
  1102. if ($row)
  1103. {
  1104. $sql = "UPDATE " . USERS_TABLE . "
  1105. SET user_lastvisit = " . (int) $row['session_time'] . ", user_session_page = '" . $db->sql_escape($row['session_page']) . "'
  1106. WHERE user_id = " . (int) $user_id;
  1107. $db->sql_query($sql);
  1108. }
  1109. // Let's also clear any current sessions for the specified user_id
  1110. // If it's the current user then we'll leave this session intact
  1111. $sql_where = 'session_user_id = ' . (int) $user_id;
  1112. $sql_where .= ($user_id === (int) $this->data['user_id']) ? " AND session_id <> '" . $db->sql_escape($this->session_id) . "'" : '';
  1113. $sql = "DELETE FROM " . SESSIONS_TABLE . "
  1114. WHERE $sql_where";
  1115. $db->sql_query($sql);
  1116. // We're changing the password of the current user and they have a key
  1117. // Lets regenerate it to be safe
  1118. if ($user_id === (int) $this->data['user_id'] && $this->cookie_data['k'])
  1119. {
  1120. $this->set_login_key($user_id);
  1121. }
  1122. }
  1123. /**
  1124. * Check if the request originated from the same page.
  1125. * @param bool $check_script_path If true, the path will be checked as well
  1126. */
  1127. function validate_referer($check_script_path = false)
  1128. {
  1129. global $config;
  1130. // no referer - nothing to validate, user's fault for turning it off (we only check on POST; so meta can't be the reason)
  1131. if (empty($this->referer) || empty($this->host))
  1132. {
  1133. return true;
  1134. }
  1135. $host = htmlspecialchars($this->host);
  1136. $ref = substr($this->referer, strpos($this->referer, '://') + 3);
  1137. if (!(stripos($ref, $host) === 0) && (!$config['force_server_vars'] || !(stripos($ref, $config['server_name']) === 0)))
  1138. {
  1139. return false;
  1140. }
  1141. elseif ($check_script_path && (rtrim($this->page['root_script_path'], '/') !== ''))
  1142. {
  1143. $ref = substr($ref, strlen($host));
  1144. $server_port = (!empty($_SERVER['SERVER_PORT'])) ? (int) $_SERVER['SERVER_PORT'] : (int) getenv('SERVER_PORT');
  1145. if (($server_port !== 80) && ($server_port !== 443) && (stripos($ref, ":$server_port") === 0))
  1146. {
  1147. $ref = substr($ref, strlen(":$server_port"));
  1148. }
  1149. if (!(stripos(rtrim($ref, '/'), rtrim($this->page['root_script_path'], '/')) === 0))
  1150. {
  1151. return false;
  1152. }
  1153. }
  1154. return true;
  1155. }
  1156. function unset_admin()
  1157. {
  1158. global $db;
  1159. $sql = "UPDATE " . SESSIONS_TABLE . "
  1160. SET session_admin = 0
  1161. WHERE session_id = '" . $db->sql_escape($this->session_id) . "'";
  1162. $db->sql_query($sql);
  1163. }
  1164. /**
  1165. * Bots check...
  1166. */
  1167. function bots_process()
  1168. {
  1169. global $config;
  1170. if (!empty($this->data))
  1171. {
  1172. $this->data['is_bot'] = false;
  1173. $this->data['bot_id'] = false;
  1174. if ($this->data['user_id'] == ANONYMOUS)
  1175. {
  1176. $bot_name_tmp = bots_parse($this->ip, $config['bots_color'], $this->browser, true);
  1177. $this->data['bot_id'] = $bot_name_tmp['name'];
  1178. if ($this->data['bot_id'] !== false)
  1179. {
  1180. $this->data['is_bot'] = true;
  1181. bots_table_update($bot_name_tmp['id']);
  1182. }
  1183. }
  1184. }
  1185. }
  1186. /**
  1187. * Process referers
  1188. */
  1189. function process_referer()
  1190. {
  1191. global $db, $cache, $config;
  1192. if (!empty($this->referer))
  1193. {
  1194. $this_page = $this->page;
  1195. $this_page_url = preg_replace('/(\?)?(&amp;|&)?sid=[a-z0-9]+/', '', $this_page['page_full']);
  1196. $ref_url = $this->referer;
  1197. $ref_url_array = parse_url($ref_url);
  1198. $ref_host = $ref_url_array['host'];
  1199. $ref_process = true;
  1200. if (strpos(strtolower($ref_url), strtolower($this->host . $config['script_path'])) !== false)
  1201. {
  1202. $ref_process = false;
  1203. }
  1204. if (strpos(strtolower($ref_host), str_replace('/', '', strtolower($config['server_name']))) !== false)
  1205. {
  1206. $ref_process = false;
  1207. }
  1208. if (!empty($ref_process))
  1209. {
  1210. include(IP_ROOT_PATH . 'includes/blacklist.' . PHP_EXT);
  1211. if (!empty($blacklist['host']))
  1212. {
  1213. foreach ($blacklist['host'] as $blacklist_entry)
  1214. {
  1215. if (strpos(strtolower($ref_host), strtolower($blacklist_entry)) !== false)
  1216. {
  1217. $ref_process = false;
  1218. break;
  1219. }
  1220. }
  1221. }
  1222. if (!empty($ref_process))
  1223. {
  1224. $sql_where_extra = !empty($this_page_url) ? (" AND t_url = '" . $db->sql_escape($this_page_url) . "' ") : "";
  1225. $sql = "SELECT url FROM " . REFERERS_TABLE . " WHERE url = '" . $db->sql_escape($ref_url) . "'" . $sql_where_extra . " LIMIT 1";
  1226. $result = $db->sql_query($sql);
  1227. $row = $db->sql_fetchrow($result);
  1228. if (empty($row))
  1229. {
  1230. $ref_insert_array = array(
  1231. 'host' => $ref_host,
  1232. 'url' => $ref_url,
  1233. 't_url' => $this_page_url,
  1234. 'ip' => $this->ip,
  1235. 'hits' => 1,
  1236. 'firstvisit' => time(),
  1237. 'lastvisit' => time(),
  1238. );
  1239. $sql = "INSERT INTO " . REFERERS_TABLE . " " . $db->sql_build_insert_update($ref_insert_array, true);
  1240. $result = $db->sql_query($sql);
  1241. }
  1242. else
  1243. {
  1244. $sql = "UPDATE " . REFERERS_TABLE . "
  1245. SET hits = hits + 1, lastvisit = " . time() . ", ip = '" . $db->sql_escape($user_ip) . "'
  1246. WHERE url = '" . $db->sql_escape($ref_url) . "'" . $sql_where_extra;
  1247. $result = $db->sql_query($sql);
  1248. }
  1249. }
  1250. }
  1251. }
  1252. }
  1253. // UPI2DB - BEGIN
  1254. /**
  1255. * UPI2DB
  1256. */
  1257. function upi2db()
  1258. {
  1259. global $config;
  1260. $this->data['upi2db_access'] = false;
  1261. if (!$config['board_disable'] && $this->data['session_logged_in'] && $config['upi2db_on'])
  1262. {
  1263. $this->data['upi2db_access'] = check_upi2db_on($this->data);
  1264. if ($this->data['upi2db_access'] != false)
  1265. {
  1266. $this->data['always_read'] = select_always_read($this->data);
  1267. $this->data['auth_forum_id'] = auth_forum_read($this->data);
  1268. sync_database($this->data);
  1269. }
  1270. }
  1271. }
  1272. // UPI2DB - END
  1273. }
  1274. /**
  1275. * Base user class
  1276. *
  1277. * This is the overarching class which contains (through session extend)
  1278. * all methods utilised for user functionality during a session.
  1279. *
  1280. * @package phpBB3
  1281. */
  1282. class user extends session
  1283. {
  1284. var $lang = array();
  1285. var $help = array();
  1286. var $theme = array();
  1287. var $date_format;
  1288. var $timezone;
  1289. var $dst;
  1290. var $lang_name = false;
  1291. var $lang_id = false;
  1292. var $lang_path;
  1293. var $img_lang;
  1294. var $img_array = array();
  1295. // Able to add new options (up to id 31)
  1296. var $keyoptions = array(
  1297. 'viewimg' => 0,
  1298. 'viewflash' => 1,
  1299. 'viewsmilies' => 2,
  1300. 'viewsigs' => 3,
  1301. 'viewavatars' => 4,
  1302. 'viewcensors' => 5,
  1303. 'attachsig' => 6,
  1304. 'bbcode' => 8,
  1305. 'smilies' => 9,
  1306. 'popuppm' => 10,
  1307. 'sig_bbcode' => 15,
  1308. 'sig_smilies' => 16,
  1309. 'sig_links' => 17
  1310. );
  1311. /**
  1312. * Constructor to set the lang path
  1313. */
  1314. function __construct()
  1315. {
  1316. $this->lang_path = IP_ROOT_PATH . 'language/';
  1317. }
  1318. /**
  1319. * Function to set custom language path (able to use directory outside of phpBB)
  1320. *
  1321. * @param string $lang_path New language path used.
  1322. * @access public
  1323. */
  1324. function set_custom_lang_path($lang_path)
  1325. {
  1326. $this->lang_path = $lang_path;
  1327. if (substr($this->lang_path, -1) != '/')
  1328. {
  1329. $this->lang_path .= '/';
  1330. }
  1331. }
  1332. /**
  1333. * Setup basic user-specific items (style, language, ...)
  1334. */
  1335. function setup($lang_set = false, $style = false)
  1336. {
  1337. global $db, $cache, $config, $auth, $template;
  1338. // We need $lang declared as global to make sure we do not miss extra $lang vars added using this function
  1339. global $theme, $images, $lang, $nav_separator;
  1340. global $class_settings, $tree;
  1341. // Get all settings
  1342. $class_settings->setup_settings();
  1343. // Mighty Gorgon - Change Lang - BEGIN
  1344. $test_language = request_var(LANG_URL, '');
  1345. if (!empty($test_language))
  1346. {
  1347. $test_language = str_replace(array('.', '/'), '', urldecode($test_language));
  1348. $config['default_lang'] = file_exists(@phpbb_realpath($this->lang_path . 'lang_' . basename($test_language) . '/lang_main.' . PHP_EXT)) ? $test_language : $config['default_lang'];
  1349. $this->set_cookie('lang', $config['default_lang'], $user->cookie_expire);
  1350. }
  1351. else
  1352. {
  1353. if (isset($_COOKIE[$config['cookie_name'] . '_lang']) && file_exists(@phpbb_realpath($this->lang_path . 'lang_' . basename($_COOKIE[$config['cookie_name'] . '_lang']) . '/lang_main.' . PHP_EXT)))
  1354. {
  1355. $config['default_lang'] = $_COOKIE[$config['cookie_name'] . '_lang'];
  1356. }
  1357. }
  1358. // Mighty Gorgon - Change Lang - END
  1359. if ($this->data['user_id'] != ANONYMOUS)
  1360. {
  1361. $this->lang_name = ((file_exists($this->lang_path . 'lang_' . basename($this->data['user_lang']) . '/lang_main.' . PHP_EXT)) ? basename($this->data['user_lang']) : basename($config['default_lang']));
  1362. $this->date_format = $this->data['user_dateformat'];
  1363. $this->timezone = $this->data['user_timezone'] * 3600;
  1364. $this->dst = $this->data['user_dst'] * 3600;
  1365. $config['board_timezone'] = !empty($this->data['user_timezone']) ? $this->data['user_timezone'] : $config['board_timezone'];
  1366. $config['default_dateformat'] = !empty($this->data['user_dateformat']) ? $this->data['user_dateformat'] : $config['default_dateformat'];
  1367. $config['topics_per_page'] = !empty($this->data['user_topics_per_page']) ? $this->data['user_topics_per_page'] : $config['topics_per_page'];
  1368. $config['posts_per_page'] = !empty($this->data['user_posts_per_page']) ? $this->data['user_posts_per_page'] : $config['posts_per_page'];
  1369. $config['hot_threshold'] = !empty($this->data['user_hot_threshold']) ? $this->data['user_hot_threshold'] : $config['hot_threshold'];
  1370. // Store CMS AUTH - BEGIN
  1371. if (empty($this->data['user_cms_auth']))
  1372. {
  1373. $auth_array = array();
  1374. $auth_to_get_array = array('cmsl_admin', 'cmss_admin', 'cmsb_admin');
  1375. foreach ($auth_to_get_array as $auth_to_get)
  1376. {
  1377. $auth_getf = $auth->acl_getf($auth_to_get, true);
  1378. foreach ($auth_getf as $auth_id => $auth_value)
  1379. {
  1380. $auth_array[$auth_to_get][$auth_id] = $auth_value[$auth_to_get];
  1381. }
  1382. }
  1383. $this->data['user_cms_auth'] = $auth_array;
  1384. $sql = "UPDATE " . USERS_TABLE . "
  1385. SET user_cms_auth = '" . $db->sql_escape(serialize($this->data['user_cms_auth'])) . "'
  1386. WHERE user_id = " . $this->data['user_id'];
  1387. $db->sql_query($sql);
  1388. }
  1389. else
  1390. {
  1391. $this->data['user_cms_auth'] = unserialize($this->data['user_cms_auth']);
  1392. }
  1393. // Store CMS AUTH - END
  1394. }
  1395. else
  1396. {
  1397. $this->lang_name = basename($config['default_lang']);
  1398. $this->date_format = $config['default_dateformat'];
  1399. $this->timezone = $config['board_timezone'] * 3600;
  1400. $this->dst = $config['board_dst'] * 3600;
  1401. }
  1402. // If we've had to change the value in any way then let's write it back to the database before we go any further since it means there is something wrong with it
  1403. if (($this->data['user_id'] != ANONYMOUS) && ($this->data['user_lang'] !== $this->lang_name) && file_exists($this->lang_path . 'lang_' . basename($this->lang_name) . '/lang_main.' . PHP_EXT))
  1404. {
  1405. $sql = 'UPDATE ' . USERS_TABLE . "
  1406. SET user_lang = '" . $db->sql_escape($this->lang_name) . "'
  1407. WHERE user_lang = '" . $this->data['user_lang'] . "'";
  1408. $result = $db->sql_query($sql);
  1409. $this->data['user_lang'] = $this->lang_name;
  1410. }
  1411. elseif (($this->data['user_id'] === ANONYMOUS) && ($config['default_lang'] !== $this->lang_name) && file_exists($this->lang_path . 'lang_' . basename($this->lang_name) . '/lang_main.' . PHP_EXT))
  1412. {
  1413. $sql = 'UPDATE ' . CONFIG_TABLE . "
  1414. SET config_value = '" . $db->sql_escape($this->lang_name) . "'
  1415. WHERE config_name = 'default_lang'";
  1416. $result = $db->sql_query($sql);
  1417. }
  1418. $config['default_lang'] = $this->lang_name;
  1419. // We include common language file here to not load it every time a custom language file is included
  1420. $lang = &$this->lang;
  1421. setup_basic_lang();
  1422. $this->add_lang($lang_set);
  1423. unset($lang_set);
  1424. $nav_separator = empty($nav_separator) ? (empty($lang['Nav_Separator']) ? '&nbsp;&raquo;&nbsp;' : $lang['Nav_Separator']) : $nav_separator;
  1425. if (empty($tree['auth']))
  1426. {
  1427. get_user_tree($this->data);
  1428. }
  1429. // MG Logs - BEGIN
  1430. if ($config['mg_log_actions'] || $config['db_log_actions'])
  1431. {
  1432. include(IP_ROOT_PATH . 'includes/log_http_cmd.' . PHP_EXT);
  1433. }
  1434. // MG Logs - END
  1435. // UPI2DB - BEGIN
  1436. if (!defined('IN_CMS') && $this->data['upi2db_access'])
  1437. {
  1438. if (!defined('UPI2DB_UNREAD'))
  1439. {
  1440. $this->data['upi2db_unread'] = upi2db_unread();
  1441. }
  1442. }
  1443. else
  1444. {
  1445. $this->data['upi2db_unread'] = array();
  1446. }
  1447. // UPI2DB - END
  1448. // Mighty Gorgon Edit
  1449. // DISABLED BY MG
  1450. /*
  1451. //if (!empty($_GET['style']) && $auth->acl_get('a_styles') && !defined('IN_ADMIN') && !defined('IN_CMS'))
  1452. if (!empty($_GET['style']) && !defined('IN_ADMIN') && !defined('IN_CMS'))
  1453. {
  1454. global $SID, $_EXTRA_URL;
  1455. $style = request_var(STYLE_URL, 0);
  1456. $SID .= '&amp;' . STYLE_URL . '=' . $style;
  1457. $_EXTRA_URL = array(STYLE_URL . '=' . $style);
  1458. }
  1459. else
  1460. {
  1461. // Set up style
  1462. $style = ($style) ? $style : ((!$config['override_user_style']) ? $this->data['user_style'] : $config['default_style']);
  1463. }
  1464. */
  1465. // Call phpbb_user_session_handler() in case external application want to "bend" some variables or replace classes...
  1466. // After calling it we continue script execution...
  1467. phpbb_user_session_handler();
  1468. // If this function got called from the error handler we are finished here.
  1469. if (defined('IN_ERROR_HANDLER'))
  1470. {
  1471. return;
  1472. }
  1473. // Disable board if the install/ directory is still present
  1474. // For the brave development army we do not care about this, else we need to comment out this everytime we develop locally
  1475. // DISABLED BY MG
  1476. /*
  1477. if (!defined('DEBUG_EXTRA') && DEBUG_EXTRA && !defined('IN_ADMIN') && !defined('IN_CMS') && !defined('IN_INSTALL') && !defined('IN_LOGIN') && file_exists(IP_ROOT_PATH . 'install') && !is_file(IP_ROOT_PATH . 'install'))
  1478. {
  1479. // Adjust the message slightly according to the permissions
  1480. if ($auth->acl_gets('a_', 'm_') || $auth->acl_getf_global('m_'))
  1481. {
  1482. $message = 'REMOVE_INSTALL';
  1483. }
  1484. else
  1485. {
  1486. $message = (!empty($config['board_disable_msg'])) ? $config['board_disable_msg'] : 'BOARD_DISABLE';
  1487. }
  1488. trigger_error($message);
  1489. }
  1490. */
  1491. // Is board disabled and user not an admin or moderator?
  1492. // DISABLED BY MG
  1493. /*
  1494. if ($config['board_disable'] && !defined('IN_LOGIN') && !$auth->acl_gets('a_', 'm_') && !$auth->acl_getf_global('m_'))
  1495. {
  1496. if ($this->data['is_bot'])
  1497. {
  1498. send_status_line(503, 'Service Unavailable');
  1499. }
  1500. $message = (!empty($config['board_disable_msg'])) ? $config['board_disable_msg'] : 'BOARD_DISABLE';
  1501. trigger_error($message);
  1502. }
  1503. */
  1504. // Is load exceeded?
  1505. // DISABLED BY MG
  1506. /*
  1507. if ($config['limit_load'] && $this->load !== false)
  1508. {
  1509. if ($this->load > floatval($config['limit_load']) && !defined('IN_LOGIN') && !defined('IN_ADMIN'))
  1510. {
  1511. // Set board disabled to true to let the admins/mods get the proper notification
  1512. $config['board_disable'] = '1';
  1513. if (!$auth->acl_gets('a_', 'm_') && !$auth->acl_getf_global('m_'))
  1514. {
  1515. if ($this->data['is_bot'])
  1516. {
  1517. send_status_line(503, 'Service Unavailable');
  1518. }
  1519. trigger_error('BOARD_UNAVAILABLE');
  1520. }
  1521. }
  1522. }
  1523. */
  1524. // DISABLED BY MG
  1525. /*
  1526. if (isset($this->data['session_viewonline']))
  1527. {
  1528. // Make sure the user is able to hide his session
  1529. if (!$this->data['session_viewonline'])
  1530. {
  1531. // Reset online status if not allowed to hide the session...
  1532. if (!$auth->acl_get('u_hideonline'))
  1533. {
  1534. $sql = 'UPDATE ' . SESSIONS_TABLE . '
  1535. SET session_viewonline = 1
  1536. WHERE session_user_id = ' . $this->data['user_id'];
  1537. $db->sql_query($sql);
  1538. $this->data['session_viewonline'] = 1;
  1539. }
  1540. }
  1541. elseif (!$this->data['user_allow_viewonline'])
  1542. {
  1543. // the user wants to hide and is allowed to -> cloaking device on.
  1544. if ($auth->acl_get('u_hideonline'))
  1545. {
  1546. $sql = 'UPDATE ' . SESSIONS_TABLE . '
  1547. SET session_viewonline = 0
  1548. WHERE session_user_id = ' . $this->data['user_id'];
  1549. $db->sql_query($sql);
  1550. $this->data['session_viewonline'] = 0;
  1551. }
  1552. }
  1553. }
  1554. */
  1555. // Set up style
  1556. $current_default_style = $config['default_style'];
  1557. $change_style = false;
  1558. $is_mobile = is_mobile();
  1559. // For debugging purpose you can force this to true
  1560. //$this->data['is_mobile'] = true;
  1561. // We need to store somewhere if the user has the mobile style enabled... so we can output a link to switch between mobile style and norma style
  1562. $this->data['mobile_style'] = false;
  1563. $disable_mobile_style = false;
  1564. // MOBILE STYLE DISABLING - BEGIN
  1565. // Let's check if the user wants to disable the mobile style
  1566. if(isset($_GET['mob']))
  1567. {
  1568. $mob_get = (isset($_GET['mob']) && (intval($_GET['mob']) == 0)) ? 0 : 1;
  1569. $_GET['mob'] = $mob_get;
  1570. $_COOKIE[$config['cookie_name'] . '_mob'] = $mob_get;
  1571. $this->set_cookie('mob', $mob_get, $user->cookie_expire);
  1572. if (empty($mob_get))
  1573. {
  1574. $disable_mobile_style = true;
  1575. }
  1576. }
  1577. $mob_cok = (isset($_COOKIE[$config['cookie_name'] . '_mob']) && (intval($_COOKIE[$config['cookie_name'] . '_mob']) == 0)) ? false : true;
  1578. if (empty($mob_cok))
  1579. {
  1580. $disable_mobile_style = true;
  1581. }
  1582. // MOBILE STYLE DISABLING - END
  1583. if (empty($disable_mobile_style) && !empty($this->data['is_mobile']) && !defined('IN_CMS') && !defined('IN_ADMIN'))
  1584. {
  1585. $this->data['mobile_style'] = true;
  1586. $_COOKIE[$config['cookie_name'] . '_mob'] = 1;
  1587. $this->set_cookie('mob', 1, $user->cookie_expire);
  1588. $theme = setup_mobile_style();
  1589. }
  1590. else
  1591. {
  1592. if (empty($config['override_user_style']))
  1593. {
  1594. // Mighty Gorgon - Change Style - BEGIN
  1595. // Check cookie as well!!!
  1596. $test_style = request_var(STYLE_URL, 0);
  1597. if ($test_style > 0)
  1598. {
  1599. $config['default_style'] = urldecode($test_style);
  1600. $config['default_style'] = (check_style_exists($config['default_style']) == false) ? $current_default_style : $config['default_style'];
  1601. $this->set_cookie('style', $config['default_style'], $user->cookie_expire);
  1602. $change_style = true;
  1603. }
  1604. else
  1605. {
  1606. if (isset($_COOKIE[$config['cookie_name'] . '_style']) && (check_style_exists($_COOKIE[$config['cookie_name'] . '_style']) != false))
  1607. {
  1608. $config['default_style'] = $_COOKIE[$config['cookie_name'] . '_style'];
  1609. }
  1610. }
  1611. // Mighty Gorgon - Change Style - END
  1612. $style = (($this->data['user_id'] != ANONYMOUS) && ($this->data['user_style'] > 0) && empty($change_style)) ? $this->data['user_style'] : $config['default_style'];
  1613. if ($theme = setup_style($style, $current_default_style))
  1614. {
  1615. if (($this->data['user_id'] != ANONYMOUS) && !empty($change_style))
  1616. {
  1617. // user logged in --> save new style ID in user profile
  1618. $sql = "UPDATE " . USERS_TABLE . "
  1619. SET user_style = " . $theme['themes_id'] . "
  1620. WHERE user_id = " . $this->data['user_id'];
  1621. $db->sql_query($sql);
  1622. $this->data['user_style'] = $theme['themes_id'];
  1623. }
  1624. return;
  1625. }
  1626. }
  1627. $theme = setup_style($config['default_style'], $current_default_style);
  1628. }
  1629. return;
  1630. }
  1631. /**
  1632. * More advanced language substitution
  1633. * Function to mimic sprintf() with the possibility of using phpBB's language system to substitute nullar/singular/plural forms.
  1634. * Params are the language key and the parameters to be substituted.
  1635. * This function/functionality is inspired by SHS` and Ashe.
  1636. *
  1637. * Example call: <samp>$user->lang('NUM_POSTS_IN_QUEUE', 1);</samp>
  1638. */
  1639. function lang()
  1640. {
  1641. $args = func_get_args();
  1642. $key = $args[0];
  1643. if (is_array($key))
  1644. {
  1645. $lang = &$this->lang[array_shift($key)];
  1646. foreach ($key as $_key)
  1647. {
  1648. $lang = &$lang[$_key];
  1649. }
  1650. }
  1651. else
  1652. {
  1653. $lang = &$this->lang[$key];
  1654. }
  1655. // Return if language string does not exist
  1656. if (!isset($lang) || (!is_string($lang) && !is_array($lang)))
  1657. {
  1658. return $key;
  1659. }
  1660. // If the language entry is a string, we simply mimic sprintf() behaviour
  1661. if (is_string($lang))
  1662. {
  1663. if (sizeof($args) == 1)
  1664. {
  1665. return $lang;
  1666. }
  1667. // Replace key with language entry and simply pass along...
  1668. $args[0] = $lang;
  1669. return call_user_func_array('sprintf', $args);
  1670. }
  1671. // It is an array... now handle different nullar/singular/plural forms
  1672. $key_found = false;
  1673. // We now get the first number passed and will select the key based upon this number
  1674. for ($i = 1, $num_args = sizeof($args); $i < $num_args; $i++)
  1675. {
  1676. if (is_int($args[$i]))
  1677. {
  1678. $numbers = array_keys($lang);
  1679. foreach ($numbers as $num)
  1680. {
  1681. if ($num > $args[$i])
  1682. {
  1683. break;
  1684. }
  1685. $key_found = $num;
  1686. }
  1687. }
  1688. }
  1689. // Ok, let's check if the key was found, else use the last entry (because it is mostly the plural form)
  1690. if ($key_found === false)
  1691. {
  1692. $numbers = array_keys($lang);
  1693. $key_found = end($numbers);
  1694. }
  1695. // Use the language string we determined and pass it to sprintf()
  1696. $args[0] = $lang[$key_found];
  1697. return call_user_func_array('sprintf', $args);
  1698. }
  1699. /**
  1700. * Add Language Items - use_db and use_help are assigned where needed (only use them to force inclusion)
  1701. *
  1702. * @param mixed $lang_set specifies the language entries to include
  1703. * @param bool $use_db internal variable for recursion, do not use
  1704. * @param bool $use_help internal variable for recursion, do not use
  1705. *
  1706. * Examples:
  1707. * <code>
  1708. * $lang_set = array('posting', 'help' => 'faq');
  1709. * $lang_set = array('posting', 'viewtopic', 'help' => array('bbcode', 'faq'))
  1710. * $lang_set = array(array('posting', 'viewtopic'), 'help' => array('bbcode', 'faq'))
  1711. * $lang_set = 'posting'
  1712. * $lang_set = array('help' => 'faq', 'db' => array('help:faq', 'posting'))
  1713. * </code>
  1714. */
  1715. function add_lang($lang_set, $use_db = false, $use_help = false)
  1716. {
  1717. if (is_array($lang_set))
  1718. {
  1719. foreach ($lang_set as $key => $lang_file)
  1720. {
  1721. // Please do not delete this line.
  1722. // We have to force the type here, else [array] language inclusion will not work
  1723. $key = (string) $key;
  1724. if ($key == 'db')
  1725. {
  1726. $this->add_lang($lang_file, true, $use_help);
  1727. }
  1728. elseif ($key == 'help')
  1729. {
  1730. $this->add_lang($lang_file, $use_db, true);
  1731. }
  1732. elseif (!is_array($lang_file))
  1733. {
  1734. $this->set_lang($this->lang, $this->help, $lang_file, $use_db, $use_help);
  1735. }
  1736. else
  1737. {
  1738. $this->add_lang($lang_file, $use_db, $use_help);
  1739. }
  1740. }
  1741. unset($lang_set);
  1742. }
  1743. elseif ($lang_set)
  1744. {
  1745. $this->set_lang($this->lang, $this->help, $lang_set, $use_db, $use_help);
  1746. }
  1747. }
  1748. /**
  1749. * Set language entry (called by add_lang)
  1750. * @access private
  1751. */
  1752. function set_lang(&$lang, &$help, $lang_file, $use_db = false, $use_help = false)
  1753. {
  1754. // In Icy Phoenix we still need to keep this global assignment for backward compatibility
  1755. global $lang;
  1756. // Make sure the language name is set (if the user setup did not happen it is not set)
  1757. if (!$this->lang_name)
  1758. {
  1759. global $config;
  1760. $this->lang_name = basename($config['default_lang']);
  1761. }
  1762. // $lang == $this->lang
  1763. // $help == $this->help
  1764. // - add appropriate variables here, name them as they are used within the language file...
  1765. if (!$use_db)
  1766. {
  1767. if ($use_help && (strpos($lang_file, '/') !== false))
  1768. {
  1769. $language_filename = $this->lang_path . 'lang_' . $this->lang_name . '/' . substr($lang_file, 0, stripos($lang_file, '/') + 1) . 'help_' . substr($lang_file, stripos($lang_file, '/') + 1) . '.' . PHP_EXT;
  1770. }
  1771. else
  1772. {
  1773. $language_filename = $this->lang_path . 'lang_' . $this->lang_name . '/' . (($use_help) ? 'help_' : '') . $lang_file . '.' . PHP_EXT;
  1774. }
  1775. if (!file_exists($language_filename))
  1776. {
  1777. global $config;
  1778. if ($this->lang_name == 'english')
  1779. {
  1780. // The user's selected language is missing the file, the board default's language is missing the file, and the file doesn't exist in /en.
  1781. $language_filename = str_replace($this->lang_path . 'lang_' . 'english', $this->lang_path . 'lang_' . $this->data['user_lang'], $language_filename);
  1782. trigger_error('Language file ' . $language_filename . ' couldn\'t be opened.', E_USER_ERROR);
  1783. }
  1784. elseif ($this->lang_name == basename($config['default_lang']))
  1785. {
  1786. // Fall back to the English Language
  1787. $this->lang_name = 'english';
  1788. $this->set_lang($lang, $help, $lang_file, $use_db, $use_help);
  1789. }
  1790. elseif ($this->lang_name == $this->data['user_lang'])
  1791. {
  1792. // Fall back to the board default language
  1793. $this->lang_name = basename($config['default_lang']);
  1794. $this->set_lang($lang, $help, $lang_file, $use_db, $use_help);
  1795. }
  1796. // Reset the lang name
  1797. $this->lang_name = (file_exists($this->lang_path . 'lang_' . $this->data['user_lang'] . '/lang_main.' . PHP_EXT)) ? $this->data['user_lang'] : basename($config['default_lang']);
  1798. return;
  1799. }
  1800. // Do not suppress error if in DEBUG_EXTRA mode
  1801. $include_result = (defined('DEBUG_EXTRA') && DEBUG_EXTRA) ? (include($language_filename)) : (@include($language_filename));
  1802. if ($include_result === false)
  1803. {
  1804. trigger_error('Language file ' . $language_filename . ' couldn\'t be opened.', E_USER_ERROR);
  1805. }
  1806. }
  1807. elseif ($use_db)
  1808. {
  1809. // Get Database Language Strings
  1810. // Put them into $lang if nothing is prefixed, put them into $help if help: is prefixed
  1811. // For example: help:faq, posting
  1812. }
  1813. }
  1814. /**
  1815. * Format user date
  1816. *
  1817. * @param int $gmepoch unix timestamp
  1818. * @param string $format date format in date() notation. | used to indicate relative dates, for example |d m Y|, h:i is translated to Today, h:i.
  1819. * @param bool $forcedate force non-relative date format.
  1820. *
  1821. * @return mixed translated date
  1822. */
  1823. function format_date($gmepoch, $format = false, $forcedate = false)
  1824. {
  1825. static $midnight;
  1826. static $date_cache;
  1827. $format = (!$format) ? $this->date_format : $format;
  1828. $now = time();
  1829. $delta = $now - $gmepoch;
  1830. if (!isset($date_cache[$format]))
  1831. {
  1832. // Is the user requesting a friendly date format (i.e. 'Today 12:42')?
  1833. $date_cache[$format] = array(
  1834. 'is_short' => strpos($format, '|'),
  1835. 'format_short' => substr($format, 0, strpos($format, '|')) . '||' . substr(strrchr($format, '|'), 1),
  1836. 'format_long' => str_replace('|', '', $format),
  1837. 'lang' => $this->lang['datetime'],
  1838. );
  1839. // Short representation of month in format? Some languages use different terms for the long and short format of May
  1840. if (((strpos($format, '\M') === false) && (strpos($format, 'M') !== false)) || ((strpos($format, '\r') === false) && (strpos($format, 'r') !== false)))
  1841. {
  1842. $months = array('Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec');
  1843. foreach ($months as $month)
  1844. {
  1845. $date_cache[$format]['lang'][$month] = $this->lang['datetime'][$month . '_short'];
  1846. }
  1847. }
  1848. }
  1849. // Zone offset
  1850. $zone_offset = $this->timezone + $this->dst;
  1851. // Show date <= 1 hour ago as 'xx min ago' but not greater than 60 seconds in the future
  1852. // A small tolerence is given for times in the future but in the same minute are displayed as '< than a minute ago'
  1853. if (($delta <= 3600) && ($delta > -60) && (($delta >= -5) || (($now / 60) % 60) == (($gmepoch / 60) % 60)) && ($date_cache[$format]['is_short'] !== false) && !$forcedate && isset($this->lang['datetime']['AGO']))
  1854. {
  1855. return $this->lang(array('datetime', 'AGO'), max(0, (int) floor($delta / 60)));
  1856. }
  1857. if (!$midnight)
  1858. {
  1859. list($d, $m, $y) = explode(' ', gmdate('j n Y', time() + $zone_offset));
  1860. $midnight = gmmktime(0, 0, 0, $m, $d, $y) - $zone_offset;
  1861. }
  1862. if (($date_cache[$format]['is_short'] !== false) && !$forcedate && !(($gmepoch < ($midnight - 86400)) || ($gmepoch > ($midnight + 172800))))
  1863. {
  1864. $day = false;
  1865. if ($gmepoch > ($midnight + 86400))
  1866. {
  1867. $day = 'TOMORROW';
  1868. }
  1869. elseif ($gmepoch > $midnight)
  1870. {
  1871. $day = 'TODAY';
  1872. }
  1873. elseif ($gmepoch > ($midnight - 86400))
  1874. {
  1875. $day = 'YESTERDAY';
  1876. }
  1877. if ($day !== false)
  1878. {
  1879. return str_replace('||', $this->lang['datetime'][$day], strtr(@gmdate($date_cache[$format]['format_short'], $gmepoch + $zone_offset), $date_cache[$format]['lang']));
  1880. }
  1881. }
  1882. return strtr(@gmdate($date_cache[$format]['format_long'], $gmepoch + $zone_offset), $date_cache[$format]['lang']);
  1883. }
  1884. /**
  1885. * Get option bit field from user options.
  1886. *
  1887. * @param int $key option key, as defined in $keyoptions property.
  1888. * @param int $data bit field value to use, or false to use $this->data['user_options']
  1889. * @return bool true if the option is set in the bit field, false otherwise
  1890. */
  1891. function optionget($key, $data = false)
  1892. {
  1893. $var = ($data !== false) ? $data : $this->data['user_options'];
  1894. return phpbb_optionget($this->keyoptions[$key], $var);
  1895. }
  1896. /**
  1897. * Set option bit field for user options.
  1898. *
  1899. * @param int $key Option key, as defined in $keyoptions property.
  1900. * @param bool $value True to set the option, false to clear the option.
  1901. * @param int $data Current bit field value, or false to use $this->data['user_options']
  1902. * @return int|bool If $data is false, the bit field is modified and
  1903. * written back to $this->data['user_options'], and
  1904. * return value is true if the bit field changed and
  1905. * false otherwise. If $data is not false, the new
  1906. * bitfield value is returned.
  1907. */
  1908. function optionset($key, $value, $data = false)
  1909. {
  1910. $var = ($data !== false) ? $data : $this->data['user_options'];
  1911. $new_var = phpbb_optionset($this->keyoptions[$key], $value, $var);
  1912. if ($data === false)
  1913. {
  1914. if ($new_var != $var)
  1915. {
  1916. $this->data['user_options'] = $new_var;
  1917. return true;
  1918. }
  1919. else
  1920. {
  1921. return false;
  1922. }
  1923. }
  1924. else
  1925. {
  1926. return $new_var;
  1927. }
  1928. }
  1929. }
  1930. ?>