1
0
mirror of https://github.com/phpbb/phpbb.git synced 2025-05-19 05:49:44 +02:00
php-phpbb/phpBB/install/install_update.php
Meik Sievertsen 2f4a618900 ok... i hope i haven't messed too much with the code and everything is still working.
Changes:
- Ascraeus now uses constants for the phpbb root path and the php extension. This ensures more security for external applications and modifications (no more overwriting of root path and extension possible through insecure mods and register globals enabled) as well as no more globalizing needed.
- A second change implemented here is an additional short-hand-notation for append_sid(). It is allowed to omit the root path and extension now (for example calling append_sid('memberlist')) - in this case the root path and extension get added automatically. The hook is called after these are added.

git-svn-id: file:///svn/phpbb/trunk@8572 89ea8834-ac86-4346-8a33-228a782c2dd0
2008-05-29 12:25:56 +00:00

1645 lines
47 KiB
PHP

<?php
/**
*
* @package install
* @version $Id$
* @copyright (c) 2006 phpBB Group
* @license http://opensource.org/licenses/gpl-license.php GNU Public License
*
* @todo check for writable cache/store/files directory
*/
/**
*/
if (!defined('IN_INSTALL'))
{
// Someone has tried to access the file directly. This is not a good idea, so exit
exit;
}
if (!empty($setmodules))
{
// If phpBB is not installed we do not include this module
if (@file_exists(PHPBB_ROOT_PATH . 'config.' . PHP_EXT) && !@file_exists(PHPBB_ROOT_PATH . 'cache/install_lock'))
{
include_once(PHPBB_ROOT_PATH . 'config.' . PHP_EXT);
if (!defined('PHPBB_INSTALLED'))
{
return;
}
}
else
{
return;
}
$module[] = array(
'module_type' => 'update',
'module_title' => 'UPDATE',
'module_filename' => substr(basename(__FILE__), 0, -strlen(PHP_EXT)-1),
'module_order' => 30,
'module_subs' => '',
'module_stages' => array('INTRO', 'VERSION_CHECK', 'UPDATE_DB', 'FILE_CHECK', 'UPDATE_FILES'),
'module_reqs' => ''
);
}
/**
* Update Installation
* @package install
*/
class install_update extends module
{
var $p_master;
var $update_info;
var $old_location;
var $new_location;
var $latest_version;
var $current_version;
var $unequal_version;
// Set to false
var $test_update = false;
function install_update(&$p_master)
{
$this->p_master = &$p_master;
}
function main($mode, $sub)
{
global $template, $user, $db, $config, $cache, $auth;
$this->tpl_name = 'install_update';
$this->page_title = 'UPDATE_INSTALLATION';
$this->unequal_version = false;
$this->old_location = PHPBB_ROOT_PATH . 'install/update/old/';
$this->new_location = PHPBB_ROOT_PATH . 'install/update/new/';
// Init DB
require(PHPBB_ROOT_PATH . 'config.' . PHP_EXT);
require(PHPBB_ROOT_PATH . 'includes/db/' . $dbms . '.' . PHP_EXT);
require(PHPBB_ROOT_PATH . 'includes/constants.' . PHP_EXT);
// Special options for conflicts/modified files
define('MERGE_NO_MERGE_NEW', 1);
define('MERGE_NO_MERGE_MOD', 2);
define('MERGE_NEW_FILE', 3);
define('MERGE_MOD_FILE', 4);
$db = new $sql_db();
// Connect to DB
$db->sql_connect($dbhost, $dbuser, $dbpasswd, $dbname, $dbport, false, false);
// We do not need this any longer, unset for safety purposes
unset($dbpasswd);
$config = array();
$sql = 'SELECT config_name, config_value
FROM ' . CONFIG_TABLE;
$result = $db->sql_query($sql);
while ($row = $db->sql_fetchrow($result))
{
$config[$row['config_name']] = $row['config_value'];
}
$db->sql_freeresult($result);
// Force template recompile
$config['load_tplcompile'] = 1;
// First of all, init the user session
$user->session_begin();
$auth->acl($user->data);
$user->setup('install');
// If we are within the intro page we need to make sure we get up-to-date version info
if ($sub == 'intro')
{
$cache->destroy('_version_info');
}
// Set custom template again. ;)
$template->set_custom_template('../adm/style', 'admin');
// still, the acp template is never stored in the database
$user->theme['template_storedb'] = false;
// Get current and latest version
if (($latest_version = $cache->get('_version_info')) === false)
{
$this->latest_version = $this->get_file('version_info');
$cache->put('_version_info', $this->latest_version);
}
else
{
$this->latest_version = $latest_version;
}
// For the current version we trick a bit. ;)
$this->current_version = (!empty($config['version_update_from'])) ? $config['version_update_from'] : $config['version'];
$up_to_date = (version_compare(str_replace('rc', 'RC', strtolower($this->current_version)), str_replace('rc', 'RC', strtolower($this->latest_version)), '<')) ? false : true;
// Check for a valid update directory, else point the user to the phpbb.com website
if (!file_exists(PHPBB_ROOT_PATH . 'install/update') || !file_exists(PHPBB_ROOT_PATH . 'install/update/index.php') || !file_exists($this->old_location) || !file_exists($this->new_location))
{
$template->assign_vars(array(
'S_ERROR' => true,
'ERROR_MSG' => ($up_to_date) ? $user->lang['NO_UPDATE_FILES_UP_TO_DATE'] : sprintf($user->lang['NO_UPDATE_FILES_OUTDATED'], $config['version'], $this->current_version, $this->latest_version))
);
return;
}
$this->update_info = $this->get_file('update_info');
// Make sure the update directory holds the correct information
// Since admins are able to run the update/checks more than once we only check if the current version is lower or equal than the version to which we update to.
if (version_compare(str_replace('rc', 'RC', strtolower($this->current_version)), str_replace('rc', 'RC', strtolower($this->update_info['version']['to'])), '>'))
{
$template->assign_vars(array(
'S_ERROR' => true,
'ERROR_MSG' => sprintf($user->lang['INCOMPATIBLE_UPDATE_FILES'], $config['version'], $this->update_info['version']['from'], $this->update_info['version']['to']))
);
return;
}
// Check if the update files stored are for the latest version...
if ($this->latest_version != $this->update_info['version']['to'])
{
$this->unequal_version = true;
$template->assign_vars(array(
'S_WARNING' => true,
'WARNING_MSG' => sprintf($user->lang['OLD_UPDATE_FILES'], $this->update_info['version']['from'], $this->update_info['version']['to'], $this->latest_version))
);
}
if ($this->test_update === false)
{
// Got the updater template itself updated? If so, we are able to directly use it - but only if all three files are present
if (in_array('adm/style/install_update.html', $this->update_info['files']))
{
$this->tpl_name = '../../install/update/new/adm/style/install_update';
}
// What about the language file? Got it updated?
if (in_array('language/en/install.php', $this->update_info['files']))
{
$lang = array();
include($this->new_location . 'language/en/install.php');
// only add new keys to user's language in english
$new_keys = array_diff(array_keys($lang), array_keys($user->lang));
foreach ($new_keys as $i => $new_key)
{
$user->lang[$new_key] = $lang[$new_key];
}
}
}
// Include renderer and engine
$this->include_file('includes/diff/diff.' . PHP_EXT);
$this->include_file('includes/diff/engine.' . PHP_EXT);
$this->include_file('includes/diff/renderer.' . PHP_EXT);
// Make sure we stay at the file check if checking the files again
if (!empty($_POST['check_again']))
{
$sub = $this->p_master->sub = 'file_check';
}
switch ($sub)
{
case 'intro':
$this->page_title = 'UPDATE_INSTALLATION';
$template->assign_vars(array(
'S_INTRO' => true,
'U_ACTION' => append_sid($this->p_master->module_url, "mode=$mode&amp;sub=version_check"),
));
// Make sure the update list is destroyed.
$cache->destroy('_update_list');
$cache->destroy('_diff_files');
break;
case 'version_check':
$this->page_title = 'STAGE_VERSION_CHECK';
$template->assign_vars(array(
'S_UP_TO_DATE' => $up_to_date,
'S_VERSION_CHECK' => true,
'U_ACTION' => append_sid($this->p_master->module_url, "mode=$mode&amp;sub=file_check"),
'U_DB_UPDATE_ACTION' => append_sid($this->p_master->module_url, "mode=$mode&amp;sub=update_db"),
'LATEST_VERSION' => $this->latest_version,
'CURRENT_VERSION' => $this->current_version)
);
// Print out version the update package updates to
if ($this->unequal_version)
{
$template->assign_var('PACKAGE_VERSION', $this->update_info['version']['to']);
}
break;
case 'update_db':
// Make sure the database update is valid for the latest version
$valid = false;
$updates_to_version = '';
if (file_exists(PHPBB_ROOT_PATH . 'install/database_update.' . PHP_EXT))
{
include_once(PHPBB_ROOT_PATH . 'install/database_update.' . PHP_EXT);
if ($updates_to_version === $this->update_info['version']['to'])
{
$valid = true;
}
}
// Should not happen at all
if (!$valid)
{
trigger_error($user->lang['DATABASE_UPDATE_INFO_OLD'], E_USER_ERROR);
}
// Just a precaution
$cache->purge();
// Redirect the user to the database update script with some explanations...
$template->assign_vars(array(
'S_DB_UPDATE' => true,
'S_DB_UPDATE_FINISHED' => ($config['version'] == $this->update_info['version']['to']) ? true : false,
'U_DB_UPDATE' => append_sid('install/database_update', 'type=1&amp;language=' . $user->data['user_lang']),
'U_DB_UPDATE_ACTION' => append_sid($this->p_master->module_url, "mode=$mode&amp;sub=update_db"),
'U_ACTION' => append_sid($this->p_master->module_url, "mode=$mode&amp;sub=file_check"),
));
break;
case 'file_check':
// Make sure the previous file collection is no longer valid...
$cache->destroy('_diff_files');
$this->page_title = 'STAGE_FILE_CHECK';
// Now make sure our update list is correct if the admin refreshes
$action = request_var('action', '');
// We are directly within an update. To make sure our update list is correct we check its status.
$update_list = (!empty($_POST['check_again'])) ? false : $cache->get('_update_list');
$modified = ($update_list !== false) ? @filemtime($cache->cache_dir . 'data_update_list.' . PHP_EXT) : 0;
// Make sure the list is up-to-date
if ($update_list !== false)
{
$get_new_list = false;
foreach ($this->update_info['files'] as $file)
{
if (file_exists(PHPBB_ROOT_PATH . $file) && filemtime(PHPBB_ROOT_PATH . $file) > $modified)
{
$get_new_list = true;
break;
}
}
}
else
{
$get_new_list = true;
}
if (!$get_new_list && $update_list['status'] != -1)
{
$get_new_list = true;
}
if ($get_new_list)
{
$this->get_update_structure($update_list);
$cache->put('_update_list', $update_list);
// Refresh the page if we are still not finished...
if ($update_list['status'] != -1)
{
$refresh_url = append_sid($this->p_master->module_url, "mode=$mode&amp;sub=file_check");
meta_refresh(2, $refresh_url);
$template->assign_vars(array(
'S_IN_PROGRESS' => true,
'S_COLLECTED' => (int) $update_list['status'],
'S_TO_COLLECT' => sizeof($this->update_info['files']),
'L_IN_PROGRESS' => $user->lang['COLLECTING_FILE_DIFFS'],
'L_IN_PROGRESS_EXPLAIN' => sprintf($user->lang['NUMBER_OF_FILES_COLLECTED'], (int) $update_list['status'], sizeof($this->update_info['files'])),
));
return;
}
}
if ($action == 'diff')
{
$this->show_diff($update_list);
return;
}
if (sizeof($update_list['no_update']))
{
$template->assign_vars(array(
'S_NO_UPDATE_FILES' => true,
'NO_UPDATE_FILES' => implode(', ', array_map('htmlspecialchars', $update_list['no_update'])))
);
}
// Now assign the list to the template
foreach ($update_list as $status => $filelist)
{
if ($status == 'no_update' || !sizeof($filelist) || $status == 'status')
{
continue;
}
$template->assign_block_vars('files', array(
'S_STATUS' => true,
'STATUS' => $status,
'L_STATUS' => $user->lang['STATUS_' . strtoupper($status)],
'TITLE' => $user->lang['FILES_' . strtoupper($status)],
'EXPLAIN' => $user->lang['FILES_' . strtoupper($status) . '_EXPLAIN'],
)
);
foreach ($filelist as $file_struct)
{
$s_binary = (!empty($this->update_info['binary']) && in_array($file_struct['filename'], $this->update_info['binary'])) ? true : false;
$filename = htmlspecialchars($file_struct['filename']);
if (strrpos($filename, '/') !== false)
{
$dir_part = substr($filename, 0, strrpos($filename, '/') + 1);
$file_part = substr($filename, strrpos($filename, '/') + 1);
}
else
{
$dir_part = '';
$file_part = $filename;
}
$diff_url = append_sid($this->p_master->module_url, "mode=$mode&amp;sub=file_check&amp;action=diff&amp;status=$status&amp;file=" . urlencode($file_struct['filename']));
$template->assign_block_vars('files', array(
'STATUS' => $status,
'FILENAME' => $filename,
'DIR_PART' => $dir_part,
'FILE_PART' => $file_part,
'NUM_CONFLICTS' => (isset($file_struct['conflicts'])) ? $file_struct['conflicts'] : 0,
'S_CUSTOM' => ($file_struct['custom']) ? true : false,
'S_BINARY' => $s_binary,
'CUSTOM_ORIGINAL' => ($file_struct['custom']) ? $file_struct['original'] : '',
'U_SHOW_DIFF' => $diff_url,
'L_SHOW_DIFF' => ($status != 'up_to_date') ? $user->lang['SHOW_DIFF_' . strtoupper($status)] : '',
'U_VIEW_MOD_FILE' => $diff_url . '&amp;op=' . MERGE_MOD_FILE,
'U_VIEW_NEW_FILE' => $diff_url . '&amp;op=' . MERGE_NEW_FILE,
'U_VIEW_NO_MERGE_MOD' => $diff_url . '&amp;op=' . MERGE_NO_MERGE_MOD,
'U_VIEW_NO_MERGE_NEW' => $diff_url . '&amp;op=' . MERGE_NO_MERGE_NEW,
));
}
}
$all_up_to_date = true;
foreach ($update_list as $status => $filelist)
{
if ($status != 'up_to_date' && $status != 'custom' && $status != 'status' && sizeof($filelist))
{
$all_up_to_date = false;
break;
}
}
$template->assign_vars(array(
'S_FILE_CHECK' => true,
'S_ALL_UP_TO_DATE' => $all_up_to_date,
'S_VERSION_UP_TO_DATE' => $up_to_date,
'U_ACTION' => append_sid($this->p_master->module_url, "mode=$mode&amp;sub=file_check"),
'U_UPDATE_ACTION' => append_sid($this->p_master->module_url, "mode=$mode&amp;sub=update_files"),
'U_DB_UPDATE_ACTION' => append_sid($this->p_master->module_url, "mode=$mode&amp;sub=update_db"),
));
if ($all_up_to_date)
{
// Add database update to log
add_log('admin', 'LOG_UPDATE_PHPBB', $this->current_version, $this->latest_version);
// Refresh prosilver css data - this may cause some unhappy users, but
$sql = 'SELECT *
FROM ' . STYLES_THEME_TABLE . "
WHERE theme_name = 'prosilver'";
$result = $db->sql_query($sql);
$theme = $db->sql_fetchrow($result);
$db->sql_freeresult($result);
if ($theme)
{
$recache = (empty($theme['theme_data'])) ? true : false;
$update_time = time();
// We test for stylesheet.css because it is faster and most likely the only file changed on common themes
if (!$recache && $theme['theme_mtime'] < @filemtime(PHPBB_ROOT_PATH . 'styles/' . $theme['theme_path'] . '/theme/stylesheet.css'))
{
$recache = true;
$update_time = @filemtime(PHPBB_ROOT_PATH . 'styles/' . $theme['theme_path'] . '/theme/stylesheet.css');
}
else if (!$recache)
{
$last_change = $theme['theme_mtime'];
$dir = @opendir(PHPBB_ROOT_PATH . "styles/{$theme['theme_path']}/theme");
if ($dir)
{
while (($entry = readdir($dir)) !== false)
{
if (substr(strrchr($entry, '.'), 1) == 'css' && $last_change < @filemtime(PHPBB_ROOT_PATH . "styles/{$theme['theme_path']}/theme/{$entry}"))
{
$recache = true;
break;
}
}
closedir($dir);
}
}
if ($recache)
{
include_once(PHPBB_ROOT_PATH . 'includes/acp/acp_styles.' . PHP_EXT);
$theme['theme_data'] = acp_styles::db_theme_data($theme);
$theme['theme_mtime'] = $update_time;
// Save CSS contents
$sql_ary = array(
'theme_mtime' => $theme['theme_mtime'],
'theme_data' => $theme['theme_data']
);
$sql = 'UPDATE ' . STYLES_THEME_TABLE . ' SET ' . $db->sql_build_array('UPDATE', $sql_ary) . '
WHERE theme_id = ' . $theme['theme_id'];
$db->sql_query($sql);
$cache->destroy('sql', STYLES_THEME_TABLE);
}
}
$db->sql_return_on_error(true);
$db->sql_query('DELETE FROM ' . CONFIG_TABLE . " WHERE config_name = 'version_update_from'");
$db->sql_return_on_error(false);
$cache->purge();
}
break;
case 'update_files':
$this->page_title = 'STAGE_UPDATE_FILES';
$s_hidden_fields = '';
$params = array();
$conflicts = request_var('conflict', array('' => 0));
$modified = request_var('modified', array('' => 0));
foreach ($conflicts as $filename => $merge_option)
{
$s_hidden_fields .= '<input type="hidden" name="conflict[' . htmlspecialchars($filename) . ']" value="' . $merge_option . '" />';
$params[] = 'conflict[' . urlencode($filename) . ']=' . urlencode($merge_option);
}
foreach ($modified as $filename => $merge_option)
{
if (!$merge_option)
{
continue;
}
$s_hidden_fields .= '<input type="hidden" name="modified[' . htmlspecialchars($filename) . ']" value="' . $merge_option . '" />';
$params[] = 'modified[' . urlencode($filename) . ']=' . urlencode($merge_option);
}
$no_update = request_var('no_update', array(0 => ''));
foreach ($no_update as $index => $filename)
{
$s_hidden_fields .= '<input type="hidden" name="no_update[]" value="' . htmlspecialchars($filename) . '" />';
$params[] = 'no_update[]=' . urlencode($filename);
}
// Before the user is choosing his preferred method, let's create the content list...
$update_list = $cache->get('_update_list');
if ($update_list === false)
{
trigger_error($user->lang['NO_UPDATE_INFO'], E_USER_ERROR);
}
// Check if the conflicts data is valid
if (sizeof($conflicts))
{
$conflict_filenames = array();
foreach ($update_list['conflict'] as $files)
{
$conflict_filenames[] = $files['filename'];
}
$new_conflicts = array();
foreach ($conflicts as $filename => $diff_method)
{
if (in_array($filename, $conflict_filenames))
{
$new_conflicts[$filename] = $diff_method;
}
}
$conflicts = $new_conflicts;
}
// Build list for modifications
if (sizeof($modified))
{
$modified_filenames = array();
foreach ($update_list['modified'] as $files)
{
$modified_filenames[] = $files['filename'];
}
$new_modified = array();
foreach ($modified as $filename => $diff_method)
{
if (in_array($filename, $modified_filenames))
{
$new_modified[$filename] = $diff_method;
}
}
$modified = $new_modified;
}
// Check number of conflicting files, they need to be equal. For modified files the number can differ
if (sizeof($update_list['conflict']) != sizeof($conflicts))
{
trigger_error($user->lang['MERGE_SELECT_ERROR'], E_USER_ERROR);
}
// Before we do anything, let us diff the files and store the raw file information "somewhere"
$get_files = false;
$file_list = $cache->get('_diff_files');
if ($file_list === false || $file_list['status'] != -1)
{
$get_files = true;
}
if ($get_files)
{
if ($file_list === false)
{
$file_list = array(
'status' => 0,
);
}
$processed = 0;
foreach ($update_list as $status => $files)
{
if (!is_array($files))
{
continue;
}
foreach ($files as $file_struct)
{
// Skip this file if the user selected to not update it
if (in_array($file_struct['filename'], $no_update))
{
continue;
}
// Already handled... then skip of course...
if (isset($file_list[$file_struct['filename']]))
{
continue;
}
// Refresh if we reach 5 diffs...
if ($processed >= 5)
{
$cache->put('_diff_files', $file_list);
if (!empty($_REQUEST['download']))
{
$params[] = 'download=1';
}
$redirect_url = append_sid($this->p_master->module_url, "mode=$mode&amp;sub=update_files&amp;" . implode('&amp;', $params));
meta_refresh(3, $redirect_url);
$template->assign_vars(array(
'S_IN_PROGRESS' => true,
'L_IN_PROGRESS' => $user->lang['MERGING_FILES'],
'L_IN_PROGRESS_EXPLAIN' => $user->lang['MERGING_FILES_EXPLAIN'],
));
return;
}
$original_filename = ($file_struct['custom']) ? $file_struct['original'] : $file_struct['filename'];
switch ($status)
{
case 'modified':
$option = (isset($modified[$file_struct['filename']])) ? $modified[$file_struct['filename']] : 0;
switch ($option)
{
case MERGE_NO_MERGE_NEW:
$contents = file_get_contents($this->new_location . $original_filename);
break;
case MERGE_NO_MERGE_MOD:
$contents = file_get_contents(PHPBB_ROOT_PATH . $file_struct['filename']);
break;
default:
$diff = $this->return_diff($this->old_location . $original_filename, PHPBB_ROOT_PATH . $file_struct['filename'], $this->new_location . $original_filename);
$contents = implode("\n", $diff->merged_output());
unset($diff);
break;
}
$file_list[$file_struct['filename']] = '_file_' . md5($file_struct['filename']);
$cache->put($file_list[$file_struct['filename']], base64_encode($contents));
$file_list['status']++;
$processed++;
break;
case 'conflict':
$option = $conflicts[$file_struct['filename']];
$contents = '';
switch ($option)
{
case MERGE_NO_MERGE_NEW:
$contents = file_get_contents($this->new_location . $original_filename);
break;
case MERGE_NO_MERGE_MOD:
$contents = file_get_contents(PHPBB_ROOT_PATH . $file_struct['filename']);
break;
default:
$diff = $this->return_diff($this->old_location . $original_filename, PHPBB_ROOT_PATH . $file_struct['filename'], $this->new_location . $original_filename);
if ($option == MERGE_NEW_FILE)
{
$contents = implode("\n", $diff->merged_new_output());
}
else if ($option == MERGE_MOD_FILE)
{
$contents = implode("\n", $diff->merged_orig_output());
}
else
{
unset($diff);
break 2;
}
unset($diff);
break;
}
$file_list[$file_struct['filename']] = '_file_' . md5($file_struct['filename']);
$cache->put($file_list[$file_struct['filename']], base64_encode($contents));
$file_list['status']++;
$processed++;
break;
}
}
}
}
$file_list['status'] = -1;
$cache->put('_diff_files', $file_list);
if (!empty($_REQUEST['download']))
{
$this->include_file('includes/functions_compress.' . PHP_EXT);
$use_method = request_var('use_method', '');
$methods = array('.tar');
$available_methods = array('.tar.gz' => 'zlib', '.tar.bz2' => 'bz2', '.zip' => 'zlib');
foreach ($available_methods as $type => $module)
{
if (!@extension_loaded($module))
{
continue;
}
$methods[] = $type;
}
// Let the user decide in which format he wants to have the pack
if (!$use_method)
{
$this->page_title = 'SELECT_DOWNLOAD_FORMAT';
$radio_buttons = '';
foreach ($methods as $method)
{
$radio_buttons .= '<label><input type="radio"' . ((!$radio_buttons) ? ' id="use_method"' : '') . ' class="radio" value="' . $method . '" name="use_method" /> ' . $method . '</label>';
}
$template->assign_vars(array(
'S_DOWNLOAD_FILES' => true,
'U_ACTION' => append_sid($this->p_master->module_url, "mode=$mode&amp;sub=update_files"),
'RADIO_BUTTONS' => $radio_buttons,
'S_HIDDEN_FIELDS' => $s_hidden_fields)
);
// To ease the update process create a file location map
$update_list = $cache->get('_update_list');
$script_path = ($config['force_server_vars']) ? (($config['script_path'] == '/') ? '/' : $config['script_path'] . '/') : $user->page['root_script_path'];
foreach ($update_list as $status => $files)
{
if ($status == 'up_to_date' || $status == 'no_update' || $status == 'status')
{
continue;
}
foreach ($files as $file_struct)
{
if (in_array($file_struct['filename'], $no_update))
{
continue;
}
$template->assign_block_vars('location', array(
'SOURCE' => htmlspecialchars($file_struct['filename']),
'DESTINATION' => $script_path . htmlspecialchars($file_struct['filename']),
));
}
}
return;
}
if (!in_array($use_method, $methods))
{
$use_method = '.tar';
}
$update_mode = 'download';
}
else
{
$this->include_file('includes/functions_transfer.' . PHP_EXT);
// Choose FTP, if not available use fsock...
$method = basename(request_var('method', ''));
$submit = (isset($_POST['submit'])) ? true : false;
$test_ftp_connection = request_var('test_connection', '');
if (!$method || !class_exists($method))
{
$method = 'ftp';
$methods = transfer::methods();
if (!in_array('ftp', $methods))
{
$method = $methods[0];
}
}
$test_connection = false;
if ($test_ftp_connection || $submit)
{
$transfer = new $method(request_var('host', ''), request_var('username', ''), request_var('password', ''), request_var('root_path', ''), request_var('port', ''), request_var('timeout', ''));
$test_connection = $transfer->open_session();
// Make sure that the directory is correct by checking for the existence of common.php
if ($test_connection === true)
{
// Check for common.php file
if (!$transfer->file_exists(PHPBB_ROOT_PATH, 'common.' . PHP_EXT))
{
$test_connection = 'ERR_WRONG_PATH_TO_PHPBB';
}
}
$transfer->close_session();
// Make sure the login details are correct before continuing
if ($submit && $test_connection !== true)
{
$submit = false;
$test_ftp_connection = true;
}
}
$s_hidden_fields .= build_hidden_fields(array('method' => $method));
if (!$submit)
{
$this->page_title = 'SELECT_FTP_SETTINGS';
if (!class_exists($method))
{
trigger_error('Method does not exist.', E_USER_ERROR);
}
$requested_data = call_user_func(array($method, 'data'));
foreach ($requested_data as $data => $default)
{
$template->assign_block_vars('data', array(
'DATA' => $data,
'NAME' => $user->lang[strtoupper($method . '_' . $data)],
'EXPLAIN' => $user->lang[strtoupper($method . '_' . $data) . '_EXPLAIN'],
'DEFAULT' => (!empty($_REQUEST[$data])) ? request_var($data, '') : $default
));
}
$template->assign_vars(array(
'S_CONNECTION_SUCCESS' => ($test_ftp_connection && $test_connection === true) ? true : false,
'S_CONNECTION_FAILED' => ($test_ftp_connection && $test_connection !== true) ? true : false,
'ERROR_MSG' => ($test_ftp_connection && $test_connection !== true) ? $user->lang[$test_connection] : '',
'S_FTP_UPLOAD' => true,
'UPLOAD_METHOD' => $method,
'U_ACTION' => append_sid($this->p_master->module_url, "mode=$mode&amp;sub=update_files"),
'S_HIDDEN_FIELDS' => $s_hidden_fields)
);
return;
}
$update_mode = 'upload';
}
// Now update the installation or download the archive...
$download_filename = 'update_' . $this->update_info['version']['from'] . '_to_' . $this->update_info['version']['to'];
$archive_filename = $download_filename . '_' . time() . '_' . unique_id();
// Now init the connection
if ($update_mode == 'download')
{
if ($use_method == '.zip')
{
$compress = new compress_zip('w', PHPBB_ROOT_PATH . 'store/' . $archive_filename . $use_method);
}
else
{
$compress = new compress_tar('w', PHPBB_ROOT_PATH . 'store/' . $archive_filename . $use_method, $use_method);
}
}
else
{
$transfer = new $method(request_var('host', ''), request_var('username', ''), request_var('password', ''), request_var('root_path', ''), request_var('port', ''), request_var('timeout', ''));
$transfer->open_session();
}
// Ok, go through the update list and do the operations based on their status
foreach ($update_list as $status => $files)
{
if (!is_array($files))
{
continue;
}
foreach ($files as $file_struct)
{
// Skip this file if the user selected to not update it
if (in_array($file_struct['filename'], $no_update))
{
continue;
}
$original_filename = ($file_struct['custom']) ? $file_struct['original'] : $file_struct['filename'];
switch ($status)
{
case 'new':
case 'new_conflict':
case 'not_modified':
if ($update_mode == 'download')
{
$compress->add_custom_file($this->new_location . $original_filename, $file_struct['filename']);
}
else
{
if ($status != 'new')
{
$transfer->rename($file_struct['filename'], $file_struct['filename'] . '.bak');
}
// New directory too?
$dirname = dirname($file_struct['filename']);
if ($dirname && !file_exists(PHPBB_ROOT_PATH . $dirname))
{
$transfer->make_dir($dirname);
}
$transfer->copy_file($this->new_location . $original_filename, $file_struct['filename']);
}
break;
case 'modified':
$contents = base64_decode($cache->get($file_list[$file_struct['filename']]));
if ($update_mode == 'download')
{
$compress->add_data($contents, $file_struct['filename']);
}
else
{
// @todo add option to specify if a backup file should be created?
$transfer->rename($file_struct['filename'], $file_struct['filename'] . '.bak');
$transfer->write_file($file_struct['filename'], $contents);
}
break;
case 'conflict':
$contents = base64_decode($cache->get($file_list[$file_struct['filename']]));
if ($update_mode == 'download')
{
$compress->add_data($contents, $file_struct['filename']);
}
else
{
$transfer->rename($file_struct['filename'], $file_struct['filename'] . '.bak');
$transfer->write_file($file_struct['filename'], $contents);
}
break;
}
}
}
if ($update_mode == 'download')
{
$compress->close();
$compress->download($archive_filename, $download_filename);
@unlink(PHPBB_ROOT_PATH . 'store/' . $archive_filename . $use_method);
exit;
}
else
{
$transfer->close_session();
$template->assign_vars(array(
'S_UPLOAD_SUCCESS' => true,
'U_ACTION' => append_sid($this->p_master->module_url, "mode=$mode&amp;sub=file_check"))
);
return;
}
break;
}
}
/**
* Show file diff
*/
function show_diff(&$update_list)
{
global $template, $user;
$this->tpl_name = 'install_update_diff';
// Got the diff template itself updated? If so, we are able to directly use it
if (in_array('adm/style/install_update_diff.html', $this->update_info['files']))
{
$this->tpl_name = '../../install/update/new/adm/style/install_update_diff';
}
$this->page_title = 'VIEWING_FILE_DIFF';
$status = request_var('status', '');
$file = request_var('file', '');
$diff_mode = request_var('diff_mode', 'side_by_side');
// First of all make sure the file is within our file update list with the correct status
$found_entry = array();
foreach ($update_list[$status] as $index => $file_struct)
{
if ($file_struct['filename'] === $file)
{
$found_entry = $update_list[$status][$index];
}
}
if (empty($found_entry))
{
trigger_error($user->lang['FILE_DIFF_NOT_ALLOWED'], E_USER_ERROR);
}
// If the status is 'up_to_date' then we do not need to show a diff
if ($status == 'up_to_date')
{
trigger_error($user->lang['FILE_ALREADY_UP_TO_DATE'], E_USER_ERROR);
}
$original_file = ($found_entry['custom']) ? $found_entry['original'] : $file;
// Get the correct diff
switch ($status)
{
case 'conflict':
$option = request_var('op', 0);
switch ($option)
{
case MERGE_NO_MERGE_NEW:
case MERGE_NO_MERGE_MOD:
$diff = $this->return_diff(array(), ($option == MERGE_NO_MERGE_NEW) ? $this->new_location . $original_file : PHPBB_ROOT_PATH . $file);
$template->assign_var('S_DIFF_NEW_FILE', true);
$diff_mode = 'inline';
$this->page_title = 'VIEWING_FILE_CONTENTS';
break;
case MERGE_NEW_FILE:
case MERGE_MOD_FILE:
$diff = $this->return_diff($this->old_location . $original_file, PHPBB_ROOT_PATH . $file, $this->new_location . $original_file);
$tmp = array(
'file1' => array(),
'file2' => ($option == MERGE_NEW_FILE) ? implode("\n", $diff->merged_new_output()) : implode("\n", $diff->merged_orig_output()),
);
$diff = &new diff($tmp['file1'], $tmp['file2']);
unset($tmp);
$template->assign_var('S_DIFF_NEW_FILE', true);
$diff_mode = 'inline';
$this->page_title = 'VIEWING_FILE_CONTENTS';
break;
default:
$diff = $this->return_diff($this->old_location . $original_file, PHPBB_ROOT_PATH . $file, $this->new_location . $original_file);
$template->assign_vars(array(
'S_DIFF_CONFLICT_FILE' => true,
'NUM_CONFLICTS' => $diff->merged_output(false, false, false, true))
);
$diff = $this->return_diff(PHPBB_ROOT_PATH . $file, $diff->merged_output());
break;
}
break;
case 'modified':
$option = request_var('op', 0);
switch ($option)
{
case MERGE_NO_MERGE_NEW:
case MERGE_NO_MERGE_MOD:
$diff = $this->return_diff(array(), ($option == MERGE_NO_MERGE_NEW) ? $this->new_location . $original_file : PHPBB_ROOT_PATH . $file);
$template->assign_var('S_DIFF_NEW_FILE', true);
$diff_mode = 'inline';
$this->page_title = 'VIEWING_FILE_CONTENTS';
break;
default:
$diff = $this->return_diff($this->old_location . $original_file, PHPBB_ROOT_PATH . $original_file, $this->new_location . $file);
break;
}
break;
case 'not_modified':
case 'new_conflict':
$diff = $this->return_diff(PHPBB_ROOT_PATH . $file, $this->new_location . $original_file);
break;
case 'new':
$diff = $this->return_diff(array(), $this->new_location . $original_file);
$template->assign_var('S_DIFF_NEW_FILE', true);
$diff_mode = 'inline';
$this->page_title = 'VIEWING_FILE_CONTENTS';
break;
}
$diff_mode_options = '';
foreach (array('side_by_side', 'inline', 'unified', 'raw') as $option)
{
$diff_mode_options .= '<option value="' . $option . '"' . (($diff_mode == $option) ? ' selected="selected"' : '') . '>' . $user->lang['DIFF_' . strtoupper($option)] . '</option>';
}
// Now the correct renderer
$render_class = 'diff_renderer_' . $diff_mode;
if (!class_exists($render_class))
{
trigger_error('Chosen diff mode is not supported', E_USER_ERROR);
}
$renderer = &new $render_class();
$template->assign_vars(array(
'DIFF_CONTENT' => $renderer->get_diff_content($diff),
'DIFF_MODE' => $diff_mode,
'S_DIFF_MODE_OPTIONS' => $diff_mode_options,
'S_SHOW_DIFF' => true,
));
unset($diff, $renderer);
}
/**
* Collect all file status infos we need for the update by diffing all files
*/
function get_update_structure(&$update_list)
{
global $user;
if ($update_list === false)
{
$update_list = array(
'up_to_date' => array(),
'new' => array(),
'not_modified' => array(),
'modified' => array(),
'new_conflict' => array(),
'conflict' => array(),
'no_update' => array(),
'status' => 0,
);
}
/* if (!empty($this->update_info['custom']))
{
foreach ($this->update_info['custom'] as $original_file => $file_ary)
{
foreach ($file_ary as $index => $file)
{
$this->make_update_diff($update_list, $original_file, $file, true);
}
}
} */
// Get a list of those files which are completely new by checking with file_exists...
$num_bytes_processed = 0;
foreach ($this->update_info['files'] as $index => $file)
{
if (is_int($update_list['status']) && $index < $update_list['status'])
{
continue;
}
if ($num_bytes_processed >= 500 * 1024)
{
return;
}
if (!file_exists(PHPBB_ROOT_PATH . $file))
{
// Make sure the update files are consistent by checking if the file is in new_files...
if (!file_exists($this->new_location . $file))
{
trigger_error($user->lang['INCOMPLETE_UPDATE_FILES'], E_USER_ERROR);
}
// If the file exists within the old directory the file got removed and we will write it back
// not a biggie, but we might want to state this circumstance separately later.
// if (file_exists($this->old_location . $file))
// {
// $update_list['removed'][] = $file;
// }
/* Only include a new file as new if the underlying path exist
// The path normally do not exist if the original style or language has been removed
if (file_exists(PHPBB_ROOT_PATH . dirname($file)))
{
$this->get_custom_info($update_list['new'], $file);
$update_list['new'][] = array('filename' => $file, 'custom' => false);
}
else
{
// Do not include style-related or language-related content
if (strpos($file, 'styles/') !== 0 && strpos($file, 'language/') !== 0)
{
$update_list['no_update'][] = $file;
}
}*/
if (file_exists(PHPBB_ROOT_PATH . dirname($file)) || (strpos($file, 'styles/') !== 0 && strpos($file, 'language/') !== 0))
{
$this->get_custom_info($update_list['new'], $file);
$update_list['new'][] = array('filename' => $file, 'custom' => false);
}
// unset($this->update_info['files'][$index]);
}
else
{
// not modified?
$this->make_update_diff($update_list, $file, $file);
}
$num_bytes_processed += (file_exists($this->new_location . $file)) ? filesize($this->new_location . $file) : 100 * 1024;
$update_list['status']++;
}
$update_list['status'] = -1;
/* if (!sizeof($this->update_info['files']))
{
return $update_list;
}
// Now diff the remaining files to get information about their status (not modified/modified/up-to-date)
// not modified?
foreach ($this->update_info['files'] as $index => $file)
{
$this->make_update_diff($update_list, $file, $file);
}
// Now to the styles...
if (empty($this->update_info['custom']))
{
return $update_list;
}
foreach ($this->update_info['custom'] as $original_file => $file_ary)
{
foreach ($file_ary as $index => $file)
{
$this->make_update_diff($update_list, $original_file, $file, true);
}
}
return $update_list;*/
}
/**
* Compare files for storage in update_list
*/
function make_update_diff(&$update_list, $original_file, $file, $custom = false)
{
global $user;
$update_ary = array('filename' => $file, 'custom' => $custom);
if ($custom)
{
$update_ary['original'] = $original_file;
}
// On a successfull update the new location file exists but the old one does not exist.
// Check for this circumstance, the new file need to be up-to-date with the current file then...
if (!file_exists($this->old_location . $original_file) && file_exists($this->new_location . $original_file) && file_exists(PHPBB_ROOT_PATH . $file))
{
$tmp = array(
'file1' => file_get_contents($this->new_location . $original_file),
'file2' => file_get_contents(PHPBB_ROOT_PATH . $file),
);
// We need to diff the contents here to make sure the file is really the one we expect
$diff = &new diff($tmp['file1'], $tmp['file2'], false);
$empty = $diff->is_empty();
unset($tmp, $diff);
// if there are no differences we have an up-to-date file...
if ($empty)
{
$update_list['up_to_date'][] = $update_ary;
return;
}
// If no other status matches we have another file in the way...
$update_list['new_conflict'][] = $update_ary;
return;
}
// Old file removed?
if (file_exists($this->old_location . $original_file) && !file_exists($this->new_location . $original_file))
{
return;
}
// Check for existance, else abort immediately
if (!file_exists($this->old_location . $original_file) || !file_exists($this->new_location . $original_file))
{
trigger_error($user->lang['INCOMPLETE_UPDATE_FILES'], E_USER_ERROR);
}
$tmp = array(
'file1' => file_get_contents($this->old_location . $original_file),
'file2' => file_get_contents(PHPBB_ROOT_PATH . $file),
);
// We need to diff the contents here to make sure the file is really the one we expect
$diff = &new diff($tmp['file1'], $tmp['file2'], false);
$empty_1 = $diff->is_empty();
unset($tmp, $diff);
$tmp = array(
'file1' => file_get_contents($this->new_location . $original_file),
'file2' => file_get_contents(PHPBB_ROOT_PATH . $file),
);
// We need to diff the contents here to make sure the file is really the one we expect
$diff = &new diff($tmp['file1'], $tmp['file2'], false);
$empty_2 = $diff->is_empty();
unset($tmp, $diff);
// If the file is not modified we are finished here...
if ($empty_1)
{
// Further check if it is already up to date - it could happen that non-modified files
// slip through
if ($empty_2)
{
$update_list['up_to_date'][] = $update_ary;
return;
}
$update_list['not_modified'][] = $update_ary;
return;
}
// If the file had been modified then we need to check if it is already up to date
// if there are no differences we have an up-to-date file...
if ($empty_2)
{
$update_list['up_to_date'][] = $update_ary;
return;
}
// if the file is modified we try to make sure a merge succeed
$tmp = array(
'file1' => file_get_contents($this->old_location . $original_file),
'file2' => file_get_contents(PHPBB_ROOT_PATH . $file),
'file3' => file_get_contents($this->new_location . $original_file),
);
$diff = &new diff3($tmp['file1'], $tmp['file2'], $tmp['file3'], false);
unset($tmp);
if ($diff->merged_output(false, false, false, true))
{
$update_ary['conflicts'] = $diff->_conflicting_blocks;
// There is one special case... users having merged with a conflicting file... we need to check this
$tmp = array(
'file1' => file_get_contents(PHPBB_ROOT_PATH . $file),
'file2' => implode("\n", $diff->merged_orig_output()),
);
$diff = &new diff($tmp['file1'], $tmp['file2'], false);
$empty = $diff->is_empty();
if ($empty)
{
unset($update_ary['conflicts']);
unset($diff);
$update_list['up_to_date'][] = $update_ary;
return;
}
$update_list['conflict'][] = $update_ary;
unset($diff);
return;
}
$tmp = array(
'file1' => file_get_contents(PHPBB_ROOT_PATH . $file),
'file2' => implode("\n", $diff->merged_output()),
);
// now compare the merged output with the original file to see if the modified file is up to date
$diff = &new diff($tmp['file1'], $tmp['file2'], false);
$empty = $diff->is_empty();
if ($empty)
{
unset($diff);
$update_list['up_to_date'][] = $update_ary;
return;
}
// If no other status matches we have a modified file...
$update_list['modified'][] = $update_ary;
}
/**
* Update update_list with custom new files
*/
function get_custom_info(&$update_list, $file)
{
if (empty($this->update_info['custom']))
{
return;
}
if (isset($this->update_info['custom'][$file]))
{
foreach ($this->update_info['custom'][$file] as $_file)
{
$update_list[] = array('filename' => $_file, 'custom' => true, 'original' => $file);
}
}
}
/**
* Get remote file
*/
function get_file($mode)
{
global $user, $db;
$errstr = '';
$errno = 0;
switch ($mode)
{
case 'version_info':
$info = get_remote_file('www.phpbb.com', '/updatecheck', ((defined('PHPBB_QA')) ? '30x_qa.txt' : '30x.txt'), $errstr, $errno);
if ($info !== false)
{
$info = explode("\n", $info);
$info = trim($info[0]);
}
if ($this->test_update !== false)
{
$info = $this->test_update;
}
// If info is false the fsockopen function may not be working. Instead get the latest version from our update file (and pray it is up-to-date)
if ($info === false)
{
$update_info = array();
include(PHPBB_ROOT_PATH . 'install/update/index.php');
$info = (empty($update_info) || !is_array($update_info)) ? false : $update_info;
if ($info !== false)
{
$info = (!empty($info['version']['to'])) ? trim($info['version']['to']) : false;
}
}
break;
case 'update_info':
$update_info = array();
include(PHPBB_ROOT_PATH . 'install/update/index.php');
$info = (empty($update_info) || !is_array($update_info)) ? false : $update_info;
$errstr = ($info === false) ? $user->lang['WRONG_INFO_FILE_FORMAT'] : '';
if ($info !== false)
{
// Adjust the update info file to hold some specific style-related information
$info['custom'] = array();
/*
// Get custom installed styles...
$sql = 'SELECT template_name, template_path
FROM ' . STYLES_TEMPLATE_TABLE . "
WHERE LOWER(template_name) NOT IN ('subsilver2', 'prosilver')";
$result = $db->sql_query($sql);
$templates = array();
while ($row = $db->sql_fetchrow($result))
{
$templates[] = $row;
}
$db->sql_freeresult($result);
if (sizeof($templates))
{
foreach ($info['files'] as $filename)
{
// Template update?
if (strpos(strtolower($filename), 'styles/prosilver/template/') === 0)
{
foreach ($templates as $row)
{
$info['custom'][$filename][] = str_replace('/prosilver/', '/' . $row['template_path'] . '/', $filename);
}
}
}
}
*/
}
break;
default:
trigger_error('Mode for getting remote file not specified', E_USER_ERROR);
break;
}
if ($info === false)
{
trigger_error($errstr, E_USER_ERROR);
}
return $info;
}
/**
* Function for including files...
*/
function include_file($filename)
{
if (!empty($this->update_info['files']) && in_array($filename, $this->update_info['files']))
{
include_once($this->new_location . $filename);
}
else
{
include_once(PHPBB_ROOT_PATH . $filename);
}
}
/**
* Wrapper for returning a diff object
*/
function &return_diff()
{
$args = func_get_args();
$three_way_diff = (func_num_args() > 2) ? true : false;
$file1 = array_shift($args);
$file2 = array_shift($args);
$tmp['file1'] = (!empty($file1) && is_string($file1)) ? file_get_contents($file1) : $file1;
$tmp['file2'] = (!empty($file2) && is_string($file2)) ? file_get_contents($file2) : $file2;
if ($three_way_diff)
{
$file3 = array_shift($args);
$tmp['file3'] = (!empty($file3) && is_string($file3)) ? file_get_contents($file3) : $file3;
$diff = &new diff3($tmp['file1'], $tmp['file2'], $tmp['file3']);
}
else
{
$diff = &new diff($tmp['file1'], $tmp['file2']);
}
unset($tmp);
return $diff;
}
}
?>