diff options
Diffstat (limited to 'phpBB/phpbb')
101 files changed, 2963 insertions, 409 deletions
diff --git a/phpBB/phpbb/auth/auth.php b/phpBB/phpbb/auth/auth.php index fc7cc1a0b1..dbd83f1eb0 100644 --- a/phpBB/phpbb/auth/auth.php +++ b/phpBB/phpbb/auth/auth.php @@ -514,7 +514,7 @@ class auth */ function acl_clear_prefetch($user_id = false) { - global $db, $cache; + global $db, $cache, $phpbb_dispatcher; // Rebuild options cache $cache->destroy('_role_cache'); @@ -553,6 +553,16 @@ class auth $where_sql"; $db->sql_query($sql); + /** + * Event is triggered after user(s) permission settings cache has been cleared + * + * @event core.acl_clear_prefetch_after + * @var mixed user_id User ID(s) + * @since 3.1.11-RC1 + */ + $vars = array('user_id'); + extract($phpbb_dispatcher->trigger_event('core.acl_clear_prefetch_after', compact($vars))); + return; } diff --git a/phpBB/phpbb/avatar/driver/remote.php b/phpBB/phpbb/avatar/driver/remote.php index 3a88a432d1..efc4f5ec0f 100644 --- a/phpBB/phpbb/avatar/driver/remote.php +++ b/phpBB/phpbb/avatar/driver/remote.php @@ -85,8 +85,11 @@ class remote extends \phpbb\avatar\driver\driver } // Check if this url looks alright - // This isn't perfect, but it's what phpBB 3.0 did, and might as well make sure everything is compatible - if (!preg_match('#^(http|https|ftp)://(?:(.*?\.)*?[a-z0-9\-]+?\.[a-z]{2,4}|(?:\d{1,3}\.){3,5}\d{1,3}):?([0-9]*?).*?\.('. implode('|', $this->allowed_extensions) . ')$#i', $url)) + // Do not allow specifying the port (see RFC 3986) or IP addresses + if (!preg_match('#^(http|https|ftp)://(?:(.*?\.)*?[a-z0-9\-]+?\.[a-z]{2,4}|(?:\d{1,3}\.){3,5}\d{1,3}):?([0-9]*?).*?\.('. implode('|', $this->allowed_extensions) . ')$#i', $url) || + preg_match('@^(http|https|ftp)://[^/:?#]+:[0-9]+[/:?#]@i', $url) || + preg_match('#^(http|https|ftp)://(?:(?:\d{1,2}|1\d\d|2[0-4]\d|25[0-5])\.){3}(?:\d{1,2}|1\d\d|2[0-4]\d|25[0-5])#i', $url) || + preg_match('#^(http|https|ftp)://(?:(?:(?:[\dA-F]{1,4}:){6}(?:[\dA-F]{1,4}:[\dA-F]{1,4}|(?:(?:\d{1,2}|1\d\d|2[0-4]\d|25[0-5])\.){3}(?:\d{1,2}|1\d\d|2[0-4]\d|25[0-5])))|(?:::(?:[\dA-F]{1,4}:){0,5}(?:[\dA-F]{1,4}(?::[\dA-F]{1,4})?|(?:(?:\d{1,2}|1\d\d|2[0-4]\d|25[0-5])\.){3}(?:\d{1,2}|1\d\d|2[0-4]\d|25[0-5])))|(?:(?:[\dA-F]{1,4}:):(?:[\dA-F]{1,4}:){4}(?:[\dA-F]{1,4}:[\dA-F]{1,4}|(?:(?:\d{1,2}|1\d\d|2[0-4]\d|25[0-5])\.){3}(?:\d{1,2}|1\d\d|2[0-4]\d|25[0-5])))|(?:(?:[\dA-F]{1,4}:){1,2}:(?:[\dA-F]{1,4}:){3}(?:[\dA-F]{1,4}:[\dA-F]{1,4}|(?:(?:\d{1,2}|1\d\d|2[0-4]\d|25[0-5])\.){3}(?:\d{1,2}|1\d\d|2[0-4]\d|25[0-5])))|(?:(?:[\dA-F]{1,4}:){1,3}:(?:[\dA-F]{1,4}:){2}(?:[\dA-F]{1,4}:[\dA-F]{1,4}|(?:(?:\d{1,2}|1\d\d|2[0-4]\d|25[0-5])\.){3}(?:\d{1,2}|1\d\d|2[0-4]\d|25[0-5])))|(?:(?:[\dA-F]{1,4}:){1,4}:(?:[\dA-F]{1,4}:)(?:[\dA-F]{1,4}:[\dA-F]{1,4}|(?:(?:\d{1,2}|1\d\d|2[0-4]\d|25[0-5])\.){3}(?:\d{1,2}|1\d\d|2[0-4]\d|25[0-5])))|(?:(?:[\dA-F]{1,4}:){1,5}:(?:[\dA-F]{1,4}:[\dA-F]{1,4}|(?:(?:\d{1,2}|1\d\d|2[0-4]\d|25[0-5])\.){3}(?:\d{1,2}|1\d\d|2[0-4]\d|25[0-5])))|(?:(?:[\dA-F]{1,4}:){1,6}:[\dA-F]{1,4})|(?:(?:[\dA-F]{1,4}:){1,7}:)|(?:::))#i', $url)) { $error[] = 'AVATAR_URL_INVALID'; return false; diff --git a/phpBB/phpbb/avatar/driver/upload.php b/phpBB/phpbb/avatar/driver/upload.php index 2640e1ad1e..887a0ff258 100644 --- a/phpBB/phpbb/avatar/driver/upload.php +++ b/phpBB/phpbb/avatar/driver/upload.php @@ -146,6 +146,16 @@ class upload extends \phpbb\avatar\driver\driver return false; } + // Do not allow specifying the port (see RFC 3986) or IP addresses + // remote_upload() will do its own check for allowed filetypes + if (preg_match('@^(http|https|ftp)://[^/:?#]+:[0-9]+[/:?#]@i', $url) || + preg_match('#^(http|https|ftp)://(?:(?:\d{1,2}|1\d\d|2[0-4]\d|25[0-5])\.){3}(?:\d{1,2}|1\d\d|2[0-4]\d|25[0-5])#i', $url) || + preg_match('#^(http|https|ftp)://(?:(?:(?:[\dA-F]{1,4}:){6}(?:[\dA-F]{1,4}:[\dA-F]{1,4}|(?:(?:\d{1,2}|1\d\d|2[0-4]\d|25[0-5])\.){3}(?:\d{1,2}|1\d\d|2[0-4]\d|25[0-5])))|(?:::(?:[\dA-F]{1,4}:){0,5}(?:[\dA-F]{1,4}(?::[\dA-F]{1,4})?|(?:(?:\d{1,2}|1\d\d|2[0-4]\d|25[0-5])\.){3}(?:\d{1,2}|1\d\d|2[0-4]\d|25[0-5])))|(?:(?:[\dA-F]{1,4}:):(?:[\dA-F]{1,4}:){4}(?:[\dA-F]{1,4}:[\dA-F]{1,4}|(?:(?:\d{1,2}|1\d\d|2[0-4]\d|25[0-5])\.){3}(?:\d{1,2}|1\d\d|2[0-4]\d|25[0-5])))|(?:(?:[\dA-F]{1,4}:){1,2}:(?:[\dA-F]{1,4}:){3}(?:[\dA-F]{1,4}:[\dA-F]{1,4}|(?:(?:\d{1,2}|1\d\d|2[0-4]\d|25[0-5])\.){3}(?:\d{1,2}|1\d\d|2[0-4]\d|25[0-5])))|(?:(?:[\dA-F]{1,4}:){1,3}:(?:[\dA-F]{1,4}:){2}(?:[\dA-F]{1,4}:[\dA-F]{1,4}|(?:(?:\d{1,2}|1\d\d|2[0-4]\d|25[0-5])\.){3}(?:\d{1,2}|1\d\d|2[0-4]\d|25[0-5])))|(?:(?:[\dA-F]{1,4}:){1,4}:(?:[\dA-F]{1,4}:)(?:[\dA-F]{1,4}:[\dA-F]{1,4}|(?:(?:\d{1,2}|1\d\d|2[0-4]\d|25[0-5])\.){3}(?:\d{1,2}|1\d\d|2[0-4]\d|25[0-5])))|(?:(?:[\dA-F]{1,4}:){1,5}:(?:[\dA-F]{1,4}:[\dA-F]{1,4}|(?:(?:\d{1,2}|1\d\d|2[0-4]\d|25[0-5])\.){3}(?:\d{1,2}|1\d\d|2[0-4]\d|25[0-5])))|(?:(?:[\dA-F]{1,4}:){1,6}:[\dA-F]{1,4})|(?:(?:[\dA-F]{1,4}:){1,7}:)|(?:::))#i', $url)) + { + $error[] = 'AVATAR_URL_INVALID'; + return false; + } + $file = $upload->handle_upload('files.types.remote', $url); } else @@ -281,12 +291,20 @@ class upload extends \phpbb\avatar\driver\driver ); extract($this->dispatcher->trigger_event('core.avatar_driver_upload_delete_before', compact($vars))); - if (!sizeof($error) && file_exists($filename)) + if (!sizeof($error) && $this->filesystem->exists($filename)) { - @unlink($filename); + try + { + $this->filesystem->remove($filename); + return true; + } + catch (\phpbb\filesystem\exception\filesystem_exception $e) + { + // Fail is covered by return statement below + } } - return true; + return false; } /** @@ -304,6 +322,6 @@ class upload extends \phpbb\avatar\driver\driver */ protected function can_upload() { - return (file_exists($this->phpbb_root_path . $this->config['avatar_path']) && $this->filesystem->is_writable($this->phpbb_root_path . $this->config['avatar_path']) && (@ini_get('file_uploads') || strtolower(@ini_get('file_uploads')) == 'on')); + return ($this->filesystem->exists($this->phpbb_root_path . $this->config['avatar_path']) && $this->filesystem->is_writable($this->phpbb_root_path . $this->config['avatar_path']) && (@ini_get('file_uploads') || strtolower(@ini_get('file_uploads')) == 'on')); } } diff --git a/phpBB/phpbb/cache/driver/apcu.php b/phpBB/phpbb/cache/driver/apcu.php new file mode 100644 index 0000000000..40192e4026 --- /dev/null +++ b/phpBB/phpbb/cache/driver/apcu.php @@ -0,0 +1,70 @@ +<?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 APCU +*/ +class apcu extends \phpbb\cache\driver\memory +{ + var $extension = 'apcu'; + + /** + * {@inheritDoc} + */ + function purge() + { + apcu_clear_cache(); + + parent::purge(); + } + + /** + * Fetch an item from the cache + * + * @access protected + * @param string $var Cache key + * @return mixed Cached data + */ + function _read($var) + { + return apcu_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 apcu_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 apcu_delete($this->key_prefix . $var); + } +} diff --git a/phpBB/phpbb/cache/driver/file.php b/phpBB/phpbb/cache/driver/file.php index a210d877f0..497f00c06b 100644 --- a/phpBB/phpbb/cache/driver/file.php +++ b/phpBB/phpbb/cache/driver/file.php @@ -608,6 +608,6 @@ class file extends \phpbb\cache\driver\base */ protected function clean_varname($varname) { - return str_replace('/', '-', $varname); + return str_replace(array('/', '\\'), '-', $varname); } } diff --git a/phpBB/phpbb/cache/driver/memcache.php b/phpBB/phpbb/cache/driver/memcache.php index caa82fb0b1..57f138f574 100644 --- a/phpBB/phpbb/cache/driver/memcache.php +++ b/phpBB/phpbb/cache/driver/memcache.php @@ -52,8 +52,8 @@ class memcache extends \phpbb\cache\driver\memory $this->memcache = new \Memcache; foreach (explode(',', PHPBB_ACM_MEMCACHE) as $u) { - $parts = explode('/', $u); - $this->memcache->addServer(trim($parts[0]), trim($parts[1])); + preg_match('#(.*)/(\d+)#', $u, $parts); + $this->memcache->addServer(trim($parts[1]), (int) trim($parts[2])); } $this->flags = (PHPBB_ACM_MEMCACHE_COMPRESS) ? MEMCACHE_COMPRESSED : 0; } diff --git a/phpBB/phpbb/cache/driver/memcached.php b/phpBB/phpbb/cache/driver/memcached.php new file mode 100644 index 0000000000..808e15afe8 --- /dev/null +++ b/phpBB/phpbb/cache/driver/memcached.php @@ -0,0 +1,134 @@ +<?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; + +if (!defined('PHPBB_ACM_MEMCACHED_PORT')) +{ + define('PHPBB_ACM_MEMCACHED_PORT', 11211); +} + +if (!defined('PHPBB_ACM_MEMCACHED_COMPRESS')) +{ + define('PHPBB_ACM_MEMCACHED_COMPRESS', true); +} + +if (!defined('PHPBB_ACM_MEMCACHED_HOST')) +{ + define('PHPBB_ACM_MEMCACHED_HOST', 'localhost'); +} + +if (!defined('PHPBB_ACM_MEMCACHED')) +{ + //can define multiple servers with host1/port1,host2/port2 format + define('PHPBB_ACM_MEMCACHED', PHPBB_ACM_MEMCACHED_HOST . '/' . PHPBB_ACM_MEMCACHED_PORT); +} + +/** +* ACM for Memcached +*/ +class memcached extends \phpbb\cache\driver\memory +{ + /** @var string Extension to use */ + protected $extension = 'memcached'; + + /** @var \Memcached Memcached class */ + protected $memcached; + + /** @var int Flags */ + protected $flags = 0; + + /** + * Memcached constructor + */ + public function __construct() + { + // Call the parent constructor + parent::__construct(); + + $this->memcached = new \Memcached(); + $this->memcached->setOption(\Memcached::OPT_BINARY_PROTOCOL, true); + // Memcached defaults to using compression, disable if we don't want + // to use it + if (!PHPBB_ACM_MEMCACHED_COMPRESS) + { + $this->memcached->setOption(\Memcached::OPT_COMPRESSION, false); + } + + foreach (explode(',', PHPBB_ACM_MEMCACHE) as $u) + { + preg_match('#(.*)/(\d+)#', $u, $parts); + $this->memcached->addServer(trim($parts[1]), (int) trim($parts[2])); + } + } + + /** + * {@inheritDoc} + */ + public function unload() + { + parent::unload(); + + unset($this->memcached); + } + + /** + * {@inheritDoc} + */ + public function purge() + { + $this->memcached->flush(); + + parent::purge(); + } + + /** + * Fetch an item from the cache + * + * @param string $var Cache key + * + * @return mixed Cached data + */ + protected function _read($var) + { + return $this->memcached->get($this->key_prefix . $var); + } + + /** + * Store data in the cache + * + * @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 + */ + protected function _write($var, $data, $ttl = 2592000) + { + if (!$this->memcached->replace($this->key_prefix . $var, $data, $ttl)) + { + return $this->memcached->set($this->key_prefix . $var, $data, $ttl); + } + return true; + } + + /** + * Remove an item from the cache + * + * @param string $var Cache key + * @return bool True if the operation succeeded + */ + protected function _delete($var) + { + return $this->memcached->delete($this->key_prefix . $var); + } +} diff --git a/phpBB/phpbb/console/command/extension/disable.php b/phpBB/phpbb/console/command/extension/disable.php index d022755753..b2e10fb960 100644 --- a/phpBB/phpbb/console/command/extension/disable.php +++ b/phpBB/phpbb/console/command/extension/disable.php @@ -37,6 +37,13 @@ class disable extends command $io = new SymfonyStyle($input, $output); $name = $input->getArgument('extension-name'); + + if (!$this->manager->is_enabled($name)) + { + $io->error($this->user->lang('CLI_EXTENSION_DISABLED', $name)); + return 2; + } + $this->manager->disable($name); $this->manager->load_extensions(); diff --git a/phpBB/phpbb/console/command/extension/enable.php b/phpBB/phpbb/console/command/extension/enable.php index 14077d688b..f92de0069c 100644 --- a/phpBB/phpbb/console/command/extension/enable.php +++ b/phpBB/phpbb/console/command/extension/enable.php @@ -37,6 +37,20 @@ class enable extends command $io = new SymfonyStyle($input, $output); $name = $input->getArgument('extension-name'); + $extension = $this->manager->get_extension($name); + + if (!$extension->is_enableable()) + { + $io->error($this->user->lang('CLI_EXTENSION_NOT_ENABLEABLE', $name)); + return 1; + } + + if ($this->manager->is_enabled($name)) + { + $io->error($this->user->lang('CLI_EXTENSION_ENABLED', $name)); + return 1; + } + $this->manager->enable($name); $this->manager->load_extensions(); diff --git a/phpBB/phpbb/console/command/fixup/fix_left_right_ids.php b/phpBB/phpbb/console/command/fixup/fix_left_right_ids.php new file mode 100644 index 0000000000..271b099a6c --- /dev/null +++ b/phpBB/phpbb/console/command/fixup/fix_left_right_ids.php @@ -0,0 +1,137 @@ +<?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\console\command\fixup; + +use Symfony\Component\Console\Input\InputInterface; +use Symfony\Component\Console\Output\OutputInterface; +use Symfony\Component\Console\Style\SymfonyStyle; + +class fix_left_right_ids extends \phpbb\console\command\command +{ + /** @var \phpbb\user */ + protected $user; + + /** @var \phpbb\db\driver\driver_interface */ + protected $db; + + /** @var \phpbb\cache\driver\driver_interface */ + protected $cache; + + /** + * Constructor + * + * @param \phpbb\user $user User instance + * @param \phpbb\db\driver\driver_interface $db Database connection + * @param \phpbb\cache\driver\driver_interface $cache Cache instance + */ + public function __construct(\phpbb\user $user, \phpbb\db\driver\driver_interface $db, \phpbb\cache\driver\driver_interface $cache) + { + $this->user = $user; + $this->db = $db; + $this->cache = $cache; + + parent::__construct($user); + } + + /** + * {@inheritdoc} + */ + protected function configure() + { + $this + ->setName('fixup:fix-left-right-ids') + ->setDescription($this->user->lang('CLI_DESCRIPTION_FIX_LEFT_RIGHT_IDS')) + ; + } + + /** + * Executes the command fixup:fix-left-right-ids. + * + * Repairs the tree structure of the forums and modules. + * The code is mainly borrowed from Support toolkit for phpBB Olympus + * + * @param InputInterface $input An InputInterface instance + * @param OutputInterface $output An OutputInterface instance + * + * @return void + */ + protected function execute(InputInterface $input, OutputInterface $output) + { + $io = new SymfonyStyle($input, $output); + + // Fix Left/Right IDs for the modules table + $result = $this->db->sql_query('SELECT DISTINCT(module_class) FROM ' . MODULES_TABLE); + while ($row = $this->db->sql_fetchrow($result)) + { + $i = 1; + $where = array("module_class = '" . $this->db->sql_escape($row['module_class']) . "'"); + $this->fix_ids_tree($i, 'module_id', MODULES_TABLE, 0, $where); + } + $this->db->sql_freeresult($result); + + // Fix the Left/Right IDs for the forums table + $i = 1; + $this->fix_ids_tree($i, 'forum_id', FORUMS_TABLE); + + $this->cache->purge(); + + $io->success($this->user->lang('CLI_FIXUP_FIX_LEFT_RIGHT_IDS_SUCCESS')); + } + + /** + * Item's tree structure rebuild helper + * The item is either forum or ACP/MCP/UCP module + * + * @param int $i Item id offset index + * @param string $field The key field to fix, forum_id|module_id + * @param string $table The table name to perform, FORUMS_TABLE|MODULES_TABLE + * @param int $parent_id Parent item id + * @param array $where Additional WHERE clause condition + * + * @return bool True on rebuild success, false otherwise + */ + protected function fix_ids_tree(&$i, $field, $table, $parent_id = 0, $where = array()) + { + $changes_made = false; + $sql = 'SELECT * FROM ' . $table . ' + WHERE parent_id = ' . (int) $parent_id . + ((!empty($where)) ? ' AND ' . implode(' AND ', $where) : '') . ' + ORDER BY left_id ASC'; + $result = $this->db->sql_query($sql); + while ($row = $this->db->sql_fetchrow($result)) + { + // Update the left_id for the item + if ($row['left_id'] != $i) + { + $this->db->sql_query('UPDATE ' . $table . ' SET ' . $this->db->sql_build_array('UPDATE', array('left_id' => $i)) . " WHERE $field = " . (int) $row[$field]); + $changes_made = true; + } + $i++; + + // Go through children and update their left/right IDs + $changes_made = (($this->fix_ids_tree($i, $field, $table, $row[$field], $where)) || $changes_made) ? true : false; + + // Update the right_id for the item + if ($row['right_id'] != $i) + { + $this->db->sql_query('UPDATE ' . $table . ' SET ' . $this->db->sql_build_array('UPDATE', array('right_id' => $i)) . " WHERE $field = " . (int) $row[$field]); + $changes_made = true; + } + $i++; + } + $this->db->sql_freeresult($result); + + return $changes_made; + } +} diff --git a/phpBB/phpbb/console/command/fixup/update_hashes.php b/phpBB/phpbb/console/command/fixup/update_hashes.php new file mode 100644 index 0000000000..4bcc3b5d19 --- /dev/null +++ b/phpBB/phpbb/console/command/fixup/update_hashes.php @@ -0,0 +1,117 @@ +<?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\console\command\fixup; + +use Symfony\Component\Console\Input\InputInterface; +use Symfony\Component\Console\Output\OutputInterface; +use Symfony\Component\Console\Helper\ProgressBar; + +class update_hashes extends \phpbb\console\command\command +{ + /** @var \phpbb\config\config */ + protected $config; + + /** @var \phpbb\db\driver\driver_interface */ + protected $db; + + /** @var \phpbb\passwords\manager */ + protected $passwords_manager; + + /** @var string Default hashing type */ + protected $default_type; + + /** + * Update_hashes constructor + * + * @param \phpbb\config\config $config + * @param \phpbb\user $user + * @param \phpbb\db\driver\driver_interface $db + * @param \phpbb\passwords\manager $passwords_manager + * @param array $hashing_algorithms Hashing driver + * service collection + * @param array $defaults Default password types + */ + public function __construct(\phpbb\config\config $config, \phpbb\user $user, + \phpbb\db\driver\driver_interface $db, \phpbb\passwords\manager $passwords_manager, + $hashing_algorithms, $defaults) + { + $this->config = $config; + $this->db = $db; + + $this->passwords_manager = $passwords_manager; + + foreach ($defaults as $type) + { + if ($hashing_algorithms[$type]->is_supported()) + { + $this->default_type = $type; + break; + } + } + + parent::__construct($user); + } + + /** + * {@inheritdoc} + */ + protected function configure() + { + $this + ->setName('fixup:update-hashes') + ->setDescription($this->user->lang('CLI_DESCRIPTION_UPDATE_HASH_BCRYPT')) + ; + } + + /** + * {@inheritdoc} + */ + protected function execute(InputInterface $input, OutputInterface $output) + { + // Get count to be able to display progress + $sql = 'SELECT COUNT(user_id) AS count + FROM ' . USERS_TABLE . ' + WHERE user_password ' . $this->db->sql_like_expression('$H$' . $this->db->get_any_char()) . ' + OR user_password ' . $this->db->sql_like_expression('$CP$' . $this->db->get_any_char()); + $result = $this->db->sql_query($sql); + $total_update_passwords = $this->db->sql_fetchfield('count'); + $this->db->sql_freeresult($result); + + // Create progress bar + $progress_bar = new ProgressBar($output, $total_update_passwords); + $progress_bar->start(); + + $sql = 'SELECT user_id, user_password + FROM ' . USERS_TABLE . ' + WHERE user_password ' . $this->db->sql_like_expression('$H$' . $this->db->get_any_char()) . ' + OR user_password ' . $this->db->sql_like_expression('$CP$' . $this->db->get_any_char()); + $result = $this->db->sql_query($sql); + + while ($row = $this->db->sql_fetchrow($result)) + { + $new_hash = $this->passwords_manager->hash($row['user_password'], array($this->default_type)); + + $sql = 'UPDATE ' . USERS_TABLE . ' + SET user_password = "' . $this->db->sql_escape($new_hash) . '" + WHERE user_id = ' . (int) $row['user_id']; + $this->db->sql_query($sql); + $progress_bar->advance(); + } + + $this->config->set('update_hashes_last_cron', time()); + + $progress_bar->finish(); + + $output->writeln('<info>' . $this->user->lang('CLI_FIXUP_UPDATE_HASH_BCRYPT_SUCCESS') . '</info>'); + } +} diff --git a/phpBB/phpbb/console/command/update/check.php b/phpBB/phpbb/console/command/update/check.php index 1f1cfa25d2..9ced651e8b 100644 --- a/phpBB/phpbb/console/command/update/check.php +++ b/phpBB/phpbb/console/command/update/check.php @@ -134,7 +134,7 @@ class check extends \phpbb\console\command\command try { $ext_manager = $this->phpbb_container->get('ext.manager'); - $md_manager = $ext_manager->create_extension_metadata_manager($ext_name, null); + $md_manager = $ext_manager->create_extension_metadata_manager($ext_name); $updates_available = $ext_manager->version_check($md_manager, $recheck, false, $stability); $metadata = $md_manager->get_metadata('all'); @@ -325,7 +325,7 @@ class check extends \phpbb\console\command\command $io->table([ $this->language->lang('VERSION'), $this->language->lang('ANNOUNCEMENT_TOPIC'), - $this->language->lang('DOWNLOAD_LATEST'), + $this->language->lang('DOWNLOAD_LATEST'), ], $rows); } } diff --git a/phpBB/phpbb/cron/task/core/update_hashes.php b/phpBB/phpbb/cron/task/core/update_hashes.php new file mode 100644 index 0000000000..a4fe477d99 --- /dev/null +++ b/phpBB/phpbb/cron/task/core/update_hashes.php @@ -0,0 +1,130 @@ +<?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\cron\task\core; + +/** + * Update old hashes to the current default hashing algorithm + * + * It is intended to gradually update all "old" style hashes to the + * current default hashing algorithm. + */ +class update_hashes extends \phpbb\cron\task\base +{ + /** @var \phpbb\config\config */ + protected $config; + + /** @var \phpbb\db\driver\driver_interface */ + protected $db; + + /** @var \phpbb\lock\db */ + protected $update_lock; + + /** @var \phpbb\passwords\manager */ + protected $passwords_manager; + + /** @var string Default hashing type */ + protected $default_type; + + /** + * Constructor. + * + * @param \phpbb\config\config $config + * @param \phpbb\db\driver\driver_interface $db + * @param \phpbb\lock\db $update_lock + * @param \phpbb\passwords\manager $passwords_manager + * @param array $hashing_algorithms Hashing driver + * service collection + * @param array $defaults Default password types + */ + public function __construct(\phpbb\config\config $config, \phpbb\db\driver\driver_interface $db, \phpbb\lock\db $update_lock, \phpbb\passwords\manager $passwords_manager, $hashing_algorithms, $defaults) + { + $this->config = $config; + $this->db = $db; + $this->passwords_manager = $passwords_manager; + $this->update_lock = $update_lock; + + foreach ($defaults as $type) + { + if ($hashing_algorithms[$type]->is_supported()) + { + $this->default_type = $type; + break; + } + } + } + + /** + * {@inheritdoc} + */ + public function is_runnable() + { + return !$this->config['use_system_cron']; + } + + /** + * {@inheritdoc} + */ + public function should_run() + { + if (!empty($this->config['update_hashes_lock'])) + { + $last_run = explode(' ', $this->config['update_hashes_lock']); + if ($last_run[0] + 60 >= time()) + { + return false; + } + } + + return $this->config['enable_update_hashes'] && $this->config['update_hashes_last_cron'] < (time() - 60); + } + + /** + * {@inheritdoc} + */ + public function run() + { + if ($this->update_lock->acquire()) + { + $sql = 'SELECT user_id, user_password + FROM ' . USERS_TABLE . ' + WHERE user_password ' . $this->db->sql_like_expression('$H$' . $this->db->get_any_char()) . ' + OR user_password ' . $this->db->sql_like_expression('$CP$' . $this->db->get_any_char()); + $result = $this->db->sql_query_limit($sql, 20); + + $affected_rows = 0; + + while ($row = $this->db->sql_fetchrow($result)) + { + $new_hash = $this->passwords_manager->hash($row['user_password'], array($this->default_type)); + + // Increase number so we know that users were selected from the database + $affected_rows++; + + $sql = 'UPDATE ' . USERS_TABLE . ' + SET user_password = "' . $this->db->sql_escape($new_hash) . '" + WHERE user_id = ' . (int) $row['user_id']; + $this->db->sql_query($sql); + } + + $this->config->set('update_hashes_last_cron', time()); + $this->update_lock->release(); + + // Stop cron for good once all hashes are converted + if ($affected_rows === 0) + { + $this->config->set('enable_update_hashes', '0'); + } + } + } +} diff --git a/phpBB/phpbb/db/extractor/postgres_extractor.php b/phpBB/phpbb/db/extractor/postgres_extractor.php index a98e39621c..0219d2ac8d 100644 --- a/phpBB/phpbb/db/extractor/postgres_extractor.php +++ b/phpBB/phpbb/db/extractor/postgres_extractor.php @@ -85,7 +85,7 @@ class postgres_extractor extends base_extractor // We don't even care about storing the results. We already know the answer if we get rows back. if ($this->db->sql_fetchrow($result)) { - $sql_data .= "DROP SEQUENCE {$table_name}_seq;\n"; + $sql_data .= "DROP SEQUENCE IF EXISTS {$table_name}_seq;\n"; $sql_data .= "CREATE SEQUENCE {$table_name}_seq;\n"; } $this->db->sql_freeresult($result); diff --git a/phpBB/phpbb/db/migration/data/v30x/.htaccess b/phpBB/phpbb/db/migration/data/v30x/.htaccess new file mode 100644 index 0000000000..44242b5418 --- /dev/null +++ b/phpBB/phpbb/db/migration/data/v30x/.htaccess @@ -0,0 +1,33 @@ +# With Apache 2.4 the "Order, Deny" syntax has been deprecated and moved from +# module mod_authz_host to a new module called mod_access_compat (which may be +# disabled) and a new "Require" syntax has been introduced to mod_authz_host. +# We could just conditionally provide both versions, but unfortunately Apache +# does not explicitly tell us its version if the module mod_version is not +# available. In this case, we check for the availability of module +# mod_authz_core (which should be on 2.4 or higher only) as a best guess. +<IfModule mod_version.c> + <IfVersion < 2.4> + <Files "*"> + Order Allow,Deny + Deny from All + </Files> + </IfVersion> + <IfVersion >= 2.4> + <Files "*"> + Require all denied + </Files> + </IfVersion> +</IfModule> +<IfModule !mod_version.c> + <IfModule !mod_authz_core.c> + <Files "*"> + Order Allow,Deny + Deny from All + </Files> + </IfModule> + <IfModule mod_authz_core.c> + <Files "*"> + Require all denied + </Files> + </IfModule> +</IfModule> diff --git a/phpBB/phpbb/db/migration/data/v310/.htaccess b/phpBB/phpbb/db/migration/data/v310/.htaccess new file mode 100644 index 0000000000..44242b5418 --- /dev/null +++ b/phpBB/phpbb/db/migration/data/v310/.htaccess @@ -0,0 +1,33 @@ +# With Apache 2.4 the "Order, Deny" syntax has been deprecated and moved from +# module mod_authz_host to a new module called mod_access_compat (which may be +# disabled) and a new "Require" syntax has been introduced to mod_authz_host. +# We could just conditionally provide both versions, but unfortunately Apache +# does not explicitly tell us its version if the module mod_version is not +# available. In this case, we check for the availability of module +# mod_authz_core (which should be on 2.4 or higher only) as a best guess. +<IfModule mod_version.c> + <IfVersion < 2.4> + <Files "*"> + Order Allow,Deny + Deny from All + </Files> + </IfVersion> + <IfVersion >= 2.4> + <Files "*"> + Require all denied + </Files> + </IfVersion> +</IfModule> +<IfModule !mod_version.c> + <IfModule !mod_authz_core.c> + <Files "*"> + Order Allow,Deny + Deny from All + </Files> + </IfModule> + <IfModule mod_authz_core.c> + <Files "*"> + Require all denied + </Files> + </IfModule> +</IfModule> diff --git a/phpBB/phpbb/db/migration/data/v31x/.htaccess b/phpBB/phpbb/db/migration/data/v31x/.htaccess new file mode 100644 index 0000000000..44242b5418 --- /dev/null +++ b/phpBB/phpbb/db/migration/data/v31x/.htaccess @@ -0,0 +1,33 @@ +# With Apache 2.4 the "Order, Deny" syntax has been deprecated and moved from +# module mod_authz_host to a new module called mod_access_compat (which may be +# disabled) and a new "Require" syntax has been introduced to mod_authz_host. +# We could just conditionally provide both versions, but unfortunately Apache +# does not explicitly tell us its version if the module mod_version is not +# available. In this case, we check for the availability of module +# mod_authz_core (which should be on 2.4 or higher only) as a best guess. +<IfModule mod_version.c> + <IfVersion < 2.4> + <Files "*"> + Order Allow,Deny + Deny from All + </Files> + </IfVersion> + <IfVersion >= 2.4> + <Files "*"> + Require all denied + </Files> + </IfVersion> +</IfModule> +<IfModule !mod_version.c> + <IfModule !mod_authz_core.c> + <Files "*"> + Order Allow,Deny + Deny from All + </Files> + </IfModule> + <IfModule mod_authz_core.c> + <Files "*"> + Require all denied + </Files> + </IfModule> +</IfModule> diff --git a/phpBB/phpbb/db/migration/data/v31x/add_jabber_ssl_context_config_options.php b/phpBB/phpbb/db/migration/data/v31x/add_jabber_ssl_context_config_options.php new file mode 100644 index 0000000000..9f416fe069 --- /dev/null +++ b/phpBB/phpbb/db/migration/data/v31x/add_jabber_ssl_context_config_options.php @@ -0,0 +1,32 @@ +<?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\v31x; + +class add_jabber_ssl_context_config_options extends \phpbb\db\migration\migration +{ + static public function depends_on() + { + return array('\phpbb\db\migration\data\v31x\v3110'); + } + + public function update_data() + { + return array( + // See http://php.net/manual/en/context.ssl.php + array('config.add', array('jab_verify_peer', 1)), + array('config.add', array('jab_verify_peer_name', 1)), + array('config.add', array('jab_allow_self_signed', 0)), + ); + } +} diff --git a/phpBB/phpbb/db/migration/data/v31x/add_latest_topics_index.php b/phpBB/phpbb/db/migration/data/v31x/add_latest_topics_index.php new file mode 100644 index 0000000000..fa2899e348 --- /dev/null +++ b/phpBB/phpbb/db/migration/data/v31x/add_latest_topics_index.php @@ -0,0 +1,51 @@ +<?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\v31x; + +class add_latest_topics_index extends \phpbb\db\migration\migration +{ + static public function depends_on() + { + return array( + '\phpbb\db\migration\data\v31x\v3110', + ); + } + + public function update_schema() + { + return array( + 'add_index' => array( + $this->table_prefix . 'topics' => array( + 'latest_topics' => array( + 'forum_id', + 'topic_last_post_time', + 'topic_last_post_id', + 'topic_moved_id', + ), + ), + ), + ); + } + + public function revert_schema() + { + return array( + 'drop_keys' => array( + $this->table_prefix . 'topics' => array( + 'latest_topics', + ), + ), + ); + } +} diff --git a/phpBB/phpbb/db/migration/data/v31x/add_smtp_ssl_context_config_options.php b/phpBB/phpbb/db/migration/data/v31x/add_smtp_ssl_context_config_options.php new file mode 100644 index 0000000000..92051dc3ca --- /dev/null +++ b/phpBB/phpbb/db/migration/data/v31x/add_smtp_ssl_context_config_options.php @@ -0,0 +1,32 @@ +<?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\v31x; + +class add_smtp_ssl_context_config_options extends \phpbb\db\migration\migration +{ + static public function depends_on() + { + return array('\phpbb\db\migration\data\v31x\v3110'); + } + + public function update_data() + { + return array( + // See http://php.net/manual/en/context.ssl.php + array('config.add', array('smtp_verify_peer', 1)), + array('config.add', array('smtp_verify_peer_name', 1)), + array('config.add', array('smtp_allow_self_signed', 0)), + ); + } +} diff --git a/phpBB/phpbb/db/migration/data/v31x/update_hashes.php b/phpBB/phpbb/db/migration/data/v31x/update_hashes.php new file mode 100644 index 0000000000..aa83c3ffbf --- /dev/null +++ b/phpBB/phpbb/db/migration/data/v31x/update_hashes.php @@ -0,0 +1,33 @@ +<?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\v31x; + +class update_hashes extends \phpbb\db\migration\migration +{ + static public function depends_on() + { + return array( + '\phpbb\db\migration\data\v31x\v3110', + ); + } + + public function update_data() + { + return array( + array('config.add', array('enable_update_hashes', '1')), + array('config.add', array('update_hashes_lock', '')), + array('config.add', array('update_hashes_last_cron', '0')) + ); + } +} diff --git a/phpBB/phpbb/db/migration/data/v31x/v3111.php b/phpBB/phpbb/db/migration/data/v31x/v3111.php new file mode 100644 index 0000000000..f01bbc2bff --- /dev/null +++ b/phpBB/phpbb/db/migration/data/v31x/v3111.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\v31x; + +class v3111 extends \phpbb\db\migration\migration +{ + public function effectively_installed() + { + return phpbb_version_compare($this->config['version'], '3.1.11', '>='); + } + + static public function depends_on() + { + return array( + '\phpbb\db\migration\data\v31x\v3111rc1', + ); + } + + public function update_data() + { + return array( + array('config.update', array('version', '3.1.11')), + ); + } +} diff --git a/phpBB/phpbb/db/migration/data/v31x/v3111rc1.php b/phpBB/phpbb/db/migration/data/v31x/v3111rc1.php new file mode 100644 index 0000000000..259656283f --- /dev/null +++ b/phpBB/phpbb/db/migration/data/v31x/v3111rc1.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\v31x; + +class v3111rc1 extends \phpbb\db\migration\migration +{ + public function effectively_installed() + { + return phpbb_version_compare($this->config['version'], '3.1.11-RC1', '>='); + } + + static public function depends_on() + { + return array( + '\phpbb\db\migration\data\v31x\v3110', + '\phpbb\db\migration\data\v31x\add_log_time_index', + '\phpbb\db\migration\data\v31x\increase_size_of_emotion', + '\phpbb\db\migration\data\v31x\add_jabber_ssl_context_config_options', + '\phpbb\db\migration\data\v31x\add_smtp_ssl_context_config_options', + '\phpbb\db\migration\data\v31x\update_hashes', + '\phpbb\db\migration\data\v31x\remove_duplicate_migrations', + '\phpbb\db\migration\data\v31x\add_latest_topics_index', + ); + } + + public function update_data() + { + return array( + array('config.update', array('version', '3.1.11-RC1')), + ); + } +} diff --git a/phpBB/phpbb/db/migration/data/v320/.htaccess b/phpBB/phpbb/db/migration/data/v320/.htaccess new file mode 100644 index 0000000000..44242b5418 --- /dev/null +++ b/phpBB/phpbb/db/migration/data/v320/.htaccess @@ -0,0 +1,33 @@ +# With Apache 2.4 the "Order, Deny" syntax has been deprecated and moved from +# module mod_authz_host to a new module called mod_access_compat (which may be +# disabled) and a new "Require" syntax has been introduced to mod_authz_host. +# We could just conditionally provide both versions, but unfortunately Apache +# does not explicitly tell us its version if the module mod_version is not +# available. In this case, we check for the availability of module +# mod_authz_core (which should be on 2.4 or higher only) as a best guess. +<IfModule mod_version.c> + <IfVersion < 2.4> + <Files "*"> + Order Allow,Deny + Deny from All + </Files> + </IfVersion> + <IfVersion >= 2.4> + <Files "*"> + Require all denied + </Files> + </IfVersion> +</IfModule> +<IfModule !mod_version.c> + <IfModule !mod_authz_core.c> + <Files "*"> + Order Allow,Deny + Deny from All + </Files> + </IfModule> + <IfModule mod_authz_core.c> + <Files "*"> + Require all denied + </Files> + </IfModule> +</IfModule> diff --git a/phpBB/phpbb/db/migration/data/v320/add_help_phpbb.php b/phpBB/phpbb/db/migration/data/v320/add_help_phpbb.php index afa67fbc58..8fadb4bde4 100644 --- a/phpBB/phpbb/db/migration/data/v320/add_help_phpbb.php +++ b/phpBB/phpbb/db/migration/data/v320/add_help_phpbb.php @@ -32,10 +32,9 @@ class add_help_phpbb extends \phpbb\db\migration\migration return array( array('config.add', array('help_send_statistics', true)), array('config.add', array('help_send_statistics_time', 0)), - array('module.remove', array( - 'acp', - false, - 'ACP_SEND_STATISTICS', + array('if', array( + array('module.exists', array('acp', false, 'ACP_SEND_STATISTICS')), + array('module.remove', array('acp', false, 'ACP_SEND_STATISTICS')), )), array('module.add', array( 'acp', diff --git a/phpBB/phpbb/db/migration/data/v320/cookie_notice.php b/phpBB/phpbb/db/migration/data/v320/cookie_notice.php new file mode 100644 index 0000000000..75cb03b3ef --- /dev/null +++ b/phpBB/phpbb/db/migration/data/v320/cookie_notice.php @@ -0,0 +1,31 @@ +<?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\v320; + +class cookie_notice extends \phpbb\db\migration\migration +{ + static public function depends_on() + { + return array( + '\phpbb\db\migration\data\v320\v320rc2', + ); + } + + public function update_data() + { + return array( + array('config.add', array('cookie_notice', false)), + ); + } +} diff --git a/phpBB/phpbb/db/migration/data/v320/v320.php b/phpBB/phpbb/db/migration/data/v320/v320.php new file mode 100644 index 0000000000..20e741cb8b --- /dev/null +++ b/phpBB/phpbb/db/migration/data/v320/v320.php @@ -0,0 +1,40 @@ +<?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\v320; + +use phpbb\db\migration\migration; + +class v320 extends migration +{ + public function effectively_installed() + { + return version_compare($this->config['version'], '3.2.0', '>='); + } + + static public function depends_on() + { + return array( + '\phpbb\db\migration\data\v31x\increase_size_of_emotion', + '\phpbb\db\migration\data\v320\cookie_notice', + ); + + } + + public function update_data() + { + return array( + array('config.update', array('version', '3.2.0')), + ); + } +} diff --git a/phpBB/phpbb/db/migration/data/v32x/.htaccess b/phpBB/phpbb/db/migration/data/v32x/.htaccess new file mode 100644 index 0000000000..44242b5418 --- /dev/null +++ b/phpBB/phpbb/db/migration/data/v32x/.htaccess @@ -0,0 +1,33 @@ +# With Apache 2.4 the "Order, Deny" syntax has been deprecated and moved from +# module mod_authz_host to a new module called mod_access_compat (which may be +# disabled) and a new "Require" syntax has been introduced to mod_authz_host. +# We could just conditionally provide both versions, but unfortunately Apache +# does not explicitly tell us its version if the module mod_version is not +# available. In this case, we check for the availability of module +# mod_authz_core (which should be on 2.4 or higher only) as a best guess. +<IfModule mod_version.c> + <IfVersion < 2.4> + <Files "*"> + Order Allow,Deny + Deny from All + </Files> + </IfVersion> + <IfVersion >= 2.4> + <Files "*"> + Require all denied + </Files> + </IfVersion> +</IfModule> +<IfModule !mod_version.c> + <IfModule !mod_authz_core.c> + <Files "*"> + Order Allow,Deny + Deny from All + </Files> + </IfModule> + <IfModule mod_authz_core.c> + <Files "*"> + Require all denied + </Files> + </IfModule> +</IfModule> diff --git a/phpBB/phpbb/db/migration/data/v32x/cookie_notice_p2.php b/phpBB/phpbb/db/migration/data/v32x/cookie_notice_p2.php new file mode 100644 index 0000000000..1a83175705 --- /dev/null +++ b/phpBB/phpbb/db/migration/data/v32x/cookie_notice_p2.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 cookie_notice_p2 extends \phpbb\db\migration\migration +{ + static public function depends_on() + { + return array( + '\phpbb\db\migration\data\v320\v320', + ); + } + + public function effectively_installed() + { + return isset($this->config['cookie_notice']); + } + + public function update_data() + { + return array( + array('config.add', array('cookie_notice', '0')), + ); + } +} diff --git a/phpBB/phpbb/db/migration/data/v32x/load_user_activity_limit.php b/phpBB/phpbb/db/migration/data/v32x/load_user_activity_limit.php new file mode 100644 index 0000000000..71bb6c00bf --- /dev/null +++ b/phpBB/phpbb/db/migration/data/v32x/load_user_activity_limit.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 load_user_activity_limit extends \phpbb\db\migration\migration +{ + static public function depends_on() + { + return array( + '\phpbb\db\migration\data\v320\v320', + ); + } + + public function effectively_installed() + { + return isset($this->config['load_user_activity_limit']); + } + + public function update_data() + { + return array( + array('config.add', array('load_user_activity_limit', '5000')), + ); + } +} diff --git a/phpBB/phpbb/db/migration/data/v32x/user_notifications_table_index_p1.php b/phpBB/phpbb/db/migration/data/v32x/user_notifications_table_index_p1.php new file mode 100644 index 0000000000..93ff31ec6c --- /dev/null +++ b/phpBB/phpbb/db/migration/data/v32x/user_notifications_table_index_p1.php @@ -0,0 +1,46 @@ +<?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 user_notifications_table_index_p1 extends \phpbb\db\migration\migration +{ + static public function depends_on() + { + return array( + '\phpbb\db\migration\data\v32x\cookie_notice_p2', + ); + } + + public function update_schema() + { + return array( + 'add_index' => array( + $this->table_prefix . 'user_notifications' => array( + 'user_id' => array('user_id'), + ), + ), + ); + } + + public function revert_schema() + { + return array( + 'drop_keys' => array( + $this->table_prefix . 'user_notifications' => array( + 'user_id', + ), + ), + ); + } +} diff --git a/phpBB/phpbb/db/migration/data/v32x/user_notifications_table_index_p2.php b/phpBB/phpbb/db/migration/data/v32x/user_notifications_table_index_p2.php new file mode 100644 index 0000000000..0a471766a0 --- /dev/null +++ b/phpBB/phpbb/db/migration/data/v32x/user_notifications_table_index_p2.php @@ -0,0 +1,46 @@ +<?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 user_notifications_table_index_p2 extends \phpbb\db\migration\migration +{ + static public function depends_on() + { + return array( + '\phpbb\db\migration\data\v32x\user_notifications_table_index_p1', + ); + } + + public function update_schema() + { + return array( + 'add_index' => array( + $this->table_prefix . 'user_notifications' => array( + 'uid_itm_id' => array('user_id', 'item_id'), + ), + ), + ); + } + + public function revert_schema() + { + return array( + 'drop_keys' => array( + $this->table_prefix . 'user_notifications' => array( + 'uid_itm_id', + ), + ), + ); + } +} diff --git a/phpBB/phpbb/db/migration/data/v32x/user_notifications_table_index_p3.php b/phpBB/phpbb/db/migration/data/v32x/user_notifications_table_index_p3.php new file mode 100644 index 0000000000..1636b3024a --- /dev/null +++ b/phpBB/phpbb/db/migration/data/v32x/user_notifications_table_index_p3.php @@ -0,0 +1,46 @@ +<?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 user_notifications_table_index_p3 extends \phpbb\db\migration\migration +{ + static public function depends_on() + { + return array( + '\phpbb\db\migration\data\v32x\user_notifications_table_index_p2', + ); + } + + public function update_schema() + { + return array( + 'add_index' => array( + $this->table_prefix . 'user_notifications' => array( + 'usr_itm_tpe' => array('user_id', 'item_type', 'item_id'), + ), + ), + ); + } + + public function revert_schema() + { + return array( + 'drop_keys' => array( + $this->table_prefix . 'user_notifications' => array( + 'usr_itm_tpe', + ), + ), + ); + } +} diff --git a/phpBB/phpbb/db/migration/data/v32x/user_notifications_table_reduce_column_sizes.php b/phpBB/phpbb/db/migration/data/v32x/user_notifications_table_reduce_column_sizes.php new file mode 100644 index 0000000000..e0a107782e --- /dev/null +++ b/phpBB/phpbb/db/migration/data/v32x/user_notifications_table_reduce_column_sizes.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\v32x; + +class user_notifications_table_reduce_column_sizes extends \phpbb\db\migration\migration +{ + static public function depends_on() + { + return array( + '\phpbb\db\migration\data\v32x\user_notifications_table_index_p3', + ); + } + + public function update_schema() + { + return array( + 'change_columns' => array( + $this->table_prefix . 'user_notifications' => array( + 'item_type' => array('VCHAR:165', ''), + 'method' => array('VCHAR:165', ''), + ), + ), + ); + } + + public function revert_schema() + { + return array( + 'change_columns' => array( + $this->table_prefix . 'user_notifications' => array( + 'item_type' => array('VCHAR:255', ''), + 'method' => array('VCHAR:255', ''), + ), + ), + ); + } +} diff --git a/phpBB/phpbb/db/migration/data/v32x/user_notifications_table_remove_duplicates.php b/phpBB/phpbb/db/migration/data/v32x/user_notifications_table_remove_duplicates.php new file mode 100644 index 0000000000..50d0642056 --- /dev/null +++ b/phpBB/phpbb/db/migration/data/v32x/user_notifications_table_remove_duplicates.php @@ -0,0 +1,55 @@ +<?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 user_notifications_table_remove_duplicates extends \phpbb\db\migration\migration +{ + static public function depends_on() + { + return array( + '\phpbb\db\migration\data\v32x\user_notifications_table_temp_index', + ); + } + + public function update_data() + { + return array( + array('custom', array(array($this, 'remove_duplicates'))), + ); + } + + public function remove_duplicates() + { + $insert_buffer = new \phpbb\db\sql_insert_buffer($this->db, $this->table_prefix . 'user_notifications'); + + $sql = "SELECT item_type, item_id, user_id, method, MAX(notify) AS notify + FROM {$this->table_prefix}user_notifications + GROUP BY item_type, item_id, user_id, method + HAVING COUNT(item_type) > 1"; + + $result = $this->sql_query($sql); + while ($row = $this->db->sql_fetchrow($result)) + { + // Delete the duplicate entries + $this->sql_query("DELETE FROM {$this->table_prefix}user_notifications + WHERE user_id = {$row['user_id']} + AND item_type = '{$row['item_type']}' + AND method = '{$row['method']}'"); + + // And re-insert as a single one + $insert_buffer->insert($row); + } + $this->db->sql_freeresult($result); + } +} diff --git a/phpBB/phpbb/db/migration/data/v32x/user_notifications_table_temp_index.php b/phpBB/phpbb/db/migration/data/v32x/user_notifications_table_temp_index.php new file mode 100644 index 0000000000..80256a0e0a --- /dev/null +++ b/phpBB/phpbb/db/migration/data/v32x/user_notifications_table_temp_index.php @@ -0,0 +1,46 @@ +<?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 user_notifications_table_temp_index extends \phpbb\db\migration\migration +{ + static public function depends_on() + { + return array( + '\phpbb\db\migration\data\v32x\user_notifications_table_reduce_column_sizes', + ); + } + + public function update_schema() + { + return array( + 'add_index' => array( + $this->table_prefix . 'user_notifications' => array( + 'itm_usr_mthd' => array('item_type', 'item_id', 'user_id', 'method'), + ), + ), + ); + } + + public function revert_schema() + { + return array( + 'drop_keys' => array( + $this->table_prefix . 'user_notifications' => array( + 'itm_usr_mthd', + ), + ), + ); + } +} diff --git a/phpBB/phpbb/db/migration/data/v32x/user_notifications_table_unique_index.php b/phpBB/phpbb/db/migration/data/v32x/user_notifications_table_unique_index.php new file mode 100644 index 0000000000..51cf90c8a0 --- /dev/null +++ b/phpBB/phpbb/db/migration/data/v32x/user_notifications_table_unique_index.php @@ -0,0 +1,51 @@ +<?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 user_notifications_table_unique_index extends \phpbb\db\migration\migration +{ + static public function depends_on() + { + return array( + '\phpbb\db\migration\data\v32x\user_notifications_table_remove_duplicates', + ); + } + + public function update_schema() + { + return array( + 'drop_keys' => array( + $this->table_prefix . 'user_notifications' => array( + 'itm_usr_mthd', + ), + ), + 'add_unique_index' => array( + $this->table_prefix . 'user_notifications' => array( + 'itm_usr_mthd' => array('item_type', 'item_id', 'user_id', 'method'), + ), + ), + ); + } + + public function revert_schema() + { + return array( + 'drop_keys' => array( + $this->table_prefix . 'user_notifications' => array( + 'itm_usr_mthd', + ), + ), + ); + } +} diff --git a/phpBB/phpbb/db/migration/data/v32x/v321.php b/phpBB/phpbb/db/migration/data/v32x/v321.php new file mode 100644 index 0000000000..268f978b4b --- /dev/null +++ b/phpBB/phpbb/db/migration/data/v32x/v321.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\v32x; + +class v321 extends \phpbb\db\migration\migration +{ + public function effectively_installed() + { + return phpbb_version_compare($this->config['version'], '3.2.1', '>='); + } + + static public function depends_on() + { + return array( + '\phpbb\db\migration\data\v32x\v321rc1', + ); + + } + + public function update_data() + { + return array( + array('config.update', array('version', '3.2.1')), + ); + } +} diff --git a/phpBB/phpbb/db/migration/data/v32x/v321rc1.php b/phpBB/phpbb/db/migration/data/v32x/v321rc1.php new file mode 100644 index 0000000000..653a16f327 --- /dev/null +++ b/phpBB/phpbb/db/migration/data/v32x/v321rc1.php @@ -0,0 +1,39 @@ +<?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 v321rc1 extends \phpbb\db\migration\migration +{ + public function effectively_installed() + { + return phpbb_version_compare($this->config['version'], '3.2.1-RC1', '>='); + } + + static public function depends_on() + { + return array( + '\phpbb\db\migration\data\v320\v320', + '\phpbb\db\migration\data\v31x\v3111rc1', + '\phpbb\db\migration\data\v32x\load_user_activity_limit', + '\phpbb\db\migration\data\v32x\user_notifications_table_unique_index', + ); + } + + public function update_data() + { + return array( + array('config.update', array('version', '3.2.1-RC1')), + ); + } +} diff --git a/phpBB/phpbb/db/tools.php b/phpBB/phpbb/db/tools.php new file mode 100644 index 0000000000..4d1b91f7b4 --- /dev/null +++ b/phpBB/phpbb/db/tools.php @@ -0,0 +1,21 @@ +<?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; + +/** + * @deprecated 3.2.0-dev (To be removed 3.3.0) use \phpbb\db\tools\tools instead + */ +class tools extends \phpbb\db\tools\tools +{ +} diff --git a/phpBB/phpbb/db/tools/mssql.php b/phpBB/phpbb/db/tools/mssql.php index a132832005..d31aa2ba0b 100644 --- a/phpBB/phpbb/db/tools/mssql.php +++ b/phpBB/phpbb/db/tools/mssql.php @@ -477,7 +477,7 @@ class mssql extends tools { $statements = array(); - $statements[] = 'DROP INDEX ' . $table_name . '.' . $index_name; + $statements[] = 'DROP INDEX [' . $table_name . '].[' . $index_name . ']'; return $this->_sql_run_sql($statements); } @@ -524,7 +524,10 @@ class mssql extends tools { $statements = array(); - $this->check_index_name_length($table_name, $index_name); + if ($this->is_sql_server_2000()) + { + $this->check_index_name_length($table_name, $index_name); + } $statements[] = 'CREATE UNIQUE INDEX [' . $index_name . '] ON [' . $table_name . ']([' . implode('], [', $column) . '])'; @@ -538,7 +541,10 @@ class mssql extends tools { $statements = array(); - $this->check_index_name_length($table_name, $index_name); + if ($this->is_sql_server_2000()) + { + $this->check_index_name_length($table_name, $index_name); + } // remove index length $column = preg_replace('#:.*$#', '', $column); @@ -601,7 +607,7 @@ class mssql extends tools // Change the column $statements[] = 'ALTER TABLE [' . $table_name . '] ALTER COLUMN [' . $column_name . '] ' . $column_data['column_type_sql']; - if (!empty($column_data['default'])) + if (!empty($column_data['default']) && !$this->mssql_is_column_identity($table_name, $column_name)) { // Add new default value constraint $statements[] = 'ALTER TABLE [' . $table_name . '] ADD CONSTRAINT [DF_' . $table_name . '_' . $column_name . '_1] ' . $column_data['default'] . ' FOR [' . $column_name . ']'; @@ -679,6 +685,37 @@ class mssql extends tools } /** + * Checks to see if column is an identity column + * + * Identity columns cannot have defaults set for them. + * + * @param string $table_name + * @param string $column_name + * @return bool true if identity, false if not + */ + protected function mssql_is_column_identity($table_name, $column_name) + { + if ($this->mssql_is_sql_server_2000()) + { + // http://msdn.microsoft.com/en-us/library/aa175912%28v=sql.80%29.aspx + // Deprecated in SQL Server 2005 + $sql = "SELECT COLUMNPROPERTY(object_id('{$table_name}'), '{$column_name}', 'IsIdentity') AS is_identity"; + } + else + { + $sql = "SELECT is_identity FROM sys.columns + WHERE object_id = object_id('{$table_name}') + AND name = '{$column_name}'"; + } + + $result = $this->db->sql_query($sql); + $is_identity = $this->db->sql_fetchfield('is_identity'); + $this->db->sql_freeresult($result); + + return (bool) $is_identity; + } + + /** * Get a list with existing indexes for the column * * @param string $table_name @@ -717,6 +754,7 @@ class mssql extends tools AND cols.object_id = ix.object_id WHERE ix.object_id = object_id('{$table_name}') AND cols.name = '{$column_name}' + AND ix.is_primary_key = 0 AND ix.is_unique = " . ($unique ? '1' : '0'); } diff --git a/phpBB/phpbb/db/tools/postgres.php b/phpBB/phpbb/db/tools/postgres.php index e2a4e668a6..077d6e06f9 100644 --- a/phpBB/phpbb/db/tools/postgres.php +++ b/phpBB/phpbb/db/tools/postgres.php @@ -448,7 +448,7 @@ class postgres extends tools // We don't even care about storing the results. We already know the answer if we get rows back. if ($this->db->sql_fetchrow($result)) { - $statements[] = "DROP SEQUENCE {$table_name}_seq;\n"; + $statements[] = "DROP SEQUENCE IF EXISTS {$table_name}_seq;\n"; } $this->db->sql_freeresult($result); diff --git a/phpBB/phpbb/db/tools/tools.php b/phpBB/phpbb/db/tools/tools.php index 9273d69cd6..2f891e43d5 100644 --- a/phpBB/phpbb/db/tools/tools.php +++ b/phpBB/phpbb/db/tools/tools.php @@ -941,16 +941,19 @@ class tools implements tools_interface continue; } - // These DBMS prefix index name with the table name switch ($this->sql_layer) { + // These DBMS prefix index name with the table name case 'oracle': case 'sqlite3': - $row[$col] = substr($row[$col], strlen($table_name) + 1); + $new_index_name = $this->check_index_name_length($table_name, $table_name . '_' . $index_name, false); + break; + default: + $new_index_name = $this->check_index_name_length($table_name, $index_name, false); break; } - if (strtolower($row[$col]) == strtolower($index_name)) + if (strtolower($row[$col]) == strtolower($new_index_name)) { $this->db->sql_freeresult($result); return true; @@ -1359,12 +1362,14 @@ class tools implements tools_interface { 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; break; case 'oracle': case 'sqlite3': - $statements[] = 'DROP INDEX ' . $table_name . '_' . $index_name; + $index_name = $this->check_index_name_length($table_name, $table_name . '_' . $index_name, false); + $statements[] = 'DROP INDEX ' . $index_name; break; } @@ -1487,17 +1492,17 @@ class tools implements tools_interface { $statements = array(); - $this->check_index_name_length($table_name, $index_name); - switch ($this->sql_layer) { case 'oracle': case 'sqlite3': - $statements[] = 'CREATE UNIQUE INDEX ' . $table_name . '_' . $index_name . ' ON ' . $table_name . '(' . implode(', ', $column) . ')'; + $index_name = $this->check_index_name_length($table_name, $table_name . '_' . $index_name); + $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) . ')'; break; } @@ -1512,8 +1517,6 @@ class tools implements tools_interface { $statements = array(); - $this->check_index_name_length($table_name, $index_name); - // remove index length unless MySQL4 if ('mysql_40' != $this->sql_layer) { @@ -1524,7 +1527,8 @@ class tools implements tools_interface { case 'oracle': case 'sqlite3': - $statements[] = 'CREATE INDEX ' . $table_name . '_' . $index_name . ' ON ' . $table_name . '(' . implode(', ', $column) . ')'; + $index_name = $this->check_index_name_length($table_name, $table_name . '_' . $index_name); + $statements[] = 'CREATE INDEX ' . $index_name . ' ON ' . $table_name . '(' . implode(', ', $column) . ')'; break; case 'mysql_40': @@ -1539,6 +1543,7 @@ class tools implements tools_interface } // 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) . ')'; break; } @@ -1551,15 +1556,37 @@ class tools implements tools_interface * * @param string $table_name * @param string $index_name + * @param bool $throw_error + * @return string The index name, shortened if too long */ - protected function check_index_name_length($table_name, $index_name) + protected function check_index_name_length($table_name, $index_name, $throw_error = true) { - $table_prefix = substr(CONFIG_TABLE, 0, -6); // strlen(config) - if (strlen($table_name . $index_name) - strlen($table_prefix) > 24) + if (strlen($index_name) > 30) { - $max_length = strlen($table_prefix) + 24; - trigger_error("Index name '{$table_name}_$index_name' on table '$table_name' is too long. The maximum is $max_length characters.", E_USER_ERROR); + // Try removing the table prefix if it's at the beginning + $table_prefix = substr(CONFIG_TABLE, 0, -6); // strlen(config) + if (strpos($index_name, $table_prefix) === 0) + { + $index_name = substr($index_name, strlen($table_prefix)); + return $this->check_index_name_length($table_name, $index_name, $throw_error); + } + + // Try removing the remaining suffix part of table name then + $table_suffix = substr($table_name, strlen($table_prefix)); + if (strpos($index_name, $table_suffix) === 0) + { + // Remove the suffix and underscore separator between table_name and index_name + $index_name = substr($index_name, strlen($table_suffix) + 1); + return $this->check_index_name_length($table_name, $index_name, $throw_error); + } + + if ($throw_error) + { + trigger_error("Index name '$index_name' on table '$table_name' is too long. The maximum is 30 characters.", E_USER_ERROR); + } } + + return $index_name; } /** diff --git a/phpBB/phpbb/di/container_builder.php b/phpBB/phpbb/di/container_builder.php index 4d5f189f12..ac1a1a1733 100644 --- a/phpBB/phpbb/di/container_builder.php +++ b/phpBB/phpbb/di/container_builder.php @@ -51,6 +51,11 @@ class container_builder protected $container; /** + * @var \phpbb\db\driver\driver_interface + */ + protected $dbal_connection = null; + + /** * Indicates whether extensions should be used (default to true). * * @var bool @@ -197,6 +202,8 @@ class container_builder $this->container->set('config.php', $this->config_php_file); } + $this->inject_dbal_driver(); + return $this->container; } catch (\Exception $e) @@ -511,7 +518,38 @@ class container_builder { $this->container->setParameter($key, $value); } + } + /** + * Inject the dbal connection driver into container + */ + protected function inject_dbal_driver() + { + if (empty($this->config_php_file)) + { + return; + } + + $config_data = $this->config_php_file->get_all(); + if (!empty($config_data)) + { + if ($this->dbal_connection === null) + { + $dbal_driver_class = $this->config_php_file->convert_30_dbms_to_31($this->config_php_file->get('dbms')); + /** @var \phpbb\db\driver\driver_interface $dbal_connection */ + $this->dbal_connection = new $dbal_driver_class(); + $this->dbal_connection->sql_connect( + $this->config_php_file->get('dbhost'), + $this->config_php_file->get('dbuser'), + $this->config_php_file->get('dbpasswd'), + $this->config_php_file->get('dbname'), + $this->config_php_file->get('dbport'), + false, + defined('PHPBB_DB_NEW_LINK') && PHPBB_DB_NEW_LINK + ); + } + $this->container->set('dbal.conn.driver', $this->dbal_connection); + } } /** diff --git a/phpBB/phpbb/di/extension/config.php b/phpBB/phpbb/di/extension/config.php index 7984a783df..8c9de48823 100644 --- a/phpBB/phpbb/di/extension/config.php +++ b/phpBB/phpbb/di/extension/config.php @@ -43,12 +43,6 @@ class config extends Extension 'core.adm_relative_path' => $this->config_php->get('phpbb_adm_relative_path') ? $this->config_php->get('phpbb_adm_relative_path') : 'adm/', 'core.table_prefix' => $this->config_php->get('table_prefix'), 'cache.driver.class' => $this->convert_30_acm_type($this->config_php->get('acm_type')), - 'dbal.driver.class' => $this->config_php->convert_30_dbms_to_31($this->config_php->get('dbms')), - 'dbal.dbhost' => $this->config_php->get('dbhost'), - 'dbal.dbuser' => $this->config_php->get('dbuser'), - 'dbal.dbpasswd' => $this->config_php->get('dbpasswd'), - 'dbal.dbname' => $this->config_php->get('dbname'), - 'dbal.dbport' => $this->config_php->get('dbport'), 'dbal.new_link' => defined('PHPBB_DB_NEW_LINK') && PHPBB_DB_NEW_LINK, ); $parameter_bag = $container->getParameterBag(); diff --git a/phpBB/phpbb/di/extension/core.php b/phpBB/phpbb/di/extension/core.php index 29c0b0e44e..67150f0103 100644 --- a/phpBB/phpbb/di/extension/core.php +++ b/phpBB/phpbb/di/extension/core.php @@ -24,7 +24,7 @@ use Symfony\Component\HttpKernel\DependencyInjection\Extension; */ class core extends Extension { - const TWIG_OPTIONS_POSITION = 6; + const TWIG_OPTIONS_POSITION = 7; /** * Config path diff --git a/phpBB/phpbb/di/proxy_instantiator.php b/phpBB/phpbb/di/proxy_instantiator.php index a388e82c0e..70295a3dec 100644 --- a/phpBB/phpbb/di/proxy_instantiator.php +++ b/phpBB/phpbb/di/proxy_instantiator.php @@ -13,8 +13,6 @@ namespace phpbb\di; -use bantu\IniGetWrapper\IniGetWrapper; -use phpbb\filesystem\filesystem; use ProxyManager\Configuration; use ProxyManager\Factory\LazyLoadingValueHolderFactory; use ProxyManager\GeneratorStrategy\EvaluatingGeneratorStrategy; @@ -45,11 +43,8 @@ class proxy_instantiator implements InstantiatorInterface // Prevent trying to write to system temp dir in case of open_basedir // restrictions being in effect - $ini_wrapper = new IniGetWrapper(); - $filesystem = new filesystem(); $tmp_dir = (function_exists('sys_get_temp_dir')) ? sys_get_temp_dir() : ''; - if (empty($tmp_dir) || $ini_wrapper->getString('open_basedir') && - (!$filesystem->exists($tmp_dir) || !$filesystem->is_writable($tmp_dir))) + if (empty($tmp_dir) || !@file_exists($tmp_dir) || !@is_writable($tmp_dir)) { $config->setProxiesTargetDir($cache_dir); } diff --git a/phpBB/phpbb/event/data.php b/phpBB/phpbb/event/data.php index c7365aee35..276ab027f2 100644 --- a/phpBB/phpbb/event/data.php +++ b/phpBB/phpbb/event/data.php @@ -63,4 +63,16 @@ class data extends Event implements \ArrayAccess { unset($this->data[$offset]); } + + /** + * Returns data with updated key in specified offset. + * + * @param string $subarray Data array subarray + * @param string $key Subarray key + * @param mixed $value Value to update + */ + public function update_subarray($subarray, $key, $value) + { + $this->data[$subarray][$key] = $value; + } } diff --git a/phpBB/phpbb/event/php_exporter.php b/phpBB/phpbb/event/php_exporter.php index d2ab0595c0..26d7e2b426 100644 --- a/phpBB/phpbb/event/php_exporter.php +++ b/phpBB/phpbb/event/php_exporter.php @@ -392,7 +392,7 @@ class php_exporter public function get_vars_from_single_line_array($line, $throw_multiline = true) { $match = array(); - preg_match('#^\$vars = array\(\'([a-zA-Z0-9_\' ,]+)\'\);$#', $line, $match); + preg_match('#^\$vars = (?:\[|array\()\'([a-zA-Z0-9_\' ,]+)\'[\)\]];$#', $line, $match); if (isset($match[1])) { @@ -510,7 +510,7 @@ class php_exporter /** * Find the "@changed" Information lines * - * @param string $tag_name Should be 'changed' or 'change' + * @param string $tag_name Should be 'change', not 'changed' * @return array Absolute line numbers * @throws \LogicException */ @@ -658,7 +658,7 @@ class php_exporter { $match = array(); $line = str_replace("\t", ' ', ltrim($line, "\t ")); - preg_match('#^\* @change(d)? (\d+\.\d+\.\d+(?:-(?:a|b|RC|pl)\d+)?)( (?:.*))?$#', $line, $match); + preg_match('#^\* @changed (\d+\.\d+\.\d+(?:-(?:a|b|RC|pl)\d+)?)( (?:.*))?$#', $line, $match); if (!isset($match[2])) { throw new \LogicException("Invalid '@changed' information for event " diff --git a/phpBB/phpbb/extension/manager.php b/phpBB/phpbb/extension/manager.php index b2b60aaa9b..4b4109bd85 100644 --- a/phpBB/phpbb/extension/manager.php +++ b/phpBB/phpbb/extension/manager.php @@ -152,7 +152,12 @@ class manager */ public function create_extension_metadata_manager($name) { - return new \phpbb\extension\metadata_manager($name, $this->config, $this, $this->phpbb_root_path); + if (!isset($this->extensions[$name]['metadata'])) + { + $metadata = new \phpbb\extension\metadata_manager($name, $this->get_extension_path($name, true)); + $this->extensions[$name]['metadata'] = $metadata; + } + return $this->extensions[$name]['metadata']; } /** @@ -168,7 +173,7 @@ class manager public function enable_step($name) { // ignore extensions that are already enabled - if (isset($this->extensions[$name]) && $this->extensions[$name]['ext_active']) + if ($this->is_enabled($name)) { return false; } @@ -257,8 +262,8 @@ class manager */ public function disable_step($name) { - // ignore extensions that are already disabled - if (!isset($this->extensions[$name]) || !$this->extensions[$name]['ext_active']) + // ignore extensions that are not enabled + if (!$this->is_enabled($name)) { return false; } @@ -336,8 +341,8 @@ class manager */ public function purge_step($name) { - // ignore extensions that do not exist - if (!isset($this->extensions[$name])) + // ignore extensions that are not configured + if (!$this->is_configured($name)) { return false; } @@ -431,25 +436,11 @@ class manager if ($file_info->isFile() && $file_info->getFilename() == 'composer.json') { $ext_name = $iterator->getInnerIterator()->getSubPath(); - $composer_file = $iterator->getPath() . '/composer.json'; - - // Ignore the extension if there is no composer.json. - if (!is_readable($composer_file) || !($ext_info = file_get_contents($composer_file))) - { - continue; - } - - $ext_info = json_decode($ext_info, true); $ext_name = str_replace(DIRECTORY_SEPARATOR, '/', $ext_name); - - // Ignore the extension if directory depth is not correct or if the directory structure - // does not match the name value specified in composer.json. - if (substr_count($ext_name, '/') !== 1 || !isset($ext_info['name']) || $ext_name != $ext_info['name']) + if ($this->is_available($ext_name)) { - continue; + $available[$ext_name] = $this->get_extension_path($ext_name, true); } - - $available[$ext_name] = $this->phpbb_root_path . 'ext/' . $ext_name . '/'; } } ksort($available); @@ -472,8 +463,12 @@ class manager $configured = array(); foreach ($this->extensions as $name => $data) { - $data['ext_path'] = ($phpbb_relative ? $this->phpbb_root_path : '') . $data['ext_path']; - $configured[$name] = $data; + if ($this->is_configured($name)) + { + unset($data['metadata']); + $data['ext_path'] = ($phpbb_relative ? $this->phpbb_root_path : '') . $data['ext_path']; + $configured[$name] = $data; + } } return $configured; } @@ -490,7 +485,7 @@ class manager $enabled = array(); foreach ($this->extensions as $name => $data) { - if ($data['ext_active']) + if ($this->is_enabled($name)) { $enabled[$name] = ($phpbb_relative ? $this->phpbb_root_path : '') . $data['ext_path']; } @@ -511,7 +506,7 @@ class manager $disabled = array(); foreach ($this->extensions as $name => $data) { - if (!$data['ext_active']) + if ($this->is_disabled($name)) { $disabled[$name] = ($phpbb_relative ? $this->phpbb_root_path : '') . $data['ext_path']; } @@ -527,7 +522,15 @@ class manager */ public function is_available($name) { - return file_exists($this->get_extension_path($name, true)); + $md_manager = $this->create_extension_metadata_manager($name); + try + { + return $md_manager->get_metadata('all') && $md_manager->validate_enable(); + } + catch (\phpbb\extension\exception $e) + { + return false; + } } /** @@ -538,7 +541,7 @@ class manager */ public function is_enabled($name) { - return isset($this->extensions[$name]) && $this->extensions[$name]['ext_active']; + return isset($this->extensions[$name]['ext_active']) && $this->extensions[$name]['ext_active']; } /** @@ -549,7 +552,7 @@ class manager */ public function is_disabled($name) { - return isset($this->extensions[$name]) && !$this->extensions[$name]['ext_active']; + return isset($this->extensions[$name]['ext_active']) && !$this->extensions[$name]['ext_active']; } /** @@ -563,7 +566,7 @@ class manager */ public function is_configured($name) { - return isset($this->extensions[$name]); + return isset($this->extensions[$name]['ext_active']); } /** @@ -573,7 +576,7 @@ class manager * @param bool $force_update Ignores cached data. Defaults to false. * @param bool $force_cache Force the use of the cache. Override $force_update. * @param string $stability Force the stability (null by default). - * @return string + * @return array * @throws runtime_exception */ public function version_check(\phpbb\extension\metadata_manager $md_manager, $force_update = false, $force_cache = false, $stability = null) @@ -589,10 +592,10 @@ class manager $version_helper = new \phpbb\version_helper($this->cache, $this->config, new file_downloader()); $version_helper->set_current_version($meta['version']); - $version_helper->set_file_location($version_check['host'], $version_check['directory'], $version_check['filename']); + $version_helper->set_file_location($version_check['host'], $version_check['directory'], $version_check['filename'], isset($version_check['ssl']) ? $version_check['ssl'] : false); $version_helper->force_stability($stability); - return $updates = $version_helper->get_suggested_updates($force_update, $force_cache); + return $version_helper->get_ext_update_on_branch($force_update, $force_cache); } /** diff --git a/phpBB/phpbb/extension/metadata_manager.php b/phpBB/phpbb/extension/metadata_manager.php index 2b8b1bbd6a..60b8db8310 100644 --- a/phpBB/phpbb/extension/metadata_manager.php +++ b/phpBB/phpbb/extension/metadata_manager.php @@ -19,24 +19,6 @@ namespace phpbb\extension; class metadata_manager { /** - * phpBB Config instance - * @var \phpbb\config\config - */ - protected $config; - - /** - * phpBB Extension Manager - * @var \phpbb\extension\manager - */ - protected $extension_manager; - - /** - * phpBB root path - * @var string - */ - protected $phpbb_root_path; - - /** * Name (including vendor) of the extension * @var string */ @@ -58,19 +40,13 @@ class metadata_manager * Creates the metadata manager * * @param string $ext_name Name (including vendor) of the extension - * @param \phpbb\config\config $config phpBB Config instance - * @param \phpbb\extension\manager $extension_manager An instance of the phpBB extension manager - * @param string $phpbb_root_path Path to the phpbb includes directory. + * @param string $ext_path Path to the extension directory including root path */ - public function __construct($ext_name, \phpbb\config\config $config, \phpbb\extension\manager $extension_manager, $phpbb_root_path) + public function __construct($ext_name, $ext_path) { - $this->config = $config; - $this->extension_manager = $extension_manager; - $this->phpbb_root_path = $phpbb_root_path; - $this->ext_name = $ext_name; $this->metadata = array(); - $this->metadata_file = ''; + $this->metadata_file = $ext_path . 'composer.json'; } /** @@ -81,92 +57,56 @@ class metadata_manager */ public function get_metadata($element = 'all') { - $this->set_metadata_file(); - - // Fetch the metadata - $this->fetch_metadata(); - - // Clean the metadata - $this->clean_metadata_array(); + // Fetch and clean the metadata if not done yet + if ($this->metadata === array()) + { + $this->fetch_metadata_from_file(); + } switch ($element) { case 'all': default: - // Validate the metadata - if (!$this->validate()) - { - return false; - } - + $this->validate(); return $this->metadata; break; case 'version': case 'name': - return ($this->validate($element)) ? $this->metadata[$element] : false; + $this->validate($element); + return $this->metadata[$element]; break; case 'display-name': - if (isset($this->metadata['extra']['display-name'])) - { - return $this->metadata['extra']['display-name']; - } - else - { - return ($this->validate('name')) ? $this->metadata['name'] : false; - } + return (isset($this->metadata['extra']['display-name'])) ? $this->metadata['extra']['display-name'] : $this->get_metadata('name'); break; } } /** - * Sets the filepath of the metadata file + * Gets the metadata file contents and cleans loaded file * * @throws \phpbb\extension\exception */ - private function set_metadata_file() + private function fetch_metadata_from_file() { - $ext_filepath = $this->extension_manager->get_extension_path($this->ext_name); - $metadata_filepath = $this->phpbb_root_path . $ext_filepath . 'composer.json'; - - $this->metadata_file = $metadata_filepath; - if (!file_exists($this->metadata_file)) { throw new \phpbb\extension\exception('FILE_NOT_FOUND', array($this->metadata_file)); } - } - /** - * Gets the contents of the composer.json file - * - * @return bool True if success, throws an exception on failure - * @throws \phpbb\extension\exception - */ - private function fetch_metadata() - { - if (!file_exists($this->metadata_file)) + if (!($file_contents = file_get_contents($this->metadata_file))) { - throw new \phpbb\extension\exception('FILE_NOT_FOUND', array($this->metadata_file)); + throw new \phpbb\extension\exception('FILE_CONTENT_ERR', array($this->metadata_file)); } - else - { - if (!($file_contents = file_get_contents($this->metadata_file))) - { - throw new \phpbb\extension\exception('FILE_CONTENT_ERR', array($this->metadata_file)); - } - - if (($metadata = json_decode($file_contents, true)) === null) - { - throw new \phpbb\extension\exception('FILE_JSON_DECODE_ERR', array($this->metadata_file)); - } - - array_walk_recursive($metadata, array($this, 'sanitize_json')); - $this->metadata = $metadata; - return true; + if (($metadata = json_decode($file_contents, true)) === null) + { + throw new \phpbb\extension\exception('FILE_JSON_DECODE_ERR', array($this->metadata_file)); } + + array_walk_recursive($metadata, array($this, 'sanitize_json')); + $this->metadata = $metadata; } /** @@ -181,16 +121,6 @@ class metadata_manager } /** - * This array handles the cleaning of the array - * - * @return array Contains the cleaned metadata array - */ - private function clean_metadata_array() - { - return $this->metadata; - } - - /** * Validate fields * * @param string $name ("all" for display and enable validation @@ -212,10 +142,8 @@ class metadata_manager switch ($name) { case 'all': - $this->validate('display'); - $this->validate_enable(); - break; + // no break case 'display': foreach ($fields as $field => $data) @@ -272,40 +200,43 @@ class metadata_manager /** * This array handles the verification that this extension can be enabled on this board * - * @return bool True if validation succeeded, False if failed + * @return bool True if validation succeeded, throws an exception if invalid + * @throws \phpbb\extension\exception */ public function validate_enable() { // Check for valid directory & phpBB, PHP versions - if (!$this->validate_dir() || !$this->validate_require_phpbb() || !$this->validate_require_php()) - { - return false; - } - - return true; + return $this->validate_dir() && $this->validate_require_phpbb() && $this->validate_require_php(); } /** * Validates the most basic directory structure to ensure it follows <vendor>/<ext> convention. * - * @return boolean True when passes validation + * @return boolean True when passes validation, throws an exception if invalid + * @throws \phpbb\extension\exception */ public function validate_dir() { - return (substr_count($this->ext_name, '/') === 1 && $this->ext_name == $this->get_metadata('name')); + if (substr_count($this->ext_name, '/') !== 1 || $this->ext_name != $this->get_metadata('name')) + { + throw new \phpbb\extension\exception('EXTENSION_DIR_INVALID'); + } + + return true; } /** * Validates the contents of the phpbb requirement field * - * @return boolean True when passes validation + * @return boolean True when passes validation, throws an exception if invalid + * @throws \phpbb\extension\exception */ public function validate_require_phpbb() { if (!isset($this->metadata['extra']['soft-require']['phpbb/phpbb'])) { - return false; + throw new \phpbb\extension\exception('META_FIELD_NOT_SET', array('soft-require')); } return true; @@ -314,51 +245,16 @@ class metadata_manager /** * Validates the contents of the php requirement field * - * @return boolean True when passes validation + * @return boolean True when passes validation, throws an exception if invalid + * @throws \phpbb\extension\exception */ public function validate_require_php() { if (!isset($this->metadata['require']['php'])) { - return false; + throw new \phpbb\extension\exception('META_FIELD_NOT_SET', array('require php')); } return true; } - - /** - * Outputs the metadata into the template - * - * @param \phpbb\template\template $template phpBB Template instance - */ - public function output_template_data(\phpbb\template\template $template) - { - $template->assign_vars(array( - 'META_NAME' => $this->metadata['name'], - 'META_TYPE' => $this->metadata['type'], - 'META_DESCRIPTION' => (isset($this->metadata['description'])) ? $this->metadata['description'] : '', - 'META_HOMEPAGE' => (isset($this->metadata['homepage'])) ? $this->metadata['homepage'] : '', - 'META_VERSION' => (isset($this->metadata['version'])) ? $this->metadata['version'] : '', - 'META_TIME' => (isset($this->metadata['time'])) ? $this->metadata['time'] : '', - 'META_LICENSE' => $this->metadata['license'], - - 'META_REQUIRE_PHP' => (isset($this->metadata['require']['php'])) ? $this->metadata['require']['php'] : '', - 'META_REQUIRE_PHP_FAIL' => !$this->validate_require_php(), - - 'META_REQUIRE_PHPBB' => (isset($this->metadata['extra']['soft-require']['phpbb/phpbb'])) ? $this->metadata['extra']['soft-require']['phpbb/phpbb'] : '', - 'META_REQUIRE_PHPBB_FAIL' => !$this->validate_require_phpbb(), - - 'META_DISPLAY_NAME' => (isset($this->metadata['extra']['display-name'])) ? $this->metadata['extra']['display-name'] : '', - )); - - foreach ($this->metadata['authors'] as $author) - { - $template->assign_block_vars('meta_authors', array( - 'AUTHOR_NAME' => $author['name'], - 'AUTHOR_EMAIL' => (isset($author['email'])) ? $author['email'] : '', - 'AUTHOR_HOMEPAGE' => (isset($author['homepage'])) ? $author['homepage'] : '', - 'AUTHOR_ROLE' => (isset($author['role'])) ? $author['role'] : '', - )); - } - } } diff --git a/phpBB/phpbb/feed/attachments_base.php b/phpBB/phpbb/feed/attachments_base.php index b14dafe15a..5d3272e0d9 100644 --- a/phpBB/phpbb/feed/attachments_base.php +++ b/phpBB/phpbb/feed/attachments_base.php @@ -25,8 +25,11 @@ abstract class attachments_base extends base /** * Retrieve the list of attachments that may be displayed + * + * @param array $post_ids Specify for which post IDs to fetch the attachments (optional) + * @param array $topic_ids Specify for which topic IDs to fetch the attachments (optional) */ - protected function fetch_attachments() + protected function fetch_attachments($post_ids = array(), $topic_ids = array()) { $sql_array = array( 'SELECT' => 'a.*', @@ -37,7 +40,20 @@ abstract class attachments_base extends base 'ORDER_BY' => 'a.filetime DESC, a.post_msg_id ASC', ); - if (isset($this->topic_id)) + if (!empty($post_ids)) + { + $sql_array['WHERE'] .= 'AND ' . $this->db->sql_in_set('a.post_msg_id', $post_ids); + } + else if (!empty($topic_ids)) + { + if (isset($this->topic_id)) + { + $topic_ids[] = $this->topic_id; + } + + $sql_array['WHERE'] .= 'AND ' . $this->db->sql_in_set('a.topic_id', $topic_ids); + } + else if (isset($this->topic_id)) { $sql_array['WHERE'] .= 'AND a.topic_id = ' . (int) $this->topic_id; } @@ -51,6 +67,11 @@ abstract class attachments_base extends base ); $sql_array['WHERE'] .= 'AND t.forum_id = ' . (int) $this->forum_id; } + else + { + // Do not allow querying the full attachments table + throw new \RuntimeException($this->user->lang('INVALID_FEED_ATTACHMENTS')); + } $sql = $this->db->sql_build_query('SELECT', $sql_array); $result = $this->db->sql_query($sql); @@ -64,15 +85,6 @@ abstract class attachments_base extends base } /** - * {@inheritDoc} - */ - public function open() - { - parent::open(); - $this->fetch_attachments(); - } - - /** * Get attachments related to a given post * * @param $post_id int Post id diff --git a/phpBB/phpbb/feed/forum.php b/phpBB/phpbb/feed/forum.php index f522e91169..0c142e8cc8 100644 --- a/phpBB/phpbb/feed/forum.php +++ b/phpBB/phpbb/feed/forum.php @@ -138,6 +138,8 @@ class forum extends post_base return false; } + parent::fetch_attachments(array(), $topic_ids); + $this->sql = array( 'SELECT' => 'p.post_id, p.topic_id, p.post_time, p.post_edit_time, p.post_visibility, p.post_subject, p.post_text, p.bbcode_bitfield, p.bbcode_uid, p.enable_bbcode, p.enable_smilies, p.enable_magic_url, p.post_attachment, ' . 'u.username, u.user_id', diff --git a/phpBB/phpbb/feed/news.php b/phpBB/phpbb/feed/news.php index fb6fa09278..13ca82c093 100644 --- a/phpBB/phpbb/feed/news.php +++ b/phpBB/phpbb/feed/news.php @@ -90,6 +90,8 @@ class news extends topic_base return false; } + parent::fetch_attachments($post_ids); + $this->sql = array( 'SELECT' => 'f.forum_id, f.forum_name, t.topic_id, t.topic_title, t.topic_poster, t.topic_first_poster_name, t.topic_posts_approved, t.topic_posts_unapproved, t.topic_posts_softdeleted, t.topic_views, t.topic_time, t.topic_last_post_time, diff --git a/phpBB/phpbb/feed/overall.php b/phpBB/phpbb/feed/overall.php index 40cf94ace0..b083df922d 100644 --- a/phpBB/phpbb/feed/overall.php +++ b/phpBB/phpbb/feed/overall.php @@ -55,6 +55,8 @@ class overall extends post_base return false; } + parent::fetch_attachments(array(), $topic_ids); + // Get the actual data $this->sql = array( 'SELECT' => 'f.forum_id, f.forum_name, ' . diff --git a/phpBB/phpbb/feed/topic.php b/phpBB/phpbb/feed/topic.php index e5f2c41468..2504e411b1 100644 --- a/phpBB/phpbb/feed/topic.php +++ b/phpBB/phpbb/feed/topic.php @@ -126,6 +126,8 @@ class topic extends post_base */ protected function get_sql() { + parent::fetch_attachments(); + $this->sql = array( 'SELECT' => 'p.post_id, p.post_time, p.post_edit_time, p.post_visibility, p.post_subject, p.post_text, p.bbcode_bitfield, p.bbcode_uid, p.enable_bbcode, p.enable_smilies, p.enable_magic_url, p.post_attachment, ' . 'u.username, u.user_id', diff --git a/phpBB/phpbb/feed/topics.php b/phpBB/phpbb/feed/topics.php index cf4a2e579e..183c29d11c 100644 --- a/phpBB/phpbb/feed/topics.php +++ b/phpBB/phpbb/feed/topics.php @@ -58,6 +58,8 @@ class topics extends topic_base return false; } + parent::fetch_attachments($post_ids); + $this->sql = array( 'SELECT' => 'f.forum_id, f.forum_name, t.topic_id, t.topic_title, t.topic_poster, t.topic_first_poster_name, t.topic_posts_approved, t.topic_posts_unapproved, t.topic_posts_softdeleted, t.topic_views, t.topic_time, t.topic_last_post_time, diff --git a/phpBB/phpbb/feed/topics_active.php b/phpBB/phpbb/feed/topics_active.php index 52340dc2d5..7ae0bde56b 100644 --- a/phpBB/phpbb/feed/topics_active.php +++ b/phpBB/phpbb/feed/topics_active.php @@ -77,6 +77,8 @@ class topics_active extends topic_base return false; } + parent::fetch_attachments($post_ids); + $this->sql = array( 'SELECT' => 'f.forum_id, f.forum_name, t.topic_id, t.topic_title, t.topic_posts_approved, t.topic_posts_unapproved, t.topic_posts_softdeleted, t.topic_views, diff --git a/phpBB/phpbb/files/filespec.php b/phpBB/phpbb/files/filespec.php index 2ff2a92c83..f1a32ef4a8 100644 --- a/phpBB/phpbb/files/filespec.php +++ b/phpBB/phpbb/files/filespec.php @@ -129,7 +129,7 @@ class filespec $this->class_initialized = true; $this->filename = $upload_ary['tmp_name']; $this->filesize = $upload_ary['size']; - $name = (STRIP) ? stripslashes($upload_ary['name']) : $upload_ary['name']; + $name = $upload_ary['name']; $name = trim(utf8_basename($name)); $this->realname = $this->uploadname = $name; $this->mimetype = $upload_ary['type']; diff --git a/phpBB/phpbb/help/controller/help.php b/phpBB/phpbb/help/controller/help.php index 9cc3b0c8b4..3bf6fe3098 100644 --- a/phpBB/phpbb/help/controller/help.php +++ b/phpBB/phpbb/help/controller/help.php @@ -64,6 +64,7 @@ class help */ public function handle($mode) { + $template_file = 'faq_body.html'; switch ($mode) { case 'faq': @@ -85,13 +86,16 @@ class help * @var string lang_file Language file containing the help data * @var string ext_name Vendor and extension name where the help * language file can be loaded from + * @var string template_file Template file name * @since 3.1.4-RC1 + * @changed 3.1.11-RC1 Added template_file var */ $vars = array( 'page_title', 'mode', 'lang_file', 'ext_name', + 'template_file', ); extract($this->dispatcher->trigger_event('core.faq_mode_validation', compact($vars))); @@ -113,7 +117,7 @@ class help $this->assign_to_template($this->user->help); make_jumpbox(append_sid("{$this->root_path}viewforum.{$this->php_ext}")); - return $this->helper->render('faq_body.html', $page_title); + return $this->helper->render($template_file, $page_title); } /** diff --git a/phpBB/phpbb/help/manager.php b/phpBB/phpbb/help/manager.php index 39f52d343b..1637c58a61 100644 --- a/phpBB/phpbb/help/manager.php +++ b/phpBB/phpbb/help/manager.php @@ -18,7 +18,7 @@ namespace phpbb\help; */ class manager { - /** @var \phpbb\event\dispatcher */ + /** @var \phpbb\event\dispatcher_interface */ protected $dispatcher; /** @var \phpbb\language\language */ @@ -33,11 +33,11 @@ class manager /** * Constructor * - * @param \phpbb\event\dispatcher $dispatcher + * @param \phpbb\event\dispatcher_interface $dispatcher * @param \phpbb\language\language $language * @param \phpbb\template\template $template */ - public function __construct(\phpbb\event\dispatcher $dispatcher, \phpbb\language\language $language, \phpbb\template\template $template) + public function __construct(\phpbb\event\dispatcher_interface $dispatcher, \phpbb\language\language $language, \phpbb\template\template $template) { $this->dispatcher = $dispatcher; $this->language = $language; diff --git a/phpBB/phpbb/install/helper/database.php b/phpBB/phpbb/install/helper/database.php index 192f0a3654..59b86a8ca7 100644 --- a/phpBB/phpbb/install/helper/database.php +++ b/phpBB/phpbb/install/helper/database.php @@ -336,6 +336,15 @@ class database ); } + // Check if SQLite database is writable + if ($dbms_info['SCHEMA'] === 'sqlite' + && (!$this->filesystem->is_writable($dbhost) || !$this->filesystem->is_writable(pathinfo($dbhost, PATHINFO_DIRNAME)))) + { + $errors[] = array( + 'title' =>'INST_ERR_DB_NO_WRITABLE', + ); + } + // Try to connect to db if (is_array($db->sql_connect($dbhost, $dbuser, $dbpass, $dbname, $dbport, false, true))) { diff --git a/phpBB/phpbb/install/helper/file_updater/ftp_file_updater.php b/phpBB/phpbb/install/helper/file_updater/ftp_file_updater.php index 258a035768..5cdc331cbc 100644 --- a/phpBB/phpbb/install/helper/file_updater/ftp_file_updater.php +++ b/phpBB/phpbb/install/helper/file_updater/ftp_file_updater.php @@ -47,7 +47,7 @@ class ftp_file_updater implements file_updater_interface * @param string $phpbb_root_path * @param string $php_ext */ - public function __constructor(update_helper $update_helper, $phpbb_root_path, $php_ext) + public function __construct(update_helper $update_helper, $phpbb_root_path, $php_ext) { $this->transfer = null; $this->update_helper = $update_helper; diff --git a/phpBB/phpbb/install/helper/iohandler/ajax_iohandler.php b/phpBB/phpbb/install/helper/iohandler/ajax_iohandler.php index a40d457466..bce0149890 100644 --- a/phpBB/phpbb/install/helper/iohandler/ajax_iohandler.php +++ b/phpBB/phpbb/install/helper/iohandler/ajax_iohandler.php @@ -186,6 +186,7 @@ class ajax_iohandler extends iohandler_base $tpl_ary['TITLE'] = $this->language->lang($input_options['label']); $tpl_ary['KEY'] = $input_name; $tpl_ary['S_EXPLAIN'] = false; + $tpl_ary['DISABLED'] = isset($input_options['disabled']) ? $input_options['disabled'] : false; if (isset($input_options['default'])) { @@ -219,6 +220,11 @@ class ajax_iohandler extends iohandler_base $this->template->assign_var('S_NOT_ONLY_BUTTON_FORM', $not_button_form); + if (!$not_button_form) + { + $this->template->destroy_block_vars('options'); + } + $this->template->set_filenames(array( 'form_install' => 'installer_form.html', )); diff --git a/phpBB/phpbb/install/module/install_data/task/create_search_index.php b/phpBB/phpbb/install/module/install_data/task/create_search_index.php new file mode 100644 index 0000000000..8a2f6aa1de --- /dev/null +++ b/phpBB/phpbb/install/module/install_data/task/create_search_index.php @@ -0,0 +1,134 @@ +<?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\install\module\install_data\task; + +use phpbb\auth\auth; +use phpbb\db\driver\driver_interface; +use phpbb\event\dispatcher; +use phpbb\config\config; +use phpbb\install\helper\container_factory; +use phpbb\language\language; +use phpbb\search\fulltext_native; +use phpbb\user; + +class create_search_index extends \phpbb\install\task_base +{ + /** + * @var auth + */ + protected $auth; + + /** + * @var config + */ + protected $config; + + /** + * @var driver_interface + */ + protected $db; + + /** + * @var dispatcher + */ + protected $phpbb_dispatcher; + + /** + * @var language + */ + protected $language; + + /** + * @var user + */ + protected $user; + + /** + * @var string phpBB root path + */ + protected $phpbb_root_path; + + /** + * @var string PHP file extension + */ + protected $php_ext; + + /** + * Constructor + * + * @param config $config phpBB config + * @param container_factory $container Installer's DI container + * @param string $phpbb_root_path phpBB root path + * @param string $php_ext PHP file extension + */ + public function __construct(config $config, container_factory $container, + $phpbb_root_path, $php_ext) + { + $this->auth = $container->get('auth'); + $this->config = $config; + $this->db = $container->get('dbal.conn'); + $this->language = $container->get('language'); + $this->phpbb_dispatcher = $container->get('dispatcher'); + $this->user = $container->get('user'); + + parent::__construct(true); + } + + /** + * {@inheritdoc} + */ + public function run() + { + // Make sure fulltext native load update is set + $this->config->set('fulltext_native_load_upd', 1); + + $error = false; + $search = new fulltext_native( + $error, + $this->phpbb_root_path, + $this->php_ext, + $this->auth, + $this->config, + $this->db, + $this->user, + $this->phpbb_dispatcher + ); + + $sql = 'SELECT post_id, post_subject, post_text, poster_id, forum_id + FROM ' . POSTS_TABLE; + $result = $this->db->sql_query($sql); + + while ($row = $this->db->sql_fetchrow($result)) + { + $search->index('post', $row['post_id'], $row['post_text'], $row['post_subject'], $row['poster_id'], $row['forum_id']); + } + $this->db->sql_freeresult($result); + } + + /** + * {@inheritdoc} + */ + static public function get_step_count() + { + return 1; + } + + /** + * {@inheritdoc} + */ + public function get_task_lang_name() + { + return 'TASK_CREATE_SEARCH_INDEX'; + } +} diff --git a/phpBB/phpbb/install/module/install_finish/task/install_extensions.php b/phpBB/phpbb/install/module/install_finish/task/install_extensions.php index 553a30ea28..eee13a6581 100644 --- a/phpBB/phpbb/install/module/install_finish/task/install_extensions.php +++ b/phpBB/phpbb/install/module/install_finish/task/install_extensions.php @@ -118,6 +118,14 @@ class install_extensions extends \phpbb\install\task_base try { + $extension = $this->extension_manager->get_extension($ext_name); + + if (!$extension->is_enableable()) + { + $this->iohandler->add_log_message(array('CLI_EXTENSION_NOT_ENABLEABLE', $ext_name)); + continue; + } + $this->extension_manager->enable($ext_name); $extensions = $this->get_extensions(); diff --git a/phpBB/phpbb/install/module/obtain_data/task/obtain_email_data.php b/phpBB/phpbb/install/module/obtain_data/task/obtain_email_data.php index 1cb4f04297..e8a9c971b7 100644 --- a/phpBB/phpbb/install/module/obtain_data/task/obtain_email_data.php +++ b/phpBB/phpbb/install/module/obtain_data/task/obtain_email_data.php @@ -84,7 +84,7 @@ class obtain_email_data extends \phpbb\install\task_base implements \phpbb\insta $email_form = array( 'email_enable' => array( 'label' => 'ENABLE_EMAIL', - 'description' => 'COOKIE_SECURE_EXPLAIN', + 'description' => 'ENABLE_EMAIL_EXPLAIN', 'type' => 'radio', 'options' => array( array( diff --git a/phpBB/phpbb/install/module/update_database/task/update_extensions.php b/phpBB/phpbb/install/module/update_database/task/update_extensions.php index 13c1591dcd..b66847b243 100644 --- a/phpBB/phpbb/install/module/update_database/task/update_extensions.php +++ b/phpBB/phpbb/install/module/update_database/task/update_extensions.php @@ -138,7 +138,7 @@ class update_extensions extends task_base $default_update_extensions = []; foreach (self::$default_extensions_update as $version => $extensions) { - if ($this->update_helper->phpbb_version_compare($version_from, $version, '<=')) + if ($this->update_helper->phpbb_version_compare($version_from, $version, '<')) { $default_update_extensions = array_merge($default_update_extensions, $extensions); } diff --git a/phpBB/phpbb/install/module/update_filesystem/task/diff_files.php b/phpBB/phpbb/install/module/update_filesystem/task/diff_files.php index e3e6db6263..1792a3b723 100644 --- a/phpBB/phpbb/install/module/update_filesystem/task/diff_files.php +++ b/phpBB/phpbb/install/module/update_filesystem/task/diff_files.php @@ -132,41 +132,62 @@ class diff_files extends task_base $file_contents = array(); // Handle the special case when user created a file with the filename that is now new in the core - $file_contents[0] = (file_exists($old_path . $filename)) ? file_get_contents($old_path . $filename) : ''; + if (file_exists($old_path . $filename)) + { + $file_contents[0] = file_get_contents($old_path . $filename); - $filenames = array( - $this->phpbb_root_path . $filename, - $new_path . $filename - ); + $filenames = array( + $this->phpbb_root_path . $filename, + $new_path . $filename + ); - foreach ($filenames as $file_to_diff) - { - $file_contents[] = file_get_contents($file_to_diff); + foreach ($filenames as $file_to_diff) + { + $file_contents[] = file_get_contents($file_to_diff); + + if ($file_contents[sizeof($file_contents) - 1] === false) + { + $this->iohandler->add_error_message(array('FILE_DIFFER_ERROR_FILE_CANNOT_BE_READ', $files_to_diff)); + unset($file_contents); + throw new user_interaction_required_exception(); + } + } - if ($file_contents[sizeof($file_contents) - 1] === false) + $diff = new \diff3($file_contents[0], $file_contents[1], $file_contents[2]); + unset($file_contents); + + // Handle conflicts + if ($diff->get_num_conflicts() !== 0) { - $this->iohandler->add_error_message(array('FILE_DIFFER_ERROR_FILE_CANNOT_BE_READ', $files_to_diff)); - unset($file_contents); - throw new user_interaction_required_exception(); + $merge_conflicts[] = $filename; } - } - $diff = new \diff3($file_contents[0], $file_contents[1], $file_contents[2]); - unset($file_contents); + // Save merged output + $this->cache->put( + '_file_' . md5($filename), + base64_encode(implode("\n", $diff->merged_output())) + ); - // Handle conflicts - if ($diff->get_num_conflicts() !== 0) - { - $merge_conflicts[] = $filename; + unset($diff); } + else + { + $new_file_content = file_get_contents($new_path . $filename); - // Save merged output - $this->cache->put( - '_file_' . md5($filename), - base64_encode(implode("\n", $diff->merged_output())) - ); + if ($new_file_content === false) + { + $this->iohandler->add_error_message(array('FILE_DIFFER_ERROR_FILE_CANNOT_BE_READ', $files_to_diff)); + unset($new_file_content ); + throw new user_interaction_required_exception(); + } - unset($diff); + // Save new file content to cache + $this->cache->put( + '_file_' . md5($filename), + base64_encode($new_file_content) + ); + unset($new_file_content); + } $progress_count++; $this->iohandler->set_progress('UPDATE_FILE_DIFF', $progress_count); diff --git a/phpBB/phpbb/install/module/update_filesystem/task/download_updated_files.php b/phpBB/phpbb/install/module/update_filesystem/task/download_updated_files.php index f911b7ac62..21aa93b7ea 100644 --- a/phpBB/phpbb/install/module/update_filesystem/task/download_updated_files.php +++ b/phpBB/phpbb/install/module/update_filesystem/task/download_updated_files.php @@ -78,16 +78,23 @@ class download_updated_files extends task_base } else if ($this->iohandler->get_input('update_recheck_files_submit', false)) { + $this->installer_config->set('file_updater_elem_progress', ''); + $this->installer_config->set('update_files', array()); throw new jump_to_restart_point_exception('check_update_files'); } else { - // Render download box - $this->iohandler->add_download_link( - 'phpbb_installer_update_file_download', - 'DOWNLOAD_UPDATE_METHOD', - 'DOWNLOAD_UPDATE_METHOD_EXPLAIN' - ); + $file_update_info = $this->installer_config->get('update_files', array()); + + if (count($file_update_info) > 0) + { + // Render download box + $this->iohandler->add_download_link( + 'phpbb_installer_update_file_download', + 'DOWNLOAD_UPDATE_METHOD', + 'DOWNLOAD_UPDATE_METHOD_EXPLAIN' + ); + } // Add form to continue update $this->iohandler->add_user_form_group('UPDATE_CONTINUE_UPDATE_PROCESS', array( @@ -96,8 +103,9 @@ class download_updated_files extends task_base 'type' => 'submit', ), 'database_update_submit' => array( - 'label' => 'UPDATE_CONTINUE_UPDATE_PROCESS', - 'type' => 'submit', + 'label' => 'UPDATE_CONTINUE_UPDATE_PROCESS', + 'type' => 'submit', + 'disabled' => count($file_update_info) > 0, ), )); diff --git a/phpBB/phpbb/install/module/update_filesystem/task/file_check.php b/phpBB/phpbb/install/module/update_filesystem/task/file_check.php index 5b48350e73..9daa8530c6 100644 --- a/phpBB/phpbb/install/module/update_filesystem/task/file_check.php +++ b/phpBB/phpbb/install/module/update_filesystem/task/file_check.php @@ -103,6 +103,29 @@ class file_check extends task_base $file_update_info = array(); $file_update_info['update_without_diff'] = array_diff($update_info['binary'], $update_info['deleted']); + foreach ($file_update_info['update_without_diff'] as $key => $binary_file) + { + $new_file = $new_path . $binary_file; + $file = $this->phpbb_root_path . $binary_file; + + if (!$this->filesystem->exists($file)) + { + continue; + } + + if (md5_file($file) === md5_file($new_file)) + { + // File already up to date + unset($file_update_info['update_without_diff'][$key]); + } + } + + // Remove update without diff info if empty + if (count($file_update_info['update_without_diff']) < 1) + { + unset($file_update_info['update_without_diff']); + } + // Filter out files that are already deleted $file_update_info['delete'] = array_filter( $update_info['deleted'], @@ -111,6 +134,12 @@ class file_check extends task_base return file_exists($root_path . $filename); } ); + + // Remove files to delete list if empty + if (count($file_update_info['delete']) < 1) + { + unset($file_update_info['delete']); + } } $progress_count = $this->installer_config->get('file_check_progress_count', 0); @@ -123,7 +152,7 @@ class file_check extends task_base $default_update_extensions = []; foreach (\phpbb\install\module\update_database\task\update_extensions::$default_extensions_update as $version => $extensions) { - if ($this->update_helper->phpbb_version_compare($update_info['version']['from'], $version, '>')) + if ($this->update_helper->phpbb_version_compare($update_info['version']['from'], $version, '>=')) { $default_update_extensions = array_merge($default_update_extensions, $extensions); } diff --git a/phpBB/phpbb/install/module/update_filesystem/task/show_file_status.php b/phpBB/phpbb/install/module/update_filesystem/task/show_file_status.php index cf1e4cf4ac..0e82f91553 100644 --- a/phpBB/phpbb/install/module/update_filesystem/task/show_file_status.php +++ b/phpBB/phpbb/install/module/update_filesystem/task/show_file_status.php @@ -129,9 +129,9 @@ class show_file_status extends task_base // Add form to continue update $this->iohandler->add_user_form_group('UPDATE_CONTINUE_FILE_UPDATE', array( - 'submit_continue_file_update' => array( - 'label' => 'UPDATE_CONTINUE_FILE_UPDATE', - 'type' => 'submit', + 'submit_continue_file_update' => array( + 'label' => 'UPDATE_CONTINUE_FILE_UPDATE', + 'type' => 'submit', ), )); diff --git a/phpBB/phpbb/module/module_manager.php b/phpBB/phpbb/module/module_manager.php index 7ae16cdb61..67bac5b33e 100644 --- a/phpBB/phpbb/module/module_manager.php +++ b/phpBB/phpbb/module/module_manager.php @@ -208,7 +208,7 @@ class module_manager WHERE m1.module_class = '" . $this->db->sql_escape($module_class) . "' AND m2.module_class = '" . $this->db->sql_escape($module_class) . "' AND m1.module_id = $module_id - ORDER BY m2.left_id DESC"; + ORDER BY m2.left_id"; $result = $this->db->sql_query($sql); while ($row = $this->db->sql_fetchrow($result)) diff --git a/phpBB/phpbb/notification/manager.php b/phpBB/phpbb/notification/manager.php index 6923d96392..1cd7e5bc9a 100644 --- a/phpBB/phpbb/notification/manager.php +++ b/phpBB/phpbb/notification/manager.php @@ -475,9 +475,10 @@ class manager if ($type instanceof \phpbb\notification\type\type_interface && $type->is_available()) { $options = array_merge(array( - 'id' => $type->get_type(), - 'lang' => 'NOTIFICATION_TYPE_' . strtoupper($type->get_type()), - 'group' => 'NOTIFICATION_GROUP_MISCELLANEOUS', + 'type' => $type, + 'id' => $type->get_type(), + 'lang' => 'NOTIFICATION_TYPE_' . strtoupper($type->get_type()), + 'group' => 'NOTIFICATION_GROUP_MISCELLANEOUS', ), (($type::$notification_option !== false) ? $type::$notification_option : array())); $this->subscription_types[$options['group']][$options['id']] = $options; @@ -509,6 +510,7 @@ class manager foreach ($this->get_available_subscription_methods() as $method_name => $method) { $subscription_methods[$method_name] = array( + 'method' => $method, 'id' => $method->get_type(), 'lang' => str_replace('.', '_', strtoupper($method->get_type())), ); diff --git a/phpBB/phpbb/notification/method/email.php b/phpBB/phpbb/notification/method/email.php index 21a6559012..56dd1e9367 100644 --- a/phpBB/phpbb/notification/method/email.php +++ b/phpBB/phpbb/notification/method/email.php @@ -13,6 +13,8 @@ namespace phpbb\notification\method; +use phpbb\notification\type\type_interface; + /** * Email notification method class * This class handles sending emails for notifications @@ -56,10 +58,14 @@ class email extends \phpbb\notification\method\messenger_base /** * Is this method available for the user? * This is checked on the notifications options + * + * @param type_interface $notification_type An optional instance of a notification type. If provided, this + * method additionally checks if the type provides an email template. + * @return bool */ - public function is_available() + public function is_available(type_interface $notification_type = null) { - return $this->config['email_enable'] && $this->user->data['user_email']; + return parent::is_available($notification_type) && $this->config['email_enable'] && $this->user->data['user_email']; } /** diff --git a/phpBB/phpbb/notification/method/jabber.php b/phpBB/phpbb/notification/method/jabber.php index 509c6b432c..81fdb378e2 100644 --- a/phpBB/phpbb/notification/method/jabber.php +++ b/phpBB/phpbb/notification/method/jabber.php @@ -13,6 +13,8 @@ namespace phpbb\notification\method; +use phpbb\notification\type\type_interface; + /** * Jabber notification method class * This class handles sending Jabber messages for notifications @@ -56,10 +58,14 @@ class jabber extends \phpbb\notification\method\messenger_base /** * Is this method available for the user? * This is checked on the notifications options + * + * @param type_interface $notification_type An optional instance of a notification type. If provided, this + * method additionally checks if the type provides an email template. + * @return bool */ - public function is_available() + public function is_available(type_interface $notification_type = null) { - return ($this->global_available() && $this->user->data['user_jabber']); + return parent::is_available($notification_type) && $this->global_available() && $this->user->data['user_jabber']; } /** diff --git a/phpBB/phpbb/notification/method/messenger_base.php b/phpBB/phpbb/notification/method/messenger_base.php index 1f5525accc..32e79aa936 100644 --- a/phpBB/phpbb/notification/method/messenger_base.php +++ b/phpBB/phpbb/notification/method/messenger_base.php @@ -13,6 +13,8 @@ namespace phpbb\notification\method; +use phpbb\notification\type\type_interface; + /** * Abstract notification method handling email and jabber notifications * using the phpBB messenger. @@ -43,6 +45,19 @@ abstract class messenger_base extends \phpbb\notification\method\base } /** + * Is this method available for the user? + * This is checked on the notifications options + * + * @param type_interface $notification_type An optional instance of a notification type. This method returns false + * only if the type is provided and if it doesn't provide an email template. + * @return bool + */ + public function is_available(type_interface $notification_type = null) + { + return $notification_type === null || $notification_type->get_email_template() !== false; + } + + /** * Notify using phpBB messenger * * @param int $notify_method Notify method for messenger (e.g. NOTIFY_IM) diff --git a/phpBB/phpbb/notification/type/post.php b/phpBB/phpbb/notification/type/post.php index b9afc6d70a..03221e7c7a 100644 --- a/phpBB/phpbb/notification/type/post.php +++ b/phpBB/phpbb/notification/type/post.php @@ -456,6 +456,12 @@ class post extends \phpbb\notification\type\base return array(); } - return array('notification_data' => $serialized_data); + $data_array = array_merge(array( + 'post_time' => $post['post_time'], + 'post_id' => $post['post_id'], + 'topic_id' => $post['topic_id'] + ), $this->get_data(false)); + + return $data_array; } } diff --git a/phpBB/phpbb/pagination.php b/phpBB/phpbb/pagination.php index 7a81c25ad2..a5a95b096d 100644 --- a/phpBB/phpbb/pagination.php +++ b/phpBB/phpbb/pagination.php @@ -284,7 +284,7 @@ class pagination */ public function get_on_page($per_page, $start) { - return floor($start / $per_page) + 1; + return floor((int) $start / (int) $per_page) + 1; } /** diff --git a/phpBB/phpbb/passwords/driver/md5_phpbb2.php b/phpBB/phpbb/passwords/driver/md5_phpbb2.php index bd8cc51e5a..b38b041d6c 100644 --- a/phpBB/phpbb/passwords/driver/md5_phpbb2.php +++ b/phpBB/phpbb/passwords/driver/md5_phpbb2.php @@ -95,7 +95,7 @@ class md5_phpbb2 extends base // in phpBB2 passwords were used exactly as they were sent, with addslashes applied $password_old_format = isset($_REQUEST['password']) ? (string) $_REQUEST['password'] : ''; - $password_old_format = (!STRIP) ? addslashes($password_old_format) : $password_old_format; + $password_old_format = addslashes($password_old_format); $password_new_format = $this->request->variable('password', '', true); if ($super_globals_disabled) diff --git a/phpBB/phpbb/profilefields/type/type_date.php b/phpBB/phpbb/profilefields/type/type_date.php index 414484920b..5a1a6dbd5c 100644 --- a/phpBB/phpbb/profilefields/type/type_date.php +++ b/phpBB/phpbb/profilefields/type/type_date.php @@ -72,7 +72,7 @@ class type_date extends type_base 'lang_options' => $field_data['lang_options'], ); - $always_now = $request->variable('always_now', -1); + $always_now = $this->request->variable('always_now', -1); if ($always_now == -1) { $s_checked = ($field_data['field_default_value'] == 'now') ? true : false; @@ -264,7 +264,7 @@ class type_date extends type_base } $profile_row['s_year_options'] = '<option value="0"' . ((!$year) ? ' selected="selected"' : '') . '>--</option>'; - for ($i = $now['year'] - 100; $i <= $now['year'] + 100; $i++) + for ($i = 1901; $i <= $now['year'] + 50; $i++) { $profile_row['s_year_options'] .= '<option value="' . $i . '"' . (($i == $year) ? ' selected="selected"' : '') . ">$i</option>"; } diff --git a/phpBB/phpbb/request/request.php b/phpBB/phpbb/request/request.php index 92d4213180..00be8fd381 100644 --- a/phpBB/phpbb/request/request.php +++ b/phpBB/phpbb/request/request.php @@ -169,12 +169,6 @@ class request implements \phpbb\request\request_interface $GLOBALS[$this->super_globals[$super_global]][$var_name] = $value; } } - - if (!$this->super_globals_disabled()) - { - unset($GLOBALS[$this->super_globals[$super_global]][$var_name]); - $GLOBALS[$this->super_globals[$super_global]][$var_name] = $value; - } } /** diff --git a/phpBB/phpbb/search/fulltext_mysql.php b/phpBB/phpbb/search/fulltext_mysql.php index 73d7bc1574..da1aad1c3a 100644 --- a/phpBB/phpbb/search/fulltext_mysql.php +++ b/phpBB/phpbb/search/fulltext_mysql.php @@ -272,6 +272,27 @@ class fulltext_mysql extends \phpbb\search\base foreach ($this->split_words as $i => $word) { + // Check for not allowed search queries for InnoDB. + // We assume similar restrictions for MyISAM, which is usually even + // slower but not as restrictive as InnoDB. + // InnoDB full-text search does not support the use of a leading + // plus sign with wildcard ('+*'), a plus and minus sign + // combination ('+-'), or leading a plus and minus sign combination. + // InnoDB full-text search only supports leading plus or minus signs. + // For example, InnoDB supports '+apple' but does not support 'apple+'. + // Specifying a trailing plus or minus sign causes InnoDB to report + // a syntax error. InnoDB full-text search does not support the use + // of multiple operators on a single search word, as in this example: + // '++apple'. Use of multiple operators on a single search word + // returns a syntax error to standard out. + // Also, ensure that the wildcard character is only used at the + // end of the line as it's intended by MySQL. + if (preg_match('#^(\+[+-]|\+\*|.+[+-]$|.+\*(?!$))#', $word)) + { + unset($this->split_words[$i]); + continue; + } + $clean_word = preg_replace('#^[+\-|"]#', '', $word); // check word length @@ -942,38 +963,45 @@ class fulltext_mysql extends \phpbb\search\base $this->get_stats(); } - $alter = array(); + $alter_list = array(); 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[] = 'MODIFY post_subject varchar(255) COLLATE utf8_unicode_ci DEFAULT \'\' NOT NULL'; + $alter_entry[] = 'MODIFY post_subject varchar(255) COLLATE utf8_unicode_ci DEFAULT \'\' NOT NULL'; } else { - $alter[] = 'MODIFY post_subject text NOT NULL'; + $alter_entry[] = 'MODIFY post_subject text NOT NULL'; } - $alter[] = 'ADD FULLTEXT (post_subject)'; + $alter_entry[] = 'ADD FULLTEXT (post_subject)'; + $alter_list[] = $alter_entry; } 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[] = 'MODIFY post_text mediumtext COLLATE utf8_unicode_ci NOT NULL'; + $alter_entry[] = 'MODIFY post_text mediumtext COLLATE utf8_unicode_ci NOT NULL'; } else { - $alter[] = 'MODIFY post_text mediumtext NOT NULL'; + $alter_entry[] = 'MODIFY post_text mediumtext NOT NULL'; } - $alter[] = 'ADD FULLTEXT post_content (post_text, post_subject)'; + $alter_entry[] = 'ADD FULLTEXT post_content (post_text, post_subject)'; + $alter_list[] = $alter_entry; } - if (sizeof($alter)) + if (sizeof($alter_list)) { - $this->db->sql_query('ALTER TABLE ' . POSTS_TABLE . ' ' . implode(', ', $alter)); + foreach ($alter_list as $alter) + { + $this->db->sql_query('ALTER TABLE ' . POSTS_TABLE . ' ' . implode(', ', $alter)); + } } $this->db->sql_query('TRUNCATE TABLE ' . SEARCH_RESULTS_TABLE); diff --git a/phpBB/phpbb/search/fulltext_native.php b/phpBB/phpbb/search/fulltext_native.php index 2071a973e5..73dcfce9a5 100644 --- a/phpBB/phpbb/search/fulltext_native.php +++ b/phpBB/phpbb/search/fulltext_native.php @@ -120,7 +120,7 @@ class fulltext_native extends \phpbb\search\base $this->phpbb_dispatcher = $phpbb_dispatcher; $this->user = $user; - $this->word_length = array('min' => $this->config['fulltext_native_min_chars'], 'max' => $this->config['fulltext_native_max_chars']); + $this->word_length = array('min' => (int) $this->config['fulltext_native_min_chars'], 'max' => (int) $this->config['fulltext_native_max_chars']); /** * Load the UTF tools diff --git a/phpBB/phpbb/session.php b/phpBB/phpbb/session.php index cbe2f02851..c5b50c2b07 100644 --- a/phpBB/phpbb/session.php +++ b/phpBB/phpbb/session.php @@ -461,6 +461,9 @@ class session $this->data['is_bot'] = (!$this->data['is_registered'] && $this->data['user_id'] != ANONYMOUS) ? true : false; $this->data['user_lang'] = basename($this->data['user_lang']); + // Is user banned? Are they excluded? Won't return on ban, exists within method + $this->check_ban_for_current_session($config); + return true; } } @@ -675,19 +678,7 @@ class session // session exists in which case session_id will also be set // Is user banned? Are they excluded? Won't return on ban, exists within method - if ($this->data['user_type'] != USER_FOUNDER) - { - if (!$config['forwarded_for_check']) - { - $this->check_ban($this->data['user_id'], $this->ip); - } - else - { - $ips = explode(' ', $this->forwarded_for); - $ips[] = $this->ip; - $this->check_ban($this->data['user_id'], $ips); - } - } + $this->check_ban_for_current_session($config); $this->data['is_registered'] = (!$bot && $this->data['user_id'] != ANONYMOUS && ($this->data['user_type'] == USER_NORMAL || $this->data['user_type'] == USER_FOUNDER)) ? true : false; $this->data['is_bot'] = ($bot) ? true : false; @@ -838,7 +829,7 @@ class session $sql = 'SELECT COUNT(session_id) AS sessions FROM ' . SESSIONS_TABLE . ' WHERE session_user_id = ' . (int) $this->data['user_id'] . ' - AND session_time >= ' . (int) ($this->time_now - (max($config['session_length'], $config['form_token_lifetime']))); + AND session_time >= ' . (int) ($this->time_now - (max((int) $config['session_length'], (int) $config['form_token_lifetime']))); $result = $db->sql_query($sql); $row = $db->sql_fetchrow($result); $db->sql_freeresult($result); @@ -1285,9 +1276,6 @@ class session $message .= ($ban_row['ban_give_reason']) ? '<br /><br />' . sprintf($this->lang['BOARD_BAN_REASON'], $ban_row['ban_give_reason']) : ''; $message .= '<br /><br /><em>' . $this->lang['BAN_TRIGGERED_BY_' . strtoupper($ban_triggered_by)] . '</em>'; - // To circumvent session_begin returning a valid value and the check_ban() not called on second page view, we kill the session again - $this->session_kill(false); - // A very special case... we are within the cron script which is not supposed to print out the ban message... show blank page if (defined('IN_CRON')) { @@ -1296,6 +1284,9 @@ class session exit; } + // To circumvent session_begin returning a valid value and the check_ban() not called on second page view, we kill the session again + $this->session_kill(false); + trigger_error($message); } @@ -1303,6 +1294,28 @@ class session } /** + * Check the current session for bans + * + * @return true if session user is banned. + */ + protected function check_ban_for_current_session($config) + { + if (!defined('SKIP_CHECK_BAN') && $this->data['user_type'] != USER_FOUNDER) + { + if (!$config['forwarded_for_check']) + { + $this->check_ban($this->data['user_id'], $this->ip); + } + else + { + $ips = explode(' ', $this->forwarded_for); + $ips[] = $this->ip; + $this->check_ban($this->data['user_id'], $ips); + } + } + } + + /** * Check if ip is blacklisted * This should be called only where absolutely necessary * @@ -1593,7 +1606,7 @@ class session } // Only update session DB a minute or so after last update or if page changes - if ($this->time_now - $this->data['session_time'] > 60 || ($this->update_session_page && $this->data['session_page'] != $this->page['page'])) + if ($this->time_now - ((isset($this->data['session_time'])) ? $this->data['session_time'] : 0) > 60 || ($this->update_session_page && $this->data['session_page'] != $this->page['page'])) { $sql_ary = array('session_time' => $this->time_now); diff --git a/phpBB/phpbb/template/base.php b/phpBB/phpbb/template/base.php index 9a40702ba8..d502aceab8 100644 --- a/phpBB/phpbb/template/base.php +++ b/phpBB/phpbb/template/base.php @@ -107,6 +107,27 @@ abstract class base implements template /** * {@inheritdoc} */ + public function retrieve_vars(array $vararray) + { + $result = array(); + foreach ($vararray as $varname) + { + $result[$varname] = $this->retrieve_var($varname); + } + return $result; + } + + /** + * {@inheritdoc} + */ + public function retrieve_var($varname) + { + return $this->context->retrieve_var($varname); + } + + /** + * {@inheritdoc} + */ public function assign_block_vars($blockname, array $vararray) { $this->context->assign_block_vars($blockname, $vararray); @@ -127,12 +148,28 @@ abstract class base implements template /** * {@inheritdoc} */ + public function retrieve_block_vars($blockname, array $vararray) + { + return $this->context->retrieve_block_vars($blockname, $vararray); + } + + /** + * {@inheritdoc} + */ public function alter_block_array($blockname, array $vararray, $key = false, $mode = 'insert') { return $this->context->alter_block_array($blockname, $vararray, $key, $mode); } /** + * {@inheritdoc} + */ + public function find_key_index($blockname, $key) + { + return $this->context->find_key_index($blockname, $key); + } + + /** * Calls hook if any is defined. * * @param string $handle Template handle being displayed. diff --git a/phpBB/phpbb/template/context.php b/phpBB/phpbb/template/context.php index 4ee48205c8..392efd5933 100644 --- a/phpBB/phpbb/template/context.php +++ b/phpBB/phpbb/template/context.php @@ -87,6 +87,17 @@ class context } /** + * Retreive a single scalar value from a single key. + * + * @param string $varname Variable name + * @return mixed Variable value, or null if not set + */ + public function retrieve_var($varname) + { + return isset($this->rootref[$varname]) ? $this->rootref[$varname] : null; + } + + /** * Returns a reference to template data array. * * This function is public so that template renderer may invoke it. @@ -264,6 +275,151 @@ class context } /** + * Retrieve key variable pairs from the specified block + * + * @param string $blockname Name of block to retrieve $vararray from + * @param array $vararray An array of variable names, empty array retrieves all vars + * @return array of hashes with variable name as key and retrieved value or null as value + */ + public function retrieve_block_vars($blockname, array $vararray) + { + // For nested block, $blockcount > 0, for top-level block, $blockcount == 0 + $blocks = explode('.', $blockname); + $blockcount = sizeof($blocks) - 1; + + $block = $this->tpldata; + for ($i = 0; $i <= $blockcount; $i++) + { + if (($pos = strpos($blocks[$i], '[')) !== false) + { + $name = substr($blocks[$i], 0, $pos); + + if (strpos($blocks[$i], '[]') === $pos) + { + $index = sizeof($block[$name]) - 1; + } + else + { + $index = min((int) substr($blocks[$i], $pos + 1, -1), sizeof($block[$name]) - 1); + } + } + else + { + $name = $blocks[$i]; + $index = sizeof($block[$name]) - 1; + } + $block = $block[$name]; + $block = $block[$index]; + } + + $result = array(); + if ($vararray === array()) + { + // The calculated vars that depend on the block position are excluded from the complete block returned results + $excluded_vars = array('S_FIRST_ROW', 'S_LAST_ROW', 'S_BLOCK_NAME', 'S_NUM_ROWS', 'S_ROW_COUNT', 'S_ROW_NUM'); + + foreach ($block as $varname => $varvalue) + { + if ($varname === strtoupper($varname) && !is_array($varvalue) && !in_array($varname, $excluded_vars)) + { + $result[$varname] = $varvalue; + } + } + } + else + { + foreach ($vararray as $varname) + { + $result[$varname] = isset($block[$varname]) ? $block[$varname] : null; + } + } + return $result; + } + + /** + * Find the index for a specified key in the innermost specified block + * + * @param string $blockname the blockname, for example 'loop' + * @param mixed $key Key to search for + * + * array: KEY => VALUE [the key/value pair to search for within the loop to determine the correct position] + * + * int: Position [the position to search for] + * + * If key is false the position is set to 0 + * If key is true the position is set to the last entry + * + * @return mixed false if not found, index position otherwise; be sure to test with === + */ + public function find_key_index($blockname, $key) + { + // For nested block, $blockcount > 0, for top-level block, $blockcount == 0 + $blocks = explode('.', $blockname); + $blockcount = sizeof($blocks) - 1; + + $block = $this->tpldata; + for ($i = 0; $i < $blockcount; $i++) + { + if (($pos = strpos($blocks[$i], '[')) !== false) + { + $name = substr($blocks[$i], 0, $pos); + + if (strpos($blocks[$i], '[]') === $pos) + { + $index = sizeof($block[$name]) - 1; + } + else + { + $index = min((int) substr($blocks[$i], $pos + 1, -1), sizeof($block[$name]) - 1); + } + } + else + { + $name = $blocks[$i]; + $index = sizeof($block[$name]) - 1; + } + if (!isset($block[$name])) + { + return false; + } + $block = $block[$name]; + if (!isset($block[$index])) + { + return false; + } + $block = $block[$index]; + } + + if (!isset($block[$blocks[$i]])) + { + return false; + } + $block = $block[$blocks[$i]]; // Traverse the last block + + // Change key to zero (change first position) if false and to last position if true + if ($key === false || $key === true) + { + return ($key === false) ? 0 : sizeof($block) - 1; + } + + // Get correct position if array given + if (is_array($key)) + { + // Search array to get correct position + list($search_key, $search_value) = @each($key); + foreach ($block as $i => $val_ary) + { + if ($val_ary[$search_key] === $search_value) + { + return $i; + } + } + } + + return (is_int($key) && ((0 <= $key) && ($key < sizeof($block)))) ? $key : false; + } + + /** * Change already assigned key variable pair (one-dimensional - single loop entry) * * An example of how to use this function: @@ -280,10 +436,11 @@ class context * If key is false the position is set to 0 * If key is true the position is set to the last entry * - * @param string $mode Mode to execute (valid modes are 'insert' and 'change') + * @param string $mode Mode to execute (valid modes are 'insert', 'change' and 'delete') * * If insert, the vararray is inserted at the given position (position counting from zero). * If change, the current block gets merged with the vararray (resulting in new key/value pairs be added and existing keys be replaced by the new \value). + * If delete, the vararray is ignored, and the block at the given position (counting from zero) is removed. * * Since counting begins by zero, inserting at the last position will result in this array: array(vararray, last positioned array) * and inserting at position 1 will result in this array: array(first positioned array, vararray, following vars) @@ -293,45 +450,49 @@ class context public function alter_block_array($blockname, array $vararray, $key = false, $mode = 'insert') { $this->num_rows_is_set = false; - if (strpos($blockname, '.') !== false) - { - // Nested block. - $blocks = explode('.', $blockname); - $blockcount = sizeof($blocks) - 1; - $block = &$this->tpldata; - for ($i = 0; $i < $blockcount; $i++) + // For nested block, $blockcount > 0, for top-level block, $blockcount == 0 + $blocks = explode('.', $blockname); + $blockcount = sizeof($blocks) - 1; + + $block = &$this->tpldata; + for ($i = 0; $i < $blockcount; $i++) + { + if (($pos = strpos($blocks[$i], '[')) !== false) { - if (($pos = strpos($blocks[$i], '[')) !== false) + $name = substr($blocks[$i], 0, $pos); + + if (strpos($blocks[$i], '[]') === $pos) { - $name = substr($blocks[$i], 0, $pos); - - if (strpos($blocks[$i], '[]') === $pos) - { - $index = sizeof($block[$name]) - 1; - } - else - { - $index = min((int) substr($blocks[$i], $pos + 1, -1), sizeof($block[$name]) - 1); - } + $index = sizeof($block[$name]) - 1; } else { - $name = $blocks[$i]; - $index = sizeof($block[$name]) - 1; + $index = min((int) substr($blocks[$i], $pos + 1, -1), sizeof($block[$name]) - 1); } - $block = &$block[$name]; - $block = &$block[$index]; } - - $block = &$block[$blocks[$i]]; // Traverse the last block + else + { + $name = $blocks[$i]; + $index = sizeof($block[$name]) - 1; + } + $block = &$block[$name]; + $block = &$block[$index]; } - else + $name = $blocks[$i]; + + // If last block does not exist and we are inserting, and not searching for key, we create it empty; otherwise, nothing to do + if (!isset($block[$name])) { - // Top-level block. - $block = &$this->tpldata[$blockname]; + if ($mode != 'insert' || is_array($key)) + { + return false; + } + $block[$name] = array(); } + $block = &$block[$name]; // Now we can traverse the last block + // Change key to zero (change first position) if false and to last position if true if ($key === false || $key === true) { @@ -365,20 +526,21 @@ class context if ($mode == 'insert') { // Make sure we are not exceeding the last iteration - if ($key >= sizeof($this->tpldata[$blockname])) + if ($key >= sizeof($block)) { - $key = sizeof($this->tpldata[$blockname]); - unset($this->tpldata[$blockname][($key - 1)]['S_LAST_ROW']); + $key = sizeof($block); + unset($block[($key - 1)]['S_LAST_ROW']); $vararray['S_LAST_ROW'] = true; } - else if ($key === 0) + if ($key <= 0) { - unset($this->tpldata[$blockname][0]['S_FIRST_ROW']); + $key = 0; + unset($block[0]['S_FIRST_ROW']); $vararray['S_FIRST_ROW'] = true; } // Assign S_BLOCK_NAME - $vararray['S_BLOCK_NAME'] = $blockname; + $vararray['S_BLOCK_NAME'] = $name; // Re-position template blocks for ($i = sizeof($block); $i > $key; $i--) @@ -398,6 +560,12 @@ class context // Which block to change? if ($mode == 'change') { + // If key is out of bounds, do not change anything + if ($key > sizeof($block) || $key < 0) + { + return false; + } + if ($key == sizeof($block)) { $key--; @@ -408,6 +576,45 @@ class context return true; } + // Delete Block + if ($mode == 'delete') + { + // If we are exceeding last iteration, do not delete anything + if ($key > sizeof($block) || $key < 0) + { + return false; + } + + // If we are positioned at the end, we remove the last element + if ($key == sizeof($block)) + { + $key--; + } + + // We are deleting the last element in the block, so remove the block + if (sizeof($block) === 1) + { + $block = null; // unset($block); does not work on references + return true; + } + + // Re-position template blocks + for ($i = $key; $i < sizeof($block)-1; $i++) + { + $block[$i] = $block[$i+1]; + $block[$i]['S_ROW_COUNT'] = $block[$i]['S_ROW_NUM'] = $i; + } + + // Remove the last element + unset($block[$i]); + + // Set first and last elements again, in case they were removed + $block[0]['S_FIRST_ROW'] = true; + $block[sizeof($block)-1]['S_LAST_ROW'] = true; + + return true; + } + return false; } diff --git a/phpBB/phpbb/template/template.php b/phpBB/phpbb/template/template.php index 041ecb12e4..df83d5bc43 100644 --- a/phpBB/phpbb/template/template.php +++ b/phpBB/phpbb/template/template.php @@ -128,6 +128,22 @@ interface template public function append_var($varname, $varval); /** + * Retrieve multiple template values + * + * @param array $vararray An array with variable names + * @return array A hash of variable name => value pairs (value is null if not set) + */ + public function retrieve_vars(array $vararray); + + /** + * Retreive a single scalar value from a single key. + * + * @param string $varname Variable name + * @return mixed Variable value, or null if not set + */ + public function retrieve_var($varname); + + /** * Assign key variable pairs from an array to a specified block * @param string $blockname Name of block to assign $vararray to * @param array $vararray A hash of variable name => value pairs @@ -144,6 +160,14 @@ interface template public function assign_block_vars_array($blockname, array $block_vars_array); /** + * Retrieve variable values from an specified block + * @param string $blockname Name of block to retrieve $vararray from + * @param array $vararray An array with variable names, empty array gets all vars + * @return array A hash of variable name => value pairs (value is null if not set) + */ + public function retrieve_block_vars($blockname, array $vararray); + + /** * Change already assigned key variable pair (one-dimensional - single loop entry) * * An example of how to use this function: @@ -160,10 +184,11 @@ interface template * If key is false the position is set to 0 * If key is true the position is set to the last entry * - * @param string $mode Mode to execute (valid modes are 'insert' and 'change') + * @param string $mode Mode to execute (valid modes are 'insert', 'change' and 'delete') * * If insert, the vararray is inserted at the given position (position counting from zero). * If change, the current block gets merged with the vararray (resulting in new \key/value pairs be added and existing keys be replaced by the new \value). + * If delete, the vararray is ignored, and the block at the given position (counting from zero) is removed. * * Since counting begins by zero, inserting at the last position will result in this array: array(vararray, last positioned array) * and inserting at position 1 will result in this array: array(first positioned array, vararray, following vars) @@ -173,6 +198,23 @@ interface template public function alter_block_array($blockname, array $vararray, $key = false, $mode = 'insert'); /** + * Find the index for a specified key in the innermost specified block + * + * @param string $blockname the blockname, for example 'loop' + * @param mixed $key Key to search for + * + * array: KEY => VALUE [the key/value pair to search for within the loop to determine the correct position] + * + * int: Position [the position to search for] + * + * If key is false the position is set to 0 + * If key is true the position is set to the last entry + * + * @return mixed false if not found, index position otherwise; be sure to test with === + */ + public function find_key_index($blockname, $key); + + /** * Get path to template for handle (required for BBCode parser) * * @param string $handle Handle to retrieve the source file diff --git a/phpBB/phpbb/template/twig/environment.php b/phpBB/phpbb/template/twig/environment.php index 179412a2e3..ac4b16e457 100644 --- a/phpBB/phpbb/template/twig/environment.php +++ b/phpBB/phpbb/template/twig/environment.php @@ -32,6 +32,9 @@ class environment extends \Twig_Environment /** @var \phpbb\extension\manager */ protected $extension_manager; + /** @var \phpbb\event\dispatcher_interface */ + protected $phpbb_dispatcher; + /** @var string */ protected $phpbb_root_path; @@ -53,15 +56,17 @@ class environment extends \Twig_Environment * @param string $cache_path The path to the cache directory * @param \phpbb\extension\manager $extension_manager phpBB extension manager * @param \Twig_LoaderInterface $loader Twig loader interface + * @param \phpbb\event\dispatcher_interface $phpbb_dispatcher Event dispatcher object * @param array $options Array of options to pass to Twig */ - public function __construct(\phpbb\config\config $phpbb_config, \phpbb\filesystem\filesystem $filesystem, \phpbb\path_helper $path_helper, $cache_path, \phpbb\extension\manager $extension_manager = null, \Twig_LoaderInterface $loader = null, $options = array()) + public function __construct(\phpbb\config\config $phpbb_config, \phpbb\filesystem\filesystem $filesystem, \phpbb\path_helper $path_helper, $cache_path, \phpbb\extension\manager $extension_manager = null, \Twig_LoaderInterface $loader = null, \phpbb\event\dispatcher_interface $phpbb_dispatcher = null, $options = array()) { $this->phpbb_config = $phpbb_config; $this->filesystem = $filesystem; $this->phpbb_path_helper = $path_helper; $this->extension_manager = $extension_manager; + $this->phpbb_dispatcher = $phpbb_dispatcher; $this->phpbb_root_path = $this->phpbb_path_helper->get_phpbb_root_path(); $this->web_root_path = $this->phpbb_path_helper->get_web_root_path(); @@ -202,8 +207,37 @@ class environment extends \Twig_Environment $context['definition']->set('STYLESHEETS', '__STYLESHEETS_' . $placeholder_salt . '__'); } + /** + * Allow changing the template output stream before rendering + * + * @event core.twig_environment_render_template_before + * @var array context Array with template variables + * @var string name The template name + * @since 3.2.1-RC1 + */ + if ($this->phpbb_dispatcher) + { + $vars = array('context', 'name'); + extract($this->phpbb_dispatcher->trigger_event('core.twig_environment_render_template_before', compact($vars))); + } + $output = parent::render($name, $context); + /** + * Allow changing the template output stream after rendering + * + * @event core.twig_environment_render_template_after + * @var array context Array with template variables + * @var string name The template name + * @var string output Rendered template output stream + * @since 3.2.1-RC1 + */ + if ($this->phpbb_dispatcher) + { + $vars = array('context', 'name', 'output'); + extract($this->phpbb_dispatcher->trigger_event('core.twig_environment_render_template_after', compact($vars))); + } + return $this->inject_assets($output, $placeholder_salt); } diff --git a/phpBB/phpbb/template/twig/extension.php b/phpBB/phpbb/template/twig/extension.php index 92f87a0331..f0e716d697 100644 --- a/phpBB/phpbb/template/twig/extension.php +++ b/phpBB/phpbb/template/twig/extension.php @@ -170,8 +170,7 @@ class extension extends \Twig_Extension $args = func_get_args(); $key = $args[0]; - $context = $this->context->get_data_ref(); - $context_vars = $context['.'][0]; + $context_vars = $this->context->get_root_ref(); if (isset($context_vars['L_' . $key])) { diff --git a/phpBB/phpbb/template/twig/lexer.php b/phpBB/phpbb/template/twig/lexer.php index f1542109a4..d0bcfa615e 100644 --- a/phpBB/phpbb/template/twig/lexer.php +++ b/phpBB/phpbb/template/twig/lexer.php @@ -22,6 +22,14 @@ class lexer extends \Twig_Lexer public function tokenize($code, $filename = null) { + // Handle \Twig_Source format input + if ($code instanceof \Twig_Source) + { + $source = $code; + $code = $source->getCode(); + $filename = $source->getName(); + } + // Our phpBB tags // Commented out tokens are handled separately from the main replace $phpbb_tags = array( @@ -125,7 +133,8 @@ class lexer extends \Twig_Lexer // Appends any filters $code = preg_replace('#{([a-zA-Z0-9_\.]+)(\|[^}]+?)?}#', '{{ $1$2 }}', $code); - return parent::tokenize($code, $filename); + // Tokenize \Twig_Source instance + return parent::tokenize(new \Twig_Source($code, $filename)); } /** diff --git a/phpBB/phpbb/template/twig/loader.php b/phpBB/phpbb/template/twig/loader.php index d2b42852ce..c13e3ee298 100644 --- a/phpBB/phpbb/template/twig/loader.php +++ b/phpBB/phpbb/template/twig/loader.php @@ -35,7 +35,7 @@ class loader extends \Twig_Loader_Filesystem { $this->filesystem = $filesystem; - parent::__construct($paths); + parent::__construct($paths, $this->filesystem->realpath(dirname(__FILE__))); } /** diff --git a/phpBB/phpbb/textformatter/data_access.php b/phpBB/phpbb/textformatter/data_access.php index 2103bf8e60..0d37e62c87 100644 --- a/phpBB/phpbb/textformatter/data_access.php +++ b/phpBB/phpbb/textformatter/data_access.php @@ -81,11 +81,8 @@ class data_access public function get_bbcodes() { $sql = 'SELECT bbcode_match, bbcode_tpl FROM ' . $this->bbcodes_table; - $result = $this->db->sql_query($sql); - $rows = $this->db->sql_fetchrowset($result); - $this->db->sql_freeresult($result); - return $rows; + return $this->fetch_decoded_rowset($sql, ['bbcode_match']); } /** @@ -101,11 +98,8 @@ class data_access $sql = 'SELECT code, emotion, smiley_url, smiley_width, smiley_height FROM ' . $this->smilies_table . ' ORDER BY display_on_posting DESC'; - $result = $this->db->sql_query($sql); - $rows = $this->db->sql_fetchrowset($result); - $this->db->sql_freeresult($result); - return $rows; + return $this->fetch_decoded_rowset($sql, ['code', 'emotion', 'smiley_url']); } /** @@ -116,11 +110,8 @@ class data_access protected function get_styles() { $sql = 'SELECT style_id, style_path, style_parent_id, bbcode_bitfield FROM ' . $this->styles_table; - $result = $this->db->sql_query($sql); - $rows = $this->db->sql_fetchrowset($result); - $this->db->sql_freeresult($result); - return $rows; + return $this->fetch_decoded_rowset($sql); } /** @@ -219,10 +210,43 @@ class data_access public function get_censored_words() { $sql = 'SELECT word, replacement FROM ' . $this->words_table; + + return $this->fetch_decoded_rowset($sql, ['word', 'replacement']); + } + + /** + * Decode HTML special chars in given rowset + * + * @param array $rows Original rowset + * @param array $columns List of columns to decode + * @return array Decoded rowset + */ + protected function decode_rowset(array $rows, array $columns) + { + foreach ($rows as &$row) + { + foreach ($columns as $column) + { + $row[$column] = htmlspecialchars_decode($row[$column]); + } + } + + return $rows; + } + + /** + * Fetch all rows for given query and decode plain text columns + * + * @param string $sql SELECT query + * @param array $columns List of columns to decode + * @return array + */ + protected function fetch_decoded_rowset($sql, array $columns = []) + { $result = $this->db->sql_query($sql); $rows = $this->db->sql_fetchrowset($result); $this->db->sql_freeresult($result); - return $rows; + return $this->decode_rowset($rows, $columns); } } diff --git a/phpBB/phpbb/textformatter/s9e/factory.php b/phpBB/phpbb/textformatter/s9e/factory.php index a310c67359..3f2e0ab8cb 100644 --- a/phpBB/phpbb/textformatter/s9e/factory.php +++ b/phpBB/phpbb/textformatter/s9e/factory.php @@ -311,7 +311,7 @@ class factory implements \phpbb\textformatter\cache_interface { $configurator->Emoticons->set( $row['code'], - '<img class="smilies" src="{$T_SMILIES_PATH}/' . htmlspecialchars($row['smiley_url']) . '" width="' . $row['smiley_width'] . '" height="' . $row['smiley_height'] . '" alt="{.}" title="' . htmlspecialchars($row['emotion']) . '"/>' + '<img class="smilies" src="{$T_SMILIES_PATH}/' . $this->escape_html_attribute($row['smiley_url']) . '" width="' . $row['smiley_width'] . '" height="' . $row['smiley_height'] . '" alt="{.}" title="' . $this->escape_html_attribute($row['emotion']) . '"/>' ); } @@ -323,6 +323,9 @@ class factory implements \phpbb\textformatter\cache_interface // Only parse emoticons at the beginning of the text or if they're preceded by any // one of: a new line, a space, a dot, or a right square bracket $configurator->Emoticons->notAfter = '[^\\n .\\]]'; + + // Ignore emoticons that are immediately followed by a "word" character + $configurator->Emoticons->notBefore = '\\w'; } // Load the censored words @@ -333,8 +336,7 @@ class factory implements \phpbb\textformatter\cache_interface $configurator->plugins->load('Censor', array('tagName' => 'censor:tag')); foreach ($censor as $row) { - // NOTE: words are stored as HTML, we need to decode them to plain text - $configurator->Censor->add(htmlspecialchars_decode($row['word']), htmlspecialchars_decode($row['replacement'])); + $configurator->Censor->add($row['word'], $row['replacement']); } } @@ -348,10 +350,10 @@ class factory implements \phpbb\textformatter\cache_interface $configurator->registeredVars['max_img_width'] = 0; // Load the Emoji plugin and modify its tag's template to obey viewsmilies - $configurator->Emoji->setImageSize(18); + $configurator->Emoji->omitImageSize(); $configurator->Emoji->useSVG(); $tag = $configurator->Emoji->getTag(); - $tag->template = '<xsl:choose><xsl:when test="$S_VIEWSMILIES">' . str_replace('class="emoji"', 'class="smilies"', $tag->template) . '</xsl:when><xsl:otherwise><xsl:value-of select="."/></xsl:otherwise></xsl:choose>'; + $tag->template = '<xsl:choose><xsl:when test="$S_VIEWSMILIES">' . str_replace('class="emoji"', 'class="emoji smilies"', $tag->template) . '</xsl:when><xsl:otherwise><xsl:value-of select="."/></xsl:otherwise></xsl:choose>'; /** * Modify the s9e\TextFormatter configurator after the default settings are set @@ -443,6 +445,20 @@ class factory implements \phpbb\textformatter\cache_interface } /** + * Escape a literal to be used in an HTML attribute in an XSL template + * + * Escapes "HTML special chars" for obvious reasons and curly braces to avoid them + * being interpreted as an attribute value template + * + * @param string $value Original string + * @return string Escaped string + */ + protected function escape_html_attribute($value) + { + return htmlspecialchars(strtr($value, ['{' => '{{', '}' => '}}']), ENT_COMPAT | ENT_XML1, 'UTF-8'); + } + + /** * Return the default BBCodes configuration * * @return array 2D array. Each element has a 'usage' key, a 'template' key, and an optional 'options' key diff --git a/phpBB/phpbb/textformatter/s9e/parser.php b/phpBB/phpbb/textformatter/s9e/parser.php index e2653d60f0..05ddfffa11 100644 --- a/phpBB/phpbb/textformatter/s9e/parser.php +++ b/phpBB/phpbb/textformatter/s9e/parser.php @@ -142,6 +142,7 @@ class parser implements \phpbb\textformatter\parser_interface public function disable_smilies() { $this->parser->disablePlugin('Emoticons'); + $this->parser->disablePlugin('Emoji'); } /** @@ -183,6 +184,7 @@ class parser implements \phpbb\textformatter\parser_interface public function enable_smilies() { $this->parser->enablePlugin('Emoticons'); + $this->parser->enablePlugin('Emoji'); } /** diff --git a/phpBB/phpbb/textformatter/s9e/quote_helper.php b/phpBB/phpbb/textformatter/s9e/quote_helper.php index 24109ac8cc..86c33c7591 100644 --- a/phpBB/phpbb/textformatter/s9e/quote_helper.php +++ b/phpBB/phpbb/textformatter/s9e/quote_helper.php @@ -39,8 +39,8 @@ class quote_helper */ public function __construct(\phpbb\user $user, $root_path, $php_ext) { - $this->post_url = append_sid($root_path . 'viewtopic.' . $php_ext, 'p={POST_ID}#p{POST_ID}'); - $this->profile_url = append_sid($root_path . 'memberlist.' . $php_ext, 'mode=viewprofile&u={USER_ID}'); + $this->post_url = append_sid($root_path . 'viewtopic.' . $php_ext, 'p={POST_ID}#p{POST_ID}', false); + $this->profile_url = append_sid($root_path . 'memberlist.' . $php_ext, 'mode=viewprofile&u={USER_ID}', false); $this->user = $user; } diff --git a/phpBB/phpbb/textformatter/s9e/renderer.php b/phpBB/phpbb/textformatter/s9e/renderer.php index 9be20b7f53..6fcd2b0a98 100644 --- a/phpBB/phpbb/textformatter/s9e/renderer.php +++ b/phpBB/phpbb/textformatter/s9e/renderer.php @@ -247,14 +247,12 @@ class renderer implements \phpbb\textformatter\renderer_interface $vars = array('renderer', 'xml'); extract($this->dispatcher->trigger_event('core.text_formatter_s9e_render_before', compact($vars))); + $html = $this->renderer->render($xml); if (isset($this->censor) && $this->viewcensors) { - // NOTE: censorHtml() is XML-safe - $xml = $this->censor->censorHtml($xml, true); + $html = $this->censor->censorHtml($html, true); } - $html = $this->renderer->render($xml); - /** * Modify a rendered text * diff --git a/phpBB/phpbb/textformatter/s9e/utils.php b/phpBB/phpbb/textformatter/s9e/utils.php index b317fe4a8d..a9a6d4b892 100644 --- a/phpBB/phpbb/textformatter/s9e/utils.php +++ b/phpBB/phpbb/textformatter/s9e/utils.php @@ -136,4 +136,17 @@ class utils implements \phpbb\textformatter\utils_interface { return \s9e\TextFormatter\Unparser::unparse($xml); } + + /** + * {@inheritdoc} + */ + public function is_empty($text) + { + if ($text === null || $text === '') + { + return true; + } + + return trim($this->unparse($text)) === ''; + } } diff --git a/phpBB/phpbb/textformatter/utils_interface.php b/phpBB/phpbb/textformatter/utils_interface.php index 4810453cd1..4b7392976a 100644 --- a/phpBB/phpbb/textformatter/utils_interface.php +++ b/phpBB/phpbb/textformatter/utils_interface.php @@ -62,10 +62,18 @@ interface utils_interface public function remove_bbcode($text, $bbcode_name, $depth = 0); /** - * Return a parsed text to its original form - * - * @param string $text Parsed text - * @return string Original plain text - */ + * Return a parsed text to its original form + * + * @param string $text Parsed text + * @return string Original plain text + */ public function unparse($text); + + /** + * Return whether or not a parsed text represent an empty text. + * + * @param string $text Parsed text + * @return bool Tue if the original text is empty + */ + public function is_empty($text); } diff --git a/phpBB/phpbb/user.php b/phpBB/phpbb/user.php index 305510851c..d4097f53ee 100644 --- a/phpBB/phpbb/user.php +++ b/phpBB/phpbb/user.php @@ -588,6 +588,7 @@ class user extends \phpbb\session */ function format_date($gmepoch, $format = false, $forcedate = false) { + global $phpbb_dispatcher; static $utc; if (!isset($utc)) @@ -595,10 +596,34 @@ class user extends \phpbb\session $utc = new \DateTimeZone('UTC'); } - $time = new $this->datetime($this, '@' . (int) $gmepoch, $utc); - $time->setTimezone($this->timezone); + $format_date_override = false; + $function_arguments = func_get_args(); + /** + * Execute code and/or override format_date() + * + * To override the format_date() function generated value + * set $format_date_override to new return value + * + * @event core.user_format_date_override + * @var DateTimeZone utc Is DateTimeZone in UTC + * @var array function_arguments is array comprising a function's argument list + * @var string format_date_override Shall we return custom format (string) or not (false) + * @since 3.2.1-RC1 + */ + $vars = array('utc', 'function_arguments', 'format_date_override'); + extract($phpbb_dispatcher->trigger_event('core.user_format_date_override', compact($vars))); - return $time->format($format, $forcedate); + if (!$format_date_override) + { + $time = new $this->datetime($this, '@' . (int) $gmepoch, $utc); + $time->setTimezone($this->timezone); + + return $time->format($format, $forcedate); + } + else + { + return $format_date_override; + } } /** diff --git a/phpBB/phpbb/version_helper.php b/phpBB/phpbb/version_helper.php index 17caaa4a60..a73fbfbfbe 100644 --- a/phpBB/phpbb/version_helper.php +++ b/phpBB/phpbb/version_helper.php @@ -60,6 +60,23 @@ class version_helper /** @var \phpbb\file_downloader */ protected $file_downloader; + protected $version_schema = array( + 'stable' => array( + 'current' => 'version', + 'download' => 'url', + 'announcement' => 'url', + 'eol' => 'url', + 'security' => 'bool', + ), + 'unstable' => array( + 'current' => 'version', + 'download' => 'url', + 'announcement' => 'url', + 'eol' => 'url', + 'security' => 'bool', + ), + ); + /** * Constructor * @@ -181,7 +198,7 @@ class version_helper $self = $this; $current_version = $this->current_version; - // Filter out any versions less than to the current version + // Filter out any versions less than the current version $versions = array_filter($versions, function($data) use ($self, $current_version) { return $self->compare($data['current'], $current_version, '>='); }); @@ -198,11 +215,117 @@ class version_helper } /** + * Gets the latest update for the current branch the user is on + * Will suggest versions from newer branches when EoL has been reached + * and/or version from newer branch is needed for having all known security + * issues fixed. + * + * @param bool $force_update Ignores cached data. Defaults to false. + * @param bool $force_cache Force the use of the cache. Override $force_update. + * @return array Version info or empty array if there are no updates + * @throws \RuntimeException + */ + public function get_update_on_branch($force_update = false, $force_cache = false) + { + $versions = $this->get_versions_matching_stability($force_update, $force_cache); + + $self = $this; + $current_version = $this->current_version; + + // Filter out any versions less than the current version + $versions = array_filter($versions, function($data) use ($self, $current_version) { + return $self->compare($data['current'], $current_version, '>='); + }); + + // Get the lowest version from the previous list. + $update_info = array_reduce($versions, function($value, $data) use ($self, $current_version) { + if ($value === null && $self->compare($data['current'], $current_version, '>=')) + { + if (!$data['eol'] && (!$data['security'] || $self->compare($data['security'], $data['current'], '<='))) + { + return ($self->compare($data['current'], $current_version, '>')) ? $data : array(); + } + else + { + return null; + } + } + + return $value; + }); + + return $update_info === null ? array() : $update_info; + } + + /** + * Gets the latest extension update for the current phpBB branch the user is on + * Will suggest versions from newer branches when EoL has been reached + * and/or version from newer branch is needed for having all known security + * issues fixed. + * + * @param bool $force_update Ignores cached data. Defaults to false. + * @param bool $force_cache Force the use of the cache. Override $force_update. + * @return array Version info or empty array if there are no updates + * @throws \RuntimeException + */ + public function get_ext_update_on_branch($force_update = false, $force_cache = false) + { + $versions = $this->get_versions_matching_stability($force_update, $force_cache); + + $self = $this; + $current_version = $this->current_version; + + // Get current phpBB branch from version, e.g.: 3.2 + preg_match('/^(\d+\.\d+).*$/', $this->config['version'], $matches); + $current_branch = $matches[1]; + + // Filter out any versions less than the current version + $versions = array_filter($versions, function($data) use ($self, $current_version) { + return $self->compare($data['current'], $current_version, '>='); + }); + + // Filter out any phpbb branches less than the current version + $branches = array_filter(array_keys($versions), function($branch) use ($self, $current_branch) { + return $self->compare($branch, $current_branch, '>='); + }); + if (!empty($branches)) + { + $versions = array_intersect_key($versions, array_flip($branches)); + } + else + { + // If branches are empty, it means the current phpBB branch is newer than any branch the + // extension was validated against. Reverse sort the versions array so we get the newest + // validated release available. + krsort($versions); + } + + // Get the first available version from the previous list. + $update_info = array_reduce($versions, function($value, $data) use ($self, $current_version) { + if ($value === null && $self->compare($data['current'], $current_version, '>=')) + { + if (!$data['eol'] && (!$data['security'] || $self->compare($data['security'], $data['current'], '<='))) + { + return $self->compare($data['current'], $current_version, '>') ? $data : array(); + } + else + { + return null; + } + } + + return $value; + }); + + return $update_info === null ? array() : $update_info; + } + + /** * Obtains the latest version information * * @param bool $force_update Ignores cached data. Defaults to false. * @param bool $force_cache Force the use of the cache. Override $force_update. - * @return string + * @return array * @throws version_check_exception */ public function get_suggested_updates($force_update = false, $force_cache = false) @@ -223,7 +346,7 @@ class version_helper * * @param bool $force_update Ignores cached data. Defaults to false. * @param bool $force_cache Force the use of the cache. Override $force_update. - * @return string Version info + * @return array Version info * @throws version_check_exception */ public function get_versions_matching_stability($force_update = false, $force_cache = false) @@ -243,7 +366,7 @@ class version_helper * * @param bool $force_update Ignores cached data. Defaults to false. * @param bool $force_cache Force the use of the cache. Override $force_update. - * @return string Version info, includes stable and unstable data + * @return array Version info, includes stable and unstable data * @throws version_check_exception */ public function get_versions($force_update = false, $force_cache = false) @@ -286,9 +409,101 @@ class version_helper $info['stable'] = (empty($info['stable'])) ? array() : $info['stable']; $info['unstable'] = (empty($info['unstable'])) ? $info['stable'] : $info['unstable']; + $info = $this->validate_versions($info); + $this->cache->put($cache_file, $info, 86400); // 24 hours } return $info; } + + /** + * Validate versions info input + * + * @param array $versions_info Decoded json data array. Will be modified + * and cleaned by this method + * + * @return array Versions info array + * @throws version_check_exception + */ + public function validate_versions($versions_info) + { + $array_diff = array_diff_key($versions_info, array($this->version_schema)); + + // Remove excessive data + if (count($array_diff) > 0) + { + $old_versions_info = $versions_info; + $versions_info = array( + 'stable' => !empty($old_versions_info['stable']) ? $old_versions_info['stable'] : array(), + 'unstable' => !empty($old_versions_info['unstable']) ? $old_versions_info['unstable'] : array(), + ); + unset($old_versions_info); + } + + foreach ($versions_info as $stability_type => &$versions_data) + { + foreach ($versions_data as $branch => &$version_data) + { + if (!preg_match('/^[0-9a-z\-\.]+$/i', $branch)) + { + unset($versions_data[$branch]); + continue; + } + + $stability_diff = array_diff_key($version_data, $this->version_schema[$stability_type]); + + if (count($stability_diff) > 0) + { + $old_version_data = $version_data; + $version_data = array(); + foreach ($this->version_schema[$stability_type] as $key => $value) + { + if (isset($old_version_data[$key])) + { + $version_data[$key] = $old_version_data[$key]; + } + } + unset($old_version_data); + } + + foreach ($version_data as $key => &$value) + { + if (!isset($this->version_schema[$stability_type][$key])) + { + unset($version_data[$key]); + throw new version_check_exception('VERSIONCHECK_INVALID_ENTRY'); + } + + switch ($this->version_schema[$stability_type][$key]) + { + case 'bool': + $value = (bool) $value; + break; + + case 'url': + if (!empty($value) && !preg_match('#^' . get_preg_expression('url') . '$#iu', $value) && + !preg_match('#^' . get_preg_expression('www_url') . '$#iu', $value)) + { + throw new version_check_exception('VERSIONCHECK_INVALID_URL'); + } + break; + + case 'version': + if (!empty($value) && !preg_match(get_preg_expression('semantic_version'), $value)) + { + throw new version_check_exception('VERSIONCHECK_INVALID_VERSION'); + } + break; + + default: + // Shouldn't be possible to trigger this + throw new version_check_exception('VERSIONCHECK_INVALID_ENTRY'); + } + } + } + } + + return $versions_info; + } } |
