1
0
mirror of https://github.com/phpbb/phpbb.git synced 2025-05-27 17:49:27 +02:00
2021-01-31 11:33:42 +01:00

866 lines
23 KiB
PHP

<?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\convert\controller;
use phpbb\cache\driver\driver_interface;
use phpbb\exception\http_exception;
use phpbb\install\controller\helper;
use phpbb\install\helper\container_factory;
use phpbb\install\helper\database;
use phpbb\install\helper\install_helper;
use phpbb\install\helper\iohandler\factory;
use phpbb\install\helper\iohandler\iohandler_interface;
use phpbb\install\helper\navigation\navigation_provider;
use phpbb\language\language;
use phpbb\request\request_interface;
use phpbb\template\template;
use Symfony\Component\HttpFoundation\StreamedResponse;
/**
* Controller for forum convertors
*
* WARNING: This file did not meant to be present in a production environment, so moving
* this file to a location which is accessible after board installation might
* lead to security issues.
*/
class convertor
{
/**
* @var driver_interface
*/
protected $cache;
/**
* @var driver_interface
*/
protected $installer_cache;
/**
* @var \phpbb\config\db
*/
protected $config;
/**
* @var \phpbb\config_php_file
*/
protected $config_php_file;
/**
* @var string
*/
protected $config_table;
/**
* @var helper
*/
protected $controller_helper;
/**
* @var database
*/
protected $db_helper;
/**
* @var \phpbb\db\driver\driver_interface
*/
protected $db;
/**
* @var install_helper
*/
protected $install_helper;
/**
* @var iohandler_interface
*/
protected $iohandler;
/**
* @var language
*/
protected $language;
/**
* @var navigation_provider
*/
protected $navigation_provider;
/**
* @var request_interface
*/
protected $request;
/**
* @var string
*/
protected $session_keys_table;
/**
* @var string
*/
protected $session_table;
/**
* @var template
*/
protected $template;
/**
* @var string
*/
protected $phpbb_root_path;
/**
* @var string
*/
protected $php_ext;
/**
* Constructor
*
* @param driver_interface $cache
* @param container_factory $container
* @param database $db_helper
* @param helper $controller_helper
* @param install_helper $install_helper
* @param factory $iohandler
* @param language $language
* @param navigation_provider $nav
* @param request_interface $request
* @param template $template
* @param string $phpbb_root_path
* @param string $php_ext
*/
public function __construct(driver_interface $cache, container_factory $container, database $db_helper, helper $controller_helper, install_helper $install_helper, factory $iohandler, language $language, navigation_provider $nav, request_interface $request, template $template, $phpbb_root_path, $php_ext)
{
$this->installer_cache = $cache;
$this->controller_helper = $controller_helper;
$this->db_helper = $db_helper;
$this->install_helper = $install_helper;
$this->language = $language;
$this->navigation_provider = $nav;
$this->request = $request;
$this->template = $template;
$this->phpbb_root_path = $phpbb_root_path;
$this->php_ext = $php_ext;
$iohandler->set_environment('ajax');
$this->iohandler = $iohandler->get();
if (!$this->install_helper->is_phpbb_installed() || !defined('IN_INSTALL'))
{
throw new http_exception(403, 'INSTALL_PHPBB_NOT_INSTALLED');
}
$this->controller_helper->handle_language_select();
$this->cache = $container->get('cache.driver');
$this->config = $container->get('config');
$this->config_php_file = new \phpbb\config_php_file($this->phpbb_root_path, $this->php_ext);
$this->db = $container->get('dbal.conn.driver');
$this->config_table = $container->get_parameter('tables.config');
$this->session_keys_table = $container->get_parameter('tables.sessions_keys');
$this->session_table = $container->get_parameter('tables.sessions');
}
/**
* Render the intro page
*
* @param bool|int $start_new Whether or not to force to start a new convertor
*
* @return \Symfony\Component\HttpFoundation\Response
*/
public function intro($start_new)
{
$this->setup_navigation('intro');
if ($start_new)
{
if ($this->request->is_ajax())
{
$response = new StreamedResponse();
$iohandler = $this->iohandler;
$url = $this->controller_helper->route('phpbb_convert_intro', array('start_new' => 'new'));
$response->setCallback(function() use ($iohandler, $url) {
$iohandler->redirect($url);
});
$response->headers->set('X-Accel-Buffering', 'no');
return $response;
}
$this->config['convert_progress'] = '';
$this->config['convert_db_server'] = '';
$this->config['convert_db_user'] = '';
$this->db->sql_query('DELETE FROM ' . $this->config_table . "
WHERE config_name = 'convert_progress'
OR config_name = 'convert_db_server'
OR config_name = 'convert_db_user'"
);
}
// Let's see if there is a conversion in the works...
$options = array();
if (!empty($this->config['convert_progress']) &&
!empty($this->config['convert_db_server']) &&
!empty($this->config['convert_db_user']) &&
!empty($this->config['convert_options']))
{
$options = unserialize($this->config['convert_progress']);
$options = array_merge($options,
unserialize($this->config['convert_db_server']),
unserialize($this->config['convert_db_user']),
unserialize($this->config['convert_options'])
);
}
// This information should have already been checked once, but do it again for safety
if (!empty($options) && !empty($options['tag']) &&
isset($options['dbms']) &&
isset($options['dbhost']) &&
isset($options['dbport']) &&
isset($options['dbuser']) &&
isset($options['dbpasswd']) &&
isset($options['dbname']) &&
isset($options['table_prefix']))
{
$this->template->assign_vars(array(
'TITLE' => $this->language->lang('CONTINUE_CONVERT'),
'BODY' => $this->language->lang('CONTINUE_CONVERT_BODY'),
'S_CONTINUE' => true,
'U_NEW_ACTION' => $this->controller_helper->route('phpbb_convert_intro', array('start_new' => 'new')),
'U_CONTINUE_ACTION' => $this->controller_helper->route('phpbb_convert_convert', array('converter' => $options['tag'])),
));
return $this->controller_helper->render('installer_convert.html', 'CONTINUE_CONVERT', true);
}
return $this->render_convert_list();
}
/**
* Obtain convertor settings
*
* @param string $converter Name of the convertor
*
* @return \Symfony\Component\HttpFoundation\Response|StreamedResponse
*/
public function settings($converter)
{
$this->setup_navigation('settings');
require_once ($this->phpbb_root_path . 'includes/constants.' . $this->php_ext);
require_once ($this->phpbb_root_path . 'includes/functions_convert.' . $this->php_ext);
// Include convertor if available
$convertor_file_path = $this->phpbb_root_path . 'install/convertors/convert_' . $converter . '.' . $this->php_ext;
if (!file_exists($convertor_file_path))
{
if ($this->request->is_ajax())
{
$response = new StreamedResponse();
$ref = $this;
$response->setCallback(function() use ($ref) {
$ref->render_error('CONVERT_NOT_EXIST');
});
$response->headers->set('X-Accel-Buffering', 'no');
return $response;
}
$this->render_error('CONVERT_NOT_EXIST');
return $this->controller_helper->render('installer_convert.html', 'STAGE_SETTINGS', true);
}
$get_info = true;
$phpbb_root_path = $this->phpbb_root_path; // These globals are required
$phpEx = $this->php_ext; // See above
include_once ($convertor_file_path);
// The test_file is a file that should be present in the location of the old board.
if (!isset($test_file))
{
if ($this->request->is_ajax())
{
$response = new StreamedResponse();
$ref = $this;
$response->setCallback(function() use ($ref) {
$ref->render_error('DEV_NO_TEST_FILE');
});
$response->headers->set('X-Accel-Buffering', 'no');
return $response;
}
$this->render_error('DEV_NO_TEST_FILE');
return $this->controller_helper->render('installer_convert.html', 'STAGE_SETTINGS', true);
}
if ($this->request->variable('submit', false))
{
// It must be an AJAX request at this point
$response = new StreamedResponse();
$ref = $this;
$response->setCallback(function() use ($ref, $converter) {
$ref->proccess_settings_form($converter);
});
$response->headers->set('X-Accel-Buffering', 'no');
return $response;
}
else
{
$this->template->assign_vars(array(
'U_ACTION' => $this->controller_helper->route('phpbb_convert_settings', array(
'converter' => $converter,
))
));
if ($this->request->is_ajax())
{
$response = new StreamedResponse();
$ref = $this;
$response->setCallback(function() use ($ref) {
$ref->render_settings_form();
});
$response->headers->set('X-Accel-Buffering', 'no');
return $response;
}
$this->render_settings_form();
}
return $this->controller_helper->render('installer_convert.html', 'STAGE_SETTINGS', true);
}
/**
* Run conversion
*/
public function convert($converter)
{
$this->setup_navigation('convert');
if ($this->request->is_ajax())
{
$route = $this->controller_helper->route('phpbb_convert_convert', array('converter' => $converter));
$response = new StreamedResponse();
$ref = $this;
$response->setCallback(function() use ($ref, $route) {
$ref->redirect_to_html($route);
});
$response->headers->set('X-Accel-Buffering', 'no');
return $response;
}
$convertor = new \phpbb\convert\convertor($this->template, $this->controller_helper);
$convertor->convert_data($converter);
return $this->controller_helper->render('installer_convert.html', 'STAGE_IN_PROGRESS');
}
/**
* Render the final page of the convertor
*/
public function finish()
{
$this->setup_navigation('finish');
$this->template->assign_vars(array(
'TITLE' => $this->language->lang('CONVERT_COMPLETE'),
'BODY' => $this->language->lang('CONVERT_COMPLETE_EXPLAIN'),
));
// If we reached this step (conversion completed) we want to purge the cache and log the user out.
// This is for making sure the session get not screwed due to the 3.0.x users table being completely new.
$this->cache->purge();
$this->installer_cache->purge();
require_once($this->phpbb_root_path . 'includes/constants.' . $this->php_ext);
require_once($this->phpbb_root_path . 'includes/functions_convert.' . $this->php_ext);
$sql = 'SELECT config_value
FROM ' . $this->config_table . '
WHERE config_name = \'search_type\'';
$result = $this->db->sql_query($sql);
if ($this->db->sql_fetchfield('config_value') != 'fulltext_mysql')
{
$this->template->assign_vars(array(
'S_ERROR_BOX' => true,
'ERROR_TITLE' => $this->language->lang('SEARCH_INDEX_UNCONVERTED'),
'ERROR_MSG' => $this->language->lang('SEARCH_INDEX_UNCONVERTED_EXPLAIN'),
));
}
$this->db->sql_freeresult($result);
switch ($this->db->get_sql_layer())
{
case 'sqlite3':
$this->db->sql_query('DELETE FROM ' . $this->session_keys_table);
$this->db->sql_query('DELETE FROM ' . $this->session_table);
break;
default:
$this->db->sql_query('TRUNCATE TABLE ' . $this->session_keys_table);
$this->db->sql_query('TRUNCATE TABLE ' . $this->session_table);
break;
}
return $this->controller_helper->render('installer_convert.html', 'CONVERT_COMPLETE');
}
/**
* Validates settings form
*
* @param string $convertor
*/
public function proccess_settings_form($convertor)
{
global $phpbb_root_path, $phpEx, $get_info;
$phpbb_root_path = $this->phpbb_root_path;
$phpEx = $this->php_ext;
$get_info = true;
require_once($this->phpbb_root_path . 'includes/constants.' . $this->php_ext);
require_once($this->phpbb_root_path . 'includes/functions_convert.' . $this->php_ext);
// Include convertor if available
$convertor_file_path = $this->phpbb_root_path . 'install/convertors/convert_' . $convertor . '.' . $this->php_ext;
include ($convertor_file_path);
// We expect to have an AJAX request here
$src_dbms = $this->request->variable('src_dbms', $convertor_data['dbms']);
$src_dbhost = $this->request->variable('src_dbhost', $convertor_data['dbhost']);
$src_dbport = $this->request->variable('src_dbport', $convertor_data['dbport']);
$src_dbuser = $this->request->variable('src_dbuser', $convertor_data['dbuser']);
$src_dbpasswd = $this->request->variable('src_dbpasswd', $convertor_data['dbpasswd']);
$src_dbname = $this->request->variable('src_dbname', $convertor_data['dbname']);
$src_table_prefix = $this->request->variable('src_table_prefix', $convertor_data['table_prefix']);
$forum_path = $this->request->variable('forum_path', $convertor_data['forum_path']);
$refresh = $this->request->variable('refresh', 1);
// Default URL of the old board
// @todo Are we going to use this for attempting to convert URL references in posts, or should we remove it?
// -> We should convert old urls to the new relative urls format
// $src_url = $request->variable('src_url', 'Not in use at the moment');
// strip trailing slash from old forum path
$forum_path = (strlen($forum_path) && $forum_path[strlen($forum_path) - 1] == '/') ? substr($forum_path, 0, -1) : $forum_path;
$error = array();
if (!file_exists($this->phpbb_root_path . $forum_path . '/' . $test_file))
{
$error[] = $this->language->lang('COULD_NOT_FIND_PATH', $forum_path);
}
$connect_test = false;
$available_dbms = $this->db_helper->get_available_dbms(false, true, true);
if (!isset($available_dbms[$src_dbms]) || !$available_dbms[$src_dbms]['AVAILABLE'])
{
$error[] = $this->language->lang('INST_ERR_NO_DB');
}
else
{
$connect_test = $this->db_helper->check_database_connection($src_dbms, $src_dbhost, $src_dbport, $src_dbuser, $src_dbpasswd, $src_dbname, $src_table_prefix);
}
extract($this->config_php_file->get_all());
// The forum prefix of the old and the new forum can only be the same if two different databases are used.
if ($src_table_prefix === $table_prefix && $src_dbms === $dbms && $src_dbhost === $dbhost && $src_dbport === $dbport && $src_dbname === $dbname)
{
$error[] = $this->language->lang('TABLE_PREFIX_SAME', $src_table_prefix);
}
if (!$connect_test)
{
$error[] = $this->language->lang('INST_ERR_DB_CONNECT');
}
$src_dbms = \phpbb\config_php_file::convert_30_dbms_to_31($src_dbms);
// Check table prefix
if (empty($error))
{
// initiate database connection to old db if old and new db differ
global $src_db, $same_db;
$src_db = $same_db = false;
if ($src_dbms != $dbms || $src_dbhost != $dbhost || $src_dbport != $dbport || $src_dbname != $dbname || $src_dbuser != $dbuser)
{
/** @var \phpbb\db\driver\driver_interface $src_db */
$src_db = new $src_dbms();
$src_db->sql_connect($src_dbhost, $src_dbuser, htmlspecialchars_decode($src_dbpasswd, ENT_COMPAT), $src_dbname, $src_dbport, false, true);
$same_db = false;
}
else
{
$src_db = $this->db;
$same_db = true;
}
$src_db->sql_return_on_error(true);
$this->db->sql_return_on_error(true);
// Try to select one row from the first table to see if the prefix is OK
$result = $src_db->sql_query_limit('SELECT * FROM ' . $src_table_prefix . $tables[0], 1);
if (!$result)
{
$prefixes = array();
$db_tools_factory = new \phpbb\db\tools\factory();
$db_tools = $db_tools_factory->get($src_db);
$tables_existing = $db_tools->sql_list_tables();
$tables_existing = array_map('strtolower', $tables_existing);
foreach ($tables_existing as $table_name)
{
compare_table($tables, $table_name, $prefixes);
}
unset($tables_existing);
foreach ($prefixes as $prefix => $count)
{
if ($count >= count($tables))
{
$possible_prefix = $prefix;
break;
}
}
$msg = '';
if (!empty($convertor_data['table_prefix']))
{
$msg .= $this->language->lang_array('DEFAULT_PREFIX_IS', array($convertor_data['forum_name'], $convertor_data['table_prefix']));
}
if (!empty($possible_prefix))
{
$msg .= '<br />';
$msg .= ($possible_prefix == '*') ? $this->language->lang('BLANK_PREFIX_FOUND') : $this->language->lang_array('PREFIX_FOUND', array($possible_prefix));
$src_table_prefix = ($possible_prefix == '*') ? '' : $possible_prefix;
}
$error[] = $msg;
}
$src_db->sql_freeresult($result);
$src_db->sql_return_on_error(false);
}
if (empty($error))
{
// Save convertor Status
$this->config->set('convert_progress', serialize(array(
'step' => '',
'table_prefix' => $src_table_prefix,
'tag' => $convertor,
)), false);
$this->config->set('convert_db_server', serialize(array(
'dbms' => $src_dbms,
'dbhost' => $src_dbhost,
'dbport' => $src_dbport,
'dbname' => $src_dbname,
)), false);
$this->config->set('convert_db_user', serialize(array(
'dbuser' => $src_dbuser,
'dbpasswd' => $src_dbpasswd,
)), false);
// Save options
$this->config->set('convert_options', serialize(array(
'forum_path' => $this->phpbb_root_path . $forum_path,
'refresh' => $refresh
)), false);
$url = $this->controller_helper->route('phpbb_convert_convert', array('converter' => $convertor));
$this->iohandler->redirect($url);
$this->iohandler->send_response(true);
}
else
{
$this->render_settings_form($error);
}
}
/**
* Renders settings form
*
* @param array $error Array of errors
*/
public function render_settings_form($error = array())
{
foreach ($error as $msg)
{
$this->iohandler->add_error_message($msg);
}
$dbms_options = array();
foreach ($this->db_helper->get_available_dbms() as $dbms_key => $dbms_array)
{
$dbms_options[] = array(
'value' => $dbms_key,
'label' => 'DB_OPTION_' . strtoupper($dbms_key),
);
}
$form_title = 'SPECIFY_OPTIONS';
$form_data = array(
'src_dbms' => array(
'label' => 'DBMS',
'type' => 'select',
'options' => $dbms_options,
),
'src_dbhost' => array(
'label' => 'DB_HOST',
'description' => 'DB_HOST_EXPLAIN',
'type' => 'text',
),
'src_dbport' => array(
'label' => 'DB_PORT',
'description' => 'DB_PORT_EXPLAIN',
'type' => 'text',
),
'src_dbname' => array(
'label' => 'DB_NAME',
'type' => 'text',
),
'src_dbuser' => array(
'label' => 'DB_USERNAME',
'type' => 'text',
),
'src_dbpasswd' => array(
'label' => 'DB_PASSWORD',
'type' => 'password',
),
'src_table_prefix' => array(
'label' => 'TABLE_PREFIX',
'description' => 'TABLE_PREFIX_EXPLAIN',
'type' => 'text',
),
'forum_path' => array(
'label' => 'FORUM_PATH',
'description' => 'FORUM_PATH_EXPLAIN',
'type' => 'text',
),
'refresh' => array(
'label' => 'REFRESH_PAGE',
'description' => 'REFRESH_PAGE_EXPLAIN',
'type' => 'radio',
'options' => array(
array(
'value' => 0,
'label' => 'NO',
'selected' => true,
),
array(
'value' => 1,
'label' => 'YES',
'selected' => false,
),
),
),
'submit' => array(
'label' => 'SUBMIT',
'type' => 'submit',
),
);
if ($this->request->is_ajax())
{
$this->iohandler->add_user_form_group($form_title, $form_data);
$this->iohandler->send_response(true);
}
else
{
$rendered_form = $this->iohandler->generate_form_render_data($form_title, $form_data);
$this->template->assign_vars(array(
'TITLE' => $this->language->lang('STAGE_SETTINGS'),
'CONTENT' => $rendered_form,
));
}
}
/**
* Render the list of available convertors
*
* @return \Symfony\Component\HttpFoundation\Response
*/
protected function render_convert_list()
{
$this->template->assign_vars(array(
'TITLE' => $this->language->lang('CONVERT_INTRO'),
'BODY' => $this->language->lang('CONVERT_INTRO_BODY'),
'S_LIST' => true,
));
$convertors = $sort = array();
$get_info = true; // Global flag
$handle = @opendir($this->phpbb_root_path . 'install/convertors/');
if (!$handle)
{
die('Unable to access the convertors directory');
}
while ($entry = readdir($handle))
{
if (preg_match('/^convert_([a-z0-9_]+).' . $this->php_ext . '$/i', $entry, $m))
{
$phpbb_root_path = $this->phpbb_root_path; // These globals are required
$phpEx = $this->php_ext; // See above
include_once($this->phpbb_root_path . 'install/convertors/' . $entry);
if (isset($convertor_data))
{
$sort[strtolower($convertor_data['forum_name'])] = count($convertors);
$convertors[] = array(
'tag' => $m[1],
'forum_name' => $convertor_data['forum_name'],
'version' => $convertor_data['version'],
'dbms' => $convertor_data['dbms'],
'dbhost' => $convertor_data['dbhost'],
'dbport' => $convertor_data['dbport'],
'dbuser' => $convertor_data['dbuser'],
'dbpasswd' => $convertor_data['dbpasswd'],
'dbname' => $convertor_data['dbname'],
'table_prefix' => $convertor_data['table_prefix'],
'author' => $convertor_data['author']
);
}
unset($convertor_data);
}
}
closedir($handle);
@ksort($sort);
foreach ($sort as $void => $index)
{
$this->template->assign_block_vars('convertors', array(
'AUTHOR' => $convertors[$index]['author'],
'SOFTWARE' => $convertors[$index]['forum_name'],
'VERSION' => $convertors[$index]['version'],
'U_CONVERT' => $this->controller_helper->route('phpbb_convert_settings', array('converter' => $convertors[$index]['tag'])),
));
}
return $this->controller_helper->render('installer_convert.html', 'SUB_INTRO', true);
}
/**
* Renders an error form
*
* @param string $msg
* @param string|bool $desc
*/
public function render_error($msg, $desc = false)
{
if ($this->request->is_ajax())
{
$this->iohandler->add_error_message($msg, $desc);
$this->iohandler->send_response(true);
}
else
{
$this->template->assign_vars(array(
'S_ERROR_BOX' => true,
'ERROR_TITLE' => $this->language->lang($msg),
));
if ($desc)
{
$this->template->assign_var('ERROR_MSG', $this->language->lang($desc));
}
}
}
/**
* Redirects an AJAX request to a non-JS version
*
* @param string $url URL to redirect to
*/
public function redirect_to_html($url)
{
$this->iohandler->redirect($url);
$this->iohandler->send_response(true);
}
private function setup_navigation($stage)
{
$active = true;
$completed = false;
switch ($stage)
{
case 'finish':
$this->navigation_provider->set_nav_property(
array('convert', 0, 'finish'),
array(
'selected' => $active,
'completed' => $completed,
)
);
$active = false;
$completed = true;
// no break;
case 'convert':
$this->navigation_provider->set_nav_property(
array('convert', 0, 'convert'),
array(
'selected' => $active,
'completed' => $completed,
)
);
$active = false;
$completed = true;
// no break;
case 'settings':
$this->navigation_provider->set_nav_property(
array('convert', 0, 'settings'),
array(
'selected' => $active,
'completed' => $completed,
)
);
$active = false;
$completed = true;
// no break;
case 'intro':
$this->navigation_provider->set_nav_property(
array('convert', 0, 'intro'),
array(
'selected' => $active,
'completed' => $completed,
)
);
break;
}
}
}