/wp-content/plugins/duplicator/files/installer/ajax.step2.php
PHP | 498 lines | 389 code | 51 blank | 58 comment | 47 complexity | 8f38b8a9ab17c13829648b848f6781eb MD5 | raw file
Possible License(s): BSD-3-Clause
- <?php
-
- /** ******************************************************
- * CLASS::DUPDBTEXTSWAP
- * Walks every table in db that then walks every row and column replacing searches with replaces
- * large tables are split into 50k row blocks to save on memory. */
- class DupDBTextSwap {
-
- /**
- * LOG ERRORS
- */
- static public function log_errors($report) {
- if ( ! empty($report['errsql']) ) {
- DupUtil::log("====================================");
- DupUtil::log("DATA-REPLACE ERRORS (MySQL)");
- foreach( $report['errsql'] as $error ) {DupUtil::log($error);}
- DupUtil::log("");
- }
- if ( ! empty($report['errser']) ) {
- DupUtil::log("====================================");
- DupUtil::log("DATA-REPLACE ERRORS (Serialization):");
- foreach( $report['errser'] as $error ) {DupUtil::log($error);}
- DupUtil::log("");
- }
- if ( ! empty($report['errkey']) ) {
- DupUtil::log("====================================");
- DupUtil::log("DATA-REPLACE ERRORS (Key):");
- DupUtil::log('Use SQL: SELECT @row := @row + 1 as row, t.* FROM some_table t, (SELECT @row := 0) r');
- foreach( $report['errkey'] as $error ) {DupUtil::log($error);}
- DupUtil::log("");
- }
- }
-
- /**
- * LOG STATS
- */
- static public function log_stats($report) {
- if ( ! empty( $report ) && is_array( $report ) ) {
- $stats = sprintf("SEARCH1:\t'%s' \nREPLACE1:\t'%s' \n", $_POST['url_old'], $_POST['url_new']);
- $stats .= sprintf("SEARCH2:\t'%s' \nREPLACE2:\t'%s' \n", $_POST['path_old'], $_POST['path_new']);
- $stats .= sprintf("SCANNED:\tTables:%d | Rows:%d | Cells:%d \n", $report['scan_tables'], $report['scan_rows'], $report['scan_cells']);
- $stats .= sprintf("UPDATED:\tTables:%d | Rows:%d |Cells:%d \n", $report['updt_tables'], $report['updt_rows'], $report['updt_cells']);
- $stats .= sprintf("ERRORS:\t\t%d \nRUNTIME:\t%f sec", $report['err_all'], $report['time'] );
- DupUtil::log($stats);
- }
- }
-
- /**
- * LOAD
- * Begins the processing for replace logic
- * @param mysql $conn The db connection object
- * @param array $list Key value pair of 'search' and 'replace' arrays
- * @param array $tables The tables we want to look at.
- * @return array Collection of information gathered during the run.
- */
- static public function load($conn, $list=array(), $tables=array(), $cols=array()) {
- $exclude_cols = $cols;
-
- $report = array('scan_tables'=>0, 'scan_rows'=>0, 'scan_cells'=>0,
- 'updt_tables'=>0, 'updt_rows'=>0, 'updt_cells'=>0,
- 'errsql'=>array(), 'errser'=>array(), 'errkey'=>array(),
- 'errsql_sum'=>0, 'errser_sum'=>0, 'errkey_sum'=>0,
- 'time'=>'', 'err_all'=>0);
-
- $profile_start = DupUtil::get_microtime();
- if ( is_array( $tables ) && ! empty( $tables ) ) {
-
- foreach ($tables as $table) {
- $report['scan_tables']++;
- $columns = array( );
-
- // Get a list of columns in this table
- $fields = mysqli_query($conn, 'DESCRIBE ' . $table);
- while ($column = mysqli_fetch_array( $fields )) {
- $columns[ $column[ 'Field' ] ] = $column[ 'Key' ] == 'PRI' ? true : false;
- }
-
- // Count the number of rows we have in the table if large we'll split into blocks, This is a mod from Simon Wheatley
- $row_count = mysqli_query($conn, 'SELECT COUNT(*) FROM ' . $table);
- $rows_result = mysqli_fetch_array( $row_count );
- @mysqli_free_result($row_count);
- $row_count = $rows_result[ 0 ];
- if ( $row_count == 0 )
- continue;
-
- $page_size = 25000;
- $pages = ceil($row_count / $page_size);
-
- for ($page = 0; $page < $pages; $page++) {
-
- $current_row = 0;
- $start = $page * $page_size;
- $end = $start + $page_size;
- // Grab the content of the table
- $data = mysqli_query($conn, sprintf( 'SELECT * FROM %s LIMIT %d, %d', $table, $start, $end ) );
-
- if (! $data)
- $report['errsql'][] = mysqli_error($conn);
-
- //Loops every row
- while ($row = mysqli_fetch_array($data)) {
- $report['scan_rows']++;
- $current_row++;
- $upd_col = array();
- $upd_sql = array();
- $where_sql = array();
- $upd = false;
- $serial_err = 0;
-
- //Loops every cell
- foreach( $columns as $column => $primary_key ) {
- if (in_array( $column, $exclude_cols ) ) {
- continue;
- }
-
- $report['scan_cells']++;
- $edited_data = $data_to_fix = $row[$column];
- $base64coverted = false;
-
- //Only replacing string values
- if (! is_numeric($row[$column])) {
-
- //Base 64 detection
- if (base64_decode($row[$column], true)) {
- $decoded = base64_decode($row[$column], true);
- if(self::is_serialized($decoded)) {
- $edited_data = $decoded;
- $base64coverted = true;
- }
- }
-
- //Replace logic - level 1: simple check on basic serilized strings
- foreach($list as $item) {
- $edited_data = self::recursive_unserialize_replace($item['search'], $item['replace'], $edited_data);
- }
-
- //Replace logic - level 2: repair larger/complex serilized strings
- $serial_check = self::fix_serial_string($edited_data);
- if ($serial_check['fixed']) {
- $edited_data = $serial_check['data'];
- }
- elseif ($serial_check['tried'] && ! $serial_check['fixed']) {
- $serial_err++;
- }
- }
-
- //Change was made
- if ( $edited_data != $data_to_fix || $serial_err > 0) {
- $report['updt_cells']++;
- //Base 64 encode
- if ($base64coverted) {
- $edited_data = base64_encode($edited_data);
- }
- $upd_col[] = $column;
- $upd_sql[] = $column . ' = "' . mysqli_real_escape_string($conn, $edited_data) . '"';
- $upd = true;
- }
-
- if ($primary_key) {
- $where_sql[] = $column . ' = "' . mysqli_real_escape_string($conn, $data_to_fix) . '"';
- }
- }
-
- //PERFORM ROW UPDATE
- if ($upd && ! empty($where_sql)) {
-
- $sql = "UPDATE `{$table}` SET " . implode( ', ', $upd_sql ) . ' WHERE ' . implode( ' AND ', array_filter( $where_sql ) );
- $result = mysqli_query($conn, $sql) or $report['errsql'][] = mysqli_error($conn);
- if ($result) {
- if ($serial_err > 0) {
- $report['errser'][] = "SELECT " . implode( ', ', $upd_col ) ." FROM `{$table}` WHERE " . implode(' AND ', array_filter( $where_sql )) . ';';
- }
- $report['updt_rows']++;
- }
-
- } elseif ($upd) {
- $report['errkey'][] = sprintf("Row [%s] on Table [%s] requires a manual update.", $current_row, $table);
- }
- DupUtil::fcgi_flush();
- }
- @mysqli_free_result($data);
- }
-
- if ($upd) {
- $report['updt_tables']++;
- }
- }
- }
- $profile_end = DupUtil::get_microtime();
- $report['time'] = DupUtil::elapsed_time($profile_end, $profile_start);
- $report['errsql_sum'] = empty($report['errsql']) ? 0 : count($report['errsql']);
- $report['errser_sum'] = empty($report['errser']) ? 0 : count($report['errser']);
- $report['errkey_sum'] = empty($report['errkey']) ? 0 : count($report['errkey']);
- $report['err_all'] = $report['errsql_sum'] + $report['errser_sum'] + $report['errkey_sum'];
- return $report;
- }
-
- /**
- * Take a serialised array and unserialise it replacing elements and
- * unserialising any subordinate arrays and performing the replace.
- * @param string $from String we're looking to replace.
- * @param string $to What we want it to be replaced with
- * @param array $data Used to pass any subordinate arrays back to in.
- * @param bool $serialised Does the array passed via $data need serialising.
- * @return array The original array with all elements replaced as needed.
- */
- static private function recursive_unserialize_replace($from = '', $to = '', $data = '', $serialised = false) {
-
- // some unseriliased data cannot be re-serialised eg. SimpleXMLElements
- try {
-
- if (is_string($data) && ($unserialized = @unserialize( $data )) !== false) {
- $data = self::recursive_unserialize_replace($from, $to, $unserialized, true);
- }
- elseif (is_array($data)) {
- $_tmp = array();
- foreach ($data as $key => $value) {
- $_tmp[$key] = self::recursive_unserialize_replace($from, $to, $value, false);
- }
- $data = $_tmp;
- unset($_tmp);
- }
- elseif (is_object($data)) {
- $dataClass = get_class($data);
- $_tmp = new $dataClass();
- foreach ($data as $key => $value) {
- $_tmp->$key = self::recursive_unserialize_replace($from, $to, $value, false);
- }
- $data = $_tmp;
- unset($_tmp);
- }
- else {
- if (is_string($data)) {
- $data = str_replace($from, $to, $data);
- }
- }
-
- if ($serialised)
- return serialize($data);
-
- } catch(Exception $error) {
- DupUtil::log("\nRECURSIVE UNSERIALIZE ERROR: With string\n". $error, 2);
- }
- return $data;
- }
-
-
- /**
- * IS_SERIALIZED
- * Test if a string in properly serialized */
- static public function is_serialized($data) {
- $test = @unserialize(($data));
- return ($test !== false || $test === 'b:0;') ? true : false;
- }
-
- /**
- * FIX_STRING
- * Fixes the string length of a string object that has been serialized but the length is broken
- * @param string $data The string ojbect to recalculate the size on.
- * @return
- */
- static private function fix_serial_string($data) {
-
- $result = array('data'=>$data, 'fixed'=>false, 'tried'=>false);
-
- if (preg_match("/s:[0-9]+:/", $data)) {
- if (! self::is_serialized($data) ) {
- $regex = '!(?<=^|;)s:(\d+)(?=:"(.*?)";(?:}|a:|s:|b:|d:|i:|o:|N;))!s';
- $serial_string = preg_match( '/^s:[0-9]+:"(.*$)/s', trim($data), $matches);
- //Nested serial string
- if ($serial_string) {
- $inner = preg_replace_callback($regex, 'DupDBTextSwap::fix_string_callback', rtrim($matches[1], '";'));
- $serialized_fixed = 's:' . strlen($inner) . ':"' . $inner . '";' ;
- } else {
- $serialized_fixed = preg_replace_callback($regex, 'DupDBTextSwap::fix_string_callback', $data);
- }
-
- if (self::is_serialized($serialized_fixed) ) {
- $result['data'] = $serialized_fixed;
- $result['fixed'] = true;
- }
- $result['tried'] = true;
- }
- }
- return $result;
- }
-
- static private function fix_string_callback($matches) {
- return 's:' . strlen(($matches[2]));
- }
-
- }
-
- //====================================================================================================
- //DATABASE UPDATES
- //====================================================================================================
-
- $ajax2_start = DupUtil::get_microtime();
-
- //MYSQL CONNECTION
- $dbh = @mysqli_connect($_POST['dbhost'], $_POST['dbuser'], $_POST['dbpass'], $_POST['dbname']);
- $charset_server = @mysqli_character_set_name($dbh);
- @mysqli_query($dbh, "SET wait_timeout = {$GLOBALS['DB_MAX_TIME']}");
- DupUtil::mysql_set_charset($dbh, $_POST['dbcharset'], $_POST['dbcollate']);
-
- //POST PARAMS
- $_POST['blogname'] = mysqli_real_escape_string($dbh, $_POST['blogname']);
- $_POST['postguid'] = isset($_POST['postguid']) && $_POST['postguid'] == 1 ? 1 : 0;
- $_POST['path_old'] = isset($_POST['path_old']) ? trim($_POST['path_old']) : null;
- $_POST['path_new'] = isset($_POST['path_new']) ? trim($_POST['path_new']) : null;
- $_POST['siteurl'] = isset($_POST['siteurl']) ? rtrim(trim($_POST['siteurl']), '/') : null;
- $_POST['tables'] = isset($_POST['tables']) && is_array($_POST['tables']) ? array_map('stripcslashes', $_POST['tables']) : array();
- $_POST['url_old'] = isset($_POST['url_old']) ? trim($_POST['url_old']) : null;
- $_POST['url_new'] = isset($_POST['url_new']) ? rtrim(trim($_POST['url_new']), '/') : null;
-
- //LOGGING
- $POST_LOG = $_POST;
- unset($POST_LOG['tables']);
- unset($POST_LOG['plugins']);
- unset($POST_LOG['dbpass']);
- ksort($POST_LOG);
-
- //GLOBAL DB-REPLACE
- DupUtil::log("\n\n\n{$GLOBALS['SEPERATOR1']}");
- DupUtil::log('DUPLICATOR INSTALL-LOG');
- DupUtil::log('STEP2 START @ ' . @date('h:i:s') );
- DupUtil::log('NOTICE: NOTICE: Do not post to public sites or forums');
- DupUtil::log("{$GLOBALS['SEPERATOR1']}");
- DupUtil::log("CHARSET SERVER:\t{$charset_server}");
- DupUtil::log("CHARSET CLIENT:\t" . @mysqli_character_set_name($dbh));
- DupUtil::log("--------------------------------------");
- DupUtil::log("POST DATA");
- DupUtil::log("--------------------------------------");
- DupUtil::log(print_r($POST_LOG, true));
-
- DupUtil::log("--------------------------------------");
- DupUtil::log("SCANNED TABLES");
- DupUtil::log("--------------------------------------");
- $msg = (isset($_POST['tables']) && count($_POST['tables'] > 0)) ? print_r($_POST['tables'], true) : 'No tables selected to update';
- DupUtil::log($msg);
-
- DupUtil::log("--------------------------------------");
- DupUtil::log("KEEP PLUGINS ACTIVE");
- DupUtil::log("--------------------------------------");
- $msg = (isset($_POST['plugins']) && count($_POST['plugins'] > 0)) ? print_r($_POST['plugins'], true) : 'No plugins selected for activation';
- DupUtil::log($msg);
-
-
- //UPDATE SETTINGS
- $serial_plugin_list = (isset($_POST['plugins']) && count($_POST['plugins'] > 0)) ? @serialize($_POST['plugins']) : '';
- mysqli_query($dbh, "UPDATE `{$GLOBALS['FW_TABLEPREFIX']}options` SET option_value = '{$_POST['blogname']}' WHERE option_name = 'blogname' ");
- mysqli_query($dbh, "UPDATE `{$GLOBALS['FW_TABLEPREFIX']}options` SET option_value = '{$_POST['url_new']}' WHERE option_name = 'home' ");
- mysqli_query($dbh, "UPDATE `{$GLOBALS['FW_TABLEPREFIX']}options` SET option_value = '{$_POST['siteurl']}' WHERE option_name = 'siteurl' ");
- mysqli_query($dbh, "UPDATE `{$GLOBALS['FW_TABLEPREFIX']}options` SET option_value = '{$serial_plugin_list}' WHERE option_name = 'active_plugins' ");
-
- DupUtil::log("--------------------------------------");
- DupUtil::log("GLOBAL DB-REPLACE");
- DupUtil::log("--------------------------------------");
-
- array_push($GLOBALS['REPLACE_LIST'],
- array('search' =>$_POST['url_old'], 'replace'=>$_POST['url_new']),
- array('search' =>$_POST['path_old'], 'replace'=>$_POST['path_new']),
- array('search' =>rtrim(DupUtil::unset_safe_path($_POST['path_old']), '\\'), 'replace'=>rtrim($_POST['path_new'], '/'))
- );
-
- @mysqli_autocommit($dbh, false);
- $report = DupDBTextSwap::load( $dbh, $GLOBALS['REPLACE_LIST'], $_POST['tables'], $GLOBALS['TABLES_SKIP_COLS']);
- @mysqli_commit($dbh);
- @mysqli_autocommit($dbh, true);
-
-
- //BUILD JSON RESPONSE
- $JSON = array();
- $JSON['step1'] = json_decode(urldecode($_POST['json']));
- $JSON['step2'] = $report;
- $JSON['step2']['warn_all'] = 0;
- $JSON['step2']['warnlist'] = array();
-
- DupDBTextSwap::log_stats($report);
- DupDBTextSwap::log_errors($report);
-
- //Reset the postguid data
- if ($_POST['postguid']) {
- mysqli_query($dbh, "UPDATE `{$GLOBALS['FW_TABLEPREFIX']}posts` SET guid = REPLACE(guid, '{$_POST['url_new']}', '{$_POST['url_old']}')");
- $update_guid = @mysqli_affected_rows($dbh) or 0;
- DupUtil::log("Reverted '{$update_guid}' post guid columns back to '{$_POST['url_old']}'");
- }
-
-
- //====================================================================================================
- //FINAL CLEANUP
- //====================================================================================================
- DupUtil::log("\n{$GLOBALS['SEPERATOR1']}");
- DupUtil::log('START FINAL CLEANUP: ' . @date('h:i:s') );
- DupUtil::log("{$GLOBALS['SEPERATOR1']}");
-
- $patterns = array("/'WP_HOME',\s*'.*?'/",
- "/'WP_SITEURL',\s*'.*?'/");
-
- $replace = array("'WP_HOME', " . '\''.$_POST['url_new'].'\'',
- "'WP_SITEURL', " . '\''.$_POST['url_new'].'\'');
-
- $config_file = @file_get_contents('wp-config.php', true);
- $config_file = preg_replace($patterns, $replace, $config_file);
- file_put_contents('wp-config.php', $config_file);
-
-
- //Create Snapshots directory
- if(!file_exists(DUPLICATOR_SSDIR_NAME)) {
- mkdir(DUPLICATOR_SSDIR_NAME, 0755);
- }
- $fp = fopen(DUPLICATOR_SSDIR_NAME . '/index.php', 'w');
- fclose($fp);
-
-
- //WEB CONFIG FILE
- $currdata = parse_url($_POST['url_old']);
- $newdata = parse_url($_POST['url_new']);
- $currpath = DupUtil::add_slash(isset($currdata['path']) ? $currdata['path'] : "");
- $newpath = DupUtil::add_slash(isset($newdata['path']) ? $newdata['path'] : "");
-
- if ($currpath != $newpath) {
- DupUtil::log("HTACCESS CHANGES:");
- @copy('.htaccess', '.htaccess.orig');
- @copy('web.config', 'web.config.orig');
- @unlink('.htaccess');
- @unlink('web.config');
- DupUtil::log("created backup of original .htaccess to htaccess.orig and web.config to web.config.orig");
-
- $tmp_htaccess = <<<HTACCESS
- # BEGIN WordPress
- <IfModule mod_rewrite.c>
- RewriteEngine On
- RewriteBase {$newpath}
- RewriteRule ^index\.php$ - [L]
- RewriteCond %{REQUEST_FILENAME} !-f
- RewriteCond %{REQUEST_FILENAME} !-d
- RewriteRule . {$newpath}index.php [L]
- </IfModule>
- # END WordPress
- HTACCESS;
-
- file_put_contents('.htaccess', $tmp_htaccess);
- DupUtil::log("created basic .htaccess file. If using IIS web.config this process will need to be done manually.");
- DupUtil::log("updated .htaccess file.");
- } else {
- DupUtil::log("web configuration file was not renamed because the paths did not change.");
- }
-
-
- //===============================
- //WARNING TESTS
- //===============================
- DupUtil::log("\n--------------------------------------");
- DupUtil::log("WARNINGS");
- DupUtil::log("--------------------------------------");
- $config_vars = array('WP_CONTENT_DIR', 'WP_CONTENT_URL', 'WPCACHEHOME', 'COOKIE_DOMAIN', 'WP_SITEURL', 'WP_HOME');
- $config_found = DupUtil::string_has_value($config_vars, $config_file);
-
- //Files
- if ($config_found) {
- $msg = 'WP-CONFIG WARNING: The wp-config.php has one or more of these values "' . implode(", ", $config_vars) . '" which may cause issues please validate these values by opening the file.';
- $JSON['step2']['warnlist'][] = $msg;
- DupUtil::log($msg);
- }
-
- //Database
- $result = @mysqli_query($dbh, "SELECT option_value FROM `{$GLOBALS['FW_TABLEPREFIX']}options` WHERE option_name IN ('upload_url_path','upload_path')");
- if ($result) {
- while ($row = mysqli_fetch_row($result)) {
- if (strlen($row[0])) {
- $msg = "MEDIA SETTINGS WARNING: The table '{$GLOBALS['FW_TABLEPREFIX']}options' has at least one the following values ['upload_url_path','upload_path'] set please validate settings. These settings can be changed in the wp-admin by going to Settings->Media area see 'Uploading Files'";
- $JSON['step2']['warnlist'][] = $msg;
- DupUtil::log($msg);
- break;
- }
- }
- }
-
- if (empty($JSON['step2']['warnlist'])) {
- DupUtil::log("No Warnings Found\n");
- }
-
- $JSON['step2']['warn_all'] = empty($JSON['step2']['warnlist']) ? 0 : count($JSON['step2']['warnlist']);
-
- mysqli_close($dbh);
- @unlink('database.sql');
-
- $ajax2_end = DupUtil::get_microtime();
- $ajax2_sum = DupUtil::elapsed_time($ajax2_end, $ajax2_start);
- DupUtil::log("{$GLOBALS['SEPERATOR1']}");
- DupUtil::log('STEP 2 COMPLETE @ ' . @date('h:i:s') . " - TOTAL RUNTIME: {$ajax2_sum}" );
- DupUtil::log("{$GLOBALS['SEPERATOR1']}");
-
- $JSON['step2']['pass'] = 1;
- die(json_encode($JSON));
-
- ?>