/** * Make sure setting a custom path + database dump filename correctly sets the database dump filepath * * @access public */ public function testCustomDatabaseDumpPath() { $this->backup->set_path(WP_CONTENT_DIR . '/custom'); $this->backup->set_database_dump_filename('dump.sql'); $this->assertEquals(HM_Backup::conform_dir(WP_CONTENT_DIR . '/custom/dump.sql'), $this->backup->get_database_dump_filepath()); $this->backup->dump_database(); $this->assertFileExists($this->backup->get_database_dump_filepath()); }
/** * Display a html list of files * * @param HMBKP_Scheduled_Backup $schedule * @param mixed $excludes (default: null) * @param string $file_method (default: 'get_included_files') * @return void */ function hmbkp_file_list(HMBKP_Scheduled_Backup $schedule, $excludes = null, $file_method = 'get_included_files') { if (!is_null($excludes)) { $schedule->set_excludes($excludes); } $exclude_string = $schedule->exclude_string('regex'); ?> <ul class="hmbkp_file_list code"> <?php foreach ($schedule->get_files() as $file) { if (!is_null($excludes) && strpos($file, str_ireplace($schedule->get_root(), '', $schedule->get_path())) !== false) { continue; } // Skip dot files, they should only exist on versions of PHP between 5.2.11 -> 5.3 if (method_exists($file, 'isDot') && $file->isDot()) { continue; } // Show only unreadable files if ($file_method === 'get_unreadable_files' && @realpath($file->getPathname()) && $file->isReadable()) { continue; } elseif ($file_method !== 'get_unreadable_files' && (!@realpath($file->getPathname()) || !$file->isReadable())) { continue; } // Show only included files if ($file_method === 'get_included_files') { if ($exclude_string && preg_match('(' . $exclude_string . ')', str_ireplace(trailingslashit($schedule->get_root()), '', HM_Backup::conform_dir($file->getPathname())))) { continue; } } // Show only excluded files if ($file_method === 'get_excluded_files') { if (!$exclude_string || !preg_match('(' . $exclude_string . ')', str_ireplace(trailingslashit($schedule->get_root()), '', HM_Backup::conform_dir($file->getPathname())))) { continue; } } if (@realpath($file->getPathname()) && !$file->isReadable() && $file->isDir()) { ?> <li title="<?php echo esc_attr(HM_Backup::conform_dir(trailingslashit($file->getPathName()))); ?> "><?php echo esc_html(ltrim(trailingslashit(str_ireplace(HM_Backup::conform_dir(trailingslashit($schedule->get_root())), '', HM_Backup::conform_dir($file->getPathName()))), '/')); ?> </li> <?php } else { ?> <li title="<?php echo esc_attr(HM_Backup::conform_dir($file->getPathName())); ?> "><?php echo esc_html(ltrim(str_ireplace(HM_Backup::conform_dir(trailingslashit($schedule->get_root())), '', HM_Backup::conform_dir($file->getPathName())), '/')); ?> </li> <?php } } ?> </ul> <?php }
/** * Get the total filesize for a given file or directory * * If $file is a file then just return the result of `filesize()`. * If $file is a directory then schedule a recursive filesize scan. * * @param SplFileInfo $file The file or directory you want to know the size of * @param bool $skip_excluded_files Skip excluded files when calculating a directories total size * @return int The total of the file or directory */ public function filesize(SplFileInfo $file, $skip_excluded_files = false) { // Skip missing or unreadable files if (!file_exists($file->getPathname()) || !@realpath($file->getPathname()) || !$file->isReadable()) { return false; } // If it's a file then just pass back the filesize if ($file->isFile() && $file->isReadable()) { return $file->getSize(); } // If it's a directory then pull it from the cached filesize array if ($file->isDir()) { // If we haven't calculated the site size yet then kick it off in a thread $directory_sizes = get_transient('hmbkp_directory_filesizes'); if (!is_array($directory_sizes)) { if (!$this->is_site_size_being_calculated()) { // Mark the filesize as being calculated set_transient('hmbkp_directory_filesizes_running', true, HOUR_IN_SECONDS); // Schedule a Backdrop task to trigger a recalculation $task = new HM_Backdrop_Task(array($this, 'recursive_filesize_scanner')); $task->schedule(); } return; } $current_pathname = trailingslashit($file->getPathname()); $root = trailingslashit($this->get_root()); foreach ($directory_sizes as $path => $size) { // Remove any files that aren't part of the current tree if (false === strpos($path, $current_pathname)) { unset($directory_sizes[$path]); } } if ($skip_excluded_files) { $excludes = $this->exclude_string('regex'); foreach ($directory_sizes as $path => $size) { // Skip excluded files if we have excludes if ($excludes && preg_match('(' . $excludes . ')', str_ireplace($root, '', HM_Backup::conform_dir($path)))) { unset($directory_sizes[$path]); } } } // Directory size is now just a sum of all files across all sub directories return array_sum($directory_sizes); } }
/** * Handles changes in the defined Constants * that users can define to control advanced * settings */ function hmbkp_constant_changes() { // If a custom backup path has been set or changed if (defined('HMBKP_PATH') && HMBKP_PATH && HM_Backup::conform_dir(HMBKP_PATH) !== ($from = HM_Backup::conform_dir(get_option('hmbkp_path')))) { hmbkp_path_move($from, HMBKP_PATH); } // If a custom backup path has been removed if (defined('HMBKP_PATH') && !HMBKP_PATH || !defined('HMBKP_PATH') && hmbkp_path_default() !== ($from = HM_Backup::conform_dir(get_option('hmbkp_path')))) { hmbkp_path_move($from, hmbkp_path_default()); } // If the custom path has changed and the new directory isn't writable if (defined('HMBKP_PATH') && HMBKP_PATH && !is_writable(HMBKP_PATH) && get_option('hmbkp_path') === HMBKP_PATH && is_dir(HMBKP_PATH)) { hmbkp_path_move(HMBKP_PATH, hmbkp_path_default()); } }
/** * Send the download file to the browser and then redirect back to the backups page */ function hmbkp_request_download_backup() { check_admin_referer('hmbkp_download_backup', 'hmbkp_download_backup_nonce'); if (!file_exists(sanitize_text_field(base64_decode($_GET['hmbkp_backup_archive'])))) { return; } $url = str_replace(HM_Backup::conform_dir(HM_Backup::get_home_path()), home_url(), trailingslashit(dirname(sanitize_text_field(base64_decode($_GET['hmbkp_backup_archive']))))) . urlencode(pathinfo(sanitize_text_field(base64_decode($_GET['hmbkp_backup_archive'])), PATHINFO_BASENAME)); global $is_apache; if ($is_apache) { HMBKP_Path::get_instance()->protect_path('reset'); $url = add_query_arg('key', HMBKP_SECURE_KEY, $url); } wp_safe_redirect($url, 303); die; }
/** * Calculate the size of the backup * * Doesn't account for * compression * * @access public * @param bool $cached Whether to return from cache * @return string */ public function get_filesize($cached = true) { $filesize = 0; if ($cached) { // Check if we have the filesize in the cache $filesize = get_transient('hmbkp_schedule_' . $this->get_id() . '_filesize'); // If we do and it's not still calculating then return it straight away if ($filesize && $filesize !== 'calculating') { return $filesize; } // If the filesize is calculating in another thread then we should wait for it to finish if ($filesize === 'calculating') { global $wpdb; $counter = 1; // Keep checking the cached filesize to see if the other thread is finished while ('calculating' === ($filesize = $wpdb->get_var("SELECT option_value FROM {$wpdb->options} WHERE option_name = '_transient_hmbkp_schedule_" . $this->get_id() . "_filesize'"))) { // Check once every 10 seconds sleep(10); // Only run for a maximum of 5 minutes (30*10) if ($counter === 30) { break; } $counter++; } // If we have the filesize then return it if ($filesize && $filesize !== 'calculating') { return $filesize; } } } // If we don't have it in cache then mark it as calculating set_transient('hmbkp_schedule_' . $this->get_id() . '_filesize', 'calculating', time() + HOUR_IN_SECONDS); // Don't include database if file only if ($this->get_type() != 'file') { global $wpdb; $res = $wpdb->get_results('SHOW TABLE STATUS FROM `' . DB_NAME . '`', ARRAY_A); foreach ($res as $r) { $filesize += (double) $r['Data_length']; } } // Don't include files if database only if ($this->get_type() != 'database') { // Get rid of any cached filesizes clearstatcache(); $excludes = $this->exclude_string('regex'); foreach ($this->get_files() as $file) { // Skip dot files, they should only exist on versions of PHP between 5.2.11 -> 5.3 if (method_exists($file, 'isDot') && $file->isDot()) { continue; } if (!@realpath($file->getPathname()) || !$file->isReadable()) { continue; } // Excludes if ($excludes && preg_match('(' . $excludes . ')', str_ireplace(trailingslashit($this->get_root()), '', HM_Backup::conform_dir($file->getPathname())))) { continue; } $filesize += (double) $file->getSize(); } } // Cache for a day set_transient('hmbkp_schedule_' . $this->get_id() . '_filesize', $filesize, time() + DAY_IN_SECONDS); return $filesize; }
/** * Send the download file to the browser and * then redirect back to the backups page */ function hmbkp_request_download_backup() { global $is_apache; if (empty($_GET['hmbkp_download_backup']) || !check_admin_referer('hmbkp-download_backup') || !file_exists(sanitize_text_field(base64_decode($_GET['hmbkp_download_backup'])))) { return; } $url = str_replace(HM_Backup::conform_dir(HM_Backup::get_home_path()), home_url(), trailingslashit(dirname(sanitize_text_field(base64_decode($_GET['hmbkp_download_backup']))))) . urlencode(pathinfo(sanitize_text_field(base64_decode($_GET['hmbkp_download_backup'])), PATHINFO_BASENAME)); if ($is_apache) { // Force the .htaccess to be rebuilt if (file_exists(hmbkp_path() . '/.htaccess')) { unlink(hmbkp_path() . '/.htaccess'); } hmbkp_path(); $url = add_query_arg('key', HMBKP_SECURE_KEY, $url); } wp_redirect($url, 303); die; }
</td> <td></td> </tr> </thead> <tbody> <?php foreach ($files as $size => $file) { $is_excluded = $is_unreadable = false; // Check if the file is excluded if ($exclude_string && preg_match('(' . $exclude_string . ')', str_ireplace(trailingslashit($schedule->get_root()), '', HM_Backup::conform_dir($file->getPathname())))) { $is_excluded = true; } // Skip unreadable files if (!@realpath($file->getPathname()) || !$file->isReadable()) { $is_unreadable = true; } ?> <tr> <td> <?php if ($is_unreadable) { ?>
public function testMixedSlashes() { $this->assertEquals(HM_Backup::conform_dir('\\/one\\//\\two\\/\\three'), $this->dir); }
/** * Recursively scans a directory to calculate the total filesize * * @param string $directory The directory to scan * @param bool $ignore_excludes Whether or not to include excluded files in the total filesize calculation * @return int $total_filesize The total filesize of all files in all subdirectories */ public function recursive_directory_filesize_scanner($directory, $ignore_excludes = true) { if (!is_dir($directory)) { return $total_filesize; } $handle = opendir($directory); if (!$handle) { return $total_filesize; } $excludes = $this->exclude_string('regex'); $transient_filesize_key = $this->get_transient_key($directory); $transient_running_key = $this->get_transient_key('running_' . $directory); if (!$ignore_excludes) { $transient_filesize_key = $this->get_transient_key($excludes . $directory); $transient_running_key = $this->get_transient_key('running_' . $excludes . $directory); } // Use the cached directory size if available $directory_size = get_transient($transient_filesize_key); if ($directory_size !== false) { delete_option($transient_running_key); return $directory_size; } update_option($transient_running_key, true); $total_filesize = 0; $files = array(); clearstatcache(); while ($file_handle = readdir($handle)) { // Ignore current dir and containing dir if ($file_handle === '.' || $file_handle === '..') { continue; } $file = new SplFileInfo(HM_Backup::conform_dir(trailingslashit($directory) . $file_handle)); // Skip unreadable files if (!@realpath($file->getPathname()) || !$file->isReadable()) { continue; } // Skip excluded files if we have excludes if (!$ignore_excludes && $excludes && preg_match('(' . $excludes . ')', str_ireplace(trailingslashit($this->get_root()), '', HM_Backup::conform_dir($file->getPathname())))) { continue; } $total_filesize += $file->getSize(); // We need to recursively calculate the size of all files in a subdirectory if ($file->isDir()) { $total_filesize += $this->recursive_directory_filesize_scanner($file->getPathname(), $ignore_excludes); } } closedir($handle); // If we have a filesize then let's cache it if ($total_filesize !== false) { set_transient($transient_filesize_key, (string) $total_filesize, WEEK_IN_SECONDS); } delete_option($transient_running_key); return $total_filesize; }
/** * * @param null $pathname */ function hmbkp_recalculate_directory_filesize($pathname = null) { if (!$pathname && (!isset($_GET['hmbkp_recalculate_directory_filesize']) || !check_admin_referer('hmbkp-recalculate_directory_filesize'))) { return; } $schedule = new HMBKP_Scheduled_Backup(sanitize_text_field($_GET['hmbkp_schedule_id'])); if (!$pathname) { $directory = sanitize_text_field($_GET['hmbkp_recalculate_directory_filesize']); } else { $directory = $pathname; } // Delete the cached directory size // TODO should use $schedule->get_transient_key delete_transient($schedule->get_transient_key($directory)); $handle = opendir($directory); while ($file_handle = readdir($handle)) { // Ignore current dir and containing dir if ($file_handle === '.' || $file_handle === '..') { continue; } $file = HM_Backup::conform_dir(trailingslashit($directory) . $file_handle); // Delete all sub directories if (is_dir($file)) { delete_transient($schedule->get_transient_key($file)); hmbkp_recalculate_directory_filesize($file); } } closedir($handle); $parent_directory = dirname($directory); // Delete the cached filesize of all parents as well while ($schedule->get_root() !== $parent_directory) { delete_transient($schedule->get_transient_key($parent_directory)); $parent_directory = dirname($parent_directory); } if (!$pathname) { $url = add_query_arg(array('action' => 'hmbkp_edit_schedule', 'hmbkp_panel' => 'hmbkp_edit_schedule_excludes'), hmbkp_get_settings_url()); if (isset($_GET['hmbkp_directory_browse'])) { $url = add_query_arg('hmbkp_directory_browse', sanitize_text_field($_GET['hmbkp_directory_browse']), $url); } wp_safe_redirect($url, '303'); die; } }