$full, 'name' => $name, 'size' => (float) $size, 'mtime' => (int) self::field( $block, 269, 12 ), 'data_offset' => ftell( $fh ), ); } /** * Checks the end-of-archive block. * * @param string $file Path. * @return bool */ public static function is_complete( $file ) { $size = (float) filesize( $file ); if ( $size < self::HEADER * 2 ) { return false; } $fh = fopen( $file, 'rb' ); fseek( $fh, -self::HEADER, SEEK_END ); $block = fread( $fh, self::HEADER ); fclose( $fh ); return self::is_eof_block( $block ); } /** * Finds package.json and converts it to a Shiftbox manifest. * * @param string $file Path. * @param float $deadline Stop scanning after this time. * @return array|WP_Error */ public static function read_manifest( $file, $deadline = 0.0 ) { if ( ! self::is_complete( $file ) ) { return new WP_Error( 'shiftbox_incomplete', __( 'This backup file is incomplete. It was probably cut off while downloading or uploading. Download it again from the old site.', 'shiftbox' ) ); } $deadline = $deadline ? $deadline : microtime( true ) + 20; $fh = fopen( $file, 'rb' ); $package = null; $has_db = false; $multi = false; try { while ( microtime( true ) < $deadline ) { $entry = self::read_header( $fh ); if ( null === $entry ) { break; } if ( 'package.json' === $entry['path'] && $entry['size'] < 1048576 ) { $package = json_decode( (string) fread( $fh, (int) $entry['size'] ), true ); } elseif ( 'multisite.json' === $entry['path'] ) { $multi = true; } elseif ( 'database.sql' === $entry['path'] ) { $has_db = true; } if ( is_array( $package ) && $has_db ) { break; } fseek( $fh, (int) ( $entry['data_offset'] + $entry['size'] ) ); } } catch ( RuntimeException $e ) { fclose( $fh ); return new WP_Error( 'shiftbox_damaged', $e->getMessage() ); } fclose( $fh ); if ( ! is_array( $package ) ) { return new WP_Error( 'shiftbox_damaged', __( 'This .wpress file has no package information, so it cannot be restored.', 'shiftbox' ) ); } if ( ! empty( $package['Encrypted'] ) && 'false' !== $package['Encrypted'] ) { return new WP_Error( 'shiftbox_encrypted', __( 'This .wpress backup is password protected. Create the backup again without a password, then upload it here.', 'shiftbox' ) ); } $compression = self::compression( $package ); if ( 'bzip2' === $compression && ! function_exists( 'bzdecompress' ) ) { return new WP_Error( 'shiftbox_bzip2', __( 'This .wpress backup uses bzip2 compression, which this server does not support. Create it again with compression turned off or set to gzip.', 'shiftbox' ) ); } if ( 'zlib' === $compression && ! function_exists( 'gzuncompress' ) ) { return new WP_Error( 'shiftbox_zlib', __( 'This .wpress backup is compressed, but the zlib PHP extension is missing on this server.', 'shiftbox' ) ); } $wp = isset( $package['WordPress'] ) && is_array( $package['WordPress'] ) ? $package['WordPress'] : array(); $db = isset( $package['Database'] ) && is_array( $package['Database'] ) ? $package['Database'] : array(); $get = static function ( array $arr, $key ) { return isset( $arr[ $key ] ) && is_string( $arr[ $key ] ) ? $arr[ $key ] : ''; }; $has_db = $has_db && empty( $package['NoDatabase'] ); return array( 'generator' => 'wpress', 'format' => 'wpress', 'compression' => $compression, 'created' => (int) filemtime( $file ), 'home' => $get( $package, 'HomeURL' ), 'siteurl' => $get( $package, 'SiteURL' ), 'abspath' => wp_normalize_path( untrailingslashit( $get( $wp, 'Absolute' ) ) ), 'content_dir' => wp_normalize_path( untrailingslashit( $get( $wp, 'Content' ) ) ), 'uploads_dir' => wp_normalize_path( untrailingslashit( $get( $wp, 'Uploads' ) ? $get( $wp, 'Uploads' ) : $get( $package, 'Uploads' ) ) ), 'uploads_url' => untrailingslashit( $get( $wp, 'UploadsURL' ) ? $get( $wp, 'UploadsURL' ) : $get( $package, 'UploadsURL' ) ), 'prefix' => $get( $db, 'Prefix' ), 'wp' => $get( $wp, 'Version' ), 'multisite' => $multi, 'database' => $has_db, 'files' => 0, ); } /** * Compression used by the archive: none, zlib or bzip2. * * @param array $package package.json data. * @return string */ private static function compression( array $package ) { if ( empty( $package['Compression'] ) || ! is_array( $package['Compression'] ) ) { return 'none'; } $c = $package['Compression']; if ( empty( $c['Enabled'] ) || 'false' === $c['Enabled'] || empty( $c['Type'] ) ) { return 'none'; } $type = strtolower( (string) $c['Type'] ); if ( 'bzip2' === $type ) { return 'bzip2'; } return in_array( $type, array( 'zlib', 'gzip' ), true ) ? 'zlib' : 'none'; } /** * Decompresses one frame. * * @param string $data Compressed bytes. * @param string $type zlib|bzip2. * @return string * @throws RuntimeException On failure. */ public static function inflate( $data, $type ) { if ( 'bzip2' === $type ) { $out = bzdecompress( $data ); $out = is_string( $out ) ? $out : false; } elseif ( "\x1f\x8b" === substr( $data, 0, 2 ) ) { $out = @gzdecode( $data ); } else { $out = @gzuncompress( $data ); if ( false === $out ) { $out = @gzinflate( $data ); } } if ( false === $out ) { throw new RuntimeException( esc_html__( 'A compressed file inside the backup could not be unpacked. The backup may be damaged.', 'shiftbox' ) ); } return $out; } }