Edit File by line
/home/barbar84/www/wp-conte.../plugins/updraftp.../includes
File: class-backup-history.php
<?php
[0] Fix | Delete
[1] Fix | Delete
if (!defined('UPDRAFTPLUS_DIR')) die('No access.');
[2] Fix | Delete
[3] Fix | Delete
/**
[4] Fix | Delete
* A class to deal with management of backup history.
[5] Fix | Delete
* N.B. All database access should come through here. However, this class is not the only place with knowledge of the data structure.
[6] Fix | Delete
*/
[7] Fix | Delete
class UpdraftPlus_Backup_History {
[8] Fix | Delete
[9] Fix | Delete
/**
[10] Fix | Delete
* Get the backup history for an indicated timestamp, or the complete set of all backup histories
[11] Fix | Delete
*
[12] Fix | Delete
* @param Integer|Boolean $timestamp - Indicate a particular timestamp to get a particular backup job, or false to get a list of them all (sorted by most recent first).
[13] Fix | Delete
*
[14] Fix | Delete
* @return Array - either the particular backup indicated, or the full list.
[15] Fix | Delete
*/
[16] Fix | Delete
public static function get_history($timestamp = false) {
[17] Fix | Delete
[18] Fix | Delete
$backup_history = UpdraftPlus_Options::get_updraft_option('updraft_backup_history');
[19] Fix | Delete
// N.B. Doing a direct wpdb->get_var() here actually *introduces* a race condition
[20] Fix | Delete
[21] Fix | Delete
if (!is_array($backup_history)) $backup_history = array();
[22] Fix | Delete
[23] Fix | Delete
$backup_history = self::build_incremental_sets($backup_history);
[24] Fix | Delete
[25] Fix | Delete
if ($timestamp) return isset($backup_history[$timestamp]) ? $backup_history[$timestamp] : array();
[26] Fix | Delete
[27] Fix | Delete
// The most recent backup will be first. Then we can array_pop().
[28] Fix | Delete
krsort($backup_history);
[29] Fix | Delete
[30] Fix | Delete
return $backup_history;
[31] Fix | Delete
[32] Fix | Delete
}
[33] Fix | Delete
[34] Fix | Delete
/**
[35] Fix | Delete
* Add jobdata to all entries in an array of history items which do not already have it (key: 'jobdata'). If none is found, it will still be set, but empty.
[36] Fix | Delete
*
[37] Fix | Delete
* @param Array $backup_history - the list of history items
[38] Fix | Delete
*
[39] Fix | Delete
* @return Array
[40] Fix | Delete
*/
[41] Fix | Delete
public static function add_jobdata($backup_history) {
[42] Fix | Delete
[43] Fix | Delete
global $wpdb;
[44] Fix | Delete
$table = is_multisite() ? $wpdb->sitemeta : $wpdb->options;
[45] Fix | Delete
$key_column = is_multisite() ? 'meta_key' : 'option_name';
[46] Fix | Delete
$value_column = is_multisite() ? 'meta_value' : 'option_value';
[47] Fix | Delete
[48] Fix | Delete
$any_more = true;
[49] Fix | Delete
[50] Fix | Delete
while ($any_more) {
[51] Fix | Delete
[52] Fix | Delete
$any_more = false;
[53] Fix | Delete
$columns = array();
[54] Fix | Delete
$nonces_map = array();
[55] Fix | Delete
[56] Fix | Delete
foreach ($backup_history as $timestamp => $backup) {
[57] Fix | Delete
if (isset($backup['jobdata'])) continue;
[58] Fix | Delete
$nonce = $backup['nonce'];
[59] Fix | Delete
$nonces_map[$nonce] = $timestamp;
[60] Fix | Delete
$columns[] = $nonce;
[61] Fix | Delete
// Approx. 2.5MB of data would be expected if they all had 5KB each (though in reality we expect very few of them to have any)
[62] Fix | Delete
if (count($columns) >= 500) {
[63] Fix | Delete
$any_more = true;
[64] Fix | Delete
break;
[65] Fix | Delete
}
[66] Fix | Delete
}
[67] Fix | Delete
[68] Fix | Delete
if (empty($columns)) break;
[69] Fix | Delete
[70] Fix | Delete
$columns_sql = '';
[71] Fix | Delete
foreach ($columns as $nonce) {
[72] Fix | Delete
if ($columns_sql) $columns_sql .= ',';
[73] Fix | Delete
$columns_sql .= "'updraft_jobdata_".esc_sql($nonce)."'";
[74] Fix | Delete
}
[75] Fix | Delete
[76] Fix | Delete
$sql = 'SELECT '.$key_column.', '.$value_column.' FROM '.$table.' WHERE '.$key_column.' IN ('.$columns_sql.')';
[77] Fix | Delete
$all_jobdata = $wpdb->get_results($sql);
[78] Fix | Delete
[79] Fix | Delete
foreach ($all_jobdata as $values) {
[80] Fix | Delete
// The 16 here is the length of 'updraft_jobdata_'
[81] Fix | Delete
$nonce = substr($values->$key_column, 16);
[82] Fix | Delete
if (empty($nonces_map[$nonce]) || empty($values->$value_column)) continue;
[83] Fix | Delete
$jobdata = maybe_unserialize($values->$value_column);
[84] Fix | Delete
$backup_history[$nonces_map[$nonce]]['jobdata'] = empty($jobdata) ? array() : $jobdata;
[85] Fix | Delete
}
[86] Fix | Delete
foreach ($columns as $nonce) {
[87] Fix | Delete
if (!empty($nonces_map[$nonce]) && !isset($backup_history[$nonces_map[$nonce]]['jobdata'])) $backup_history[$nonces_map[$nonce]]['jobdata'] = array();
[88] Fix | Delete
}
[89] Fix | Delete
}
[90] Fix | Delete
[91] Fix | Delete
return $backup_history;
[92] Fix | Delete
}
[93] Fix | Delete
[94] Fix | Delete
/**
[95] Fix | Delete
* Get the backup history for an indicated nonce
[96] Fix | Delete
*
[97] Fix | Delete
* @param String $nonce - Backup nonce to get a particular backup job
[98] Fix | Delete
*
[99] Fix | Delete
* @return Array|Boolean - either the particular backup indicated, or false
[100] Fix | Delete
*/
[101] Fix | Delete
public static function get_backup_set_by_nonce($nonce) {
[102] Fix | Delete
if (empty($nonce)) return false;
[103] Fix | Delete
$backup_history = self::get_history();
[104] Fix | Delete
foreach ($backup_history as $timestamp => $backup_info) {
[105] Fix | Delete
if ($nonce == $backup_info['nonce']) {
[106] Fix | Delete
$backup_info['timestamp'] = $timestamp;
[107] Fix | Delete
return $backup_info;
[108] Fix | Delete
}
[109] Fix | Delete
}
[110] Fix | Delete
return false;
[111] Fix | Delete
}
[112] Fix | Delete
[113] Fix | Delete
/**
[114] Fix | Delete
* Get the HTML for the table of existing backups
[115] Fix | Delete
*
[116] Fix | Delete
* @param Array|Boolean $backup_history - a list of backups to use, or false to get the current list from the database
[117] Fix | Delete
* @param Boolean $backup_count - the amount of backups currently displayed in the existing backups table
[118] Fix | Delete
*
[119] Fix | Delete
* @uses UpdraftPlus_Admin::include_template()
[120] Fix | Delete
*
[121] Fix | Delete
* @return String - HTML for the table
[122] Fix | Delete
*/
[123] Fix | Delete
public static function existing_backup_table($backup_history = false, $backup_count = 0) {
[124] Fix | Delete
[125] Fix | Delete
global $updraftplus, $updraftplus_admin;
[126] Fix | Delete
[127] Fix | Delete
if (false === $backup_history) $backup_history = self::get_history();
[128] Fix | Delete
[129] Fix | Delete
if (!is_array($backup_history) || empty($backup_history)) return '<div class="postbox"><p class="updraft-no-backups-msg">'.__('You have not yet made any backups.', 'updraftplus').'</p> <p class="updraft-no-backups-msg">'.__('If you have an existing backup that you wish to upload and restore from, then please use the "Upload backup files" link above.', 'updraftplus').' '.__('Or, if they are in remote storage, you can connect that remote storage (in the "Settings" tab), save your settings, and use the "Rescan remote storage" link.', 'updraftplus').'</p></div>';
[130] Fix | Delete
[131] Fix | Delete
if (empty($backup_count)) {
[132] Fix | Delete
$backup_count = defined('UPDRAFTPLUS_EXISTING_BACKUPS_LIMIT') ? UPDRAFTPLUS_EXISTING_BACKUPS_LIMIT : 100;
[133] Fix | Delete
}
[134] Fix | Delete
[135] Fix | Delete
// Reverse date sort - i.e. most recent first
[136] Fix | Delete
krsort($backup_history);
[137] Fix | Delete
[138] Fix | Delete
$pass_values = array(
[139] Fix | Delete
'backup_history' => self::add_jobdata($backup_history),
[140] Fix | Delete
'updraft_dir' => $updraftplus->backups_dir_location(),
[141] Fix | Delete
'backupable_entities' => $updraftplus->get_backupable_file_entities(true, true),
[142] Fix | Delete
'backup_count' => $backup_count,
[143] Fix | Delete
'show_paging_actions' => false,
[144] Fix | Delete
);
[145] Fix | Delete
[146] Fix | Delete
return $updraftplus_admin->include_template('wp-admin/settings/existing-backups-table.php', true, $pass_values);
[147] Fix | Delete
[148] Fix | Delete
}
[149] Fix | Delete
[150] Fix | Delete
/**
[151] Fix | Delete
* This function will scan the backup history and split the files up in to incremental sets, foreign backup sets will only have one incremental set.
[152] Fix | Delete
*
[153] Fix | Delete
* @param Array $backup_history - the saved backup history
[154] Fix | Delete
*
[155] Fix | Delete
* @return Array - returns the backup history but also includes the incremental sets
[156] Fix | Delete
*/
[157] Fix | Delete
public static function build_incremental_sets($backup_history) {
[158] Fix | Delete
[159] Fix | Delete
global $updraftplus;
[160] Fix | Delete
[161] Fix | Delete
$backupable_entities = array_keys($updraftplus->get_backupable_file_entities(true, false));
[162] Fix | Delete
[163] Fix | Delete
$accept = apply_filters('updraftplus_accept_archivename', array());
[164] Fix | Delete
[165] Fix | Delete
foreach ($backup_history as $btime => $bdata) {
[166] Fix | Delete
[167] Fix | Delete
$incremental_sets = array();
[168] Fix | Delete
[169] Fix | Delete
foreach ($backupable_entities as $entity) {
[170] Fix | Delete
[171] Fix | Delete
if (empty($bdata[$entity]) || !is_array($bdata[$entity])) continue;
[172] Fix | Delete
[173] Fix | Delete
foreach ($bdata[$entity] as $key => $filename) {
[174] Fix | Delete
[175] Fix | Delete
if (preg_match('/^backup_([\-0-9]{15})_.*_([0-9a-f]{12})-[\-a-z]+([0-9]+)?+(\.(zip|gz|gz\.crypt))?$/i', $filename, $matches)) {
[176] Fix | Delete
[177] Fix | Delete
$timestamp = strtotime($matches[1]);
[178] Fix | Delete
[179] Fix | Delete
if (!isset($incremental_sets[$timestamp])) $incremental_sets[$timestamp] = array();
[180] Fix | Delete
[181] Fix | Delete
if (!isset($incremental_sets[$timestamp][$entity])) $incremental_sets[$timestamp][$entity] = array();
[182] Fix | Delete
[183] Fix | Delete
$incremental_sets[$timestamp][$entity][$key] = $filename;
[184] Fix | Delete
} else {
[185] Fix | Delete
$accepted = false;
[186] Fix | Delete
[187] Fix | Delete
foreach ($accept as $fkey => $acc) {
[188] Fix | Delete
if (preg_match('/'.$acc['pattern'].'/i', $filename)) $accepted = $fkey;
[189] Fix | Delete
}
[190] Fix | Delete
[191] Fix | Delete
if (!empty($accepted) && (false != ($btime = apply_filters('updraftplus_foreign_gettime', false, $accepted, $filename))) && $btime > 0) {
[192] Fix | Delete
[193] Fix | Delete
$timestamp = $btime;
[194] Fix | Delete
[195] Fix | Delete
if (!isset($incremental_sets[$timestamp])) $incremental_sets[$timestamp] = array();
[196] Fix | Delete
[197] Fix | Delete
if (!isset($incremental_sets[$timestamp][$entity])) $incremental_sets[$timestamp][$entity] = array();
[198] Fix | Delete
[199] Fix | Delete
$incremental_sets[$timestamp][$entity][] = $filename;
[200] Fix | Delete
}
[201] Fix | Delete
}
[202] Fix | Delete
}
[203] Fix | Delete
}
[204] Fix | Delete
ksort($incremental_sets);
[205] Fix | Delete
$backup_history[$btime]["incremental_sets"] = $incremental_sets;
[206] Fix | Delete
}
[207] Fix | Delete
[208] Fix | Delete
return $backup_history;
[209] Fix | Delete
}
[210] Fix | Delete
[211] Fix | Delete
/**
[212] Fix | Delete
* Save the backup history. An abstraction function to make future changes easier.
[213] Fix | Delete
*
[214] Fix | Delete
* @param Array $backup_history - the backup history
[215] Fix | Delete
* @param Boolean $use_cache - whether or not to use the WP options cache
[216] Fix | Delete
*/
[217] Fix | Delete
public static function save_history($backup_history, $use_cache = true) {
[218] Fix | Delete
[219] Fix | Delete
global $updraftplus;
[220] Fix | Delete
[221] Fix | Delete
// This data is constructed at run-time from the other keys; we do not wish to save redundant data
[222] Fix | Delete
foreach ($backup_history as $btime => $bdata) {
[223] Fix | Delete
unset($backup_history[$btime]['incremental_sets']);
[224] Fix | Delete
}
[225] Fix | Delete
[226] Fix | Delete
// Explicitly set autoload to 'no', as the backup history can get quite big.
[227] Fix | Delete
$changed = UpdraftPlus_Options::update_updraft_option('updraft_backup_history', $backup_history, $use_cache, 'no');
[228] Fix | Delete
[229] Fix | Delete
if (!$changed) {
[230] Fix | Delete
[231] Fix | Delete
$max_packet_size = $updraftplus->max_packet_size(false, false);
[232] Fix | Delete
$serialization_size = strlen(addslashes(serialize($backup_history)));
[233] Fix | Delete
[234] Fix | Delete
// Take off the *approximate* over-head of UPDATE wp_options SET option_value='' WHERE option_name='updraft_backup_history'; (no need to be exact)
[235] Fix | Delete
if ($max_packet_size < ($serialization_size + 100)) {
[236] Fix | Delete
[237] Fix | Delete
$max_packet_size = $updraftplus->max_packet_size();
[238] Fix | Delete
[239] Fix | Delete
$changed = UpdraftPlus_Options::update_updraft_option('updraft_backup_history', $backup_history, $use_cache, 'no');
[240] Fix | Delete
[241] Fix | Delete
if (!$changed) {
[242] Fix | Delete
[243] Fix | Delete
$updraftplus->log('The attempt to write the backup history to the WP database returned a negative code and the max packet size looked small. However, WP does not distinguish between a failure and no change from a previous update, so, this code is not conclusive and if no other symptoms are observed then there is no reason to infer any problem. Info: The updated database packet size is '.$max_packet_size.'; the serialization size is '.$serialization_size);
[244] Fix | Delete
[245] Fix | Delete
}
[246] Fix | Delete
[247] Fix | Delete
}
[248] Fix | Delete
[249] Fix | Delete
}
[250] Fix | Delete
}
[251] Fix | Delete
[252] Fix | Delete
/**
[253] Fix | Delete
* Used by self::always_get_from_db()
[254] Fix | Delete
*
[255] Fix | Delete
* @return Mixed - the database option
[256] Fix | Delete
*/
[257] Fix | Delete
public static function filter_updraft_backup_history() {
[258] Fix | Delete
global $wpdb;
[259] Fix | Delete
$row = $wpdb->get_row($wpdb->prepare("SELECT option_value FROM $wpdb->options WHERE option_name = %s LIMIT 1", 'updraft_backup_history'));
[260] Fix | Delete
if (is_object($row)) return maybe_unserialize($row->option_value);
[261] Fix | Delete
return false;
[262] Fix | Delete
}
[263] Fix | Delete
[264] Fix | Delete
/**
[265] Fix | Delete
* Make sure we get the value afresh from the db, instead of using the auto-loaded/cached value (which can be out of date, especially since backups are, by their nature, long-running)
[266] Fix | Delete
*/
[267] Fix | Delete
public static function always_get_from_db() {
[268] Fix | Delete
add_filter('pre_option_updraft_backup_history', array('UpdraftPlus_Backup_History', 'filter_updraft_backup_history'));
[269] Fix | Delete
}
[270] Fix | Delete
[271] Fix | Delete
/**
[272] Fix | Delete
* This function examines inside the updraft directory to see if any new archives have been uploaded. If so, it adds them to the backup set. (Non-present items are also removed, only if the service is 'none').
[273] Fix | Delete
*
[274] Fix | Delete
* N.B. The logic is a bit more subtle than it needs to be, because of backups being keyed by backup time, instead of backup nonce, and the subsequent introduction of the possibility of incremental backup sets taken at different times. This could be cleaned up to reduce the amount of code and make it simpler in places.
[275] Fix | Delete
*
[276] Fix | Delete
* @param Boolean $remote_scan - scan not only local, but also remote storage
[277] Fix | Delete
* @param Array|String $only_add_this_file - if set to an array (with keys 'file' and (optionally) 'label'), then a file will only be taken notice of if the filename matches the 'file' key (and the label will be associated with the backup set)
[278] Fix | Delete
* @param Boolean $debug - include debugging messages. These will be keyed with keys beginning 'debug-' so that they can be distinguished.
[279] Fix | Delete
*
[280] Fix | Delete
* @return Array - an array of messages which the caller may wish to display to the user. N.B. Messages are not necessarily just strings.
[281] Fix | Delete
*/
[282] Fix | Delete
public static function rebuild($remote_scan = false, $only_add_this_file = false, $debug = false) {
[283] Fix | Delete
[284] Fix | Delete
global $updraftplus;
[285] Fix | Delete
[286] Fix | Delete
$messages = array();
[287] Fix | Delete
$gmt_offset = get_option('gmt_offset');
[288] Fix | Delete
[289] Fix | Delete
// Array of nonces keyed by filename
[290] Fix | Delete
$backup_nonces_by_filename = array();
[291] Fix | Delete
// Array of backup times keyed by nonce
[292] Fix | Delete
$backup_times_by_nonce = array();
[293] Fix | Delete
[294] Fix | Delete
$changes = false;
[295] Fix | Delete
[296] Fix | Delete
$backupable_entities = $updraftplus->get_backupable_file_entities(true, false);
[297] Fix | Delete
[298] Fix | Delete
$backup_history = self::get_history();
[299] Fix | Delete
[300] Fix | Delete
$updraft_dir = $updraftplus->backups_dir_location();
[301] Fix | Delete
if (!is_dir($updraft_dir)) return array("Internal directory path does not indicate a directory ($updraft_dir)");
[302] Fix | Delete
[303] Fix | Delete
$accept = apply_filters('updraftplus_accept_archivename', array());
[304] Fix | Delete
[305] Fix | Delete
// First, process the database backup history to get a record of what is already known there. This means populating the arrays $backup_nonces_by_filename and $backup_times_by_nonce
[306] Fix | Delete
foreach ($backup_history as $btime => $bdata) {
[307] Fix | Delete
$found_file = false;
[308] Fix | Delete
foreach ($bdata as $key => $values) {
[309] Fix | Delete
// make sure we also handle multiple databases, which has a different array structure compared to other entities (e.g. plugins, themes, etc.)
[310] Fix | Delete
// we don't do strict comparison using identical operator (===) here because we want to catch boolean false or a non-boolean value which evaluates to false, hence we use equal operator (==)
[311] Fix | Delete
if (false == preg_match('/^db[0-9]*$/i', $key) && !isset($backupable_entities[$key])) continue;
[312] Fix | Delete
// Record which set this file is found in
[313] Fix | Delete
if (!is_array($values)) $values = array($values);
[314] Fix | Delete
foreach ($values as $filename) {
[315] Fix | Delete
if (!is_string($filename)) continue;
[316] Fix | Delete
if (preg_match('/^backup_([\-0-9]{15})_.*_([0-9a-f]{12})-[\-a-z]+([0-9]+)?+(\.(zip|gz|gz\.crypt))?$/i', $filename, $matches)) {
[317] Fix | Delete
$nonce = $matches[2];
[318] Fix | Delete
if (isset($bdata['service']) && ('none' === $bdata['service'] || (is_array($bdata['service']) && (array('none') === $bdata['service'] || (1 == count($bdata['service']) && isset($bdata['service'][0]) && empty($bdata['service'][0]))))) && !is_file($updraft_dir.'/'.$filename)) {
[319] Fix | Delete
// File without remote storage is no longer locally present
[320] Fix | Delete
} else {
[321] Fix | Delete
$found_file = true;
[322] Fix | Delete
$backup_nonces_by_filename[$filename] = $nonce;
[323] Fix | Delete
if (empty($backup_times_by_nonce[$nonce]) || $backup_times_by_nonce[$nonce] < 100) {
[324] Fix | Delete
$backup_times_by_nonce[$nonce] = $btime;
[325] Fix | Delete
} elseif ($btime < $backup_times_by_nonce[$nonce]) {
[326] Fix | Delete
$backup_times_by_nonce[$nonce] = $btime;
[327] Fix | Delete
}
[328] Fix | Delete
}
[329] Fix | Delete
} else {
[330] Fix | Delete
$accepted = false;
[331] Fix | Delete
foreach ($accept as $fkey => $acc) {
[332] Fix | Delete
if (preg_match('/'.$acc['pattern'].'/i', $filename)) $accepted = $fkey;
[333] Fix | Delete
}
[334] Fix | Delete
if (!empty($accepted) && (false != ($btime = apply_filters('updraftplus_foreign_gettime', false, $accepted, $filename))) && $btime > 0) {
[335] Fix | Delete
$found_file = true;
[336] Fix | Delete
// Generate a nonce; this needs to be deterministic and based on the filename only
[337] Fix | Delete
$nonce = substr(md5($filename), 0, 12);
[338] Fix | Delete
$backup_nonces_by_filename[$filename] = $nonce;
[339] Fix | Delete
if (empty($backup_times_by_nonce[$nonce]) || $backup_times_by_nonce[$nonce] < 100) {
[340] Fix | Delete
$backup_times_by_nonce[$nonce] = $btime;
[341] Fix | Delete
} elseif ($btime < $backup_times_by_nonce[$nonce]) {
[342] Fix | Delete
$backup_times_by_nonce[$nonce] = $btime;
[343] Fix | Delete
}
[344] Fix | Delete
}
[345] Fix | Delete
}
[346] Fix | Delete
}
[347] Fix | Delete
}
[348] Fix | Delete
if (!$found_file) {
[349] Fix | Delete
// File recorded as being without remote storage is no longer present. It may in fact exist in remote storage, and this will be picked up later (when we scan the remote storage).
[350] Fix | Delete
unset($backup_history[$btime]);
[351] Fix | Delete
$changes = true;
[352] Fix | Delete
}
[353] Fix | Delete
}
[354] Fix | Delete
[355] Fix | Delete
// Secondly, scan remote storage and get back lists of files and their sizes
[356] Fix | Delete
[357] Fix | Delete
// $remote_files has a key for each filename (basename), and the value is a list of remote destinations (e.g. 'dropbox', 's3') in which the file was found
[358] Fix | Delete
$remote_files = array();
[359] Fix | Delete
[360] Fix | Delete
// A list of nonces found remotely (to help with handling sets split across destinations)
[361] Fix | Delete
$remote_nonces_found = array();
[362] Fix | Delete
[363] Fix | Delete
$remote_sizes = array();
[364] Fix | Delete
[365] Fix | Delete
if ($remote_scan) {
[366] Fix | Delete
[367] Fix | Delete
$updraftplus->register_wp_http_option_hooks(true);
[368] Fix | Delete
[369] Fix | Delete
$storage_objects_and_ids = UpdraftPlus_Storage_Methods_Interface::get_storage_objects_and_ids(array_keys($updraftplus->backup_methods));
[370] Fix | Delete
[371] Fix | Delete
foreach ($storage_objects_and_ids as $method => $method_information) {
[372] Fix | Delete
[373] Fix | Delete
$object = $method_information['object'];
[374] Fix | Delete
[375] Fix | Delete
if (!method_exists($object, 'listfiles')) continue;
[376] Fix | Delete
[377] Fix | Delete
// Support of multi_options is now required for storage methods that implement listfiles()
[378] Fix | Delete
if (!$object->supports_feature('multi_options')) {
[379] Fix | Delete
error_log("UpdraftPlus: Multi-options not supported by: ".$method);
[380] Fix | Delete
continue;
[381] Fix | Delete
}
[382] Fix | Delete
[383] Fix | Delete
foreach ($method_information['instance_settings'] as $instance_id => $options) {
[384] Fix | Delete
[385] Fix | Delete
$object->set_options($options, false, $instance_id);
[386] Fix | Delete
[387] Fix | Delete
$files = $object->listfiles('backup_');
[388] Fix | Delete
[389] Fix | Delete
$method_description = $object->get_description();
[390] Fix | Delete
[391] Fix | Delete
if (is_array($files)) {
[392] Fix | Delete
[393] Fix | Delete
if ($debug) {
[394] Fix | Delete
$messages[] = array(
[395] Fix | Delete
'method' => $method,
[396] Fix | Delete
'desc' => $method_description,
[397] Fix | Delete
'code' => 'file-listing',
[398] Fix | Delete
'message' => '',
[399] Fix | Delete
'data' => $files,
[400] Fix | Delete
'service_instance_id' => $instance_id,
[401] Fix | Delete
);
[402] Fix | Delete
}
[403] Fix | Delete
[404] Fix | Delete
foreach ($files as $entry) {
[405] Fix | Delete
$filename = $entry['name'];
[406] Fix | Delete
if (!preg_match('/^backup_([\-0-9]{15})_.*_([0-9a-f]{12})-([\-a-z]+)([0-9]+)?(\.(zip|gz|gz\.crypt))?$/i', $filename, $matches)) continue;
[407] Fix | Delete
[408] Fix | Delete
$nonce = $matches[2];
[409] Fix | Delete
$btime = strtotime($matches[1]);
[410] Fix | Delete
// Of course, it's possible that the site doing the scanning has a different timezone from the site that the backups were created in, in which case, this calculation will have a confusing result to the user. That outcome cannot be completely eliminated (without making the filename to reflect UTC, which confuses more users).
[411] Fix | Delete
if (!empty($gmt_offset)) $btime -= $gmt_offset * 3600;
[412] Fix | Delete
[413] Fix | Delete
// Is the set already known?
[414] Fix | Delete
if (isset($backup_times_by_nonce[$nonce])) {
[415] Fix | Delete
// N.B. With an incremental set, the newly found file may be earlier than the known elements, so tha the backup array should be re-keyed.
[416] Fix | Delete
$btime_exact = $backup_times_by_nonce[$nonce];
[417] Fix | Delete
if ($btime > 100 && $btime_exact - $btime > 60 && !empty($backup_history[$btime_exact])) {
[418] Fix | Delete
$changes = true;
[419] Fix | Delete
$backup_history[$btime] = $backup_history[$btime_exact];
[420] Fix | Delete
unset($backup_history[$btime_exact]);
[421] Fix | Delete
$btime_exact = $btime;
[422] Fix | Delete
$backup_times_by_nonce[$nonce] = $btime;
[423] Fix | Delete
}
[424] Fix | Delete
$btime = $btime_exact;
[425] Fix | Delete
}
[426] Fix | Delete
if ($btime <= 100) continue;
[427] Fix | Delete
[428] Fix | Delete
// We need to set this so that if a second file is found in remote storage then the time will be picked up.
[429] Fix | Delete
$backup_times_by_nonce[$nonce] = $btime;
[430] Fix | Delete
[431] Fix | Delete
if (empty($backup_history[$btime]['service_instance_ids']) || empty($backup_history[$btime]['service_instance_ids'][$method])) {
[432] Fix | Delete
$backup_history[$btime]['service_instance_ids'][$method] = array($instance_id);
[433] Fix | Delete
$changes = true;
[434] Fix | Delete
} elseif (!in_array($instance_id, $backup_history[$btime]['service_instance_ids'][$method])) {
[435] Fix | Delete
$backup_history[$btime]['service_instance_ids'][$method][] = $instance_id;
[436] Fix | Delete
$changes = true;
[437] Fix | Delete
}
[438] Fix | Delete
[439] Fix | Delete
if (isset($remote_files[$filename])) {
[440] Fix | Delete
$remote_files[$filename][] = $method;
[441] Fix | Delete
} else {
[442] Fix | Delete
$remote_files[$filename] = array($method);
[443] Fix | Delete
}
[444] Fix | Delete
[445] Fix | Delete
if (!in_array($nonce, $remote_nonces_found)) $remote_nonces_found[] = $nonce;
[446] Fix | Delete
[447] Fix | Delete
if (!empty($entry['size'])) {
[448] Fix | Delete
if (empty($remote_sizes[$filename]) || $remote_sizes[$filename] < $entry['size']) $remote_sizes[$filename] = $entry['size'];
[449] Fix | Delete
}
[450] Fix | Delete
}
[451] Fix | Delete
} elseif (is_wp_error($files)) {
[452] Fix | Delete
foreach ($files->get_error_codes() as $code) {
[453] Fix | Delete
// Skip various codes which are not conditions to show to the user
[454] Fix | Delete
if (in_array($code, array('no_settings', 'no_addon', 'insufficient_php', 'no_listing'))) continue;
[455] Fix | Delete
$messages[] = array(
[456] Fix | Delete
'method' => $method,
[457] Fix | Delete
'desc' => $method_description,
[458] Fix | Delete
'code' => $code,
[459] Fix | Delete
'message' => $files->get_error_message($code),
[460] Fix | Delete
'data' => $files->get_error_data($code),
[461] Fix | Delete
'service_instance_id' => $instance_id,
[462] Fix | Delete
);
[463] Fix | Delete
}
[464] Fix | Delete
}
[465] Fix | Delete
}
[466] Fix | Delete
}
[467] Fix | Delete
$updraftplus->register_wp_http_option_hooks(false);
[468] Fix | Delete
}
[469] Fix | Delete
[470] Fix | Delete
// Thirdly, see if there are any more files in the local directory than the ones already known about (possibly subject to a limitation specified via $only_add_this_file)
[471] Fix | Delete
if (!$handle = opendir($updraft_dir)) return array("Failed to open the internal directory ($updraft_dir)");
[472] Fix | Delete
[473] Fix | Delete
while (false !== ($entry = readdir($handle))) {
[474] Fix | Delete
[475] Fix | Delete
if ('.' == $entry || '..' == $entry) continue;
[476] Fix | Delete
[477] Fix | Delete
$accepted_foreign = false;
[478] Fix | Delete
$potmessage = false;
[479] Fix | Delete
[480] Fix | Delete
if (false !== $only_add_this_file && $entry != $only_add_this_file['file']) continue;
[481] Fix | Delete
[482] Fix | Delete
if (preg_match('/^backup_([\-0-9]{15})_.*_([0-9a-f]{12})-([\-a-z]+)([0-9]+)?(\.(zip|gz|gz\.crypt))?$/i', $entry, $matches)) {
[483] Fix | Delete
// Interpret the time as one from the blog's local timezone, rather than as UTC
[484] Fix | Delete
// $matches[1] is YYYY-MM-DD-HHmm, to be interpreted as being the local timezone
[485] Fix | Delete
$btime = strtotime($matches[1]);
[486] Fix | Delete
if (!empty($gmt_offset)) $btime -= $gmt_offset * 3600;
[487] Fix | Delete
$nonce = $matches[2];
[488] Fix | Delete
$type = $matches[3];
[489] Fix | Delete
if ('db' == $type) {
[490] Fix | Delete
$type .= empty($matches[4]) ? '' : $matches[4];
[491] Fix | Delete
$index = 0;
[492] Fix | Delete
} else {
[493] Fix | Delete
$index = empty($matches[4]) ? '0' : max((int) $matches[4]-1, 0);
[494] Fix | Delete
}
[495] Fix | Delete
$itext = (0 == $index) ? '' : $index;
[496] Fix | Delete
} elseif (false != ($accepted_foreign = apply_filters('updraftplus_accept_foreign', false, $entry)) && false !== ($btime = apply_filters('updraftplus_foreign_gettime', false, $accepted_foreign, $entry))) {
[497] Fix | Delete
$nonce = substr(md5($entry), 0, 12);
[498] Fix | Delete
$type = (preg_match('/\.sql(\.(bz2|gz))?$/i', $entry) || preg_match('/-database-([-0-9]+)\.zip$/i', $entry) || preg_match('/backup_db_/', $entry)) ? 'db' : 'wpcore';
[499] Fix | Delete
12
It is recommended that you Edit text format, this type of Fix handles quite a lot in one request
Function