0byt3m1n1
Path:
/
home1
/
aserty
/
public_html
/
bonniescraftygifts.com
/
iFzj4
/
configCHM
/
Jump
/
0-aserty
/
natureultra.com
/
wp-content
/
plugins
/
backupbuddy v4.2.10.6
/
lib
/
zipbuddy
/
[
Home
]
File: zipbuddy.php
<?php /** * pluginbuddy_zipbuddy Class (Experimental) * * Handles zipping and unzipping, using the best methods available and falling back to worse methods * as needed for compatibility. Allows for forcing compatibility modes. * * Version: 1.0.0 * Author: * Author URI: * * */ // Test if we are loading as standard or experimental - if experimental just drop through if ( 0 === strcmp( basename( dirname( __FILE__ ) ), 'zipbuddy' ) ) { // Currently loading as standard so determine if we need to load experimental if ( isset( pb_backupbuddy::$options['alternative_zip_2'] ) && ( '1' == pb_backupbuddy::$options['alternative_zip_2'] ) ) { // User enabled experimental so look for it and load it is found, otherwise log $experimental_zipbuddy = dirname( dirname( __FILE__ ) ) . '/xzipbuddy/zipbuddy.php'; if ( @is_readable( $experimental_zipbuddy ) ) { require_once( $experimental_zipbuddy ); } else { pb_backupbuddy::status( 'details', sprintf( __('Alternate Zip System enabled but not found/readable at: %1$s','it-l10n-backupbuddy' ), $experimental_zipbuddy ) ); } } } if ( !class_exists( "pluginbuddy_zipbuddy" ) ) { class pluginbuddy_zipbuddy { const ZIP_METHODS_TRANSIENT = 'pb_backupbuddy_avail_zip_methods'; const ZIP_METHODS_TRANSIENT_EXPERIMENTAL = 'pb_backupbuddy_avail_xzip_methods'; const ZIP_METHODS_TRANSIENT_LIFE = 43200; // 12 Hours - really shouldn't change unless server problem const NORM_DIRECTORY_SEPARATOR = '/'; const DIRECTORY_SEPARATORS = '/\\'; /** * The plugin path for this plugin * * @var string */ public $_pluginPath = ''; /** * The path of the temporary directory that can be used for creating files and stuff * * @var string */ protected $_tempdir = ""; /** * The list of zip methods that are requested to be used * * @var array of string */ protected $_requested_zip_methods = array(); /** * The mode which the object is being created for * Note: This is now ignored as an object is instantiated for both zip & unzip services * * @var string */ protected $_mode = ""; /** * Status message array used when calling other methods to get status information back * * @var array of string */ public $_status = array(); /** * The list of zip methods that are to be used or are available * Had to make this public for now because something accesses it directly - bad karma * * @var array of string */ public $_zip_methods = array(); /** * The details of the various zip methods that are available * Have to make this a separate array indexed by the method tag. Ideally would be combined * with the zip methods array but that would involve more general changes elsewhere so that * refactoring can be done later - main problem is the direct access to the zip methods * array that is made rather than through a function. * * @var array of array of array */ protected $_zip_methods_details = array(); /** * The list of zip methods that are supported, i.e., there is a supporting class defined * * @var array of string */ protected $_supported_zip_methods = array(); /** * Whether or not we can call a status calback * * @var bool */ protected $_have_status_callback = false; /** * Object->method array for status function * * @var array */ protected $_status_callback = array(); /** * The directory name that we are loaded from (not: not path) * * @var string */ protected $_whereami = ""; /** * Whether we are loaded as the experimental zipbuddy * * @var bool */ protected $_is_experimental = false; /** * The name of the zip methods transient will be dependent on if we are standard or experimental * * @var string */ protected $_zip_methods_transient = ""; /** * The Server API that is in use * * @var string */ protected $_sapi_name = ""; /** * * get_transient_names_static() * * Get the transient name(s) that may be in use * * @return array The transient name(s) * */ public static function get_transient_names_static() { return array( self::ZIP_METHODS_TRANSIENT, self::ZIP_METHODS_TRANSIENT_EXPERIMENTAL ); } /** * __construct() * * Default constructor. * * @param string $temp_dir The path of the temporary directory to use * @param array $zip_methods Optional: The set of zip methods requested to use * @param string $mode Optional: The zip mode for the object (ignored currently - may be reused?) * @return null * */ public function __construct( $temp_dir, $zip_methods = array(), $mode = 'zip' ) { // Normalize the trailing directory separator on the path $temp_dir = rtrim( $temp_dir, self::DIRECTORY_SEPARATORS ) . self::NORM_DIRECTORY_SEPARATOR; // Normalize platform specific directory separators in path $this->_tempdir = str_replace( DIRECTORY_SEPARATOR, self::NORM_DIRECTORY_SEPARATOR, $temp_dir ); // Note: this will be removed and should no longer be used - an object is instantiated for // both zip & unzip services $this->_mode = $mode; // Record where we are located (the directory name) $this->_whereami = basename( dirname( __FILE__ ) ); // Use our location to determine which zip methods transient we should be using $this->_zip_methods_transient = ( 0 === strcmp( $this->_whereami, 'zipbuddy' ) ) ? self::ZIP_METHODS_TRANSIENT : self::ZIP_METHODS_TRANSIENT_EXPERIMENTAL ; // Set a flag for easy conditional testing $this->_is_experimental = ( 0 === strcmp( $this->_whereami, 'zipbuddy' ) ) ? false : true ; // Set the sapi name so we can use it later $this->set_sapi_name(); // Make sure we load the core abstract class as this will always be needed require_once( pb_backupbuddy::plugin_path() . '/lib/' . $this->_whereami . '/zbzipcore.php' ); // If we loaded that ok then try the method specific classes // Could make this more generic based on config or somesuch if ( class_exists( 'pluginbuddy_zbzipcore' ) ) { // Only provide proc mode when experimental zip enabled if ( true === $this->_is_experimental ) { include_once( pb_backupbuddy::plugin_path() . '/lib/' . $this->_whereami . '/zbzipproc.php' ); if ( class_exists( 'pluginbuddy_zbzipproc' ) ) { if ( $this->check_method_dependencies( 'pluginbuddy_zbzipproc' ) ) { $this->set_supported_zip_methods( pluginbuddy_zbzipproc::get_method_tag_static() ); } } } include_once( pb_backupbuddy::plugin_path() . '/lib/' . $this->_whereami . '/zbzipexec.php' ); if ( class_exists( 'pluginbuddy_zbzipexec' ) ) { if ( $this->check_method_dependencies( 'pluginbuddy_zbzipexec' ) ) { $this->set_supported_zip_methods( pluginbuddy_zbzipexec::get_method_tag_static() ); } } include_once( pb_backupbuddy::plugin_path() . '/lib/' . $this->_whereami . '/zbzipziparchive.php' ); if ( class_exists( 'pluginbuddy_zbzipziparchive' ) ) { if ( $this->check_method_dependencies( 'pluginbuddy_zbzipziparchive' ) ) { $this->set_supported_zip_methods( pluginbuddy_zbzipziparchive::get_method_tag_static() ); } } include_once( pb_backupbuddy::plugin_path() . '/lib/' . $this->_whereami . '/zbzippclzip.php' ); if ( class_exists( 'pluginbuddy_zbzippclzip' ) ) { if ( $this->check_method_dependencies( 'pluginbuddy_zbzippclzip' ) ) { $this->set_supported_zip_methods( pluginbuddy_zbzippclzip::get_method_tag_static() ); } } } // Work out the list of zip methods from the requested and available along with their details $this->set_zip_methods( $zip_methods ); } /** * __destruct() * * Default destructor. * * @return null * */ public function __destruct( ) { } /** * set_sapi_name() * * Sets the sapi name to that given or retrieves from PHP * TODO: Extend to also set a sapi id constant based on the name? * * @param string $name A sapi name to set (default empty) * @return object This object */ public function set_sapi_name( $sapi_name = "" ) { if ( empty( $sapi_name ) ) { $sapi_name = php_sapi_name(); } $this->_sapi_name = $sapi_name; return $this; } /** * get_sapi_name() * * Returns the previously set sapi name * * @return string The stored sapi name */ public function get_sapi_name() { return $this->_sapi_name; } /** * set_supported_zip_methods() * * Appends or prepends the method or methods passed to the existing supported methods array * * @param string/array $methods Either a (comma separated) string of methods or an array * @param bool $append True if $methods should be appended to existing supported methods * @return bool True if set succeeded, otherwise false */ protected function set_supported_zip_methods( $methods, $append = true ) { $result = false; // If $methods is a string we need to turn it into an array (of one or more elements) or // otherwise assume it is an array already (but we double check in a mo) ( is_string( $methods ) ) ? $methods_to_add = explode( ",", $methods ) : $methods_to_add = $methods; // Make sure we have an array and if so then either append or prepend to existing supported methods if ( is_array( $methods_to_add ) ) { ( $append ) ? $this->_supported_zip_methods = array_merge( $this->_supported_zip_methods, $methods_to_add ) : $this->_supported_zip_methods = array_merge( $methods_to_add, $this->_supported_zip_methods ); $result = true; } // Will return false if we somehow didn't end up with an array to merge return $result; } /** * check_method_dependencies() * * Checks the dependencies that a method defines for itself - this may optionally also mean * calling a given callback function that allows the method to add it's own very specific checks * beyond those that are run as standard. * * @param string $class_name The name of the class to check, needed because this is static checking * @return bool True if dependency check succeeded, otherwise false */ protected function check_method_dependencies( $class_name ) { // Assume dependency checks will pass - will be set false if a check fails $result = true; if ( !method_exists( $class_name, 'get_method_dependencies_static' ) ) { $result = false; } else { $method_dependencies = call_user_func( array( $class_name, 'get_method_dependencies_static' ) ); } if ( ( $result ) && isset( $method_dependencies[ 'classes' ] ) && !empty( $method_dependencies[ 'classes' ] ) ) { $classes = $method_dependencies[ 'classes' ]; $disabled_classes = array_map( "trim", explode( ',', ini_get( 'disable_classes' ) ) ); // Check each function dependency and bail out on first failure foreach ( $classes as $class ) { $class = trim( $class ); if ( !( ( class_exists( $class ) ) && ( !in_array( $class, $disabled_classes ) ) ) ) { $result = false; break; } } } if ( ( $result ) && isset( $method_dependencies[ 'functions' ] ) && !empty( $method_dependencies[ 'functions' ] ) ) { $functions = $method_dependencies[ 'functions' ]; $disabled_functions = array_map( "trim", explode( ',', ini_get( 'disable_functions' ) ) ); // Check each function dependency and bail out on first failure foreach ( $functions as $function ) { $function = trim( $function ); if ( !( ( function_exists( $function ) ) && ( !in_array( $function, $disabled_functions ) ) ) ) { $result = false; break; } } } // No extension checks yet // No file checks yet (need to determine how this might work a bit better) if ( ( $result ) && isset( $method_dependencies[ 'check_func' ] ) && !empty( $method_dependencies[ 'check_func' ] ) ) { $result = call_user_func( array( $class_name, $method_dependencies[ 'check_func' ] ) ); } return $result; } /** * deduce_zip_methods() * * Returns the array of zip methods that are available (or just the best) filtered by requested methods. * Because the available methods don't really change often (rarely once stable) we use a transient * which has some defined lifetime so we don't waste time repeating the testing which involves creating * objects and processes and files which can be time consuming. * The using script can decide to have the transient refreshed by deleting it and then it will be regenerated. * Note: There is an included "signature" so that we can detect server or other moves and regenerate. * Note: filemtime() is used because this will (should) force the transient to update if the plugin is * updated because the file modification time of the file will change because the plugin is installed in a * different disk location with newly written files - the same should apply if the site is restored/migrated. * * @param array Array reference for the deduced zip methods * @param array Arry reference for the details of the deduced methods * @param array Flat array of requested (preferred) zip methods * @param bool True if only the best available method wanted * @param string Which zip mode being tested * @return bool True if methods are available, False otherwise * */ protected function deduce_zip_methods( array &$methods, array &$methods_details, array $requested, $best_only ) { $available_methods = array(); $available_methods_details = array(); $aggregate_available_methods = array(); $server_signature_string = ""; $server_signature = ""; // Decide if we should try for cached methods or not if ( $this->use_cached_methods() ) { $aggregate_available_methods = get_transient( $this->_zip_methods_transient ); // Drop through if we didn't get a transient otherwise we'll test it for validity if ( false !== $aggregate_available_methods ) { // Generate server signature and check it matches the cached signature $server_signature_string = php_uname() . " : " . ( ( $filemodtime = filemtime( __FILE__ ) ) ? (string) $filemodtime : '1' ); $server_signature = md5( $server_signature_string ); if ( ( false === isset( $aggregate_available_methods[ 'control' ][ 'signature' ] ) ) || ( $server_signature !== $aggregate_available_methods[ 'control' ][ 'signature' ] ) ) { // Either no signature previously set or it has changed - either way we need to reevaluate available methods $aggregate_available_methods = false; } } } else { $aggregate_available_methods = false; } // Create new transient if we didn't have one, it was expired or we nuked it because invalid if ( false === $aggregate_available_methods ) { // Get all available methods in $available_methods - must return them in order best -> worst // Also getting the method details array which is keyed by method tag $this->get_available_zip_methods( $this->_supported_zip_methods, $available_methods, $available_methods_details ); // Now we have to combine the two arrays into an aggregate to save $aggregate_available_methods[ 'methods' ] = $available_methods; $aggregate_available_methods[ 'details' ] = $available_methods_details; // Only save if we are using caching if ( $this->use_cached_methods() ) { // Add the server signature for detecting invalidated methods details on a migration or some other change // Note: See discussion above on derivation of signature // TODO: Check, probably can use the server signature calculated above $server_signature_string = php_uname() . " : " . ( ( $filemodtime = filemtime( __FILE__ ) ) ? (string) $filemodtime : '1' ); $server_signature = md5( $server_signature_string ); $aggregate_available_methods[ 'control' ][ 'signature' ] = $server_signature; set_transient( $this->_zip_methods_transient, $aggregate_available_methods, self::ZIP_METHODS_TRANSIENT_LIFE ); } } else { // We got a valid transient value so now separate the aggregate into two $available_methods = $aggregate_available_methods[ 'methods' ]; $available_methods_details = $aggregate_available_methods[ 'details' ]; } // Check whether these need to be filtered by requested methods if ( !empty( $requested ) ) { // Filter the available methods - result could be empty // Order will be retained regardless of order of requested methods $available_methods = array_intersect( $available_methods, $requested ); } // If just the best available requested then slice it off if ( ( true === $best_only ) && ( !empty( $available_methods ) ) ) { $methods = array_slice( $available_methods, 0, 1 ); $methods_details = $available_methods_details; } else { $methods = $available_methods; $methods_details = $available_methods_details; } if ( !empty( $methods ) ) { return true; } else { return false; } } /** * use_cached_methods() * * Returns whether conditions/configuration indicate cached methods should be used * Note: Temporarily add the condition for whether get_/set_transient functions * exist - if not (meaning we are probably running under importbuddy) then we also * skip caching. This adds a little time when instantiating because we have to test * every time but it's acceptable for now. In the longer term we will have an * alternative way to handle the transient data outside of WordPress. * * @return bool true if use cached methods, false otherwise * */ protected function use_cached_methods() { // By default we'll be using caching $result = true; // Has caching been explicitly disabled $caching_disabled = ( isset( pb_backupbuddy::$options['disable_zipmethod_caching'] ) && ( pb_backupbuddy::$options['disable_zipmethod_caching'] == '1') ); // Do we have the means to cache $caching_unavailable = ( !( function_exists( 'get_transient' ) && function_exists( 'set_transient' ) ) ); if ( $caching_disabled || $caching_unavailable ) { pb_backupbuddy::status( 'details', 'Zip method caching disabled based on settings or unavailable.' ); $result = false; } return $result; } /** * get_zip_methods() * * Returns the array of zip methods previously deduced * * @return array Flat array of zip methods (could be empty) * */ public function get_zip_methods() { return $this->_zip_methods; } /** * set_zip_methods() * * Resets the zip methods based on new criteria and returns the array of zip methods * * @param array $requested Flat array of requested (preferred) zip methods * @param bool $best_only Optional: True if only the best available method wanted * @return object This object * */ public function set_zip_methods( array $requested, $best_only = false ) { // Update the memory of what zip methods were requested - make it clean $this->_requested_zip_methods = array_map( 'trim', $requested ); // Work out the list of zip methods from the requested and available $this->deduce_zip_methods( $this->_zip_methods, $this->_zip_methods_details, $this->_requested_zip_methods, $best_only ); return $this; } /** * refresh_zip_methods() * * Refresh the information on the available zip methods * TODO: Perhaps the transient could be deleted here rather than by the caller? * * @param array $zip_methods Optional: The set of zip methods requested to use * @return object This object */ public function refresh_zip_methods( $zip_methods = array() ) { $this->set_zip_methods( $zip_methods ); return $this; } /** * sanitize_excludes() * * Take an exclusion list of directories and/or files and produce a sanitized exclusion list * Directories will be recognized by having a trailing directory separator otherwise will be * treated as a file (note that here we are working with patterns and not testing to see * whether something _is_ a directory or not because we don't necessarily have the full * directory path). * Note: Anything that contains a wildcard character (* or ?) is ignored as these are not * currently supported (and maybe never will across the board). For command zip zip we can * consider an option to have these as separate exclusions (and currently we can accomodate * then through specifying environment ZIPOPTS. * * @param array List of primary exclusions - may be empty * @param array List of secondary exclusions - may be empty * @param string The base directory to be used if normalizing * * @return mixed array on success, false otherwise */ protected function sanitize_excludes( $primary, $secondary, $base = '' ) { $sanitized = array(); $basedir = trim( $base ); $normalize = !empty( $basedir ); // Normalize the trailing directory separator on the path $basedir = rtrim( $basedir, self::DIRECTORY_SEPARATORS ) . self::NORM_DIRECTORY_SEPARATOR; // Normalize platform specific directory separators in path $basedir = str_replace( DIRECTORY_SEPARATOR, self::NORM_DIRECTORY_SEPARATOR, $basedir ); // $primary is considered to be unclean foreach ( $primary as $exclude ) { // Reset flag for whether this exclude is a directory-like exclude $is_directory_path = false; // Get rid of standard prefix/suffix detritus $exclude = trim( $exclude ); // Possible that we could end up with an empty entry // Also ignore if any wildcard characters present if ( !empty( $exclude ) && ( !preg_match( '|[?*]|', $exclude ) ) ) { // Remember if it has a directory separator suffix if ( preg_match( '|[' . addslashes( self::DIRECTORY_SEPARATORS ) . ']$|', $exclude ) ) { $is_directory_path = true; } // Remove what could be multiple prefix or suffix directory separators $exclude = trim( $exclude, self::DIRECTORY_SEPARATORS ); // Make sure platform specific directory separators in path become normalized $exclude = str_replace( DIRECTORY_SEPARATOR, self::NORM_DIRECTORY_SEPARATOR, $exclude ); // And add back a single instance prefix $exclude = self::NORM_DIRECTORY_SEPARATOR . $exclude; // And optionally a single instance suffix if ( $is_directory_path ) { $exclude .= self::NORM_DIRECTORY_SEPARATOR; } $sanitized[] = $exclude; } } // $secondary is considered to be clean if ( !empty( $secondary ) ) { $sanitized = array_merge( $sanitized, $secondary ); } // Get unique entries and renumber numeric keys $sanitized = array_merge( array_unique( $sanitized ) ); if ( true == $normalize ) { // Make sure the normalize base has a trailing directory separator $basedir = ( rtrim( $basedir, self::NORM_DIRECTORY_SEPARATOR ) ) . self::NORM_DIRECTORY_SEPARATOR; foreach ( $sanitized as &$exclusion ) { // Must remove any leading DIRECTORY_SEPARATOR because $basedir always has trailing $exclusion = ltrim( $exclusion, self::NORM_DIRECTORY_SEPARATOR ); $exclusion = ( $basedir . $exclusion ); } } return $sanitized; } /** * get_available_zip_methods() * * Returns the array of zip methods that are available for the mode of this object * Libraries must have been loaded already * * @param array The supported zip methods * @param array The array which will hold the available methods * @param array The array that will hold the available methods attributes (method tag is key) * @return bool True if methods available, False otherwise * */ protected function get_available_zip_methods( array $supported_zip_methods, &$available_methods, &$available_methods_details ) { // Make sure these are cleared as the caller might not have done so $available_methods = array(); $available_methods_details = array(); // Try each supported zip method to see what it can do on this system foreach ( $supported_zip_methods as $method_tag ) { $class_name = 'pluginbuddy_zbzip' . $method_tag; $zipper = new $class_name( $this ); if ( true === $zipper->is_available( $this->_tempdir ) ) { $available_methods[] = $method_tag; $available_methods_details[ $method_tag ] = $zipper->get_method_details(); } unset( $zipper ); } return ( !empty( $available_methods ) ); } /** * get_compatibility_zip_methods() * * Returns the array of zip methods that are regarded as "compatibility" methods * Libraries must have been loaded already * * @return array Flat array of zip methods (could be empty) * */ protected function get_compatibility_zip_methods() { $compatibility_methods = array(); foreach ( $this->_zip_methods as $method_tag ) { $class_name = 'pluginbuddy_zbzip' . $method_tag; $zipper = new $class_name( $this ); if ( $zipper->get_is_compatibility_method() === true ) { $compatibility_methods[] = $method_tag; } unset( $zipper ); } return $compatibility_methods; } /** * get_best_zip_methods() * * Returns the array of best zip method(s) * For now we assume only one "best" method as being the first method in the list * that has the requested attribute(s). * Libraries must have been loaded already * * @param array $attributes Array of attributes to check method supports * @return array Flat array of zip methods (could be empty) * */ protected function get_best_zip_methods( $attributes = array() ) { $best_methods = array(); if ( !empty( $this->_zip_methods ) ) { if ( empty( $attributes ) ) { // No attributes to test for so just take the first in the list $best_methods[] = $this->_zip_methods[ 0 ]; } else { // Have some attributes to test so work along the list until we find a match or end of list foreach ( $this->_zip_methods as $method_tag ) { // Start afresh each time - assume success $match = true; foreach ( $attributes as $attribute ) { // Check each attribute in turn (precondition that all attributes set true/false) $match = ( $match && ( $this->_zip_methods_details[ $method_tag ][ 'attr' ][ $attribute ] === true ) ); } if ( true === $match ) { // Found our matching method so break out of the test loop to return $best_methods[] = $method_tag; break; } } } } return $best_methods; } /** * add_directory_to_zip() * * Adds a directory to a new or existing (TODO: not yet available) ZIP file. * * @param string Full path & filename of ZIP file to create. * @param string Full directory to add to zip file. * @param array( string ) Array of strings of paths/files to exclude from zipping * @param string Full directory path to directory to temporarily place ZIP * @param boolean True: only use PCLZip. False: try all available * * @return true on success, false otherwise * */ public function add_directory_to_zip( $zip_file, $add_directory, $excludes = array(), $temporary_zip_directory = '' ) { if ( true === $this->_is_experimental ) { pb_backupbuddy::status( 'message', __('Running alternative ZIP system (BETA) based on settings.','it-l10n-backupbuddy' ) ); } else { pb_backupbuddy::status( 'message', __('Running standard ZIP system based on settings.','it-l10n-backupbuddy' ) ); } // Let's just log if this is a 32 or 64 bit system $php_size = ( pluginbuddy_stat::is_php( pluginbuddy_stat::THIRTY_TWO_BIT ) ) ? "32" : "64" ; pb_backupbuddy::status( 'details', sprintf( __( 'Running under %1$s-bit PHP', 'it-l10n-backupbuddy' ), $php_size ) ); // Make sure we tell what the sapi is pb_backupbuddy::status( 'details', sprintf( __( 'Server API: %1$s', 'it-l10n-backupbuddy' ), $this->get_sapi_name() ) ); $zip_methods = array(); $sanitized_excludes = array(); $listmaker = NULL; // Set some additional system excludes here for now - these are all from the site install root $additional_excludes = array( self::NORM_DIRECTORY_SEPARATOR . 'importbuddy' . self::NORM_DIRECTORY_SEPARATOR, self::NORM_DIRECTORY_SEPARATOR . 'importbuddy.php', self::NORM_DIRECTORY_SEPARATOR . 'wp-content' . self::NORM_DIRECTORY_SEPARATOR . 'uploads' . self::NORM_DIRECTORY_SEPARATOR . 'pb_backupbuddy' . self::NORM_DIRECTORY_SEPARATOR, ); // Make sure we have a valid zip method strategy setting to use otherwise fall back to emergency compatibility if ( isset( pb_backupbuddy::$options[ 'zip_method_strategy' ] ) && ( '0' !== pb_backupbuddy::$options[ 'zip_method_strategy' ] ) ) { $zip_method_strategy = pb_backupbuddy::$options[ 'zip_method_strategy' ]; switch ( $zip_method_strategy ) { case "1": // Best Available $zip_methods = $this->get_best_zip_methods( array( 'is_archiver' ) ); pb_backupbuddy::status( 'details', __('Using Best Available zip method based on settings.','it-l10n-backupbuddy' ) ); break; case "2": // All Available $zip_methods = $this->_zip_methods; pb_backupbuddy::status( 'details', __('Using All Available zip methods in preferred order based on settings.','it-l10n-backupbuddy' ) ); break; case "3": // Force Compatibility $zip_methods = $this->get_compatibility_zip_methods(); pb_backupbuddy::status( 'message', __('Using Forced Compatibility zip method based on settings.','it-l10n-backupbuddy' ) ); break; default: // Hmm...unrecognized value - emergency compatibility $zip_methods = $this->get_compatibility_zip_methods(); pb_backupbuddy::status( 'message', sprintf( __('Forced Compatibility Mode as Zip Method Strategy setting not recognized: %1$s','it-l10n-backupbuddy' ), $zip_method_strategy ) ); } } else { // We got no or an invalid zip method strategy which is a bad situation - emergency compatibility is the order of the day $zip_methods = $this->get_compatibility_zip_methods(); pb_backupbuddy::status( 'message', __('Forced Compatibility Mode as Zip Method Strategy not set or setting not recognized.','it-l10n-backupbuddy' ) ); } // Better make sure we have some available methods if ( empty( $zip_methods ) ) { // Hmm, we don't seem to have any available methods, oops, best go no further pb_backupbuddy::status( 'details', __('Failed to create a Zip Archive file - no available methods.','it-l10n-backupbuddy' ) ); // We should have a temporary directory, must get rid of it, can simply rmdir it as it will (should) be empty if ( !empty( $temporary_zip_directory ) && file_exists( $temporary_zip_directory ) ) { if ( !rmdir( $temporary_zip_directory ) ) { pb_backupbuddy::status( 'details', __('Temporary directory could not be deleted: ','it-l10n-backupbuddy' ) . $temporary_zip_directory ); } } return false; } pb_backupbuddy::status( 'details', __('Creating ZIP file','it-l10n-backupbuddy' ) . ' `' . $zip_file . '`. ' . __('Adding directory','it-l10n-backupbuddy' ) . ' `' . $add_directory . '`. ' . __('Excludes','it-l10n-backupbuddy' ) . ': ' . implode( ',', $excludes ) ); // We'll try and allow exclusions for pclzip if we can include_once( pb_backupbuddy::plugin_path() . '/lib/' . $this->_whereami . '/zbdir.php' ); if ( class_exists( 'pluginbuddy_zbdir' ) ) { // Generate our sanitized list of directories/files to exclude as absolute paths (normalized) for zbdir $sanitized_excludes = $this->sanitize_excludes( $excludes, $additional_excludes, $add_directory ); // Now let's create the list of items to add to the zip - first build the tree $listmaker = new pluginbuddy_zbdir( $add_directory, $sanitized_excludes ); // Re-generate our sanitized list of directories/files to exclude as relative paths // Slight kludge to deal with being able to enable/disable the inclusion processing // (currently configured in wp-config.php) so always need to provide the excludes as // relative path for now. This needs to be tidied up in future if/when the capability // is established as standard $sanitized_excludes = $this->sanitize_excludes( $excludes, $additional_excludes ); } else { // Generate our sanitized list of directories/files to exclude as relative paths $sanitized_excludes = $this->sanitize_excludes( $excludes, $additional_excludes ); } // Iterate over the methods - once we succeed just return directly otherwise drop through foreach ( $zip_methods as $method_tag ) { // First make sure we can archive with this method if ( $this->_zip_methods_details[ $method_tag ][ 'attr' ][ 'is_archiver' ] === true ) { $class_name = 'pluginbuddy_zbzip' . $method_tag; $zipper = new $class_name( $this ); $zipper->set_status_callback( array( &$this, 'status' ) ); // We need to tell the method what details belong to it $zipper->set_method_details( $this->_zip_methods_details[ $method_tag ] ); // Tell the method the server api in use $zipper->set_sapi_name( $this->get_sapi_name() ); pb_backupbuddy::status( 'details', __('Trying ', 'it-l10n-backupbuddy' ) . $method_tag . __(' method for ZIP.','it-l10n-backupbuddy' ) ); // As we are looping make sure we have no stale file information clearstatcache(); // The temporary zip directory _must_ exist if ( !empty( $temporary_zip_directory ) ) { if ( !file_exists( $temporary_zip_directory ) ) { // Create temp dir if it does not exist. mkdir( $temporary_zip_directory ); } } // Now we are ready to try and produce the backup if ( $zipper->create( $zip_file, $add_directory, $sanitized_excludes, $temporary_zip_directory, $listmaker ) === true ) { // Got a valid zip file so we can just return - method will have cleaned up the temporary directory pb_backupbuddy::status( 'details', __('The ', 'it-l10n-backupbuddy' ) . $method_tag . __(' method for ZIP was successful.','it-l10n-backupbuddy' ) ); unset( $zipper ); // We have to return here because we cannot break out of foreach return true; } else { // Method will have cleaned up the temporary directory pb_backupbuddy::status( 'details', __('The ', 'it-l10n-backupbuddy' ) . $method_tag . __(' method for ZIP was unsuccessful.','it-l10n-backupbuddy' ) ); unset( $zipper ); } } else { // This method is not considered suitable (reliable enough) for creating archives or lacked zip capability pb_backupbuddy::status( 'details', __('The ','it-l10n-backupbuddy' ) . $method_tag . __(' method is not currently supported for backup.','it-l10n-backupbuddy' ) ); } } // If we get here then have failed in all attempts pb_backupbuddy::status( 'details', __('Failed to create a Zip Archive file with any nominated method.','it-l10n-backupbuddy' ) ); return false; } /** * unzip() * * Extracts the contents of a zip file to the specified directory using the best unzip methods possible. * * @param string $zip_file Full path & filename of ZIP file to extract from. * @param string $destination_directory Full directory path to extract into. * @param bool|string $force_compatibility_mode False: use all available, String: use that method * * @return bool true on success, false otherwise */ public function unzip( $zip_file, $destination_directory, $force_compatibility_mode = false ) { $zip_methods = array(); // The following is just to match current functionality for importbuddy - ideally would rather // do it by selecting available compatibility methods based on method attributes - may do that later // (would also need get_compatibility_zip_methods() to be updated to take parameter to check // whether compatibility method for that particular function. // Decide which methods we are going to try if ( $force_compatibility_mode == 'ziparchive' ) { $zip_methods = array( 'ziparchive' ); pb_backupbuddy::status( 'message', __('Forced compatibility unzip method (ZipArchive; medium speed) based on settings.','it-l10n-backupbuddy' ) ); } elseif ( $force_compatibility_mode == 'pclzip' ) { $zip_methods = array( 'pclzip' ); pb_backupbuddy::status( 'message', __('Forced compatibility unzip method (PCLZip; slow speed) based on settings.','it-l10n-backupbuddy' ) ); } else { $zip_methods = $this->_zip_methods; pb_backupbuddy::status( 'details', __('Using all available unzip methods in preferred order.','it-l10n-backupbuddy' ) ); } // Better make sure we have some available methods if ( empty( $zip_methods ) ) { // Hmm, we don't seem to have any available methods, oops, best go no further pb_backupbuddy::status( 'details', sprintf( __('Unable to extract backup file contents (%1$s to %2$s): No available unzip methods found.','it-l10n-backupbuddy' ), $zip_file, $destination_directory ) ); return false; } // Make sure we have a normalized directory separator suffix $destination_directory = rtrim( $destination_directory, self::DIRECTORY_SEPARATORS ) . self::NORM_DIRECTORY_SEPARATOR; // Iterate over the methods - once we succeed just return directly otherwise drop through foreach ( $zip_methods as $method_tag ) { // First make sure we can check file existence with this method (ignore silently if not) // Note: has to be able to unzip as well but if that functionality wasn't available in // the method the is_checker attribute will have been set false if ( $this->_zip_methods_details[ $method_tag ][ 'attr' ][ 'is_unarchiver' ] === true ) { $class_name = 'pluginbuddy_zbzip' . $method_tag; $zipper = new $class_name( $this ); $zipper->set_status_callback( array( &$this, 'status' ) ); // We need to tell the method what details belong to it $zipper->set_method_details( $this->_zip_methods_details[ $method_tag ] ); // Now we are ready to try and extract the backup $result = $zipper->extract( $zip_file, $destination_directory ); // Will be false if we couldn't extract the backup if ( $result === true ) { // Must assume that we extracted ok unset( $zipper ); // We have to return here because we cannot break out of foreach return true; } else { // The zipper encountered an error so we need to drop through and loop round to try another // We'll not process the result here, just drop through silently (the method will have logged it) unset( $zipper ); } } } // If we got this far then no method to extract backup content was available or worked pb_backupbuddy::status( 'details', sprintf( __('Unable to extract backup file contents (%1$s to %2$s): No compatible zip method found.','it-l10n-backupbuddy' ), $zip_file, $destination_directory ) ); return false; } /** * extract() * * Extracts the specified contents of a zip file to the specified destination using the best unzip methods possible. * The destination directory _must_ already exist and be writable - this function does not create it * The items are an array of mapping of what => where, e.g. * array( "dir/myfile.txt" => "", "dir/myfile.txt" => "tmpfilename", "dir/myfile.txt" => "dir/myfile.txt" ) * In the first case the file is extracted to $destination_directory/myfile.txt * In the second case the file is extracted to $destination_directory/tmpfilename * In the third case the file is extracted to $destination_directory/dir/myfile.txt * Note: in the second case the file is initially extrcated as myfile.txt and then rename to tmpfilename * Another example is for directory extraction: * array( "dir/*" => "dir/*" ) * Whereby the directory dir and all it's content (recursively) is extracted to $destination/dir * Note: the * is required otherwise you just get an empty directory * * @param string $zip_file Full path & filename of ZIP file to extract from. * @param string $destination_directory Full directory path to extract to * @param array $items Mapping of what to extract and to what * * @return bool true on success (all extractions successful), false otherwise */ public function extract( $zip_file, $destination_directory, $items ) { $zip_methods = array(); // The following is just to match current functionality for importbuddy - ideally would rather // do it by selecting available compatibility methods based on method attributes - may do that later // (would also need get_compatibility_zip_methods() to be updated to take parameter to check // whether compatibility method for that particular function. $zip_methods = $this->_zip_methods; // Better make sure we have some available methods if ( empty( $zip_methods ) ) { // Hmm, we don't seem to have any available methods, oops, best go no further pb_backupbuddy::status( 'details', sprintf( __('Unable to extract from backup file (%1$s to %2$s): No available unzip methods found.','it-l10n-backupbuddy' ), $zip_file, $destination ) ); return false; } if ( !( file_exists( $destination_directory ) && is_dir( $destination_directory ) && is_writable( $destination_directory ) ) ) { pb_backupbuddy::status( 'details', sprintf( __('Unable to extract from backup file (%1$s to %2$s): %2$s does not exist, is not a directory or is not writeable','it-l10n-backupbuddy' ), $zip_file, $destination_directory ) ); return false; } // Make sure we have a normalized directory separator suffix $destination_directory = rtrim( $destination_directory, self::DIRECTORY_SEPARATORS ) . self::NORM_DIRECTORY_SEPARATOR; // Iterate over the methods - once we succeed just return directly otherwise drop through foreach ( $zip_methods as $method_tag ) { // First make sure we can check file existence with this method (ignore silently if not) // Note: has to be able to unzip as well but if that functionality wasn't available in // the method the is_checker attribute will have been set false if ( $this->_zip_methods_details[ $method_tag ][ 'attr' ][ 'is_extractor' ] === true ) { $class_name = 'pluginbuddy_zbzip' . $method_tag; $zipper = new $class_name( $this ); $zipper->set_status_callback( array( &$this, 'status' ) ); // We need to tell the method what details belong to it $zipper->set_method_details( $this->_zip_methods_details[ $method_tag ] ); // Now we are ready to try and extract from the backup $result = $zipper->extract( $zip_file, $destination_directory, $items ); // Will be false if we couldn't extract from the backup if ( $result === true ) { // Must assume that we extracted ok unset( $zipper ); // We have to return here because we cannot break out of foreach return true; } else { // The zipper encountered an error so we need to drop through and loop round to try another // We'll not process the result here, just drop through silently (the method will have logged it) unset( $zipper ); } } } // If we got this far then no method to extract from backup content was available or worked pb_backupbuddy::status( 'details', sprintf( __('Unable to extract from backup file (%1$s to %2$s): No compatible zip method found.','it-l10n-backupbuddy' ), $zip_file, $destination_directory ) ); return false; } /** * file_exists() * * Tests whether a file (with path) exists in the given zip file * If leave_open is true then the zip object will be left open for faster checking for subsequent files within this zip * * @param string $zip_file The zip file to check * @param string $locate_file The file to test for * @param bool $leave_open Optional: True if the zip file should be left open * @return bool True if the file is found in the zip otherwise false * */ public function file_exists( $zip_file, $locate_file, $leave_open = false ) { $zip_methods = array(); $zip_methods = $this->_zip_methods; // Better make sure we have some available methods if ( empty( $zip_methods ) ) { // Hmm, we don't seem to have any available methods, oops, best go no further pb_backupbuddy::status( 'details', __('Failed to check file exists - no available methods.','it-l10n-backupbuddy' ) ); return false; } // Iterate over the methods - once we succeed just return directly otherwise drop through foreach ( $zip_methods as $method_tag ) { // First make sure we can check file existence with this method (ignore silently if not) // Note: has to be able to unzip as well but if that functionality wasn't available in // the method the is_checker attribute will have been set false if ( $this->_zip_methods_details[ $method_tag ][ 'attr' ][ 'is_checker' ] === true ) { $class_name = 'pluginbuddy_zbzip' . $method_tag; $zipper = new $class_name( $this ); $zipper->set_status_callback( array( &$this, 'status' ) ); // We need to tell the method what details belong to it $zipper->set_method_details( $this->_zip_methods_details[ $method_tag ] ); // Now we are ready to try and test for the file existence $result = $zipper->file_exists( $zip_file, $locate_file, $leave_open ); // Will be true/false if file found/not-found otherwise error information array if ( !is_array( $result ) ) { // Either we found the file or we didn't but we have a valid result unset( $zipper ); // We have to return here because we cannot break out of foreach return $result; } else { // The zipper encountered an error so we need to drop through and loop round to try another // We'll not process the result here, just drop through silently (the method will have logged it) unset( $zipper ); } } } // If we got this far then no method to check backup content was available or worked pb_backupbuddy::status( 'details', sprintf( __('Unable to check if file exists (looking for %1$s in %2$s): No compatible zip method found.','it-l10n-backupbuddy' ), $locate_file, $zip_file ) ); return false; } /* get_file_list() * * Get an array of all files in a zip file with some file properties. * * @param string $zip_file The file to list the content of * @return bool|array false on failure, otherwise array of file properties (may be empty) */ public function get_file_list( $zip_file ) { $zip_methods = array(); $zip_methods = $this->_zip_methods; // Better make sure we have some available methods if ( empty( $zip_methods ) ) { // Hmm, we don't seem to have any available methods, oops, best go no further pb_backupbuddy::status( 'details', __('Failed to list backup file contents - no available methods.','it-l10n-backupbuddy' ) ); return false; } // Iterate over the methods - once we succeed just return directly otherwise drop through foreach ( $zip_methods as $method_tag ) { // First make sure we can list backup file content with this method (ignore silently if not) // Note: has to be able to unzip as well but if that functionality wasn't available in // the method the is_lister attribute will have been set false if ( $this->_zip_methods_details[ $method_tag ][ 'attr' ][ 'is_lister' ] === true ) { $class_name = 'pluginbuddy_zbzip' . $method_tag; $zipper = new $class_name( $this ); $zipper->set_status_callback( array( &$this, 'status' ) ); // We need to tell the method what details belong to it $zipper->set_method_details( $this->_zip_methods_details[ $method_tag ] ); // Now we are ready to try and test for the file existence $result = $zipper->get_file_list( $zip_file ); // Will be false if we couldn't list contents or file list array otherwise if ( is_array( $result ) ) { // We got a list so better assume it is ok unset( $zipper ); // We have to return here because we cannot break out of foreach return $result; } else { // The zipper encountered an error so we need to drop through and loop round to try another // We'll not process the result here, just drop through silently (the method will have logged it) unset( $zipper ); } } } // If we got this far then no method to list backup file content was available or worked pb_backupbuddy::status( 'details', sprintf( __('Unable to check file content of backup (%1$s): No compatible zip method found.','it-l10n-backupbuddy' ), $zip_file ) ); return false; } /* set_comment() * * Retrieve archive comment. * * @param string $zip_file Filename of archive to set comment on. * @param string|array $comment Comment to apply to archive. If array, json encoded. Deliminated with MetaData: and MetaData-End:. * @return bool|string true on success, error message otherwise. */ public function set_comment( $zip_file, $comment ) { $zip_methods = array(); $zip_methods = $this->_zip_methods; // Better make sure we have some available methods if ( empty( $zip_methods ) ) { // Hmm, we don't seem to have any available methods, oops, best go no further pb_backupbuddy::status( 'details', __('Failed to set comment in backup file - no available methods.','it-l10n-backupbuddy' ) ); return false; } // Encode $comment if an array. Handle delimination. if ( is_array( $comment ) ) { $comment = json_encode( $comment ); } $comment = 'MetaData:' . $comment . 'MetaData-End:'; // Iterate over the methods - once we succeed just return directly otherwise drop through foreach ( $zip_methods as $method_tag ) { // First make sure we can manage comments with this method (ignore silently if not) if ( $this->_zip_methods_details[ $method_tag ][ 'attr' ][ 'is_commenter' ] === true ) { $class_name = 'pluginbuddy_zbzip' . $method_tag; $zipper = new $class_name( $this ); $zipper->set_status_callback( array( &$this, 'status' ) ); // We need to tell the method what details belong to it $zipper->set_method_details( $this->_zip_methods_details[ $method_tag ] ); // Now we are ready to try and test for the file existence $result = $zipper->set_comment( $zip_file, $comment ); // Will be false if we couldn't set the comment if ( $result === true ) { // Must assume that comment was set ok unset( $zipper ); // We have to return here because we cannot break out of foreach return true; } else { // The zipper encountered an error so we need to drop through and loop round to try another // We'll not process the result here, just drop through silently (the method will have logged it) unset( $zipper ); } } } // If we got this far then couldn't set a comment at all - either no available method or all method failed pb_backupbuddy::status( 'details', sprintf( __('Unable to set comment in file %1$s: No compatible zip method found or all methods failed - note stored internally only.','it-l10n-backupbuddy' ), $zip_file ) ); // Return message for display - maybe should return false and have caller display it's own message? $message = "\n\nUnable to set note in file.\nThe note will only be stored internally in your settings and not in the zip file itself."; return $message; } /* get_comment() * * Retrieve archive comment. * * @param string $zip_file Filename of archive to retrieve comment from. * @param bool $raw_comment If true then raw comment field data returned without processing deliminators nor json. Defaults false. * @return bool|string|array false on failure, Zip comment otherwise. If comment is json encoded array returns array. */ public function get_comment( $zip_file, $raw_comment = false ) { $zip_methods = array(); $zip_methods = $this->_zip_methods; // Better make sure we have some available methods if ( empty( $zip_methods ) ) { // Hmm, we don't seem to have any available methods, oops, best go no further pb_backupbuddy::status( 'details', __('Failed to get comment from backup file - no available methods.','it-l10n-backupbuddy' ) ); return false; } // Iterate over the methods - once we succeed just return directly otherwise drop through foreach ( $zip_methods as $method_tag ) { // First make sure we can manage comments with this method (ignore silently if not) if ( $this->_zip_methods_details[ $method_tag ][ 'attr' ][ 'is_commenter' ] === true ) { $class_name = 'pluginbuddy_zbzip' . $method_tag; $zipper = new $class_name( $this ); $zipper->set_status_callback( array( &$this, 'status' ) ); // We need to tell the method what details belong to it $zipper->set_method_details( $this->_zip_methods_details[ $method_tag ] ); // Now we are ready to try and test for the file existence $result = $zipper->get_comment( $zip_file ); // Will be false if we couldn't set the comment if ( is_string ( $result ) ) { // Format has changed and no longer encoding as htmlemtities when setting comment // For older backups may need to remove encoding - action _should_ be null if N/A // Only spanner would be if someone had put an entity in their comment but that is // really an outsider and in any case the correction is simply to edit and resave // TODO: Remove this when new format has been in use for some time $result = html_entity_decode( $result ); // Must assume that comment was retrieved ok unset( $zipper ); // Return raw comment as-is with no processing if specified. if ( true === $raw_comment ) { return $result; } // Handle delimination. Decode $result if json decoded (associative array mode). $start_deliminator = strpos( $result, 'MetaData:' ); $end_deliminator = strpos( $result, 'MetaData-End:' ); if ( ( false !== $start_deliminator ) && ( false !== $end_deliminator ) ) { // Found both deliminators. $result = substr( $result, $start_deliminator+9, $end_deliminator-9 ); if ( NULL === ( $decoded_result = json_decode( $result, true ) ) ) { // Json decode failed so return string. return $result; } else { // Json decode success so returning variable (should be an array most likely). return $decoded_result; } } // No deliminators found if made it to this point so assuming plain text legacy comment (or deliminators missing/corrupt). // We have to return here because we cannot break out of foreach return $result; } else { // The zipper encountered an error so we need to drop through and loop round to try another // We'll not process the result here, just drop through silently (the method will have logged it) unset( $zipper ); } } } // If we got this far then couldn't get a comment at all - either no available method or all method failed pb_backupbuddy::status( 'details', sprintf( __('Unable to get comment in file %1$s: No compatible zip method found or all methods failed.','it-l10n-backupbuddy' ), $zip_file ) ); return false; } // FOR FUTURE USE; NOT YET IMPLEMENTED. Use to check .sql file is non-empty. function file_stats( $zip_file, $locate_file, $leave_open = false ) { if ( in_array( 'ziparchive', $this->_zip_methods ) ) { $this->_zip = new ZipArchive; if ( $this->_zip->open( $zip_file ) === true ) { if ( ( $stats = $this->_zip->statName( $locate_file ) ) === false ) { // File not found in zip. $this->_zip->close(); pb_backupbuddy::status( 'details', __('File not found (ziparchive) for stats','it-l10n-backupbuddy' ) . ': ' . $locate_file ); return false; } $this->_zip->close(); return $stats; } else { pb_backupbuddy::status( 'details', sprintf( __('ZipArchive failed to open file to check stats (looking in %1$s).','it-l10n-backupbuddy' ), $zip_file ) ); return false; } } // If we made it this far then ziparchive not available/failed. if ( in_array( 'pclzip', $this->_zip_methods ) ) { require_once( ABSPATH . 'wp-admin/includes/class-pclzip.php' ); $this->_zip = new PclZip( $zip_file ); if ( ( $file_list = $this->_zip->listContent() ) == 0 ) { // If zero, zip is corrupt or empty. pb_backupbuddy::status( 'details', $this->_zip->errorInfo( true ) ); } else { foreach( $file_list as $file ) { if ( $file['filename'] == $locate_file ) { // Found file. return true; } } pb_backupbuddy::status( 'details', __('File not found (pclzip)','it-l10n-backupbuddy' ) . ': ' . $locate_file ); return false; } } else { pb_backupbuddy::status( 'details', __('Unable to check if file exists: No compatible zip method found.','it-l10n-backupbuddy' ) ); return false; } } /** * set_status_callback() * * Sets a reference to the function to call for each status update. * Argument must at least be a non-empty array with 2 elements * * @param array $callback Object->method to call for status updates. * @return null * */ public function set_status_callback( $callback = array() ) { if ( is_array( $callback ) && !empty( $callback ) && ( 2 == count( $callback ) ) ) { $this->_status_callback = $callback; $this->_have_status_callback = true; } } /** * status() * * Invoke status method of parent if it exists * Must be at least one parameter otherwise ignore the call * * @param string (Expected) Status message type. * @param string (Expected) Status message. * @return null * */ public function status() { if ( $this->_have_status_callback && ( func_num_args() > 0 ) ) { $args = func_get_args(); call_user_func_array( $this->_status_callback, $args ); } } } // End class //$pluginbuddy_zipbuddy = new pluginbuddy_zipbuddy( $this->_options['backup_directory'] ); }