function Local_Retention($job) { global $wpdb; // Grab info on existing local storage $current = $wpdb->get_row('SELECT COUNT(*) AS gens, SUM(filesize) AS storage ' . 'FROM `' . $this->db_prefix . 'wponlinebackup_local` ' . 'WHERE locked = 0', ARRAY_A); if (is_null($current)) { return $this->DBError(__FILE__, __LINE__); } // Grab minimum generation count to keep $min_gens = $this->WPOnlineBackup->Get_Setting('local_min_gens'); // Grab maximum size for local backup storage - it's in MiB so multiply to get bytes $max_storage = $this->WPOnlineBackup->Get_Setting('local_max_storage') * 1048576; if ($job['progress'] == 0) { // Log an event to show current storage amount $this->Log_Event(WPONLINEBACKUP_EVENT_INFORMATION, sprintf(_n('Local Backups contains %1$s generation with a total size of %2$s.', 'Local Backups contains %1$s generations with a total size of %2$s.', $current['gens'], 'wponlinebackup'), $current['gens'], WPOnlineBackup_Formatting::Fix_B($current['storage'], true))); $job['progress'] = 25; // Set the message $this->status['progress']['message'] = __('Performing retention...', 'wponlinebackup'); // Prevent duplicate log entries $this->Tick(false, true); } if ($job['progress'] == 25) { // If we're storing min_gens or less, no retention needed // Also, if storage is not full or gone over, no retention needed if ($current['gens'] <= $min_gens || $current['storage'] <= $max_storage) { // Log that no retention was required and mark as complete $this->Log_Event(WPONLINEBACKUP_EVENT_INFORMATION, __('Retention is not required.', 'wponlinebackup')); $job['progress'] = 100; // Prevent duplicate log entries $this->Tick(false, true); return true; } $job['progress'] = 50; } if ($job['progress'] == 50) { // Start removing the oldest backups until we're within the threshold while (true) { // Grab oldest 5 $result = $wpdb->get_results('SELECT filename, filesize ' . 'FROM `' . $this->db_prefix . 'wponlinebackup_local` ' . 'WHERE locked = 0 ' . 'ORDER BY creation_date ASC ' . 'LIMIT 5', ARRAY_A); if (count($result) == 0) { break; } foreach ($result as $row) { // Delete it - if it's not there assume deleted (user may have deleted manually) if (!file_exists(WPONLINEBACKUP_LOCALBACKUPDIR . '/' . $row['filename']) || false !== @unlink(WPONLINEBACKUP_LOCALBACKUPDIR . '/' . $row['filename'])) { // Drop from database $esc_filename = $row['filename']; $wpdb->escape_by_ref($esc_filename); $wpdb->query('DELETE FROM `' . $this->db_prefix . 'wponlinebackup_local` ' . 'WHERE filename = \'' . $esc_filename . '\''); $update = false; } else { $err = OBFW_Tidy_Exception(); // Error! $this->Log_Event(WPONLINEBACKUP_EVENT_ERROR, sprintf(__('Failed to delete local backup %1$s: %2$s', 'wponlinebackup'), $row['filename'], $err)); // Mark as error occured so we can log a bit of info after retention completes $job['delete_error'] = 1; // Force update to prevent dupe messages $update = true; } // Log how much we deleted - even if we failed so we don't remove backups we SHOULD be keeping $job['deleted_gens']++; $job['deleted_storage'] += $row['filesize']; // If we've now dropped the storage enough, leave the loops if ($current['gens'] - $job['deleted_gens'] <= $min_gens || $current['storage'] - $job['deleted_storage'] <= $max_storage) { break 2; } $this->Tick(false, $update); } } $job['progress'] = 95; // Force an update so we don't need to loop again $this->Tick(false, true); } if ($job['delete_error']) { // Explain that retention might grow larger than normal $this->Log_Event(WPONLINEBACKUP_EVENT_WARNING, __('Errors were encountered trying to delete one or more Local Backups. Disk space used by Local Backups will be higher than configured until they can be successfully removed.', 'wponlinebackup')); } // Mark as complete and log retention completed $this->Log_Event(WPONLINEBACKUP_EVENT_INFORMATION, sprintf(_n('Retention completed; deleted %d file with a total size of %s.', 'Retention completed; deleted %d files with a total size of %s.', $job['deleted_gens'], 'wponlinebackup'), $job['deleted_gens'], WPOnlineBackup_Formatting::Fix_B($job['deleted_storage'], true))); $job['progress'] = 100; // Prevent duplicate log entries $this->Tick(false, true); return true; }
function Backup(&$bootstrap, &$stream, &$progress, &$job) { $this->bootstrap =& $bootstrap; $this->stream =& $stream; $this->progress =& $progress; $this->job =& $job; // Is the file open? if ($this->file === false) { // Open it if (false === ($this->file = @fopen(WPONLINEBACKUP_LOCALBACKUPDIR . '/' . $progress['config']['file'], 'rb'))) { $ret = OBFW_Tidy_Exception(); return sprintf(__('Failed to open the encrypted backup file at %s: %s', 'wponlinebackup'), $progress['config']['file'], $ret); } // Seek - don't forget to seek past header if we've finished it also, since done_bytes only counts the size AFTER the header if (0 != @fseek($this->file, $job['done_bytes'] + $job['header_bytes'], SEEK_SET)) { $ret = OBFW_Exception(); return sprintf(__('Failed to access the encrypted backup file at %s: %s', 'wponlinebackup'), $progress['config']['file'], $ret); } } if ($job['progress'] < 10) { $this->progress['message'] = __('Validating the provided encryption details...', 'wponlinebackup'); // Force update to update the message $bootstrap->Tick(false, true); // Read the header if (true !== ($ret = $this->_Read_Header())) { if ($ret === false) { return __('The encryption details provided were incorrect.', 'wponlinebackup'); } return sprintf(__('Failed to validate the encryption details provided. The error was: %s', 'wponlinebackup'), $ret); } // If legacy we might not have a len, in which case it will be false if ($job['header']['len'] === false) { $bootstrap->Log_Event(WPONLINEBACKUP_EVENT_INFORMATION, __('Encryption details validated successfully.', 'wponlinebackup')); } else { $bootstrap->Log_Event(WPONLINEBACKUP_EVENT_INFORMATION, sprintf(__('Encryption details validated successfully. The total decrypted backup size will be %s.', 'wponlinebackup'), WPOnlineBackup_Formatting::Fix_B($job['header']['len'], true))); } $job['progress'] = 10; // Next message $this->progress['message'] = __('Decrypting...', 'wponlinebackup'); // Force update to update the message again and to prevent duplicated events $bootstrap->Tick(false, true); } // Is decryption initialised? if ($this->cipher === false) { // Pass false to prevent key validation, which is already done during _Read_Header if (true !== ($ret = $this->_Load_Decryption_Cipher(false))) { return sprintf(__('Failed to initialise the decryption process. The error was: %s', 'wponlinebackup'), $ret); } } // Initialise hash if ($this->WPOnlineBackup->Get_Env('inc_hash_available')) { $this->hash_ctx = hash_init('crc32b'); } else { $this->hash_ctx = false; } // Do we have a saved hash_ctx we can load? if (isset($job['saved_hash_ctx'])) { if ($job['saved_hash_ctx'] !== false) { if ($job['crc'] !== false) { $job['crc'] = WPOnlineBackup_Functions::Combine_CRC32($job['crc'], $job['saved_hash_ctx'], $job['hash_len']); } else { $job['crc'] = $job['saved_hash_ctx']; } } $job['saved_hash_ctx'] = false; } $job['hash_len'] = 0; if (true !== ($ret = $this->_Decryption_Loop())) { // False means CRC failure if ($ret === false) { return __('The file integrity check failed. The file may be corrupt or the encryption details validated but were actually incorrect (there is a small chance of this happening.)', 'wponlinebackup'); } return sprintf(__('Decryption failed. The error was: %s', 'wponlinebackup'), $ret); } // Clean up cipher $this->_CleanUp_Cipher(); // Completed! $job['progress'] = 100; $progress['rcount']++; $progress['rsize'] += $job['done_bytes']; $bootstrap->Log_Event(WPONLINEBACKUP_EVENT_INFORMATION, __('Decryption completed successfully.', 'wponlinebackup')); // Force update so no duplicate events $bootstrap->Tick(false, true); return true; }
function Fetch_Stat($file) { if (($file_size = @filesize($file)) === false) { $ret = OBFW_Tidy_Exception(); return OBFW_FOpen_Exception($file, $ret); } if (($mod_time = @filemtime($file)) === false) { $ret = OBFW_Tidy_Exception(); return OBFW_FOpen_Exception($file, $ret); } return compact('file_size', 'mod_time'); }
function OBFW_FOpen_Exception($path, $original) { // We call this when we failed to call filesize or stat, because they simply return "stat failed" which is pretty useless // Calling fopen and getting the error from that will give us a better error, such as "permission denied" if (false === ($f = @fopen($path, 'rb'))) { return OBFW_Tidy_Exception(); } // We opened successfully, which is odd, so just close and return the original exception @fclose($f); return $original; }
function Add_Large_File($bin, $file, $path, &$size, $status) { // Open file if (!($fh = @fopen($path, 'rb'))) { $size = OBFW_Tidy_Exception(); return true; } // Force compressor and writer to register temporary files $this->compressor->Register_Temps(true); $this->writer->Register_Temps(true); $size = 0; $zlen = 0; // Start the stream if (true !== ($ret = $this->Start_Stream($bin, $file, $size, $status))) { @fclose($fh); return $ret; } $writing = false; // Read each block, update crc and zlen and write data while (true) { // Read a block if (($data = @fread($fh, $this->WPOnlineBackup->Get_Setting('max_block_size'))) === false) { $size = OBFW_Tidy_Exception(); @fclose($fh); // Restore compressor and writer $this->writer->Register_Temps(false); $this->compressor->Register_Temps(false); return true; } // Get length of data read $len = strlen($data); // Update size $size += $len; // Write to the stream if (($ret = $this->Write_Stream($data, $len)) !== true) { @fclose($fh); return $ret; } // End of file? End loop if (@feof($fh)) { break; } } // Close the file @fclose($fh); // End the stream if (true !== ($ret = $this->End_Stream())) { return $ret; } // Commit the stream if (true !== ($ret = $this->Commit_Stream())) { return $ret; } // Clean up the stream if (true !== ($ret = $this->CleanUp_Stream())) { return $ret; } // Restore compressor and writer $this->writer->Register_Temps(false); $this->compressor->Register_Temps(false); return true; }
function _Delete_Local($filename) { global $wpdb; // Delete the file - if it isn't found, just display the backup page $esc_filename = $filename; $wpdb->escape_by_ref($esc_filename); $result = $wpdb->get_row('SELECT filename ' . 'FROM `' . $wpdb->prefix . 'wponlinebackup_local` ' . 'WHERE filename = \'' . $esc_filename . '\'', ARRAY_A); if (!is_null($result)) { if (file_exists(WPONLINEBACKUP_LOCALBACKUPDIR . '/' . $result['filename'])) { $ret = @unlink(WPONLINEBACKUP_LOCALBACKUPDIR . '/' . $result['filename']); } else { $ret = true; } if ($ret === true) { $wpdb->query('DELETE FROM `' . $wpdb->prefix . 'wponlinebackup_local` ' . 'WHERE filename = \'' . $result['filename'] . '\''); $this->Register_Messages(array(array('icon' => 'accept', 'text' => __('Deleted backup.', 'wponlinebackup')))); } else { $ret = OBFW_Tidy_Exception(); $this->Register_Messages(array(array('icon' => 'error', 'text' => sprintf(__('Failed to delete the backup: %s', 'wponlinebackup'), $ret)))); } return true; } return false; }