PageRenderTime 79ms CodeModel.GetById 41ms RepoModel.GetById 1ms app.codeStats 0ms

/phpBB/includes/classes/user.php

https://github.com/prototech/phpbb
PHP | 1124 lines | 737 code | 121 blank | 266 comment | 141 complexity | bba55d6121828a5f54d3b32a4896b6e3 MD5 | raw file
Possible License(s): AGPL-1.0
  1. <?php
  2. /**
  3. *
  4. * @package phpBB3
  5. * @version $Id$
  6. * @copyright (c) 2005, 2008 phpBB Group
  7. * @license http://opensource.org/licenses/gpl-license.php GNU Public License
  8. *
  9. */
  10. /**
  11. * @ignore
  12. */
  13. if (!defined('IN_PHPBB'))
  14. {
  15. exit;
  16. }
  17. /**
  18. * Base user class
  19. *
  20. * This is the overarching class which contains (through session extend)
  21. * all methods utilised for user functionality during a session.
  22. *
  23. * @package phpBB3
  24. */
  25. class phpbb_user extends phpbb_session
  26. {
  27. /**
  28. * @var array required phpBB objects
  29. */
  30. public $phpbb_required = array('config', 'acl', 'db', 'template', 'security', 'server-vars', 'acm', 'api:user');
  31. /**
  32. * @var array Optional phpBB objects
  33. */
  34. public $phpbb_optional = array();
  35. /**
  36. * @var array language entries
  37. */
  38. public $lang = array();
  39. /**
  40. * @var array help entries
  41. */
  42. public $help = array();
  43. /**
  44. * @var array theme entries
  45. */
  46. public $theme = array();
  47. /**
  48. * @var string users date format
  49. */
  50. public $date_format;
  51. /**
  52. * @var double users time zone
  53. */
  54. public $timezone;
  55. /**
  56. * @var int users summer time setting. 0 if not in DST, else DST offset.
  57. */
  58. public $dst;
  59. /**
  60. * @var string language name, for example 'en'
  61. */
  62. public $lang_name = false;
  63. /**
  64. * @var int the language id for the specified language name
  65. */
  66. public $lang_id = false;
  67. /**
  68. * @var string the language path phpBB can find installed languages
  69. */
  70. public $lang_path;
  71. /**
  72. * @var string Imageset language name. If no fallback happens it is the same as {@link lang_name lang_name}.
  73. */
  74. public $img_lang;
  75. /**
  76. * @var array Holds information about the imageset to build {@link img() images}.
  77. */
  78. public $img_array = array();
  79. /**
  80. * @var bool Is true if the user is a logged in registered user
  81. */
  82. public $is_registered = false;
  83. /**
  84. * @var bool Is true if the user is logged in and a search engine/bot
  85. */
  86. public $is_bot = false;
  87. /**
  88. * @var bool Is true if user is founder
  89. */
  90. public $is_founder = false;
  91. /**
  92. * @var bool Is true if user is anonymous/guest
  93. */
  94. public $is_guest = true;
  95. /**
  96. * Ablility to add new option (id 7). Enabled user options is stored in $data['user_options'].
  97. * @var array user options defining their possibilities to view flash, images, etc. User options only supports set/unset (true/false)
  98. */
  99. public $keyoptions = array('viewimg' => 0, 'viewflash' => 1, 'viewsmilies' => 2, 'viewsigs' => 3, 'viewavatars' => 4, 'viewcensors' => 5, 'attachsig' => 6, 'bbcode' => 8, 'smilies' => 9, 'popuppm' => 10);
  100. /**
  101. * @var array The respective true/false values for the {@link keyoptions keyoptions}.
  102. */
  103. public $keyvalues = array();
  104. /**
  105. * Constructor to set the lang path. Calls parrent::__construct()
  106. *
  107. * @param string $auth_method The authentication method to use, for example 'db'
  108. * @param string $lang_path Language pack path
  109. * @access public
  110. */
  111. public function __construct($auth_method, $lang_path)
  112. {
  113. parent::__construct();
  114. // Init auth object
  115. $method = basename(trim($auth_method));
  116. $class = 'phpbb_auth_' . $method;
  117. if (class_exists($class))
  118. {
  119. $this->auth = new $class();
  120. }
  121. // Set language path
  122. $this->lang_path = $lang_path;
  123. // Make sure last character is a directory separator
  124. if (substr($this->lang_path, -1) != '/')
  125. {
  126. $this->lang_path .= '/';
  127. }
  128. }
  129. /**
  130. * Determine and set language
  131. */
  132. public function set_language($lang_name = '')
  133. {
  134. // Try to determine language from browser
  135. if (!$lang_name && !empty($_SERVER['HTTP_ACCEPT_LANGUAGE']))
  136. {
  137. $accept_lang_ary = explode(',', strtolower($_SERVER['HTTP_ACCEPT_LANGUAGE']));
  138. foreach ($accept_lang_ary as $accept_lang)
  139. {
  140. // Set correct format ... guess full xx_yy form
  141. $accept_lang = substr($accept_lang, 0, 2) . '_' . substr($accept_lang, 3, 2);
  142. if (file_exists($this->lang_path . basename($accept_lang)))
  143. {
  144. $lang_name = $accept_lang;
  145. break;
  146. }
  147. else
  148. {
  149. // No match on xx_yy so try xx
  150. $accept_lang = substr($accept_lang, 0, 2);
  151. if (file_exists($this->lang_path . basename($accept_lang)))
  152. {
  153. $lang_name = $accept_lang;
  154. break;
  155. }
  156. }
  157. }
  158. }
  159. // Still no luck?
  160. $lang_name = (!$lang_name && isset(phpbb::$config['default_lang'])) ? phpbb::$config['default_lang'] : basename($lang_name);
  161. // No appropriate language found ... so let's use the first one in the language
  162. // dir, this may or may not be English
  163. if (!$lang_name)
  164. {
  165. $dir = @opendir($this->lang_path);
  166. if (!$dir)
  167. {
  168. trigger_error('Unable to access the language directory', E_USER_ERROR);
  169. }
  170. while (($file = readdir($dir)) !== false)
  171. {
  172. $path = $this->lang_path . $file;
  173. if (!is_file($path) && !is_link($path) && file_exists($path . '/iso.txt'))
  174. {
  175. $lang_name = $file;
  176. break;
  177. }
  178. }
  179. closedir($dir);
  180. }
  181. // Update language name if changed
  182. if ($this->lang_name !== $lang_name)
  183. {
  184. $this->lang_name = $lang_name;
  185. }
  186. // Set data language in case we have a user where this is not set
  187. if (empty($this->data['user_lang']))
  188. {
  189. $this->data['user_lang'] = $this->lang_name;
  190. }
  191. // We include common language file here to not load it every time a custom language file is included
  192. $lang = &$this->lang;
  193. if ((include $this->lang_path . $this->lang_name . "/common." . PHP_EXT) === false)
  194. {
  195. die('Language file ' . $this->lang_path . $this->lang_name . "/common." . PHP_EXT . " couldn't be opened.");
  196. }
  197. }
  198. /**
  199. * Setup basic user-specific items (style, language, ...)
  200. *
  201. * @param string|array $lang_set Language set to setup.
  202. * Can be a string or an array of language files without a path and extension.
  203. * Format must match {@link add_lang() add_lang}.
  204. * @param int $style If not set to false this specifies the style id to use.
  205. * The page will then use the specified style id instead of the default one.
  206. * @access public
  207. */
  208. public function setup($lang_set = false, $style = false)
  209. {
  210. // Check if there is a valid session
  211. if (empty($this->data))
  212. {
  213. $this->session_begin();
  214. if (phpbb::registered('acl'))
  215. {
  216. phpbb::$acl->init($this->data);
  217. }
  218. }
  219. if ($this->data['user_id'] != ANONYMOUS)
  220. {
  221. $this->lang_name = (file_exists($this->lang_path . $this->data['user_lang'] . "/common." . PHP_EXT)) ? $this->data['user_lang'] : basename(phpbb::$config['default_lang']);
  222. $this->date_format = $this->data['user_dateformat'];
  223. $this->timezone = $this->data['user_timezone'] * 3600;
  224. $this->dst = $this->data['user_dst'] * 3600;
  225. }
  226. else
  227. {
  228. $this->lang_name = basename(phpbb::$config['default_lang']);
  229. $this->date_format = phpbb::$config['default_dateformat'];
  230. $this->timezone = phpbb::$config['board_timezone'] * 3600;
  231. $this->dst = phpbb::$config['board_dst'] * 3600;
  232. }
  233. $this->set_language($this->lang_name);
  234. $this->add_lang($lang_set);
  235. unset($lang_set);
  236. if (phpbb_request::variable('style', false, false, phpbb_request::GET) && phpbb::$acl->acl_get('a_styles'))
  237. {
  238. $style = request_var('style', 0);
  239. $this->extra_url = array('style=' . $style);
  240. }
  241. else
  242. {
  243. // Set up style
  244. $style = ($style) ? $style : ((!phpbb::$config['override_user_style']) ? $this->data['user_style'] : phpbb::$config['default_style']);
  245. }
  246. $sql = 'SELECT s.style_id, t.template_path, t.template_id, t.bbcode_bitfield, c.theme_path, c.theme_name, c.theme_storedb, c.theme_id, i.imageset_path, i.imageset_id, i.imageset_name
  247. FROM ' . STYLES_TABLE . ' s, ' . STYLES_TEMPLATE_TABLE . ' t, ' . STYLES_THEME_TABLE . ' c, ' . STYLES_IMAGESET_TABLE . " i
  248. WHERE s.style_id = $style
  249. AND t.template_id = s.template_id
  250. AND c.theme_id = s.theme_id
  251. AND i.imageset_id = s.imageset_id";
  252. $result = phpbb::$db->sql_query($sql, 3600);
  253. $this->theme = phpbb::$db->sql_fetchrow($result);
  254. phpbb::$db->sql_freeresult($result);
  255. // User has wrong style
  256. if (!$this->theme && $style == $this->data['user_style'])
  257. {
  258. $style = $this->data['user_style'] = phpbb::$config['default_style'];
  259. $sql = 'UPDATE ' . USERS_TABLE . "
  260. SET user_style = $style
  261. WHERE user_id = {$this->data['user_id']}";
  262. phpbb::$db->sql_query($sql);
  263. $sql = 'SELECT s.style_id, t.template_path, t.template_id, t.bbcode_bitfield, c.theme_path, c.theme_name, c.theme_storedb, c.theme_id, i.imageset_path, i.imageset_id, i.imageset_name
  264. FROM ' . STYLES_TABLE . ' s, ' . STYLES_TEMPLATE_TABLE . ' t, ' . STYLES_THEME_TABLE . ' c, ' . STYLES_IMAGESET_TABLE . " i
  265. WHERE s.style_id = $style
  266. AND t.template_id = s.template_id
  267. AND c.theme_id = s.theme_id
  268. AND i.imageset_id = s.imageset_id";
  269. $result = phpbb::$db->sql_query($sql, 3600);
  270. $this->theme = phpbb::$db->sql_fetchrow($result);
  271. phpbb::$db->sql_freeresult($result);
  272. }
  273. if (!$this->theme)
  274. {
  275. trigger_error('Could not get style data', E_USER_ERROR);
  276. }
  277. // Now parse the cfg file and cache it,
  278. // we are only interested in the theme configuration for now
  279. $parsed_items = phpbb_cache::obtain_cfg_item($this->theme, 'theme');
  280. $check_for = array(
  281. 'parse_css_file' => (int) 0,
  282. 'pagination_sep' => (string) ', ',
  283. );
  284. foreach ($check_for as $key => $default_value)
  285. {
  286. $this->theme[$key] = (isset($parsed_items[$key])) ? $parsed_items[$key] : $default_value;
  287. settype($this->theme[$key], gettype($default_value));
  288. if (is_string($default_value))
  289. {
  290. $this->theme[$key] = htmlspecialchars($this->theme[$key]);
  291. }
  292. }
  293. // If the style author specified the theme needs to be cached
  294. // (because of the used paths and variables) than make sure it is the case.
  295. // For example, if the theme uses language-specific images it needs to be stored in db.
  296. if (!$this->theme['theme_storedb'] && $this->theme['parse_css_file'])
  297. {
  298. $this->theme['theme_storedb'] = 1;
  299. $stylesheet = file_get_contents(PHPBB_ROOT_PATH . "styles/{$this->theme['theme_path']}/theme/stylesheet.css");
  300. // Match CSS imports
  301. $matches = array();
  302. preg_match_all('/@import url\(["\'](.*)["\']\);/i', $stylesheet, $matches);
  303. if (sizeof($matches))
  304. {
  305. $content = '';
  306. foreach ($matches[0] as $idx => $match)
  307. {
  308. if ($content = @file_get_contents(PHPBB_ROOT_PATH . "styles/{$this->theme['theme_path']}/theme/" . $matches[1][$idx]))
  309. {
  310. $content = trim($content);
  311. }
  312. else
  313. {
  314. $content = '';
  315. }
  316. $stylesheet = str_replace($match, $content, $stylesheet);
  317. }
  318. unset($content);
  319. }
  320. $stylesheet = str_replace('./', 'styles/' . $this->theme['theme_path'] . '/theme/', $stylesheet);
  321. $sql_ary = array(
  322. 'theme_data' => $stylesheet,
  323. 'theme_mtime' => time(),
  324. 'theme_storedb' => 1
  325. );
  326. phpbb::$db->sql_query('UPDATE ' . STYLES_THEME_TABLE . ' SET ' . phpbb::$db->sql_build_array('UPDATE', $sql_ary) . ' WHERE theme_id = ' . $this->theme['theme_id']);
  327. unset($sql_ary);
  328. }
  329. phpbb::$template->set_template();
  330. $this->img_lang = (file_exists(PHPBB_ROOT_PATH . 'styles/' . $this->theme['imageset_path'] . '/imageset/' . $this->lang_name)) ? $this->lang_name : phpbb::$config['default_lang'];
  331. $sql = 'SELECT image_name, image_filename, image_lang, image_height, image_width
  332. FROM ' . STYLES_IMAGESET_DATA_TABLE . '
  333. WHERE imageset_id = ' . $this->theme['imageset_id'] . "
  334. AND image_filename <> ''
  335. AND image_lang IN ('" . phpbb::$db->sql_escape($this->img_lang) . "', '')";
  336. $result = phpbb::$db->sql_query($sql, 3600);
  337. $localised_images = false;
  338. while ($row = phpbb::$db->sql_fetchrow($result))
  339. {
  340. if ($row['image_lang'])
  341. {
  342. $localised_images = true;
  343. }
  344. $row['image_filename'] = rawurlencode($row['image_filename']);
  345. $this->img_array[$row['image_name']] = $row;
  346. }
  347. phpbb::$db->sql_freeresult($result);
  348. // there were no localised images, try to refresh the localised imageset for the user's language
  349. if (!$localised_images)
  350. {
  351. // Attention: this code ignores the image definition list from acp_styles and just takes everything
  352. // that the config file contains
  353. $sql_ary = array();
  354. phpbb::$db->sql_transaction('begin');
  355. $sql = 'DELETE FROM ' . STYLES_IMAGESET_DATA_TABLE . '
  356. WHERE imageset_id = ' . $this->theme['imageset_id'] . '
  357. AND image_lang = \'' . phpbb::$db->sql_escape($this->img_lang) . '\'';
  358. $result = phpbb::$db->sql_query($sql);
  359. if (@file_exists(PHPBB_ROOT_PATH . "styles/{$this->theme['imageset_path']}/imageset/{$this->img_lang}/imageset.cfg"))
  360. {
  361. $cfg_data_imageset_data = parse_cfg_file(PHPBB_ROOT_PATH . "styles/{$this->theme['imageset_path']}/imageset/{$this->img_lang}/imageset.cfg");
  362. foreach ($cfg_data_imageset_data as $image_name => $value)
  363. {
  364. if (strpos($value, '*') !== false)
  365. {
  366. if (substr($value, -1, 1) === '*')
  367. {
  368. list($image_filename, $image_height) = explode('*', $value);
  369. $image_width = 0;
  370. }
  371. else
  372. {
  373. list($image_filename, $image_height, $image_width) = explode('*', $value);
  374. }
  375. }
  376. else
  377. {
  378. $image_filename = $value;
  379. $image_height = $image_width = 0;
  380. }
  381. if (strpos($image_name, 'img_') === 0 && $image_filename)
  382. {
  383. $image_name = substr($image_name, 4);
  384. $sql_ary[] = array(
  385. 'image_name' => (string) $image_name,
  386. 'image_filename' => (string) $image_filename,
  387. 'image_height' => (int) $image_height,
  388. 'image_width' => (int) $image_width,
  389. 'imageset_id' => (int) $this->theme['imageset_id'],
  390. 'image_lang' => (string) $this->img_lang,
  391. );
  392. }
  393. }
  394. }
  395. if (sizeof($sql_ary))
  396. {
  397. phpbb::$db->sql_multi_insert(STYLES_IMAGESET_DATA_TABLE, $sql_ary);
  398. phpbb::$db->sql_transaction('commit');
  399. phpbb::$acm->destroy_sql(STYLES_IMAGESET_DATA_TABLE);
  400. add_log('admin', 'LOG_IMAGESET_LANG_REFRESHED', $this->theme['imageset_name'], $this->img_lang);
  401. }
  402. else
  403. {
  404. phpbb::$db->sql_transaction('commit');
  405. add_log('admin', 'LOG_IMAGESET_LANG_MISSING', $this->theme['imageset_name'], $this->img_lang);
  406. }
  407. }
  408. // Call phpbb_user_session_handler() in case external application want to "bend" some variables or replace classes...
  409. // After calling it we continue script execution...
  410. // phpbb_user_session_handler();
  411. // If this function got called from the error handler we are finished here.
  412. if (defined('IN_ERROR_HANDLER'))
  413. {
  414. return;
  415. }
  416. // Disable board if the install/ directory is still present
  417. // For the brave development army we do not care about this, else we need to comment out this everytime we develop locally
  418. if (!phpbb::$base_config['debug_extra'] && !defined('ADMIN_START') && !defined('IN_INSTALL') && !defined('IN_LOGIN') && file_exists(PHPBB_ROOT_PATH . 'install'))
  419. {
  420. // Adjust the message slightly according to the permissions
  421. if (phpbb::$acl->acl_gets('a_', 'm_') || phpbb::$acl->acl_getf_global('m_'))
  422. {
  423. $message = 'REMOVE_INSTALL';
  424. }
  425. else
  426. {
  427. $message = (!empty(phpbb::$config['board_disable_msg'])) ? phpbb::$config['board_disable_msg'] : 'BOARD_DISABLE';
  428. }
  429. trigger_error($message);
  430. }
  431. // Is board disabled and user not an admin or moderator?
  432. if (phpbb::$config['board_disable'] && !defined('IN_LOGIN') && !phpbb::$acl->acl_gets('a_', 'm_') && !phpbb::$acl->acl_getf_global('m_'))
  433. {
  434. header('HTTP/1.1 503 Service Unavailable');
  435. $message = (!empty(phpbb::$config['board_disable_msg'])) ? phpbb::$config['board_disable_msg'] : 'BOARD_DISABLE';
  436. trigger_error($message);
  437. }
  438. // Is load exceeded?
  439. if (phpbb::$config['limit_load'] && $this->load !== false)
  440. {
  441. if ($this->load > floatval(phpbb::$config['limit_load']) && !defined('IN_LOGIN'))
  442. {
  443. // Set board disabled to true to let the admins/mods get the proper notification
  444. phpbb::$config['board_disable'] = '1';
  445. if (!phpbb::$acl->acl_gets('a_', 'm_') && !phpbb::$acl->acl_getf_global('m_'))
  446. {
  447. header('HTTP/1.1 503 Service Unavailable');
  448. trigger_error('BOARD_UNAVAILABLE');
  449. }
  450. }
  451. }
  452. if (isset($this->data['session_viewonline']))
  453. {
  454. // Make sure the user is able to hide his session
  455. if (!$this->data['session_viewonline'])
  456. {
  457. // Reset online status if not allowed to hide the session...
  458. if (!phpbb::$acl->acl_get('u_hideonline'))
  459. {
  460. $sql = 'UPDATE ' . SESSIONS_TABLE . '
  461. SET session_viewonline = 1
  462. WHERE session_user_id = ' . $this->data['user_id'];
  463. phpbb::$db->sql_query($sql);
  464. $this->data['session_viewonline'] = 1;
  465. }
  466. }
  467. else if (!$this->data['user_allow_viewonline'])
  468. {
  469. // the user wants to hide and is allowed to -> cloaking device on.
  470. if (phpbb::$acl->acl_get('u_hideonline'))
  471. {
  472. $sql = 'UPDATE ' . SESSIONS_TABLE . '
  473. SET session_viewonline = 0
  474. WHERE session_user_id = ' . $this->data['user_id'];
  475. phpbb::$db->sql_query($sql);
  476. $this->data['session_viewonline'] = 0;
  477. }
  478. }
  479. }
  480. // Does the user need to change their password? If so, redirect to the
  481. // ucp profile reg_details page ... of course do not redirect if we're already in the ucp
  482. if (!defined('IN_ADMIN') && !defined('ADMIN_START') && phpbb::$config['chg_passforce'] && $this->is_registered && phpbb::$acl->acl_get('u_chgpasswd') && $this->data['user_passchg'] < time() - (phpbb::$config['chg_passforce'] * 86400))
  483. {
  484. if (strpos($this->page['query_string'], 'mode=reg_details') === false && $this->page['page_name'] != 'ucp.' . PHP_EXT)
  485. {
  486. redirect(append_sid('ucp', 'i=profile&amp;mode=reg_details'));
  487. }
  488. }
  489. return;
  490. }
  491. /**
  492. * More advanced language substitution
  493. *
  494. * Function to mimic sprintf() with the possibility of using phpBB's language system to substitute nullar/singular/plural forms.
  495. * Params are the language key and the parameters to be substituted.
  496. * This function/functionality was inspired by SHS` and Ashe.
  497. *
  498. * For example:
  499. * <code>
  500. * phpbb::$user->lang('NUM_POSTS_IN_QUEUE', 1);
  501. * </code>
  502. *
  503. * @param string $key The language key to use
  504. * @param mixed $parameter,... An unlimited number of parameter to apply.
  505. *
  506. * @return string Substituted language string
  507. * @see sprintf()
  508. * @access public
  509. */
  510. public function lang()
  511. {
  512. $args = func_get_args();
  513. $key = $args[0];
  514. if (is_array($key))
  515. {
  516. $lang = &$this->lang[array_shift($key)];
  517. foreach ($key as $_key)
  518. {
  519. $lang = &$lang[$_key];
  520. }
  521. }
  522. else
  523. {
  524. $lang = &$this->lang[$key];
  525. }
  526. // Return if language string does not exist
  527. if (!isset($lang) || (!is_string($lang) && !is_array($lang)))
  528. {
  529. return $key;
  530. }
  531. // If the language entry is a string, we simply mimic sprintf() behaviour
  532. if (is_string($lang))
  533. {
  534. if (sizeof($args) == 1)
  535. {
  536. return $lang;
  537. }
  538. // Replace key with language entry and simply pass along...
  539. $args[0] = $lang;
  540. return call_user_func_array('sprintf', $args);
  541. }
  542. // It is an array... now handle different nullar/singular/plural forms
  543. $key_found = false;
  544. // We now get the first number passed and will select the key based upon this number
  545. for ($i = 1, $num_args = sizeof($args); $i < $num_args; $i++)
  546. {
  547. if (is_int($args[$i]))
  548. {
  549. $numbers = array_keys($lang);
  550. foreach ($numbers as $num)
  551. {
  552. if ($num > $args[$i])
  553. {
  554. break;
  555. }
  556. $key_found = $num;
  557. }
  558. }
  559. }
  560. // Ok, let's check if the key was found, else use the last entry (because it is mostly the plural form)
  561. if ($key_found === false)
  562. {
  563. $numbers = array_keys($lang);
  564. $key_found = end($numbers);
  565. }
  566. // Use the language string we determined and pass it to sprintf()
  567. $args[0] = $lang[$key_found];
  568. return call_user_func_array('sprintf', $args);
  569. }
  570. /**
  571. * Add Language Items - use_db and use_help are assigned where needed (only use them to force inclusion)
  572. *
  573. * Examples:
  574. * <code>
  575. * $lang_set = array('posting', 'help' => 'faq');
  576. * $lang_set = array('posting', 'viewtopic', 'help' => array('bbcode', 'faq'))
  577. * $lang_set = array(array('posting', 'viewtopic'), 'help' => array('bbcode', 'faq'))
  578. * $lang_set = 'posting'
  579. * $lang_set = array('help' => 'faq', 'db' => array('help:faq', 'posting'))
  580. * </code>
  581. *
  582. * @param mixed $lang_set specifies the language entries to include
  583. * @param bool $use_db internal variable for recursion, do not use
  584. * @param bool $use_help internal variable for recursion, do not use
  585. * @access public
  586. */
  587. public function add_lang($lang_set, $use_db = false, $use_help = false)
  588. {
  589. if (is_array($lang_set))
  590. {
  591. foreach ($lang_set as $key => $lang_file)
  592. {
  593. // Please do not delete this line.
  594. // We have to force the type here, else [array] language inclusion will not work
  595. $key = (string) $key;
  596. if ($key == 'db')
  597. {
  598. $this->add_lang($lang_file, true, $use_help);
  599. }
  600. else if ($key == 'help')
  601. {
  602. $this->add_lang($lang_file, $use_db, true);
  603. }
  604. else if (!is_array($lang_file))
  605. {
  606. $this->set_lang($this->lang, $this->help, $lang_file, $use_db, $use_help);
  607. }
  608. else
  609. {
  610. $this->add_lang($lang_file, $use_db, $use_help);
  611. }
  612. }
  613. unset($lang_set);
  614. }
  615. else if ($lang_set)
  616. {
  617. $this->set_lang($this->lang, $this->help, $lang_set, $use_db, $use_help);
  618. }
  619. }
  620. /**
  621. * Set language entry (called by {@link add_lang() add_lang})
  622. *
  623. * @param array &$lang A reference to the language array phpbb::$user->lang
  624. * @param array &$help A reference to the language help array phpbb::$user->help
  625. * @param string $lang_file Language filename
  626. * @param bool $use_db True if the database is used for obtaining the information
  627. * @param bool $use_help True if we fetch help entries instead of language entries
  628. * @access private
  629. */
  630. private function set_lang(&$lang, &$help, $lang_file, $use_db = false, $use_help = false)
  631. {
  632. // Make sure the language name is set (if the user setup did not happen it is not set)
  633. if (!$this->lang_name)
  634. {
  635. $this->lang_name = basename(phpbb::$config['default_lang']);
  636. }
  637. // $lang == $this->lang
  638. // $help == $this->help
  639. // - add appropriate variables here, name them as they are used within the language file...
  640. if (!$use_db)
  641. {
  642. if ($use_help && strpos($lang_file, '/') !== false)
  643. {
  644. $language_filename = $this->lang_path . $this->lang_name . '/' . substr($lang_file, 0, stripos($lang_file, '/') + 1) . 'help_' . substr($lang_file, stripos($lang_file, '/') + 1) . '.' . PHP_EXT;
  645. }
  646. else
  647. {
  648. $language_filename = $this->lang_path . $this->lang_name . '/' . (($use_help) ? 'help_' : '') . $lang_file . '.' . PHP_EXT;
  649. }
  650. if (!file_exists($language_filename))
  651. {
  652. if ($this->lang_name == 'en')
  653. {
  654. // 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.
  655. $language_filename = str_replace($this->lang_path . 'en', $this->lang_path . $this->data['user_lang'], $language_filename);
  656. trigger_error('Language file ' . $language_filename . ' couldn\'t be opened.', E_USER_ERROR);
  657. }
  658. else if ($this->lang_name == basename(phpbb::$config['default_lang']))
  659. {
  660. // Fall back to the English Language
  661. $this->lang_name = 'en';
  662. $this->set_lang($lang, $help, $lang_file, $use_db, $use_help);
  663. }
  664. else if (file_exists($this->lang_path . $this->data['user_lang'] . '/common.' . PHP_EXT))
  665. {
  666. // Fall back to the board default language
  667. $this->lang_name = basename(phpbb::$config['default_lang']);
  668. $this->set_lang($lang, $help, $lang_file, $use_db, $use_help);
  669. }
  670. // Reset the lang name
  671. $this->lang_name = (file_exists($this->lang_path . $this->data['user_lang'] . '/common.' . PHP_EXT)) ? $this->data['user_lang'] : basename(phpbb::$config['default_lang']);
  672. return;
  673. }
  674. if ((include $language_filename) === false)
  675. {
  676. trigger_error('Language file ' . $language_filename . ' couldn\'t be opened.', E_USER_ERROR);
  677. }
  678. }
  679. else if ($use_db)
  680. {
  681. // Get Database Language Strings
  682. // Put them into $lang if nothing is prefixed, put them into $help if help: is prefixed
  683. // For example: help:faq, posting
  684. }
  685. }
  686. /**
  687. * Format user date
  688. *
  689. * @param int $gmepoch Unix timestamp to format
  690. * @param string $format Date format in date() notation.
  691. * The character | used to indicate relative dates, for example |d m Y|, h:i is translated to Today, h:i.
  692. * @param bool $forcedate Force non-relative date format.
  693. *
  694. * @staticvar int $midnight Midnight time offset
  695. * @staticvar array $date_cache Array to cache commonly needed structures within this function
  696. *
  697. * @return mixed translated date
  698. * @access public
  699. */
  700. public function format_date($gmepoch, $format = false, $forcedate = false)
  701. {
  702. static $midnight;
  703. static $date_cache;
  704. $format = (!$format) ? $this->date_format : $format;
  705. $now = time();
  706. $delta = $now - $gmepoch;
  707. if (!isset($date_cache[$format]))
  708. {
  709. // Is the user requesting a friendly date format (i.e. 'Today 12:42')?
  710. $date_cache[$format] = array(
  711. 'is_short' => strpos($format, '|'),
  712. 'format_short' => substr($format, 0, strpos($format, '|')) . '||' . substr(strrchr($format, '|'), 1),
  713. 'format_long' => str_replace('|', '', $format),
  714. 'lang' => $this->lang['datetime'],
  715. );
  716. // Short representation of month in format? Some languages use different terms for the long and short format of May
  717. if ((strpos($format, '\M') === false && strpos($format, 'M') !== false) || (strpos($format, '\r') === false && strpos($format, 'r') !== false))
  718. {
  719. $date_cache[$format]['lang']['May'] = $this->lang['datetime']['May_short'];
  720. }
  721. }
  722. // Zone offset
  723. $zone_offset = $this->timezone + $this->dst;
  724. // Show date <= 1 hour ago as 'xx min ago'
  725. // A small tolerence is given for times in the future and times in the future but in the same minute are displayed as '< than a minute ago'
  726. if ($delta <= 3600 && ($delta >= -5 || (($now / 60) % 60) == (($gmepoch / 60) % 60)) && $date_cache[$format]['is_short'] !== false && !$forcedate && isset($this->lang['datetime']['AGO']))
  727. {
  728. return $this->lang(array('datetime', 'AGO'), max(0, (int) floor($delta / 60)));
  729. }
  730. if (!$midnight)
  731. {
  732. list($d, $m, $y) = explode(' ', gmdate('j n Y', time() + $zone_offset));
  733. $midnight = gmmktime(0, 0, 0, $m, $d, $y) - $zone_offset;
  734. }
  735. if ($date_cache[$format]['is_short'] !== false && !$forcedate && !($gmepoch < $midnight - 86400 || $gmepoch > $midnight + 172800))
  736. {
  737. $day = false;
  738. if ($gmepoch > $midnight + 86400)
  739. {
  740. $day = 'TOMORROW';
  741. }
  742. else if ($gmepoch > $midnight)
  743. {
  744. $day = 'TODAY';
  745. }
  746. else if ($gmepoch > $midnight - 86400)
  747. {
  748. $day = 'YESTERDAY';
  749. }
  750. if ($day !== false)
  751. {
  752. return str_replace('||', $this->lang['datetime'][$day], strtr(@gmdate($date_cache[$format]['format_short'], $gmepoch + $zone_offset), $date_cache[$format]['lang']));
  753. }
  754. }
  755. return strtr(@gmdate($date_cache[$format]['format_long'], $gmepoch + $zone_offset), $date_cache[$format]['lang']);
  756. }
  757. /**
  758. * Get language id currently used by the user
  759. *
  760. * @return int language id
  761. * @access public
  762. */
  763. public function get_iso_lang_id()
  764. {
  765. if (!empty($this->lang_id))
  766. {
  767. return $this->lang_id;
  768. }
  769. if (!$this->lang_name)
  770. {
  771. $this->lang_name = phpbb::$config['default_lang'];
  772. }
  773. $sql = 'SELECT lang_id
  774. FROM ' . LANG_TABLE . "
  775. WHERE lang_iso = '" . phpbb::$db->sql_escape($this->lang_name) . "'";
  776. $result = phpbb::$db->sql_query($sql);
  777. $this->lang_id = (int) phpbb::$db->sql_fetchfield('lang_id');
  778. phpbb::$db->sql_freeresult($result);
  779. return $this->lang_id;
  780. }
  781. /**
  782. * Get users profile fields
  783. *
  784. * @param int $user_id User id. If not specified the current users profile fields are grabbed.
  785. *
  786. * @return array Profile fields. If the current user then they are also stored as property $profile_fields.
  787. * @access public
  788. */
  789. public function get_profile_fields($user_id = false)
  790. {
  791. $user_id = ($user_id === false) ? $this->data['user_id'] : $user_id;
  792. if (isset($this->profile_fields) && $user_id === $this->data['user_id'])
  793. {
  794. return $this->profile_fields;
  795. }
  796. $sql = 'SELECT *
  797. FROM ' . PROFILE_FIELDS_DATA_TABLE . "
  798. WHERE user_id = $user_id";
  799. $result = phpbb::$db->sql_query_limit($sql, 1);
  800. $row = phpbb::$db->sql_fetchrow($result);
  801. phpbb::$db->sql_freeresult($result);
  802. if ($user_id === $this->data['user_id'])
  803. {
  804. $this->profile_fields = (!$row) ? array() : $row;
  805. }
  806. return $row;
  807. }
  808. /**
  809. * Specify/Get image from style imageset
  810. *
  811. * @param string $img The imageset image key name
  812. * @param string $alt An optional alternative image attribute.
  813. * If a corresponding language key exist it will be used: phpbb::$user->lang[$alt]
  814. * @param string $type The preferred type to return. Allowed types are: full_tag, src, width, height
  815. * @param int $width Set image width
  816. *
  817. * @return mixed returns the preferred type from $type
  818. * @access public
  819. */
  820. public function img($img, $alt = '', $type = 'full_tag', $width = false)
  821. {
  822. static $imgs;
  823. $img_data = &$imgs[$img];
  824. if (empty($img_data))
  825. {
  826. if (!isset($this->img_array[$img]))
  827. {
  828. // Do not fill the image to let designers decide what to do if the image is empty
  829. $img_data = '';
  830. return $img_data;
  831. }
  832. $img_data['src'] = PHPBB_ROOT_PATH . 'styles/' . rawurlencode($this->theme['imageset_path']) . '/imageset/' . ($this->img_array[$img]['image_lang'] ? $this->img_array[$img]['image_lang'] .'/' : '') . $this->img_array[$img]['image_filename'];
  833. $img_data['width'] = $this->img_array[$img]['image_width'];
  834. $img_data['height'] = $this->img_array[$img]['image_height'];
  835. }
  836. $alt = (!empty($this->lang[$alt])) ? $this->lang[$alt] : $alt;
  837. switch ($type)
  838. {
  839. case 'src':
  840. return $img_data['src'];
  841. break;
  842. case 'width':
  843. return ($width === false) ? $img_data['width'] : $width;
  844. break;
  845. case 'height':
  846. return $img_data['height'];
  847. break;
  848. default:
  849. $use_width = ($width === false) ? $img_data['width'] : $width;
  850. return '<img src="' . $img_data['src'] . '"' . (($use_width) ? ' width="' . $use_width . '"' : '') . (($img_data['height']) ? ' height="' . $img_data['height'] . '"' : '') . ' alt="' . $alt . '" title="' . $alt . '" />';
  851. break;
  852. }
  853. }
  854. /**
  855. * Get option bit field from user options.
  856. *
  857. * @param string $key The option key from {@link $keyoptions keyoptions}
  858. * @param int $data Optional user options bitfield.
  859. * If not specified then {@link $data $data['user_options']} is used.
  860. *
  861. * @return bool Corresponding option value returned. Is the option enabled or disabled.
  862. * @access public
  863. */
  864. public function optionget($key, $data = false)
  865. {
  866. if ($data !== false)
  867. {
  868. return $data & 1 << $this->keyoptions[$key];
  869. }
  870. if (!isset($this->keyvalues[$key]))
  871. {
  872. $this->keyvalues[$key] = $this->data['user_options'] & 1 << $this->keyoptions[$key];
  873. }
  874. return $this->keyvalues[$key];
  875. }
  876. /**
  877. * Set option bit field for user options.
  878. *
  879. * @param string $key The option key from {@link $keyoptions keyoptions}
  880. * @param bool $value True to enable the option, false to disable it
  881. * @param int $data Optional user options bitfield.
  882. * If not specified then {@link $data $data['user_options']} is used.
  883. *
  884. * @return bool The new user options bitfield is returned if $data is specified.
  885. * Else: false is returned if user options not changed, true if changed.
  886. * @access public
  887. */
  888. public function optionset($key, $value, $data = false)
  889. {
  890. $var = ($data) ? $data : $this->data['user_options'];
  891. if ($value && !($var & 1 << $this->keyoptions[$key]))
  892. {
  893. $var += 1 << $this->keyoptions[$key];
  894. }
  895. else if (!$value && ($var & 1 << $this->keyoptions[$key]))
  896. {
  897. $var -= 1 << $this->keyoptions[$key];
  898. }
  899. else
  900. {
  901. return ($data) ? $var : false;
  902. }
  903. if (!$data)
  904. {
  905. $this->data['user_options'] = $this->keyvalues[$key] = $var;
  906. return true;
  907. }
  908. return $var;
  909. }
  910. /**
  911. * User login. Log the user in.
  912. *
  913. * @param string $username The specified user name
  914. * @param string $password The specified password
  915. * @param bool $autologin Enable/disable persistent login
  916. * @param bool $viewonline If false then the user will be logged in as hidden
  917. * @param bool $admin If true the user requests an admin login
  918. *
  919. * @return array Login result array. This array returns results to the login script to show errors, notices, confirmations.
  920. * @access public
  921. */
  922. public function login($username, $password, $autologin = false, $viewonline = 1, $admin = 0)
  923. {
  924. if ($this->auth === false || !method_exists($this->auth, 'login'))
  925. {
  926. trigger_error('Authentication method not found', E_USER_ERROR);
  927. }
  928. $login = $this->auth->login($username, $password);
  929. // If the auth module wants us to create an empty profile do so and then treat the status as LOGIN_SUCCESS
  930. if ($login['status'] == LOGIN_SUCCESS_CREATE_PROFILE)
  931. {
  932. phpbb::$api->user->add($login['user_row'], (isset($login['cp_data'])) ? $login['cp_data'] : false);
  933. $sql = 'SELECT user_id, username, user_password, user_passchg, user_email, user_type
  934. FROM ' . USERS_TABLE . "
  935. WHERE username_clean = '" . phpbb::$db->sql_escape(utf8_clean_string($username)) . "'";
  936. $result = phpbb::$db->sql_query($sql);
  937. $row = phpbb::$db->sql_fetchrow($result);
  938. phpbb::$db->sql_freeresult($result);
  939. if (!$row)
  940. {
  941. return array(
  942. 'status' => LOGIN_ERROR_EXTERNAL_AUTH,
  943. 'error_msg' => 'AUTH_NO_PROFILE_CREATED',
  944. 'user_row' => array('user_id' => ANONYMOUS),
  945. );
  946. }
  947. $login = array(
  948. 'status' => LOGIN_SUCCESS,
  949. 'error_msg' => false,
  950. 'user_row' => $row,
  951. );
  952. }
  953. // If login succeeded, we will log the user in... else we pass the login array through...
  954. if ($login['status'] == LOGIN_SUCCESS)
  955. {
  956. // We create a new session. The session creation makes sure the old session is killed if there was one.
  957. $result = $this->session_create($login['user_row']['user_id'], $admin, $autologin, $viewonline);
  958. // Successful session creation
  959. if ($result === true)
  960. {
  961. return array(
  962. 'status' => LOGIN_SUCCESS,
  963. 'error_msg' => false,
  964. 'user_row' => $login['user_row'],
  965. );
  966. }
  967. return array(
  968. 'status' => LOGIN_BREAK,
  969. 'error_msg' => $result,
  970. 'user_row' => $login['user_row'],
  971. );
  972. }
  973. return $login;
  974. }
  975. }
  976. ?>