isLink() ) {
$files_size += $file->getSize();
}
}
return self::format_size( $files_size );
}
/**
* Format size in human readable format.
*
* @param int $size The size in bytes.
*
* @return string
*/
protected static function format_size( $size ): string {
return ' (' . size_format( $size, 2 ) . ')';
}
/**
* Get an absolute path if it is relative.
*
* @param string $path The path to resolve.
*
* @return string
*/
public static function get_absolute_path( $path = '/' ) {
$path = BackWPup_Path_Fixer::slashify( $path );
$content_path = trailingslashit( BackWPup_Path_Fixer::slashify( (string) WP_CONTENT_DIR ) );
// Use WP_CONTENT_DIR as root folder.
if ( empty( $path ) || '/' === $path ) {
$path = $content_path;
}
// Make relative path to absolute.
if ( '/' !== substr( $path, 0, 1 ) && ! preg_match( '#^[a-zA-Z]+:/#', $path ) ) {
$path = $content_path . $path;
}
return self::resolve_path( $path );
}
/**
* Check if folder is readable and exists. Create it if not.
* Add .htaccess or index.html file in folder to prevent directory listing.
*
* @param string $folder The folder to check.
* @param bool $donotbackup Create a file that the folder will not be backed up.
*
* @return string Error message if any.
*/
public static function check_folder( string $folder, bool $donotbackup = false ): string {
$folder = self::get_absolute_path( $folder );
$folder = untrailingslashit( $folder );
// Check that is not home of WP.
$uploads = self::get_upload_dir();
if ( untrailingslashit( BackWPup_Path_Fixer::slashify( ABSPATH ) ) === $folder
|| untrailingslashit( BackWPup_Path_Fixer::slashify( dirname( ABSPATH ) ) ) === $folder
|| untrailingslashit( BackWPup_Path_Fixer::slashify( WP_PLUGIN_DIR ) ) === $folder
|| untrailingslashit( BackWPup_Path_Fixer::slashify( WP_CONTENT_DIR ) ) === $folder
|| untrailingslashit( BackWPup_Path_Fixer::slashify( $uploads ) ) === $folder
) {
return sprintf(
/* translators: %s: folder path. */
__( 'Folder %1$s not allowed, please use another folder.', 'backwpup' ),
$folder
);
}
// Open base dir check.
if ( ! self::is_in_open_basedir( $folder ) ) {
return sprintf(
/* translators: %s: folder path. */
__( 'Folder %1$s is not in open basedir, please use another folder.', 'backwpup' ),
$folder
);
}
// We always want to at least process `$folder`.
$folders_to_process = [ $folder ];
$parent_folder = dirname( $folder );
while ( ! file_exists( $parent_folder ) ) {
array_unshift( $folders_to_process, $parent_folder );
$parent_folder = dirname( $parent_folder );
}
// Process each child folder separately.
foreach ( $folders_to_process as $child_folder ) {
if ( ! is_dir( $child_folder ) && ! wp_mkdir_p( $child_folder ) ) {
return sprintf(
/* translators: %s: folder path. */
__( 'Cannot create folder: %1$s', 'backwpup' ),
$child_folder
);
}
// phpcs:ignore WordPress.WP.AlternativeFunctions.file_system_operations_is_writable
if ( ! is_writable( $child_folder ) ) {
return sprintf(
/* translators: %s: folder path. */
__( 'Folder "%1$s" is not writable', 'backwpup' ),
$child_folder
);
}
// Create files for securing folder.
/**
* Filter whether BackWPup will protect the folders.
*
* @param bool $protect_folders Whether the folder will be protect or not.
*/
$protect_folders = wpm_apply_filters_typed( 'boolean', 'backwpup_protect_folders', true );
if ( $protect_folders ) {
self::protect_folder( $child_folder ); // phpcs:ignore
}
// Create do not backup file for this folder.
if ( $donotbackup ) {
self::write_do_not_backup_file( $child_folder );
}
}
return '';
}
/**
* Normalize a relative path.
*
* @param string $path The path to normalize.
*
* @return string The normalized path.
* @throws InvalidArgumentException If path is absolute or attempts to navigate above root.
*/
public static function normalize_path( string $path ): string {
if ( 0 === strpos( $path, '/' ) ) {
throw new InvalidArgumentException( 'Absolute paths are not allowed.' );
}
$parts = explode( '/', $path );
$normalized = [];
foreach ( $parts as $part ) {
if ( '..' === $part ) {
if ( empty( $normalized ) ) {
throw new InvalidArgumentException(
'Invalid path: Attempting to navigate above the root directory.'
);
}
array_pop( $normalized );
} elseif ( '.' !== $part && '' !== $part ) {
$normalized[] = $part;
}
}
if ( empty( $normalized ) ) {
throw new InvalidArgumentException( 'The path resolves to an empty path.' );
}
return implode( '/', $normalized );
}
/**
* Resolve internal .. within a path.
*
* @param string $path The path to resolve.
*
* @return string The resolved path
*/
protected static function resolve_path( $path ): string {
$parts = explode( '/', $path );
$resolved_parts = [];
foreach ( $parts as $part ) {
if ( '..' === $part ) {
if ( ! empty( $resolved_parts ) ) {
array_pop( $resolved_parts );
}
} elseif ( '.' === $part ) {
continue;
} else {
$resolved_parts[] = $part;
}
}
return implode( '/', $resolved_parts );
}
/**
* Protect a folder from being listed.
*
* @param string $folder The folder to protect.
*
* @return void
*/
private static function protect_folder( string $folder ): void {
$server_software = '';
if ( isset( $_SERVER['SERVER_SOFTWARE'] ) ) {
$server_software = sanitize_text_field( wp_unslash( $_SERVER['SERVER_SOFTWARE'] ) );
}
$server_software = strtolower( $server_software );
if ( strstr( $server_software, 'microsoft-iis' ) ) {
if ( ! file_exists( $folder . '/Web.config' ) ) {
// phpcs:ignore WordPress.WP.AlternativeFunctions.file_system_operations_file_put_contents -- Writing small protection files in-place.
file_put_contents(
$folder . '/Web.config',
'' . PHP_EOL .
"\t" . PHP_EOL .
"\t\t" . PHP_EOL .
"\t\t\t" . PHP_EOL .
"\t\t" . PHP_EOL .
"\t" . PHP_EOL .
''
);
}
} elseif ( strstr( $server_software, 'nginx' ) ) {
if ( ! file_exists( $folder . '/index.php' ) ) {
// phpcs:ignore WordPress.WP.AlternativeFunctions.file_system_operations_file_put_contents -- Writing small protection files in-place.
file_put_contents(
$folder . '/index.php',
'' . PHP_EOL .
'' . PHP_EOL .
'Require all denied' . PHP_EOL .
'' . PHP_EOL .
'' . PHP_EOL .
'' . PHP_EOL .
'Deny from all' . PHP_EOL .
'' . PHP_EOL .
'' . PHP_EOL .
'';
$needs_write = ! file_exists( $htaccess_path )
|| strpos( (string) file_get_contents( $htaccess_path ), 'mod_access.c' ) !== false; // phpcs:ignore WordPress.WP.AlternativeFunctions.file_get_contents_file_get_contents -- Reading a local file for stale-content detection.
if ( $needs_write ) {
// phpcs:ignore WordPress.WP.AlternativeFunctions.file_system_operations_file_put_contents -- Writing small protection files in-place.
file_put_contents( $htaccess_path, $htaccess_content );
}
if ( ! file_exists( $folder . '/index.php' ) ) {
// phpcs:ignore WordPress.WP.AlternativeFunctions.file_system_operations_file_put_contents -- Writing small protection files in-place.
file_put_contents(
$folder . '/index.php',
'