Code Coverage |
||||||||||
Classes and Traits |
Functions and Methods |
Lines |
||||||||
| Total | |
0.00% |
0 / 1 |
|
0.00% |
0 / 4 |
CRAP | |
0.00% |
0 / 458 |
| get_usable_memory | |
0.00% |
0 / 1 |
0 | |
0.00% |
0 / 32 |
|||
| sanitize_data_mssql | |
0.00% |
0 / 1 |
0 | |
0.00% |
0 / 17 |
|||
| sanitize_data_oracle | |
0.00% |
0 / 1 |
0 | |
0.00% |
0 / 17 |
|||
| sanitize_data_generic | |
0.00% |
0 / 1 |
0 | |
0.00% |
0 / 17 |
|||
| fgetd | |
0.00% |
0 / 1 |
0 | |
0.00% |
0 / 23 |
|||
| fgetd_seekless | |
0.00% |
0 / 1 |
0 | |
0.00% |
0 / 30 |
|||
| acp_database | |
0.00% |
0 / 1 |
|
0.00% |
0 / 4 |
3906 | |
0.00% |
0 / 318 |
| main | |
0.00% |
0 / 1 |
2550 | |
0.00% |
0 / 267 |
|||
| get_backup_file | |
0.00% |
0 / 1 |
20 | |
0.00% |
0 / 18 |
|||
| get_file_list | |
0.00% |
0 / 1 |
30 | |
0.00% |
0 / 20 |
|||
| get_supported_extensions | |
0.00% |
0 / 1 |
12 | |
0.00% |
0 / 13 |
|||
| <?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. | |
| * | |
| */ | |
| /** | |
| * @ignore | |
| */ | |
| if (!defined('IN_PHPBB')) | |
| { | |
| exit; | |
| } | |
| class acp_database | |
| { | |
| var $db_tools; | |
| var $u_action; | |
| public $page_title; | |
| function main($id, $mode) | |
| { | |
| global $cache, $db, $user, $template, $table_prefix, $request; | |
| global $phpbb_root_path, $phpbb_container, $phpbb_log; | |
| $this->db_tools = $phpbb_container->get('dbal.tools'); | |
| $user->add_lang('acp/database'); | |
| $this->tpl_name = 'acp_database'; | |
| $this->page_title = 'ACP_DATABASE'; | |
| $action = $request->variable('action', ''); | |
| $form_key = 'acp_database'; | |
| add_form_key($form_key); | |
| $template->assign_vars(array( | |
| 'MODE' => $mode | |
| )); | |
| switch ($mode) | |
| { | |
| case 'backup': | |
| $this->page_title = 'ACP_BACKUP'; | |
| switch ($action) | |
| { | |
| case 'download': | |
| $type = $request->variable('type', ''); | |
| $table = array_intersect($this->db_tools->sql_list_tables(), $request->variable('table', array(''))); | |
| $format = $request->variable('method', ''); | |
| if (!count($table)) | |
| { | |
| trigger_error($user->lang['TABLE_SELECT_ERROR'] . adm_back_link($this->u_action), E_USER_WARNING); | |
| } | |
| if (!check_form_key($form_key)) | |
| { | |
| trigger_error($user->lang['FORM_INVALID'] . adm_back_link($this->u_action), E_USER_WARNING); | |
| } | |
| $store = true; | |
| $structure = false; | |
| $schema_data = false; | |
| if ($type == 'full' || $type == 'structure') | |
| { | |
| $structure = true; | |
| } | |
| if ($type == 'full' || $type == 'data') | |
| { | |
| $schema_data = true; | |
| } | |
| @set_time_limit(1200); | |
| @set_time_limit(0); | |
| $time = time(); | |
| $filename = 'backup_' . $time . '_' . unique_id(); | |
| /** @var phpbb\db\extractor\extractor_interface $extractor Database extractor */ | |
| $extractor = $phpbb_container->get('dbal.extractor'); | |
| $extractor->init_extractor($format, $filename, $time, false, $store); | |
| $extractor->write_start($table_prefix); | |
| foreach ($table as $table_name) | |
| { | |
| // Get the table structure | |
| if ($structure) | |
| { | |
| $extractor->write_table($table_name); | |
| } | |
| else | |
| { | |
| // We might wanna empty out all that junk :D | |
| switch ($db->get_sql_layer()) | |
| { | |
| case 'sqlite3': | |
| $extractor->flush('DELETE FROM ' . $table_name . ";\n"); | |
| break; | |
| case 'mssql_odbc': | |
| case 'mssqlnative': | |
| $extractor->flush('TRUNCATE TABLE ' . $table_name . "GO\n"); | |
| break; | |
| case 'oracle': | |
| $extractor->flush('TRUNCATE TABLE ' . $table_name . "/\n"); | |
| break; | |
| default: | |
| $extractor->flush('TRUNCATE TABLE ' . $table_name . ";\n"); | |
| break; | |
| } | |
| } | |
| // Data | |
| if ($schema_data) | |
| { | |
| $extractor->write_data($table_name); | |
| } | |
| } | |
| $extractor->write_end(); | |
| $phpbb_log->add('admin', $user->data['user_id'], $user->ip, 'LOG_DB_BACKUP'); | |
| trigger_error($user->lang['BACKUP_SUCCESS'] . adm_back_link($this->u_action)); | |
| break; | |
| default: | |
| $tables = $this->db_tools->sql_list_tables(); | |
| asort($tables); | |
| foreach ($tables as $table_name) | |
| { | |
| if (strlen($table_prefix) === 0 || stripos($table_name, $table_prefix) === 0) | |
| { | |
| $template->assign_block_vars('tables', array( | |
| 'TABLE' => $table_name | |
| )); | |
| } | |
| } | |
| unset($tables); | |
| $template->assign_vars(array( | |
| 'U_ACTION' => $this->u_action . '&action=download' | |
| )); | |
| $available_methods = array('gzip' => 'zlib', 'bzip2' => 'bz2'); | |
| foreach ($available_methods as $type => $module) | |
| { | |
| if (!@extension_loaded($module)) | |
| { | |
| continue; | |
| } | |
| $template->assign_block_vars('methods', array( | |
| 'TYPE' => $type | |
| )); | |
| } | |
| $template->assign_block_vars('methods', array( | |
| 'TYPE' => 'text' | |
| )); | |
| break; | |
| } | |
| break; | |
| case 'restore': | |
| $this->page_title = 'ACP_RESTORE'; | |
| switch ($action) | |
| { | |
| case 'submit': | |
| $delete = $request->variable('delete', ''); | |
| $file = $request->variable('file', ''); | |
| $backup_info = $this->get_backup_file($phpbb_root_path . 'store/', $file); | |
| if (empty($backup_info) || !is_readable($backup_info['file_name'])) | |
| { | |
| trigger_error($user->lang['BACKUP_INVALID'] . adm_back_link($this->u_action), E_USER_WARNING); | |
| } | |
| if ($delete) | |
| { | |
| if (confirm_box(true)) | |
| { | |
| unlink($backup_info['file_name']); | |
| $phpbb_log->add('admin', $user->data['user_id'], $user->ip, 'LOG_DB_DELETE'); | |
| trigger_error($user->lang['BACKUP_DELETE'] . adm_back_link($this->u_action)); | |
| } | |
| else | |
| { | |
| confirm_box(false, $user->lang['DELETE_SELECTED_BACKUP'], build_hidden_fields(array('delete' => $delete, 'file' => $file))); | |
| } | |
| } | |
| else if (confirm_box(true)) | |
| { | |
| switch ($backup_info['extension']) | |
| { | |
| case 'sql': | |
| $fp = fopen($backup_info['file_name'], 'rb'); | |
| $read = 'fread'; | |
| $seek = 'fseek'; | |
| $eof = 'feof'; | |
| $close = 'fclose'; | |
| $fgetd = 'fgetd'; | |
| break; | |
| case 'sql.bz2': | |
| $fp = bzopen($backup_info['file_name'], 'r'); | |
| $read = 'bzread'; | |
| $seek = ''; | |
| $eof = 'feof'; | |
| $close = 'bzclose'; | |
| $fgetd = 'fgetd_seekless'; | |
| break; | |
| case 'sql.gz': | |
| $fp = gzopen($backup_info['file_name'], 'rb'); | |
| $read = 'gzread'; | |
| $seek = 'gzseek'; | |
| $eof = 'gzeof'; | |
| $close = 'gzclose'; | |
| $fgetd = 'fgetd'; | |
| break; | |
| default: | |
| trigger_error($user->lang['BACKUP_INVALID'] . adm_back_link($this->u_action), E_USER_WARNING); | |
| return; | |
| } | |
| switch ($db->get_sql_layer()) | |
| { | |
| case 'mysql': | |
| case 'mysql4': | |
| case 'mysqli': | |
| case 'sqlite3': | |
| while (($sql = $fgetd($fp, ";\n", $read, $seek, $eof)) !== false) | |
| { | |
| $db->sql_query($sql); | |
| } | |
| break; | |
| case 'postgres': | |
| $delim = ";\n"; | |
| while (($sql = $fgetd($fp, $delim, $read, $seek, $eof)) !== false) | |
| { | |
| $query = trim($sql); | |
| if (substr($query, 0, 13) == 'CREATE DOMAIN') | |
| { | |
| list(, , $domain) = explode(' ', $query); | |
| $sql = "SELECT domain_name | |
| FROM information_schema.domains | |
| WHERE domain_name = '$domain';"; | |
| $result = $db->sql_query($sql); | |
| if (!$db->sql_fetchrow($result)) | |
| { | |
| $db->sql_query($query); | |
| } | |
| $db->sql_freeresult($result); | |
| } | |
| else | |
| { | |
| $db->sql_query($query); | |
| } | |
| if (substr($query, 0, 4) == 'COPY') | |
| { | |
| while (($sub = $fgetd($fp, "\n", $read, $seek, $eof)) !== '\.') | |
| { | |
| if ($sub === false) | |
| { | |
| trigger_error($user->lang['RESTORE_FAILURE'] . adm_back_link($this->u_action), E_USER_WARNING); | |
| } | |
| pg_put_line($db->get_db_connect_id(), $sub . "\n"); | |
| } | |
| pg_put_line($db->get_db_connect_id(), "\\.\n"); | |
| pg_end_copy($db->get_db_connect_id()); | |
| } | |
| } | |
| break; | |
| case 'oracle': | |
| while (($sql = $fgetd($fp, "/\n", $read, $seek, $eof)) !== false) | |
| { | |
| $db->sql_query($sql); | |
| } | |
| break; | |
| case 'mssql_odbc': | |
| case 'mssqlnative': | |
| while (($sql = $fgetd($fp, "GO\n", $read, $seek, $eof)) !== false) | |
| { | |
| $db->sql_query($sql); | |
| } | |
| break; | |
| } | |
| $close($fp); | |
| // Purge the cache due to updated data | |
| $cache->purge(); | |
| $phpbb_log->add('admin', $user->data['user_id'], $user->ip, 'LOG_DB_RESTORE'); | |
| trigger_error($user->lang['RESTORE_SUCCESS'] . adm_back_link($this->u_action)); | |
| break; | |
| } | |
| else | |
| { | |
| confirm_box(false, $user->lang['RESTORE_SELECTED_BACKUP'], build_hidden_fields(array('file' => $file))); | |
| } | |
| default: | |
| $backup_files = $this->get_file_list($phpbb_root_path . 'store/'); | |
| if (!empty($backup_files)) | |
| { | |
| krsort($backup_files); | |
| foreach ($backup_files as $name => $file) | |
| { | |
| $template->assign_block_vars('files', array( | |
| 'FILE' => sha1($file), | |
| 'NAME' => $user->format_date($name, 'd-m-Y H:i', true), | |
| 'SUPPORTED' => true, | |
| )); | |
| } | |
| } | |
| $template->assign_vars(array( | |
| 'U_ACTION' => $this->u_action . '&action=submit' | |
| )); | |
| break; | |
| } | |
| break; | |
| } | |
| } | |
| /** | |
| * Get backup file from file hash | |
| * | |
| * @param string $directory Relative path to directory | |
| * @param string $file_hash Hash of selected file | |
| * | |
| * @return array Backup file data or empty array if unable to find file | |
| */ | |
| protected function get_backup_file($directory, $file_hash) | |
| { | |
| $backup_data = []; | |
| $file_list = $this->get_file_list($directory); | |
| $supported_extensions = $this->get_supported_extensions(); | |
| foreach ($file_list as $file) | |
| { | |
| preg_match('#^backup_(\d{10,})_(?:[a-z\d]{16}|[a-z\d]{32})\.(sql(?:\.(?:gz|bz2))?)$#i', $file, $matches); | |
| if (sha1($file) === $file_hash && in_array($matches[2], $supported_extensions)) | |
| { | |
| $backup_data = [ | |
| 'file_name' => $directory . $file, | |
| 'extension' => $matches[2], | |
| ]; | |
| break; | |
| } | |
| } | |
| return $backup_data; | |
| } | |
| /** | |
| * Get backup file list for directory | |
| * | |
| * @param string $directory Relative path to backup directory | |
| * | |
| * @return array List of backup files in specified directory | |
| */ | |
| protected function get_file_list($directory) | |
| { | |
| $supported_extensions = $this->get_supported_extensions(); | |
| $dh = @opendir($directory); | |
| $backup_files = []; | |
| if ($dh) | |
| { | |
| while (($file = readdir($dh)) !== false) | |
| { | |
| if (preg_match('#^backup_(\d{10,})_(?:[a-z\d]{16}|[a-z\d]{32})\.(sql(?:\.(?:gz|bz2))?)$#i', $file, $matches)) | |
| { | |
| if (in_array($matches[2], $supported_extensions)) | |
| { | |
| $backup_files[(int) $matches[1]] = $file; | |
| } | |
| } | |
| } | |
| closedir($dh); | |
| } | |
| return $backup_files; | |
| } | |
| /** | |
| * Get supported extensions for backup | |
| * | |
| * @return array List of supported extensions | |
| */ | |
| protected function get_supported_extensions() | |
| { | |
| $extensions = ['sql']; | |
| $available_methods = ['sql.gz' => 'zlib', 'sql.bz2' => 'bz2']; | |
| foreach ($available_methods as $type => $module) | |
| { | |
| if (!@extension_loaded($module)) | |
| { | |
| continue; | |
| } | |
| $extensions[] = $type; | |
| } | |
| return $extensions; | |
| } | |
| } | |
| // get how much space we allow for a chunk of data, very similar to phpMyAdmin's way of doing things ;-) (hey, we only do this for MySQL anyway :P) | |
| function get_usable_memory() | |
| { | |
| $val = trim(@ini_get('memory_limit')); | |
| if (preg_match('/(\\d+)([mkg]?)/i', $val, $regs)) | |
| { | |
| $memory_limit = (int) $regs[1]; | |
| switch ($regs[2]) | |
| { | |
| case 'k': | |
| case 'K': | |
| $memory_limit *= 1024; | |
| break; | |
| case 'm': | |
| case 'M': | |
| $memory_limit *= 1048576; | |
| break; | |
| case 'g': | |
| case 'G': | |
| $memory_limit *= 1073741824; | |
| break; | |
| } | |
| // how much memory PHP requires at the start of export (it is really a little less) | |
| if ($memory_limit > 6100000) | |
| { | |
| $memory_limit -= 6100000; | |
| } | |
| // allow us to consume half of the total memory available | |
| $memory_limit /= 2; | |
| } | |
| else | |
| { | |
| // set the buffer to 1M if we have no clue how much memory PHP will give us :P | |
| $memory_limit = 1048576; | |
| } | |
| return $memory_limit; | |
| } | |
| function sanitize_data_mssql($text) | |
| { | |
| $data = preg_split('/[\n\t\r\b\f]/', $text); | |
| preg_match_all('/[\n\t\r\b\f]/', $text, $matches); | |
| $val = array(); | |
| foreach ($data as $value) | |
| { | |
| if (strlen($value)) | |
| { | |
| $val[] = "'" . $value . "'"; | |
| } | |
| if (count($matches[0])) | |
| { | |
| $val[] = 'char(' . ord(array_shift($matches[0])) . ')'; | |
| } | |
| } | |
| return implode('+', $val); | |
| } | |
| function sanitize_data_oracle($text) | |
| { | |
| // $data = preg_split('/[\0\n\t\r\b\f\'"\/\\\]/', $text); | |
| // preg_match_all('/[\0\n\t\r\b\f\'"\/\\\]/', $text, $matches); | |
| $data = preg_split('/[\0\b\f\'\/]/', $text); | |
| preg_match_all('/[\0\r\b\f\'\/]/', $text, $matches); | |
| $val = array(); | |
| foreach ($data as $value) | |
| { | |
| if (strlen($value)) | |
| { | |
| $val[] = "'" . $value . "'"; | |
| } | |
| if (count($matches[0])) | |
| { | |
| $val[] = 'chr(' . ord(array_shift($matches[0])) . ')'; | |
| } | |
| } | |
| return implode('||', $val); | |
| } | |
| function sanitize_data_generic($text) | |
| { | |
| $data = preg_split('/[\n\t\r\b\f]/', $text); | |
| preg_match_all('/[\n\t\r\b\f]/', $text, $matches); | |
| $val = array(); | |
| foreach ($data as $value) | |
| { | |
| if (strlen($value)) | |
| { | |
| $val[] = "'" . $value . "'"; | |
| } | |
| if (count($matches[0])) | |
| { | |
| $val[] = "'" . array_shift($matches[0]) . "'"; | |
| } | |
| } | |
| return implode('||', $val); | |
| } | |
| // modified from PHP.net | |
| function fgetd(&$fp, $delim, $read, $seek, $eof, $buffer = 8192) | |
| { | |
| $record = ''; | |
| $delim_len = strlen($delim); | |
| while (!$eof($fp)) | |
| { | |
| $pos = strpos($record, $delim); | |
| if ($pos === false) | |
| { | |
| $record .= $read($fp, $buffer); | |
| if ($eof($fp) && ($pos = strpos($record, $delim)) !== false) | |
| { | |
| $seek($fp, $pos + $delim_len - strlen($record), SEEK_CUR); | |
| return substr($record, 0, $pos); | |
| } | |
| } | |
| else | |
| { | |
| $seek($fp, $pos + $delim_len - strlen($record), SEEK_CUR); | |
| return substr($record, 0, $pos); | |
| } | |
| } | |
| return false; | |
| } | |
| function fgetd_seekless(&$fp, $delim, $read, $seek, $eof, $buffer = 8192) | |
| { | |
| static $array = array(); | |
| static $record = ''; | |
| if (!count($array)) | |
| { | |
| while (!$eof($fp)) | |
| { | |
| if (strpos($record, $delim) !== false) | |
| { | |
| $array = explode($delim, $record); | |
| $record = array_pop($array); | |
| break; | |
| } | |
| else | |
| { | |
| $record .= $read($fp, $buffer); | |
| } | |
| } | |
| if ($eof($fp) && strpos($record, $delim) !== false) | |
| { | |
| $array = explode($delim, $record); | |
| $record = array_pop($array); | |
| } | |
| } | |
| if (count($array)) | |
| { | |
| return array_shift($array); | |
| } | |
| return false; | |
| } |