diff options
Diffstat (limited to 'phpBB/phpbb')
36 files changed, 1041 insertions, 914 deletions
diff --git a/phpBB/phpbb/auth/provider/oauth/oauth.php b/phpBB/phpbb/auth/provider/oauth/oauth.php index 0d94acfbca..e3f8394bba 100644 --- a/phpBB/phpbb/auth/provider/oauth/oauth.php +++ b/phpBB/phpbb/auth/provider/oauth/oauth.php @@ -216,10 +216,15 @@ class oauth extends \phpbb\auth\provider\base $this->service_providers[$service_name]->set_external_service_provider($service); $unique_id = $this->service_providers[$service_name]->perform_auth_login(); - // Check to see if this provider is already assosciated with an account + /** + * Check to see if this provider is already associated with an account. + * + * Enforcing a data type to make data contains strings and not integers, + * so values are quoted in the SQL WHERE statement. + */ $data = array( - 'provider' => $service_name_original, - 'oauth_provider_id' => $unique_id + 'provider' => (string) $service_name_original, + 'oauth_provider_id' => (string) $unique_id ); $sql = 'SELECT user_id FROM ' . $this->auth_provider_oauth_token_account_assoc . ' diff --git a/phpBB/phpbb/cache/driver/apc.php b/phpBB/phpbb/cache/driver/apc.php deleted file mode 100644 index 521d5d41ea..0000000000 --- a/phpBB/phpbb/cache/driver/apc.php +++ /dev/null @@ -1,70 +0,0 @@ -<?php -/** -* -* This file is part of the phpBB Forum Software package. -* -* @copyright (c) phpBB Limited <https://www.phpbb.com> -* @license GNU General Public License, version 2 (GPL-2.0) -* -* For full copyright and license information, please see -* the docs/CREDITS.txt file. -* -*/ - -namespace phpbb\cache\driver; - -/** -* ACM for APC -*/ -class apc extends \phpbb\cache\driver\memory -{ - var $extension = 'apc'; - - /** - * {@inheritDoc} - */ - function purge() - { - apc_clear_cache('user'); - - parent::purge(); - } - - /** - * Fetch an item from the cache - * - * @access protected - * @param string $var Cache key - * @return mixed Cached data - */ - function _read($var) - { - return apc_fetch($this->key_prefix . $var); - } - - /** - * Store data in the cache - * - * @access protected - * @param string $var Cache key - * @param mixed $data Data to store - * @param int $ttl Time-to-live of cached data - * @return bool True if the operation succeeded - */ - function _write($var, $data, $ttl = 2592000) - { - return apc_store($this->key_prefix . $var, $data, $ttl); - } - - /** - * Remove an item from the cache - * - * @access protected - * @param string $var Cache key - * @return bool True if the operation succeeded - */ - function _delete($var) - { - return apc_delete($this->key_prefix . $var); - } -} diff --git a/phpBB/phpbb/config_php_file.php b/phpBB/phpbb/config_php_file.php index 7445e7df22..e3f7357720 100644 --- a/phpBB/phpbb/config_php_file.php +++ b/phpBB/phpbb/config_php_file.php @@ -155,6 +155,12 @@ class config_php_file return $dbms; } + // Force use of mysqli when specifying mysql + if (preg_match('/(phpbb\\\db\\\driver\\\)?mysql$/i', $dbms)) + { + return 'phpbb\db\driver\mysqli'; + } + throw new \RuntimeException("You have specified an invalid dbms driver: $dbms"); } } diff --git a/phpBB/phpbb/console/command/extension/enable.php b/phpBB/phpbb/console/command/extension/enable.php index a6f5b10e86..504b5546b3 100644 --- a/phpBB/phpbb/console/command/extension/enable.php +++ b/phpBB/phpbb/console/command/extension/enable.php @@ -46,9 +46,11 @@ class enable extends command $extension = $this->manager->get_extension($name); - if (!$extension->is_enableable()) + if (($enableable = $extension->is_enableable()) !== true) { - $io->error($this->user->lang('CLI_EXTENSION_NOT_ENABLEABLE', $name)); + $message = !empty($enableable) ? $enableable : $this->user->lang('CLI_EXTENSION_NOT_ENABLEABLE', $name); + $message = is_array($message) ? implode(PHP_EOL, $message) : $message; + $io->error($message); return 1; } diff --git a/phpBB/phpbb/console/command/user/add.php b/phpBB/phpbb/console/command/user/add.php index c60a059251..303216a93d 100644 --- a/phpBB/phpbb/console/command/user/add.php +++ b/phpBB/phpbb/console/command/user/add.php @@ -239,7 +239,7 @@ class add extends command array('string', false, $this->config['min_name_chars'], $this->config['max_name_chars']), array('username', '')), 'new_password' => array( - array('string', false, $this->config['min_pass_chars'], $this->config['max_pass_chars']), + array('string', false, $this->config['min_pass_chars'], 0), array('password')), 'email' => array( array('string', false, 6, 60), diff --git a/phpBB/phpbb/db/driver/mysql.php b/phpBB/phpbb/db/driver/mysql.php deleted file mode 100644 index 8ce70444c2..0000000000 --- a/phpBB/phpbb/db/driver/mysql.php +++ /dev/null @@ -1,502 +0,0 @@ -<?php -/** -* -* This file is part of the phpBB Forum Software package. -* -* @copyright (c) phpBB Limited <https://www.phpbb.com> -* @license GNU General Public License, version 2 (GPL-2.0) -* -* For full copyright and license information, please see -* the docs/CREDITS.txt file. -* -*/ - -namespace phpbb\db\driver; - -/** -* MySQL4 Database Abstraction Layer -* Compatible with: -* MySQL 3.23+ -* MySQL 4.0+ -* MySQL 4.1+ -* MySQL 5.0+ -*/ -class mysql extends \phpbb\db\driver\mysql_base -{ - var $multi_insert = true; - var $connect_error = ''; - - /** - * {@inheritDoc} - */ - function sql_connect($sqlserver, $sqluser, $sqlpassword, $database, $port = false, $persistency = false, $new_link = false) - { - $this->persistency = $persistency; - $this->user = $sqluser; - $this->server = $sqlserver . (($port) ? ':' . $port : ''); - $this->dbname = $database; - - $this->sql_layer = 'mysql4'; - - if ($this->persistency) - { - if (!function_exists('mysql_pconnect')) - { - $this->connect_error = 'mysql_pconnect function does not exist, is mysql extension installed?'; - return $this->sql_error(''); - } - $this->db_connect_id = @mysql_pconnect($this->server, $this->user, $sqlpassword); - } - else - { - if (!function_exists('mysql_connect')) - { - $this->connect_error = 'mysql_connect function does not exist, is mysql extension installed?'; - return $this->sql_error(''); - } - $this->db_connect_id = @mysql_connect($this->server, $this->user, $sqlpassword, $new_link); - } - - if ($this->db_connect_id && $this->dbname != '') - { - if (@mysql_select_db($this->dbname, $this->db_connect_id)) - { - // Determine what version we are using and if it natively supports UNICODE - if (version_compare($this->sql_server_info(true), '4.1.0', '>=')) - { - @mysql_query("SET NAMES 'utf8'", $this->db_connect_id); - - // enforce strict mode on databases that support it - if (version_compare($this->sql_server_info(true), '5.0.2', '>=')) - { - $result = @mysql_query('SELECT @@session.sql_mode AS sql_mode', $this->db_connect_id); - if ($result) - { - $row = mysql_fetch_assoc($result); - mysql_free_result($result); - $modes = array_map('trim', explode(',', $row['sql_mode'])); - } - else - { - $modes = array(); - } - - // TRADITIONAL includes STRICT_ALL_TABLES and STRICT_TRANS_TABLES - if (!in_array('TRADITIONAL', $modes)) - { - if (!in_array('STRICT_ALL_TABLES', $modes)) - { - $modes[] = 'STRICT_ALL_TABLES'; - } - - if (!in_array('STRICT_TRANS_TABLES', $modes)) - { - $modes[] = 'STRICT_TRANS_TABLES'; - } - } - - $mode = implode(',', $modes); - @mysql_query("SET SESSION sql_mode='{$mode}'", $this->db_connect_id); - } - } - else if (version_compare($this->sql_server_info(true), '4.0.0', '<')) - { - $this->sql_layer = 'mysql'; - } - - return $this->db_connect_id; - } - } - - return $this->sql_error(''); - } - - /** - * {@inheritDoc} - */ - function sql_server_info($raw = false, $use_cache = true) - { - global $cache; - - if (!$use_cache || empty($cache) || ($this->sql_server_version = $cache->get('mysql_version')) === false) - { - $result = @mysql_query('SELECT VERSION() AS version', $this->db_connect_id); - if ($result) - { - $row = mysql_fetch_assoc($result); - mysql_free_result($result); - - $this->sql_server_version = $row['version']; - - if (!empty($cache) && $use_cache) - { - $cache->put('mysql_version', $this->sql_server_version); - } - } - } - - return ($raw) ? $this->sql_server_version : 'MySQL ' . $this->sql_server_version; - } - - /** - * SQL Transaction - * @access private - */ - function _sql_transaction($status = 'begin') - { - switch ($status) - { - case 'begin': - return @mysql_query('BEGIN', $this->db_connect_id); - break; - - case 'commit': - return @mysql_query('COMMIT', $this->db_connect_id); - break; - - case 'rollback': - return @mysql_query('ROLLBACK', $this->db_connect_id); - break; - } - - return true; - } - - /** - * {@inheritDoc} - */ - function sql_query($query = '', $cache_ttl = 0) - { - if ($query != '') - { - global $cache; - - if ($this->debug_sql_explain) - { - $this->sql_report('start', $query); - } - else if ($this->debug_load_time) - { - $this->curtime = microtime(true); - } - - $this->query_result = ($cache && $cache_ttl) ? $cache->sql_load($query) : false; - $this->sql_add_num_queries($this->query_result); - - if ($this->query_result === false) - { - if (($this->query_result = @mysql_query($query, $this->db_connect_id)) === false) - { - $this->sql_error($query); - } - - if ($this->debug_sql_explain) - { - $this->sql_report('stop', $query); - } - else if ($this->debug_load_time) - { - $this->sql_time += microtime(true) - $this->curtime; - } - - if (!$this->query_result) - { - return false; - } - - if ($cache && $cache_ttl) - { - $this->open_queries[(int) $this->query_result] = $this->query_result; - $this->query_result = $cache->sql_save($this, $query, $this->query_result, $cache_ttl); - } - else if (strpos($query, 'SELECT') === 0) - { - $this->open_queries[(int) $this->query_result] = $this->query_result; - } - } - else if ($this->debug_sql_explain) - { - $this->sql_report('fromcache', $query); - } - } - else - { - return false; - } - - return $this->query_result; - } - - /** - * {@inheritDoc} - */ - function sql_affectedrows() - { - if ($this->db_connect_id) - { - // We always want the number of matched rows - // instead of changed rows, when running an update. - // So when mysql_info() returns the number of matched rows - // we return that one instead of mysql_affected_rows() - $mysql_info = @mysql_info($this->db_connect_id); - if ($mysql_info !== false) - { - $match = array(); - preg_match('#^Rows matched: (\d)+ Changed: (\d)+ Warnings: (\d)+$#', $mysql_info, $match); - if (isset($match[1])) - { - return $match[1]; - } - } - - return @mysql_affected_rows($this->db_connect_id); - } - return false; - } - - /** - * {@inheritDoc} - */ - function sql_fetchrow($query_id = false) - { - global $cache; - - if ($query_id === false) - { - $query_id = $this->query_result; - } - - if ($cache && $cache->sql_exists($query_id)) - { - return $cache->sql_fetchrow($query_id); - } - - return ($query_id) ? mysql_fetch_assoc($query_id) : false; - } - - /** - * {@inheritDoc} - */ - function sql_rowseek($rownum, &$query_id) - { - global $cache; - - if ($query_id === false) - { - $query_id = $this->query_result; - } - - if ($cache && $cache->sql_exists($query_id)) - { - return $cache->sql_rowseek($rownum, $query_id); - } - - return ($query_id !== false) ? @mysql_data_seek($query_id, $rownum) : false; - } - - /** - * {@inheritDoc} - */ - function sql_nextid() - { - return ($this->db_connect_id) ? @mysql_insert_id($this->db_connect_id) : false; - } - - /** - * {@inheritDoc} - */ - function sql_freeresult($query_id = false) - { - global $cache; - - if ($query_id === false) - { - $query_id = $this->query_result; - } - - if ($cache && !is_object($query_id) && $cache->sql_exists($query_id)) - { - return $cache->sql_freeresult($query_id); - } - - if (isset($this->open_queries[(int) $query_id])) - { - unset($this->open_queries[(int) $query_id]); - return mysql_free_result($query_id); - } - - return false; - } - - /** - * {@inheritDoc} - */ - function sql_escape($msg) - { - if (!$this->db_connect_id) - { - return @mysql_real_escape_string($msg); - } - - return @mysql_real_escape_string($msg, $this->db_connect_id); - } - - /** - * return sql error array - * @access private - */ - function _sql_error() - { - if ($this->db_connect_id) - { - $error = array( - 'message' => @mysql_error($this->db_connect_id), - 'code' => @mysql_errno($this->db_connect_id), - ); - } - else if (function_exists('mysql_error')) - { - $error = array( - 'message' => @mysql_error(), - 'code' => @mysql_errno(), - ); - } - else - { - $error = array( - 'message' => $this->connect_error, - 'code' => '', - ); - } - - return $error; - } - - /** - * Close sql connection - * @access private - */ - function _sql_close() - { - return @mysql_close($this->db_connect_id); - } - - /** - * Build db-specific report - * @access private - */ - function _sql_report($mode, $query = '') - { - static $test_prof; - - // current detection method, might just switch to see the existence of INFORMATION_SCHEMA.PROFILING - if ($test_prof === null) - { - $test_prof = false; - if (version_compare($this->sql_server_info(true), '5.0.37', '>=') && version_compare($this->sql_server_info(true), '5.1', '<')) - { - $test_prof = true; - } - } - - switch ($mode) - { - case 'start': - - $explain_query = $query; - if (preg_match('/UPDATE ([a-z0-9_]+).*?WHERE(.*)/s', $query, $m)) - { - $explain_query = 'SELECT * FROM ' . $m[1] . ' WHERE ' . $m[2]; - } - else if (preg_match('/DELETE FROM ([a-z0-9_]+).*?WHERE(.*)/s', $query, $m)) - { - $explain_query = 'SELECT * FROM ' . $m[1] . ' WHERE ' . $m[2]; - } - - if (preg_match('/^SELECT/', $explain_query)) - { - $html_table = false; - - // begin profiling - if ($test_prof) - { - @mysql_query('SET profiling = 1;', $this->db_connect_id); - } - - if ($result = @mysql_query("EXPLAIN $explain_query", $this->db_connect_id)) - { - while ($row = mysql_fetch_assoc($result)) - { - $html_table = $this->sql_report('add_select_row', $query, $html_table, $row); - } - mysql_free_result($result); - } - - if ($html_table) - { - $this->html_hold .= '</table>'; - } - - if ($test_prof) - { - $html_table = false; - - // get the last profile - if ($result = @mysql_query('SHOW PROFILE ALL;', $this->db_connect_id)) - { - $this->html_hold .= '<br />'; - while ($row = mysql_fetch_assoc($result)) - { - // make <unknown> HTML safe - if (!empty($row['Source_function'])) - { - $row['Source_function'] = str_replace(array('<', '>'), array('<', '>'), $row['Source_function']); - } - - // remove unsupported features - foreach ($row as $key => $val) - { - if ($val === null) - { - unset($row[$key]); - } - } - $html_table = $this->sql_report('add_select_row', $query, $html_table, $row); - } - mysql_free_result($result); - } - - if ($html_table) - { - $this->html_hold .= '</table>'; - } - - @mysql_query('SET profiling = 0;', $this->db_connect_id); - } - } - - break; - - case 'fromcache': - $endtime = explode(' ', microtime()); - $endtime = $endtime[0] + $endtime[1]; - - $result = @mysql_query($query, $this->db_connect_id); - if ($result) - { - while ($void = mysql_fetch_assoc($result)) - { - // Take the time spent on parsing rows into account - } - mysql_free_result($result); - } - - $splittime = explode(' ', microtime()); - $splittime = $splittime[0] + $splittime[1]; - - $this->sql_report('record_fromcache', $query, $endtime, $splittime); - - break; - } - } -} diff --git a/phpBB/phpbb/db/driver/mysqli.php b/phpBB/phpbb/db/driver/mysqli.php index df8b88c315..0c1c063262 100644 --- a/phpBB/phpbb/db/driver/mysqli.php +++ b/phpBB/phpbb/db/driver/mysqli.php @@ -68,6 +68,9 @@ class mysqli extends \phpbb\db\driver\mysql_base if ($this->db_connect_id && $this->dbname != '') { + // Disable loading local files on client side + @mysqli_options($this->db_connect_id, MYSQLI_OPT_LOCAL_INFILE, false); + @mysqli_query($this->db_connect_id, "SET NAMES 'utf8'"); // enforce strict mode on databases that support it diff --git a/phpBB/phpbb/db/extractor/mysql_extractor.php b/phpBB/phpbb/db/extractor/mysql_extractor.php index 534e8b7653..f3cb0db457 100644 --- a/phpBB/phpbb/db/extractor/mysql_extractor.php +++ b/phpBB/phpbb/db/extractor/mysql_extractor.php @@ -79,14 +79,7 @@ class mysql_extractor extends base_extractor throw new extractor_not_initialized_exception(); } - if ($this->db->get_sql_layer() === 'mysqli') - { - $this->write_data_mysqli($table_name); - } - else - { - $this->write_data_mysql($table_name); - } + $this->write_data_mysqli($table_name); } /** @@ -180,101 +173,6 @@ class mysql_extractor extends base_extractor } /** - * Extracts data from database table (for MySQL driver) - * - * @param string $table_name name of the database table - * @return null - * @throws \phpbb\db\extractor\exception\extractor_not_initialized_exception when calling this function before init_extractor() - */ - protected function write_data_mysql($table_name) - { - if (!$this->is_initialized) - { - throw new extractor_not_initialized_exception(); - } - - $sql = "SELECT * - FROM $table_name"; - $result = mysql_unbuffered_query($sql, $this->db->get_db_connect_id()); - - if ($result != false) - { - $fields_cnt = mysql_num_fields($result); - - // Get field information - $field = array(); - for ($i = 0; $i < $fields_cnt; $i++) - { - $field[] = mysql_fetch_field($result, $i); - } - $field_set = array(); - - for ($j = 0; $j < $fields_cnt; $j++) - { - $field_set[] = $field[$j]->name; - } - - $search = array("\\", "'", "\x00", "\x0a", "\x0d", "\x1a", '"'); - $replace = array("\\\\", "\\'", '\0', '\n', '\r', '\Z', '\\"'); - $fields = implode(', ', $field_set); - $sql_data = 'INSERT INTO ' . $table_name . ' (' . $fields . ') VALUES '; - $first_set = true; - $query_len = 0; - $max_len = get_usable_memory(); - - while ($row = mysql_fetch_row($result)) - { - $values = array(); - if ($first_set) - { - $query = $sql_data . '('; - } - else - { - $query .= ',('; - } - - for ($j = 0; $j < $fields_cnt; $j++) - { - if (!isset($row[$j]) || is_null($row[$j])) - { - $values[$j] = 'NULL'; - } - else if ($field[$j]->numeric && ($field[$j]->type !== 'timestamp')) - { - $values[$j] = $row[$j]; - } - else - { - $values[$j] = "'" . str_replace($search, $replace, $row[$j]) . "'"; - } - } - $query .= implode(', ', $values) . ')'; - - $query_len += strlen($query); - if ($query_len > $max_len) - { - $this->flush($query . ";\n\n"); - $query = ''; - $query_len = 0; - $first_set = true; - } - else - { - $first_set = false; - } - } - mysql_free_result($result); - - // check to make sure we have nothing left to flush - if (!$first_set && $query) - { - $this->flush($query . ";\n\n"); - } - } - } - - /** * Extracts database table structure (for MySQLi or MySQL 3.23.20+) * * @param string $table_name name of the database table diff --git a/phpBB/phpbb/db/migration/data/v32x/v328.php b/phpBB/phpbb/db/migration/data/v32x/v328.php new file mode 100644 index 0000000000..28ff2c7033 --- /dev/null +++ b/phpBB/phpbb/db/migration/data/v32x/v328.php @@ -0,0 +1,36 @@ +<?php +/** +* +* This file is part of the phpBB Forum Software package. +* +* @copyright (c) phpBB Limited <https://www.phpbb.com> +* @license GNU General Public License, version 2 (GPL-2.0) +* +* For full copyright and license information, please see +* the docs/CREDITS.txt file. +* +*/ + +namespace phpbb\db\migration\data\v32x; + +class v328 extends \phpbb\db\migration\migration +{ + public function effectively_installed() + { + return phpbb_version_compare($this->config['version'], '3.2.8', '>='); + } + + static public function depends_on() + { + return array( + '\phpbb\db\migration\data\v32x\v328rc1', + ); + } + + public function update_data() + { + return array( + array('config.update', array('version', '3.2.8')), + ); + } +} diff --git a/phpBB/phpbb/db/migration/data/v330/jquery_update.php b/phpBB/phpbb/db/migration/data/v330/jquery_update.php new file mode 100644 index 0000000000..f1ac6cdd41 --- /dev/null +++ b/phpBB/phpbb/db/migration/data/v330/jquery_update.php @@ -0,0 +1,37 @@ +<?php +/** + * + * This file is part of the phpBB Forum Software package. + * + * @copyright (c) phpBB Limited <https://www.phpbb.com> + * @license GNU General Public License, version 2 (GPL-2.0) + * + * For full copyright and license information, please see + * the docs/CREDITS.txt file. + * + */ + +namespace phpbb\db\migration\data\v330; + +class jquery_update extends \phpbb\db\migration\migration +{ + public function effectively_installed() + { + return $this->config['load_jquery_url'] === '//ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.min.js'; + } + + static public function depends_on() + { + return array( + '\phpbb\db\migration\data\v330\dev', + ); + } + + public function update_data() + { + return array( + array('config.update', array('load_jquery_url', '//ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.min.js')), + ); + } + +} diff --git a/phpBB/phpbb/db/migration/data/v330/remove_max_pass_chars.php b/phpBB/phpbb/db/migration/data/v330/remove_max_pass_chars.php new file mode 100644 index 0000000000..10e5ee385d --- /dev/null +++ b/phpBB/phpbb/db/migration/data/v330/remove_max_pass_chars.php @@ -0,0 +1,43 @@ +<?php +/** + * + * This file is part of the phpBB Forum Software package. + * + * @copyright (c) phpBB Limited <https://www.phpbb.com> + * @license GNU General Public License, version 2 (GPL-2.0) + * + * For full copyright and license information, please see + * the docs/CREDITS.txt file. + * + */ + +namespace phpbb\db\migration\data\v330; + +class remove_max_pass_chars extends \phpbb\db\migration\migration +{ + public function effectively_installed() + { + return !$this->config->offsetExists('max_pass_chars'); + } + + public static function depends_on() + { + return [ + '\phpbb\db\migration\data\v330\dev', + ]; + } + + public function update_data() + { + return [ + ['config.remove', ['max_pass_chars']], + ]; + } + + public function revert_data() + { + return [ + ['config.add', ['max_pass_chars', 100]], + ]; + } +} diff --git a/phpBB/phpbb/db/migration/data/v330/reset_password.php b/phpBB/phpbb/db/migration/data/v330/reset_password.php new file mode 100644 index 0000000000..953d478ccc --- /dev/null +++ b/phpBB/phpbb/db/migration/data/v330/reset_password.php @@ -0,0 +1,48 @@ +<?php +/** + * + * This file is part of the phpBB Forum Software package. + * + * @copyright (c) phpBB Limited <https://www.phpbb.com> + * @license GNU General Public License, version 2 (GPL-2.0) + * + * For full copyright and license information, please see + * the docs/CREDITS.txt file. + * + */ + +namespace phpbb\db\migration\data\v330; + +class reset_password extends \phpbb\db\migration\migration +{ + static public function depends_on() + { + return [ + '\phpbb\db\migration\data\v330\dev', + ]; + } + + public function update_schema() + { + return [ + 'add_columns' => [ + $this->table_prefix . 'users' => [ + 'reset_token' => ['VCHAR:64', '', 'after' => 'user_actkey'], + 'reset_token_expiration' => ['TIMESTAMP', 0, 'after' => 'reset_token'], + ], + ], + ]; + } + + public function revert_schema() + { + return [ + 'drop_columns' => [ + $this->table_prefix . 'users' => [ + 'reset_token', + 'reset_token_expiration', + ], + ], + ]; + } +} diff --git a/phpBB/phpbb/db/migration/data/v330/v330b1.php b/phpBB/phpbb/db/migration/data/v330/v330b1.php new file mode 100644 index 0000000000..3df44504fc --- /dev/null +++ b/phpBB/phpbb/db/migration/data/v330/v330b1.php @@ -0,0 +1,41 @@ +<?php +/** +* +* This file is part of the phpBB Forum Software package. +* +* @copyright (c) phpBB Limited <https://www.phpbb.com> +* @license GNU General Public License, version 2 (GPL-2.0) +* +* For full copyright and license information, please see +* the docs/CREDITS.txt file. +* +*/ + +namespace phpbb\db\migration\data\v330; + +class v330b1 extends \phpbb\db\migration\migration +{ + public function effectively_installed() + { + return version_compare($this->config['version'], '3.3.0-b1', '>='); + } + + static public function depends_on() + { + return array( + '\phpbb\db\migration\data\v330\jquery_update', + '\phpbb\db\migration\data\v330\reset_password', + '\phpbb\db\migration\data\v330\remove_attachment_flash', + '\phpbb\db\migration\data\v330\remove_max_pass_chars', + '\phpbb\db\migration\data\v32x\v328', + '\phpbb\db\migration\data\v330\dev', + ); + } + + public function update_data() + { + return array( + array('config.update', array('version', '3.3.0-b1')), + ); + } +} diff --git a/phpBB/phpbb/db/tools/tools.php b/phpBB/phpbb/db/tools/tools.php index d128df96c4..1250a8901d 100644 --- a/phpBB/phpbb/db/tools/tools.php +++ b/phpBB/phpbb/db/tools/tools.php @@ -74,37 +74,6 @@ class tools implements tools_interface 'VARBINARY' => 'varbinary(255)', ), - 'mysql_40' => array( - 'INT:' => 'int(%d)', - 'BINT' => 'bigint(20)', - 'ULINT' => 'INT(10) UNSIGNED', - 'UINT' => 'mediumint(8) UNSIGNED', - 'UINT:' => 'int(%d) UNSIGNED', - 'TINT:' => 'tinyint(%d)', - 'USINT' => 'smallint(4) UNSIGNED', - 'BOOL' => 'tinyint(1) UNSIGNED', - 'VCHAR' => 'varbinary(255)', - 'VCHAR:' => 'varbinary(%d)', - 'CHAR:' => 'binary(%d)', - 'XSTEXT' => 'blob', - 'XSTEXT_UNI'=> 'blob', - 'STEXT' => 'blob', - 'STEXT_UNI' => 'blob', - 'TEXT' => 'blob', - 'TEXT_UNI' => 'blob', - 'MTEXT' => 'mediumblob', - 'MTEXT_UNI' => 'mediumblob', - 'TIMESTAMP' => 'int(11) UNSIGNED', - 'DECIMAL' => 'decimal(5,2)', - 'DECIMAL:' => 'decimal(%d,2)', - 'PDECIMAL' => 'decimal(6,3)', - 'PDECIMAL:' => 'decimal(%d,3)', - 'VCHAR_UNI' => 'blob', - 'VCHAR_UNI:'=> array('varbinary(%d)', 'limit' => array('mult', 3, 255, 'blob')), - 'VCHAR_CI' => 'blob', - 'VARBINARY' => 'varbinary(255)', - ), - 'oracle' => array( 'INT:' => 'number(%d)', 'BINT' => 'number(20)', @@ -197,21 +166,6 @@ class tools implements tools_interface // Determine mapping database type switch ($this->db->get_sql_layer()) { - case 'mysql': - $this->sql_layer = 'mysql_40'; - break; - - case 'mysql4': - if (version_compare($this->db->sql_server_info(true), '4.1.3', '>=')) - { - $this->sql_layer = 'mysql_41'; - } - else - { - $this->sql_layer = 'mysql_40'; - } - break; - case 'mysqli': $this->sql_layer = 'mysql_41'; break; @@ -240,8 +194,6 @@ class tools implements tools_interface { switch ($this->db->get_sql_layer()) { - case 'mysql': - case 'mysql4': case 'mysqli': $sql = 'SHOW TABLES'; break; @@ -359,7 +311,6 @@ class tools implements tools_interface switch ($this->sql_layer) { - case 'mysql_40': case 'mysql_41': case 'sqlite3': $table_sql .= ",\n\t PRIMARY KEY (" . implode(', ', $table_data['PRIMARY_KEY']) . ')'; @@ -381,7 +332,6 @@ class tools implements tools_interface $statements[] = $table_sql; break; - case 'mysql_40': case 'sqlite3': $table_sql .= "\n);"; $statements[] = $table_sql; @@ -834,7 +784,6 @@ class tools implements tools_interface switch ($this->sql_layer) { - case 'mysql_40': case 'mysql_41': $sql = "SHOW COLUMNS FROM $table_name"; break; @@ -911,7 +860,6 @@ class tools implements tools_interface { switch ($this->sql_layer) { - case 'mysql_40': case 'mysql_41': $sql = 'SHOW KEYS FROM ' . $table_name; @@ -936,7 +884,7 @@ class tools implements tools_interface $result = $this->db->sql_query($sql); while ($row = $this->db->sql_fetchrow($result)) { - if (($this->sql_layer == 'mysql_40' || $this->sql_layer == 'mysql_41') && !$row['Non_unique']) + if ($this->sql_layer == 'mysql_41' && !$row['Non_unique']) { continue; } @@ -971,7 +919,6 @@ class tools implements tools_interface { switch ($this->sql_layer) { - case 'mysql_40': case 'mysql_41': $sql = 'SHOW KEYS FROM ' . $table_name; @@ -996,7 +943,7 @@ class tools implements tools_interface $result = $this->db->sql_query($sql); while ($row = $this->db->sql_fetchrow($result)) { - if (($this->sql_layer == 'mysql_40' || $this->sql_layer == 'mysql_41') && ($row['Non_unique'] || $row[$col] == 'PRIMARY')) + if ($this->sql_layer == 'mysql_41' && ($row['Non_unique'] || $row[$col] == 'PRIMARY')) { continue; } @@ -1094,7 +1041,6 @@ class tools implements tools_interface switch ($this->sql_layer) { - case 'mysql_40': case 'mysql_41': $sql .= " {$column_type} "; @@ -1248,7 +1194,6 @@ class tools implements tools_interface switch ($this->sql_layer) { - case 'mysql_40': case 'mysql_41': $after = (!empty($column_data['after'])) ? ' AFTER ' . $column_data['after'] : ''; $statements[] = 'ALTER TABLE `' . $table_name . '` ADD COLUMN `' . $column_name . '` ' . $column_data['column_type_sql'] . $after; @@ -1281,7 +1226,6 @@ class tools implements tools_interface switch ($this->sql_layer) { - case 'mysql_40': case 'mysql_41': $statements[] = 'ALTER TABLE `' . $table_name . '` DROP COLUMN `' . $column_name . '`'; break; @@ -1360,7 +1304,6 @@ class tools implements tools_interface switch ($this->sql_layer) { - case 'mysql_40': case 'mysql_41': $index_name = $this->check_index_name_length($table_name, $index_name, false); $statements[] = 'DROP INDEX ' . $index_name . ' ON ' . $table_name; @@ -1422,7 +1365,6 @@ class tools implements tools_interface switch ($this->sql_layer) { - case 'mysql_40': case 'mysql_41': $statements[] = 'ALTER TABLE ' . $table_name . ' ADD PRIMARY KEY (' . implode(', ', $column) . ')'; break; @@ -1500,7 +1442,6 @@ class tools implements tools_interface $statements[] = 'CREATE UNIQUE INDEX ' . $index_name . ' ON ' . $table_name . '(' . implode(', ', $column) . ')'; break; - case 'mysql_40': case 'mysql_41': $index_name = $this->check_index_name_length($table_name, $index_name); $statements[] = 'ALTER TABLE ' . $table_name . ' ADD UNIQUE INDEX ' . $index_name . '(' . implode(', ', $column) . ')'; @@ -1517,11 +1458,7 @@ class tools implements tools_interface { $statements = array(); - // remove index length unless MySQL4 - if ('mysql_40' != $this->sql_layer) - { - $column = preg_replace('#:.*$#', '', $column); - } + $column = preg_replace('#:.*$#', '', $column); switch ($this->sql_layer) { @@ -1531,17 +1468,6 @@ class tools implements tools_interface $statements[] = 'CREATE INDEX ' . $index_name . ' ON ' . $table_name . '(' . implode(', ', $column) . ')'; break; - case 'mysql_40': - // add index size to definition as required by MySQL4 - foreach ($column as $i => $col) - { - if (false !== strpos($col, ':')) - { - list($col, $index_size) = explode(':', $col); - $column[$i] = "$col($index_size)"; - } - } - // no break case 'mysql_41': $index_name = $this->check_index_name_length($table_name, $index_name); $statements[] = 'ALTER TABLE ' . $table_name . ' ADD INDEX ' . $index_name . ' (' . implode(', ', $column) . ')'; @@ -1609,7 +1535,6 @@ class tools implements tools_interface switch ($this->sql_layer) { - case 'mysql_40': case 'mysql_41': $sql = 'SHOW KEYS FROM ' . $table_name; @@ -1634,7 +1559,7 @@ class tools implements tools_interface $result = $this->db->sql_query($sql); while ($row = $this->db->sql_fetchrow($result)) { - if (($this->sql_layer == 'mysql_40' || $this->sql_layer == 'mysql_41') && !$row['Non_unique']) + if ($this->sql_layer == 'mysql_41' && !$row['Non_unique']) { continue; } @@ -1677,7 +1602,6 @@ class tools implements tools_interface switch ($this->sql_layer) { - case 'mysql_40': case 'mysql_41': $statements[] = 'ALTER TABLE `' . $table_name . '` CHANGE `' . $column_name . '` `' . $column_name . '` ' . $column_data['column_type_sql']; break; @@ -1826,7 +1750,6 @@ class tools implements tools_interface { switch ($this->sql_layer) { - case 'mysql_40': case 'mysql_41': case 'sqlite3': // Not supported diff --git a/phpBB/phpbb/extension/extension_interface.php b/phpBB/phpbb/extension/extension_interface.php index 6a6b6adb8f..46072d420c 100644 --- a/phpBB/phpbb/extension/extension_interface.php +++ b/phpBB/phpbb/extension/extension_interface.php @@ -22,7 +22,8 @@ interface extension_interface /** * Indicate whether or not the extension can be enabled. * - * @return bool + * @return bool|array True if extension is enableable, array of reasons + * if not, false for generic reason. */ public function is_enableable(); diff --git a/phpBB/phpbb/filesystem/filesystem.php b/phpBB/phpbb/filesystem/filesystem.php index 943bce3910..9acead0876 100644 --- a/phpBB/phpbb/filesystem/filesystem.php +++ b/phpBB/phpbb/filesystem/filesystem.php @@ -835,7 +835,7 @@ class filesystem implements filesystem_interface $current_path = $resolved_path . '/' . $path_part; // Resolve symlinks - if (is_link($current_path)) + if (@is_link($current_path)) { if (!function_exists('readlink')) { @@ -872,12 +872,12 @@ class filesystem implements filesystem_interface $resolved_path = false; } - else if (is_dir($current_path . '/')) + else if (@is_dir($current_path . '/')) { $resolved[] = $path_part; $resolved_path = $current_path; } - else if (is_file($current_path)) + else if (@is_file($current_path)) { $resolved[] = $path_part; $resolved_path = $current_path; diff --git a/phpBB/phpbb/install/controller/helper.php b/phpBB/phpbb/install/controller/helper.php index ff7e691224..f61c7de41f 100644 --- a/phpBB/phpbb/install/controller/helper.php +++ b/phpBB/phpbb/install/controller/helper.php @@ -267,7 +267,7 @@ class helper 'L_SKIP' => $this->language->lang('SKIP'), 'PAGE_TITLE' => $this->language->lang($page_title), 'T_IMAGE_PATH' => $this->path_helper->get_web_root_path() . $path . 'images', - 'T_JQUERY_LINK' => $this->path_helper->get_web_root_path() . $path . '../assets/javascript/jquery.min.js', + 'T_JQUERY_LINK' => $this->path_helper->get_web_root_path() . $path . '../assets/javascript/jquery-3.4.1.min.js', 'T_TEMPLATE_PATH' => $this->path_helper->get_web_root_path() . $path . 'style', 'T_ASSETS_PATH' => $this->path_helper->get_web_root_path() . $path . '../assets', diff --git a/phpBB/phpbb/install/helper/database.php b/phpBB/phpbb/install/helper/database.php index fa5a10c6fc..51fd18f874 100644 --- a/phpBB/phpbb/install/helper/database.php +++ b/phpBB/phpbb/install/helper/database.php @@ -45,15 +45,6 @@ class database 'AVAILABLE' => true, '2.0.x' => true, ), - 'mysql' => array( - 'LABEL' => 'MySQL', - 'SCHEMA' => 'mysql', - 'MODULE' => 'mysql', - 'DELIM' => ';', - 'DRIVER' => 'phpbb\db\driver\mysql', - 'AVAILABLE' => true, - '2.0.x' => true, - ), 'mssql_odbc'=> array( 'LABEL' => 'MS SQL Server [ ODBC ]', 'SCHEMA' => 'mssql', @@ -256,7 +247,6 @@ class database $dbms_info = $this->get_available_dbms($dbms); switch ($dbms_info[$dbms]['SCHEMA']) { - case 'mysql': case 'mysql_41': $prefix_length = 36; break; @@ -382,14 +372,6 @@ class database // Check if database version is supported switch ($dbms) { - case 'mysqli': - if (version_compare($db->sql_server_info(true), '4.1.3', '<')) - { - $errors[] = array( - 'title' => 'INST_ERR_DB_NO_MYSQLI', - ); - } - break; case 'sqlite3': if (version_compare($db->sql_server_info(true), '3.6.15', '<')) { diff --git a/phpBB/phpbb/install/module/install_database/task/create_schema.php b/phpBB/phpbb/install/module/install_database/task/create_schema.php index a5635d5dbe..983bb42122 100644 --- a/phpBB/phpbb/install/module/install_database/task/create_schema.php +++ b/phpBB/phpbb/install/module/install_database/task/create_schema.php @@ -129,14 +129,7 @@ class create_schema extends \phpbb\install\task_base if ($dbms === 'mysql') { - if (version_compare($this->db->sql_server_info(true), '4.1.3', '>=')) - { - $schema_name .= '_41'; - } - else - { - $schema_name .= '_40'; - } + $schema_name .= '_41'; } $db_schema_path = $this->phpbb_root_path . 'install/schemas/' . $schema_name . '_schema.sql'; diff --git a/phpBB/phpbb/install/module/install_database/task/set_up_database.php b/phpBB/phpbb/install/module/install_database/task/set_up_database.php index 49c8ea23ad..4da5ece228 100644 --- a/phpBB/phpbb/install/module/install_database/task/set_up_database.php +++ b/phpBB/phpbb/install/module/install_database/task/set_up_database.php @@ -102,14 +102,7 @@ class set_up_database extends \phpbb\install\task_base if ($dbms === 'mysql') { - if (version_compare($this->db->sql_server_info(true), '4.1.3', '>=')) - { - $schema_name .= '_41'; - } - else - { - $schema_name .= '_40'; - } + $schema_name .= '_41'; } $this->schema_file_path = $this->phpbb_root_path . 'install/schemas/' . $schema_name . '_schema.sql'; diff --git a/phpBB/phpbb/message/form.php b/phpBB/phpbb/message/form.php index 63bada91ff..6573a04f8b 100644 --- a/phpBB/phpbb/message/form.php +++ b/phpBB/phpbb/message/form.php @@ -136,7 +136,7 @@ abstract class form { if (!check_form_key('memberlist_email')) { - $this->errors[] = 'FORM_INVALID'; + $this->errors[] = $this->user->lang('FORM_INVALID'); } if (!count($this->errors)) diff --git a/phpBB/phpbb/notification/type/approve_post.php b/phpBB/phpbb/notification/type/approve_post.php index 1e8afec3f9..139b5fabb9 100644 --- a/phpBB/phpbb/notification/type/approve_post.php +++ b/phpBB/phpbb/notification/type/approve_post.php @@ -78,10 +78,7 @@ class approve_post extends \phpbb\notification\type\post 'ignore_users' => array(), ), $options); - $users = array(); - $users[$post['poster_id']] = $this->notification_manager->get_default_methods(); - - return $this->get_authorised_recipients(array_keys($users), $post['forum_id'], array_merge($options, array( + return $this->get_authorised_recipients(array($post['poster_id']), $post['forum_id'], array_merge($options, array( 'item_type' => static::$notification_option['id'], ))); } diff --git a/phpBB/phpbb/notification/type/approve_topic.php b/phpBB/phpbb/notification/type/approve_topic.php index f0bbf3f6b0..0c343646ee 100644 --- a/phpBB/phpbb/notification/type/approve_topic.php +++ b/phpBB/phpbb/notification/type/approve_topic.php @@ -78,10 +78,7 @@ class approve_topic extends \phpbb\notification\type\topic 'ignore_users' => array(), ), $options); - $users = array(); - $users[$post['poster_id']] = $this->notification_manager->get_default_methods(); - - return $this->get_authorised_recipients(array_keys($users), $post['forum_id'], array_merge($options, array( + return $this->get_authorised_recipients(array($post['poster_id']), $post['forum_id'], array_merge($options, array( 'item_type' => static::$notification_option['id'], ))); } diff --git a/phpBB/phpbb/plupload/plupload.php b/phpBB/phpbb/plupload/plupload.php index eb698fb35d..5a5b8a1874 100644 --- a/phpBB/phpbb/plupload/plupload.php +++ b/phpBB/phpbb/plupload/plupload.php @@ -216,38 +216,36 @@ class plupload } /** - * Looks at the list of allowed extensions and generates a string - * appropriate for use in configuring plupload with - * - * @param \phpbb\cache\service $cache - * @param string $forum_id The ID of the forum - * - * @return string - */ + * Looks at the list of allowed extensions and generates a string + * appropriate for use in configuring plupload with + * + * @param \phpbb\cache\service $cache Cache service object + * @param string $forum_id The forum identifier + * + * @return string + */ public function generate_filter_string(\phpbb\cache\service $cache, $forum_id) { + $groups = []; + $filters = []; + $attach_extensions = $cache->obtain_attach_extensions($forum_id); unset($attach_extensions['_allowed_']); - $groups = array(); // Re-arrange the extension array to $groups[$group_name][] foreach ($attach_extensions as $extension => $extension_info) { - if (!isset($groups[$extension_info['group_name']])) - { - $groups[$extension_info['group_name']] = array(); - } - - $groups[$extension_info['group_name']][] = $extension; + $groups[$extension_info['group_name']]['extensions'][] = $extension; + $groups[$extension_info['group_name']]['max_file_size'] = (int) $extension_info['max_filesize']; } - $filters = array(); - foreach ($groups as $group => $extensions) + foreach ($groups as $group => $group_info) { $filters[] = sprintf( - "{title: '%s', extensions: '%s'}", + "{title: '%s', extensions: '%s', max_file_size: %s}", addslashes(ucfirst(strtolower($group))), - addslashes(implode(',', $extensions)) + addslashes(implode(',', $group_info['extensions'])), + $group_info['max_file_size'] ); } @@ -276,22 +274,37 @@ class plupload } /** - * Checks various php.ini values and the maximum file size to determine - * the maximum size chunks a file can be split up into for upload - * - * @return int - */ + * Checks various php.ini values to determine the maximum chunk + * size a file should be split into for upload. + * + * The intention is to calculate a value which reflects whatever + * the most restrictive limit is set to. And to then set the chunk + * size to half that value, to ensure any required transfer overhead + * and POST data remains well within the limit. Or, if all of the + * limits are set to unlimited, the chunk size will also be unlimited. + * + * @return int + * + * @access public + */ public function get_chunk_size() { - $max = min( + $max = 0; + + $limits = [ + $this->php_ini->getBytes('memory_limit'), $this->php_ini->getBytes('upload_max_filesize'), $this->php_ini->getBytes('post_max_size'), - max(1, $this->php_ini->getBytes('memory_limit')), - $this->config['max_filesize'] - ); + ]; + + foreach ($limits as $limit_type) + { + if ($limit_type > 0) + { + $max = ($max !== 0) ? min($limit_type, $max) : $limit_type; + } + } - // Use half of the maximum possible to leave plenty of room for other - // POST data. return floor($max / 2); } diff --git a/phpBB/phpbb/search/fulltext_mysql.php b/phpBB/phpbb/search/fulltext_mysql.php index 4d3e13663d..8bdc31e128 100644 --- a/phpBB/phpbb/search/fulltext_mysql.php +++ b/phpBB/phpbb/search/fulltext_mysql.php @@ -154,7 +154,7 @@ class fulltext_mysql extends \phpbb\search\base */ public function init() { - if ($this->db->get_sql_layer() != 'mysql4' && $this->db->get_sql_layer() != 'mysqli') + if ($this->db->get_sql_layer() != 'mysqli') { return $this->user->lang['FULLTEXT_MYSQL_INCOMPATIBLE_DATABASE']; } @@ -1005,14 +1005,7 @@ class fulltext_mysql extends \phpbb\search\base if (!isset($this->stats['post_subject'])) { $alter_entry = array(); - if ($this->db->get_sql_layer() == 'mysqli' || version_compare($this->db->sql_server_info(true), '4.1.3', '>=')) - { - $alter_entry[] = 'MODIFY post_subject varchar(255) COLLATE utf8_unicode_ci DEFAULT \'\' NOT NULL'; - } - else - { - $alter_entry[] = 'MODIFY post_subject text NOT NULL'; - } + $alter_entry[] = 'MODIFY post_subject varchar(255) COLLATE utf8_unicode_ci DEFAULT \'\' NOT NULL'; $alter_entry[] = 'ADD FULLTEXT (post_subject)'; $alter_list[] = $alter_entry; } @@ -1020,15 +1013,7 @@ class fulltext_mysql extends \phpbb\search\base if (!isset($this->stats['post_content'])) { $alter_entry = array(); - if ($this->db->get_sql_layer() == 'mysqli' || version_compare($this->db->sql_server_info(true), '4.1.3', '>=')) - { - $alter_entry[] = 'MODIFY post_text mediumtext COLLATE utf8_unicode_ci NOT NULL'; - } - else - { - $alter_entry[] = 'MODIFY post_text mediumtext NOT NULL'; - } - + $alter_entry[] = 'MODIFY post_text mediumtext COLLATE utf8_unicode_ci NOT NULL'; $alter_entry[] = 'ADD FULLTEXT post_content (post_text, post_subject)'; $alter_list[] = $alter_entry; } diff --git a/phpBB/phpbb/search/fulltext_native.php b/phpBB/phpbb/search/fulltext_native.php index ecebbd37cd..295c2cf33c 100644 --- a/phpBB/phpbb/search/fulltext_native.php +++ b/phpBB/phpbb/search/fulltext_native.php @@ -889,7 +889,6 @@ class fulltext_native extends \phpbb\search\base switch ($this->db->get_sql_layer()) { - case 'mysql4': case 'mysqli': // 3.x does not support SQL_CALC_FOUND_ROWS @@ -1184,7 +1183,6 @@ class fulltext_native extends \phpbb\search\base { switch ($this->db->get_sql_layer()) { - case 'mysql4': case 'mysqli': // $select = 'SQL_CALC_FOUND_ROWS ' . $select; $is_mysql = true; diff --git a/phpBB/phpbb/search/fulltext_sphinx.php b/phpBB/phpbb/search/fulltext_sphinx.php index d8331d3815..6230f92da3 100644 --- a/phpBB/phpbb/search/fulltext_sphinx.php +++ b/phpBB/phpbb/search/fulltext_sphinx.php @@ -214,7 +214,7 @@ class fulltext_sphinx */ public function init() { - if ($this->db->get_sql_layer() != 'mysql' && $this->db->get_sql_layer() != 'mysql4' && $this->db->get_sql_layer() != 'mysqli' && $this->db->get_sql_layer() != 'postgres') + if ($this->db->get_sql_layer() != 'mysqli' && $this->db->get_sql_layer() != 'postgres') { return $this->user->lang['FULLTEXT_SPHINX_WRONG_DATABASE']; } @@ -233,7 +233,7 @@ class fulltext_sphinx protected function config_generate() { // Check if Database is supported by Sphinx - if ($this->db->get_sql_layer() =='mysql' || $this->db->get_sql_layer() == 'mysql4' || $this->db->get_sql_layer() == 'mysqli') + if ($this->db->get_sql_layer() == 'mysqli') { $this->dbtype = 'mysql'; } diff --git a/phpBB/phpbb/session.php b/phpBB/phpbb/session.php index 38e40ba29e..7c76c08b73 100644 --- a/phpBB/phpbb/session.php +++ b/phpBB/phpbb/session.php @@ -250,8 +250,7 @@ class session $ips = explode(' ', $this->forwarded_for); foreach ($ips as $ip) { - // check IPv4 first, the IPv6 is hopefully only going to be used very seldom - if (!empty($ip) && !preg_match(get_preg_expression('ipv4'), $ip) && !preg_match(get_preg_expression('ipv6'), $ip)) + if (!filter_var($ip, FILTER_VALIDATE_IP)) { // contains invalid data, don't use the forwarded for header $this->forwarded_for = ''; @@ -311,49 +310,17 @@ class session foreach ($ips as $ip) { - if (function_exists('phpbb_ip_normalise')) - { - // Normalise IP address - $ip = phpbb_ip_normalise($ip); - - if (empty($ip)) - { - // IP address is invalid. - break; - } - - // IP address is valid. - $this->ip = $ip; - - // Skip legacy code. - continue; - } - - if (preg_match(get_preg_expression('ipv4'), $ip)) - { - $this->ip = $ip; - } - else if (preg_match(get_preg_expression('ipv6'), $ip)) - { - // Quick check for IPv4-mapped address in IPv6 - if (stripos($ip, '::ffff:') === 0) - { - $ipv4 = substr($ip, 7); - - if (preg_match(get_preg_expression('ipv4'), $ipv4)) - { - $ip = $ipv4; - } - } + // Normalise IP address + $ip = phpbb_ip_normalise($ip); - $this->ip = $ip; - } - else + if ($ip === false) { - // We want to use the last valid address in the chain - // Leave foreach loop when address is invalid + // IP address is invalid. break; } + + // IP address is valid. + $this->ip = $ip; } $this->load = false; @@ -1077,7 +1044,7 @@ class session */ function set_cookie($name, $cookiedata, $cookietime, $httponly = true) { - global $config; + global $config, $phpbb_dispatcher; // If headers are already set, we just return if (headers_sent()) @@ -1085,6 +1052,32 @@ class session return; } + $disable_cookie = false; + /** + * Event to modify or disable setting cookies + * + * @event core.set_cookie + * @var bool disable_cookie Set to true to disable setting this cookie + * @var string name Name of the cookie + * @var string cookiedata The data to hold within the cookie + * @var int cookietime The expiration time as UNIX timestamp + * @var bool httponly Use HttpOnly? + * @since 3.2.9-RC1 + */ + $vars = array( + 'disable_cookie', + 'name', + 'cookiedata', + 'cookietime', + 'httponly', + ); + extract($phpbb_dispatcher->trigger_event('core.set_cookie', compact($vars))); + + if ($disable_cookie) + { + return; + } + $name_data = rawurlencode($config['cookie_name'] . '_' . $name) . '=' . rawurlencode($cookiedata); $expire = gmdate('D, d-M-Y H:i:s \\G\\M\\T', $cookietime); $domain = (!$config['cookie_domain'] || $config['cookie_domain'] == '127.0.0.1' || strpos($config['cookie_domain'], '.') === false) ? '' : '; domain=' . $config['cookie_domain']; @@ -1374,7 +1367,7 @@ class session foreach ($dnsbl_check as $dnsbl => $lookup) { - if (phpbb_checkdnsrr($reverse_ip . '.' . $dnsbl . '.', 'A') === true) + if (checkdnsrr($reverse_ip . '.' . $dnsbl . '.', 'A') === true) { $info = array($dnsbl, $lookup . $ip); } @@ -1418,7 +1411,7 @@ class session { // One problem here... the return parameter for the "windows" method is different from what // we expect... this may render this check useless... - if (phpbb_checkdnsrr($uri . '.multi.uribl.com.', 'A') === true) + if (checkdnsrr($uri . '.multi.uribl.com.', 'A') === true) { return true; } diff --git a/phpBB/phpbb/template/twig/extension.php b/phpBB/phpbb/template/twig/extension.php index c5b3db1aaf..1131a7f3aa 100644 --- a/phpBB/phpbb/template/twig/extension.php +++ b/phpBB/phpbb/template/twig/extension.php @@ -30,7 +30,6 @@ class extension extends \Twig_Extension * @param \phpbb\template\context $context * @param \phpbb\template\twig\environment $environment * @param \phpbb\language\language $language - * @return \phpbb\template\twig\extension */ public function __construct(\phpbb\template\context $context, \phpbb\template\twig\environment $environment, $language) { @@ -91,6 +90,7 @@ class extension extends \Twig_Extension return array( new \Twig_SimpleFunction('lang', array($this, 'lang')), new \Twig_SimpleFunction('lang_defined', array($this, 'lang_defined')), + new \Twig_SimpleFunction('get_class', 'get_class'), ); } @@ -190,10 +190,10 @@ class extension extends \Twig_Extension } /** - * Check if a language variable exists - * - * @return bool - */ + * Check if a language variable exists + * + * @return bool + */ public function lang_defined($key) { return call_user_func_array([$this->language, 'is_set'], [$key]); diff --git a/phpBB/phpbb/template/twig/extension/avatar.php b/phpBB/phpbb/template/twig/extension/avatar.php new file mode 100644 index 0000000000..7a17fd4b42 --- /dev/null +++ b/phpBB/phpbb/template/twig/extension/avatar.php @@ -0,0 +1,80 @@ +<?php +/** + * + * This file is part of the phpBB Forum Software package. + * + * @copyright (c) phpBB Limited <https://www.phpbb.com> + * @license GNU General Public License, version 2 (GPL-2.0) + * + * For full copyright and license information, please see + * the docs/CREDITS.txt file. + * + */ + +namespace phpbb\template\twig\extension; + +class avatar extends \Twig_Extension +{ + /** + * Get the name of this extension + * + * @return string + */ + public function getName() + { + return 'avatar'; + } + + /** + * Returns a list of global functions to add to the existing list. + * + * @return array An array of global functions + */ + public function getFunctions() + { + return array( + new \Twig_SimpleFunction('avatar', array($this, 'get_avatar')), + ); + } + + /** + * Get avatar for placing into templates. + * + * How to use in a template: + * - {{ avatar('mode', row, alt, ignore_config, lazy) }} + * + * The mode and row (group_row or user_row) are required. + * The other fields (alt|ignore_config|lazy) are optional. + * + * @uses \phpbb_get_group_avatar() + * @uses \phpbb_get_user_avatar() + * + * @return string The avatar HTML for the specified mode + */ + public function get_avatar() + { + $args = func_get_args(); + + $mode = (string) $args[0]; + $row = (array) $args[1]; + $alt = isset($args[2]) ? (string) $args[2] : false; + $ignore_config = isset($args[3]) ? (bool) $args[3] : false; + $lazy = isset($args[4]) ? (bool) $args[4] : false; + + // To prevent having to redefine alt attribute ('USER_AVATAR'|'GROUP_AVATAR'), we check if an alternative has been provided + switch ($mode) + { + case 'group': + return $alt ? phpbb_get_group_avatar($row, $alt, $ignore_config, $lazy) : phpbb_get_group_avatar($row); + break; + + case 'user': + return $alt ? phpbb_get_user_avatar($row, $alt, $ignore_config, $lazy) : phpbb_get_user_avatar($row); + break; + + default: + return ''; + break; + } + } +} diff --git a/phpBB/phpbb/template/twig/extension/config.php b/phpBB/phpbb/template/twig/extension/config.php new file mode 100644 index 0000000000..cbf6e505c5 --- /dev/null +++ b/phpBB/phpbb/template/twig/extension/config.php @@ -0,0 +1,64 @@ +<?php +/** + * + * This file is part of the phpBB Forum Software package. + * + * @copyright (c) phpBB Limited <https://www.phpbb.com> + * @license GNU General Public License, version 2 (GPL-2.0) + * + * For full copyright and license information, please see + * the docs/CREDITS.txt file. + * + */ + +namespace phpbb\template\twig\extension; + +class config extends \Twig_Extension +{ + /** @var \phpbb\config\config */ + protected $config; + + /** + * Constructor. + * + * @param \phpbb\config\config $config Configuration object + */ + public function __construct(\phpbb\config\config $config) + { + $this->config = $config; + } + + /** + * Get the name of this extension + * + * @return string + */ + public function getName() + { + return 'config'; + } + + /** + * Returns a list of global functions to add to the existing list. + * + * @return array An array of global functions + */ + public function getFunctions() + { + return array( + new \Twig_SimpleFunction('config', array($this, 'get_config')), + ); + } + + /** + * Retrieves a configuration value for use in templates. + * + * @return string The configuration value + */ + public function get_config() + { + $args = func_get_args(); + + return $this->config->offsetGet($args[0]); + } +} diff --git a/phpBB/phpbb/template/twig/extension/username.php b/phpBB/phpbb/template/twig/extension/username.php new file mode 100644 index 0000000000..ef149693a0 --- /dev/null +++ b/phpBB/phpbb/template/twig/extension/username.php @@ -0,0 +1,84 @@ +<?php +/** + * + * This file is part of the phpBB Forum Software package. + * + * @copyright (c) phpBB Limited <https://www.phpbb.com> + * @license GNU General Public License, version 2 (GPL-2.0) + * + * For full copyright and license information, please see + * the docs/CREDITS.txt file. + * + */ + +namespace phpbb\template\twig\extension; + +class username extends \Twig_Extension +{ + /** + * Get the name of this extension + * + * @return string + */ + public function getName() + { + return 'username'; + } + + /** + * Returns a list of global functions to add to the existing list. + * + * @return array An array of global functions + */ + public function getFunctions() + { + return array( + new \Twig_SimpleFunction('username', array($this, 'get_username')), + ); + } + + /** + * Get username details for placing into templates. + * + * How to use in a template: + * - {{ username('mode', user_id, username, user_colour, guest_username, custom_profile_url) }} + * - {{ username('mode', user_row, guest_username, custom_profile_url) }} + * It's possible to provide the user identifier, name and colour separately, + * or provide the entire user row at once as an array. + * + * The mode, user_id and username are required (separately or through a user row). + * The other fields (user_colour|guest_username|custom_profile_url) are optional. + * + * @uses \get_username_string() + * + * @return string A string based on what is wanted depending on $mode + */ + public function get_username() + { + $args = func_get_args(); + + $mode = $args[0]; + $user = $args[1]; + + // If the entire user row is provided + if (is_array($user)) + { + $user_id = isset($user['user_id']) ? $user['user_id'] : ''; + $username = isset($user['username']) ? $user['username'] : ''; + $user_colour = isset($user['user_colour']) ? $user['user_colour'] : ''; + $guest_username = isset($args[2]) ? $args[2] : false; + $custom_profile_url = isset($args[3]) ? $args[3] : false; + } + else + { + // Options are provided separately + $user_id = $user; + $username = $args[2]; + $user_colour = isset($args[3]) ? $args[3] : ''; + $guest_username = isset($args[4]) ? $args[4] : false; + $custom_profile_url = isset($args[5]) ? $args[5] : false; + } + + return get_username_string($mode, $user_id, $username, $user_colour, $guest_username, $custom_profile_url); + } +} diff --git a/phpBB/phpbb/textformatter/s9e/factory.php b/phpBB/phpbb/textformatter/s9e/factory.php index 6191b9a315..dca1c78d40 100644 --- a/phpBB/phpbb/textformatter/s9e/factory.php +++ b/phpBB/phpbb/textformatter/s9e/factory.php @@ -110,7 +110,7 @@ class factory implements \phpbb\textformatter\cache_interface 'i' => '<span style="font-style: italic"><xsl:apply-templates/></span>', 'u' => '<span style="text-decoration: underline"><xsl:apply-templates/></span>', 'img' => '<img src="{IMAGEURL}" class="postimage" alt="{L_IMAGE}"/>', - 'size' => '<span style="font-size: {FONTSIZE}%; line-height: normal"><xsl:apply-templates/></span>', + 'size' => '<span><xsl:attribute name="style"><xsl:text>font-size: </xsl:text><xsl:value-of select="substring(@size, 1, 4)"/><xsl:text>%; line-height: normal</xsl:text></xsl:attribute><xsl:apply-templates/></span>', 'color' => '<span style="color: {COLOR}"><xsl:apply-templates/></span>', 'email' => '<a> <xsl:attribute name="href"> diff --git a/phpBB/phpbb/textformatter/s9e/parser.php b/phpBB/phpbb/textformatter/s9e/parser.php index 3698dca224..a36fc63141 100644 --- a/phpBB/phpbb/textformatter/s9e/parser.php +++ b/phpBB/phpbb/textformatter/s9e/parser.php @@ -342,7 +342,7 @@ class parser implements \phpbb\textformatter\parser_interface return false; } - if ($size < 1) + if ($size < 1 || !is_numeric($size)) { return false; } diff --git a/phpBB/phpbb/ucp/controller/reset_password.php b/phpBB/phpbb/ucp/controller/reset_password.php new file mode 100644 index 0000000000..7bd1b20cb3 --- /dev/null +++ b/phpBB/phpbb/ucp/controller/reset_password.php @@ -0,0 +1,443 @@ +<?php +/** +* +* This file is part of the phpBB Forum Software package. +* +* @copyright (c) phpBB Limited <https://www.phpbb.com> +* @license GNU General Public License, version 2 (GPL-2.0) +* +* For full copyright and license information, please see +* the docs/CREDITS.txt file. +* +*/ + +namespace phpbb\ucp\controller; + +use phpbb\auth\auth; +use phpbb\config\config; +use phpbb\controller\helper; +use phpbb\db\driver\driver_interface; +use phpbb\event\dispatcher; +use phpbb\exception\http_exception; +use phpbb\language\language; +use phpbb\log\log_interface; +use phpbb\passwords\manager; +use phpbb\request\request_interface; +use phpbb\template\template; +use phpbb\user; +use Symfony\Component\HttpFoundation\Response; + +/** +* Handling forgotten passwords via reset password functionality +*/ +class reset_password +{ + /** @var config */ + protected $config; + + /** @var driver_interface */ + protected $db; + + /** @var dispatcher */ + protected $dispatcher; + + /** @var helper */ + protected $helper; + + /** @var language */ + protected $language; + + /** @var log_interface */ + protected $log; + + /** @var manager */ + protected $passwords_manager; + + /** @var request_interface */ + protected $request; + + /** @var template */ + protected $template; + + /** @var user */ + protected $user; + + /** @var array phpBB DB table names */ + protected $users_table; + + /** @var string phpBB root path */ + protected $root_path; + + /** @var string PHP extension */ + protected $php_ext; + + /** + * Reset password controller constructor. + * + * @param config $config + * @param driver_interface $db + * @param dispatcher $dispatcher + * @param helper $helper + * @param language $language + * @param log_interface $log + * @param manager $passwords_manager + * @param request_interface $request + * @param template $template + * @param user $user + * @param string $users_table + * @param string $root_path + * @param string $php_ext + */ + public function __construct(config $config, driver_interface $db, dispatcher $dispatcher, helper $helper, + language $language, log_interface $log, manager $passwords_manager, + request_interface $request, template $template, user $user, string $users_table, + string $root_path, string $php_ext) + { + $this->config = $config; + $this->db = $db; + $this->dispatcher = $dispatcher; + $this->helper = $helper; + $this->language = $language; + $this->log = $log; + $this->passwords_manager = $passwords_manager; + $this->request = $request; + $this->template = $template; + $this->user = $user; + $this->users_table = $users_table; + $this->root_path = $root_path; + $this->php_ext = $php_ext; + } + + /** + * Init controller + */ + protected function init_controller() + { + $this->language->add_lang('ucp'); + + if (!$this->config['allow_password_reset']) + { + throw new http_exception(Response::HTTP_OK, 'UCP_PASSWORD_RESET_DISABLED', [ + '<a href="mailto:' . htmlspecialchars($this->config['board_contact']) . '">', + '</a>' + ]); + } + } + + /** + * Remove reset token for specified user + * + * @param int $user_id User ID + */ + protected function remove_reset_token(int $user_id) + { + $sql_ary = [ + 'reset_token' => '', + 'reset_token_expiration' => 0, + ]; + + $sql = 'UPDATE ' . $this->users_table . ' + SET ' . $this->db->sql_build_array('UPDATE', $sql_ary) . ' + WHERE user_id = ' . $user_id; + $this->db->sql_query($sql); + } + + /** + * Handle password reset request + * + * @return Response + */ + public function request() + { + $this->init_controller(); + + $submit = $this->request->is_set_post('submit'); + $username = $this->request->variable('username', '', true); + $email = strtolower($this->request->variable('email', '')); + + add_form_key('ucp_reset_password'); + + if ($submit) + { + if (!check_form_key('ucp_reset_password')) + { + throw new http_exception(Response::HTTP_UNAUTHORIZED, 'FORM_INVALID'); + } + + if (empty($email)) + { + return $this->helper->message('NO_EMAIL_USER'); + } + + $sql_array = [ + 'SELECT' => 'user_id, username, user_permissions, user_email, user_jabber, user_notify_type, user_type,' + . ' user_lang, user_inactive_reason, reset_token, reset_token_expiration', + 'FROM' => [$this->users_table => 'u'], + 'WHERE' => "user_email_hash = '" . $this->db->sql_escape(phpbb_email_hash($email)) . "'" . + (!empty($username) ? " AND username_clean = '" . $this->db->sql_escape(utf8_clean_string($username)) . "'" : ''), + ]; + + /** + * Change SQL query for fetching user data + * + * @event core.ucp_remind_modify_select_sql + * @var string email User's email from the form + * @var string username User's username from the form + * @var array sql_array Fully assembled SQL query with keys SELECT, FROM, WHERE + * @since 3.1.11-RC1 + * @changed 3.3.0-b1 Moved to reset password controller + */ + $vars = [ + 'email', + 'username', + 'sql_array', + ]; + extract($this->dispatcher->trigger_event('core.ucp_remind_modify_select_sql', compact($vars))); + + $sql = $this->db->sql_build_query('SELECT', $sql_array); + $result = $this->db->sql_query_limit($sql, 2); // don't waste resources on more rows than we need + $rowset = $this->db->sql_fetchrowset($result); + $this->db->sql_freeresult($result); + + if (count($rowset) > 1) + { + $this->template->assign_vars([ + 'USERNAME_REQUIRED' => true, + 'EMAIL' => $email, + ]); + } + else + { + $message = $this->language->lang('PASSWORD_RESET_LINK_SENT') . '<br /><br />' . $this->language->lang('RETURN_INDEX', '<a href="' . append_sid("{$this->root_path}index.{$this->php_ext}") . '">', '</a>'); + + if (empty($rowset)) + { + return $this->helper->message($message); + } + + $user_row = $rowset[0]; + + if ($user_row['user_type'] == USER_IGNORE || $user_row['user_type'] == USER_INACTIVE) + { + return $this->helper->message($message); + } + + // Do not create multiple valid reset tokens + if (!empty($user_row['reset_token']) && (int) $user_row['reset_token_expiration'] >= time()) + { + return $this->helper->message($message); + } + + // Check users permissions + $auth = new auth(); + $auth->acl($user_row); + + if (!$auth->acl_get('u_chgpasswd')) + { + return $this->helper->message($message); + } + + // Generate reset token + $reset_token = strtolower(gen_rand_string(32)); + + $sql_ary = [ + 'reset_token' => $reset_token, + 'reset_token_expiration' => strtotime('+1 day'), + ]; + + $sql = 'UPDATE ' . $this->users_table . ' + SET ' . $this->db->sql_build_array('UPDATE', $sql_ary) . ' + WHERE user_id = ' . $user_row['user_id']; + $this->db->sql_query($sql); + + if (!class_exists('messenger')) + { + include($this->root_path . 'includes/functions_messenger.' . $this->php_ext); + } + + /** @var \messenger $messenger */ + $messenger = new \messenger(false); + + $messenger->template('user_forgot_password', $user_row['user_lang']); + + $messenger->set_addresses($user_row); + + $messenger->anti_abuse_headers($this->config, $this->user); + + $messenger->assign_vars([ + 'USERNAME' => htmlspecialchars_decode($user_row['username']), + 'U_RESET_PASSWORD' => generate_board_url(true) . $this->helper->route('phpbb_ucp_reset_password_controller', [ + 'u' => $user_row['user_id'], + 'token' => $reset_token, + ], false) + ]); + + $messenger->send($user_row['user_notify_type']); + + return $this->helper->message($message); + } + } + + $this->template->assign_vars([ + 'USERNAME' => $username, + 'EMAIL' => $email, + 'U_RESET_PASSWORD_ACTION' => $this->helper->route('phpbb_ucp_forgot_password_controller'), + ]); + + return $this->helper->render('ucp_reset_password.html', $this->language->lang('RESET_PASSWORD')); + } + + /** + * Handle controller requests + * + * @return Response + */ + public function reset() + { + $this->init_controller(); + + $submit = $this->request->is_set_post('submit'); + $reset_token = $this->request->variable('token', ''); + $user_id = $this->request->variable('u', 0); + + if (empty($reset_token)) + { + return $this->helper->message('NO_RESET_TOKEN'); + } + + if (!$user_id) + { + return $this->helper->message('NO_USER'); + } + + add_form_key('ucp_reset_password'); + + $sql_array = [ + 'SELECT' => 'user_id, username, user_permissions, user_email, user_jabber, user_notify_type, user_type,' + . ' user_lang, user_inactive_reason, reset_token, reset_token_expiration', + 'FROM' => [$this->users_table => 'u'], + 'WHERE' => 'user_id = ' . $user_id, + ]; + + /** + * Change SQL query for fetching user data + * + * @event core.ucp_reset_password_modify_select_sql + * @var int user_id User ID from the form + * @var string reset_token Reset token + * @var array sql_array Fully assembled SQL query with keys SELECT, FROM, WHERE + * @since 3.3.0-b1 + */ + $vars = [ + 'user_id', + 'reset_token', + 'sql_array', + ]; + extract($this->dispatcher->trigger_event('core.ucp_reset_password_modify_select_sql', compact($vars))); + + $sql = $this->db->sql_build_query('SELECT', $sql_array); + $result = $this->db->sql_query_limit($sql, 1); + $user_row = $this->db->sql_fetchrow($result); + $this->db->sql_freeresult($result); + + $message = $this->language->lang('RESET_TOKEN_EXPIRED_OR_INVALID') . '<br /><br />' . $this->language->lang('RETURN_INDEX', '<a href="' . append_sid("{$this->root_path}index.{$this->php_ext}") . '">', '</a>'); + + if (empty($user_row)) + { + return $this->helper->message($message); + } + + if (!hash_equals($reset_token, $user_row['reset_token'])) + { + return $this->helper->message($message); + } + + if ($user_row['reset_token_expiration'] < time()) + { + $this->remove_reset_token($user_id); + + return $this->helper->message($message); + } + + $errors = []; + + if ($submit) + { + if (!check_form_key('ucp_reset_password')) + { + return $this->helper->message('FORM_INVALID'); + } + + if ($user_row['user_type'] == USER_IGNORE || $user_row['user_type'] == USER_INACTIVE) + { + return $this->helper->message($message); + } + + // Check users permissions + $auth = new auth(); + $auth->acl($user_row); + + if (!$auth->acl_get('u_chgpasswd')) + { + return $this->helper->message($message); + } + + if (!function_exists('validate_data')) + { + include($this->root_path . 'includes/functions_user.' . $this->php_ext); + } + + $data = [ + 'new_password' => $this->request->untrimmed_variable('new_password', '', true), + 'password_confirm' => $this->request->untrimmed_variable('new_password_confirm', '', true), + ]; + $check_data = [ + 'new_password' => [ + ['string', false, $this->config['min_pass_chars'], 0], + ['password'], + ], + 'password_confirm' => ['string', true, $this->config['min_pass_chars'], 0], + ]; + $errors = array_merge($errors, validate_data($data, $check_data)); + if (strcmp($data['new_password'], $data['password_confirm']) !== 0) + { + $errors[] = $data['password_confirm'] ? 'NEW_PASSWORD_ERROR' : 'NEW_PASSWORD_CONFIRM_EMPTY'; + } + if (empty($errors)) + { + $sql_ary = [ + 'user_password' => $this->passwords_manager->hash($data['new_password']), + 'user_login_attempts' => 0, + 'reset_token' => '', + 'reset_token_expiration' => 0, + ]; + $sql = 'UPDATE ' . $this->users_table . ' + SET ' . $this->db->sql_build_array('UPDATE', $sql_ary) . ' + WHERE user_id = ' . (int) $user_row['user_id']; + $this->db->sql_query($sql); + $this->log->add('user', $user_row['user_id'], $this->user->ip, 'LOG_USER_NEW_PASSWORD', false, [ + 'reportee_id' => $user_row['user_id'], + $user_row['username'] + ]); + meta_refresh(3, append_sid("{$this->root_path}index.{$this->php_ext}")); + return $this->helper->message($this->language->lang('PASSWORD_RESET')); + } + } + + if (!empty($errors)) + { + $this->template->assign_block_vars_array('PASSWORD_RESET_ERRORS', array_map([$this->language, 'lang'], $errors)); + } + + $this->template->assign_vars([ + 'S_IS_PASSWORD_RESET' => true, + 'U_RESET_PASSWORD_ACTION' => $this->helper->route('phpbb_ucp_reset_password_controller'), + 'S_HIDDEN_FIELDS' => build_hidden_fields([ + 'u' => $user_id, + 'token' => $reset_token, + ]), + ]); + + return $this->helper->render('ucp_reset_password.html', $this->language->lang('RESET_PASSWORD')); + } +} diff --git a/phpBB/phpbb/user.php b/phpBB/phpbb/user.php index 5a06becb52..21d156e060 100644 --- a/phpBB/phpbb/user.php +++ b/phpBB/phpbb/user.php @@ -281,9 +281,43 @@ class user extends \phpbb\session $db->sql_freeresult($result); } + // Fallback to board's default style if (!$this->style) { - trigger_error('NO_STYLE_DATA', E_USER_ERROR); + // Verify default style exists in the database + $sql = 'SELECT style_id + FROM ' . STYLES_TABLE . ' + WHERE style_id = ' . (int) $config['default_style']; + $result = $db->sql_query($sql); + $style_id = (int) $db->sql_fetchfield('style_id'); + $db->sql_freeresult($result); + + if ($style_id > 0) + { + $db->sql_transaction('begin'); + + // Update $user row + $sql = 'SELECT * + FROM ' . STYLES_TABLE . ' + WHERE style_id = ' . (int) $config['default_style']; + $result = $db->sql_query($sql); + $this->style = $db->sql_fetchrow($result); + $db->sql_freeresult($result); + + // Update user style preference + $sql = 'UPDATE ' . USERS_TABLE . ' + SET user_style = ' . (int) $style_id . ' + WHERE user_id = ' . (int) $this->data['user_id']; + $db->sql_query($sql); + + $db->sql_transaction('commit'); + } + } + + // This should never happen + if (!$this->style) + { + trigger_error($this->language->lang('NO_STYLE_DATA', $this->data['user_style'], $this->data['user_id']), E_USER_ERROR); } // Now parse the cfg file and cache it |