+ // Unzip can use a lot of memory, but not this much hopefully
+ @ini_set( 'memory_limit', apply_filters( 'admin_memory_limit', WP_MAX_MEMORY_LIMIT ) );
+
+ $needed_dirs = array();
+ $to = trailingslashit($to);
+
+ // Determine any parent dir's needed (of the upgrade directory)
+ if ( ! $wp_filesystem->is_dir($to) ) { //Only do parents if no children exist
+ $path = preg_split('![/\\\]!', untrailingslashit($to));
+ for ( $i = count($path); $i >= 0; $i-- ) {
+ if ( empty($path[$i]) )
+ continue;
+
+ $dir = implode('/', array_slice($path, 0, $i+1) );
+ if ( preg_match('!^[a-z]:$!i', $dir) ) // Skip it if it looks like a Windows Drive letter.
+ continue;
+
+ if ( ! $wp_filesystem->is_dir($dir) )
+ $needed_dirs[] = $dir;
+ else
+ break; // A folder exists, therefor, we dont need the check the levels below this
+ }
+ }
+
+ if ( class_exists('ZipArchive') && apply_filters('unzip_file_use_ziparchive', true ) ) {
+ $result = _unzip_file_ziparchive($file, $to, $needed_dirs);
+ if ( true === $result ) {
+ return $result;
+ } elseif ( is_wp_error($result) ) {
+ if ( 'incompatible_archive' != $result->get_error_code() )
+ return $result;
+ }
+ }
+ // Fall through to PclZip if ZipArchive is not available, or encountered an error opening the file.
+ return _unzip_file_pclzip($file, $to, $needed_dirs);
+}
+
+/**
+ * This function should not be called directly, use unzip_file instead. Attempts to unzip an archive using the ZipArchive class.
+ * Assumes that WP_Filesystem() has already been called and set up.
+ *
+ * @since 3.0.0
+ * @see unzip_file
+ * @access private
+ *
+ * @param string $file Full path and filename of zip archive
+ * @param string $to Full path on the filesystem to extract archive to
+ * @param array $needed_dirs A partial list of required folders needed to be created.
+ * @return mixed WP_Error on failure, True on success
+ */
+function _unzip_file_ziparchive($file, $to, $needed_dirs = array() ) {
+ global $wp_filesystem;
+
+ $z = new ZipArchive();
+
+ $zopen = $z->open( $file, ZIPARCHIVE::CHECKCONS );
+ if ( true !== $zopen )
+ return new WP_Error( 'incompatible_archive', __( 'Incompatible Archive.' ), array( 'ziparchive_error' => $zopen ) );
+
+ $uncompressed_size = 0;
+
+ for ( $i = 0; $i < $z->numFiles; $i++ ) {
+ if ( ! $info = $z->statIndex($i) )
+ return new WP_Error( 'stat_failed_ziparchive', __( 'Could not retrieve file from archive.' ) );
+
+ if ( '__MACOSX/' === substr($info['name'], 0, 9) ) // Skip the OS X-created __MACOSX directory
+ continue;
+
+ $uncompressed_size += $info['size'];
+
+ if ( '/' == substr($info['name'], -1) ) // directory
+ $needed_dirs[] = $to . untrailingslashit($info['name']);
+ else
+ $needed_dirs[] = $to . untrailingslashit(dirname($info['name']));
+ }
+
+ /*
+ * disk_free_space() could return false. Assume that any falsey value is an error.
+ * A disk that has zero free bytes has bigger problems.
+ * Require we have enough space to unzip the file and copy its contents, with a 10% buffer.
+ */
+ if ( defined( 'DOING_CRON' ) && DOING_CRON ) {
+ $available_space = @disk_free_space( WP_CONTENT_DIR );
+ if ( $available_space && ( $uncompressed_size * 2.1 ) > $available_space )
+ return new WP_Error( 'disk_full_unzip_file', __( 'Could not copy files. You may have run out of disk space.' ), compact( 'uncompressed_size', 'available_space' ) );
+ }
+
+ $needed_dirs = array_unique($needed_dirs);
+ foreach ( $needed_dirs as $dir ) {
+ // Check the parent folders of the folders all exist within the creation array.
+ if ( untrailingslashit($to) == $dir ) // Skip over the working directory, We know this exists (or will exist)
+ continue;
+ if ( strpos($dir, $to) === false ) // If the directory is not within the working directory, Skip it
+ continue;
+
+ $parent_folder = dirname($dir);
+ while ( !empty($parent_folder) && untrailingslashit($to) != $parent_folder && !in_array($parent_folder, $needed_dirs) ) {
+ $needed_dirs[] = $parent_folder;
+ $parent_folder = dirname($parent_folder);
+ }
+ }
+ asort($needed_dirs);
+
+ // Create those directories if need be:
+ foreach ( $needed_dirs as $_dir ) {
+ if ( ! $wp_filesystem->mkdir($_dir, FS_CHMOD_DIR) && ! $wp_filesystem->is_dir($_dir) ) // Only check to see if the Dir exists upon creation failure. Less I/O this way.
+ return new WP_Error( 'mkdir_failed_ziparchive', __( 'Could not create directory.' ), substr( $_dir, strlen( $to ) ) );
+ }
+ unset($needed_dirs);
+
+ for ( $i = 0; $i < $z->numFiles; $i++ ) {
+ if ( ! $info = $z->statIndex($i) )
+ return new WP_Error( 'stat_failed_ziparchive', __( 'Could not retrieve file from archive.' ) );
+
+ if ( '/' == substr($info['name'], -1) ) // directory
+ continue;
+
+ if ( '__MACOSX/' === substr($info['name'], 0, 9) ) // Don't extract the OS X-created __MACOSX directory files
+ continue;
+
+ $contents = $z->getFromIndex($i);
+ if ( false === $contents )
+ return new WP_Error( 'extract_failed_ziparchive', __( 'Could not extract file from archive.' ), $info['name'] );
+
+ if ( ! $wp_filesystem->put_contents( $to . $info['name'], $contents, FS_CHMOD_FILE) )
+ return new WP_Error( 'copy_failed_ziparchive', __( 'Could not copy file.' ), $info['name'] );
+ }
+
+ $z->close();
+
+ return true;
+}
+
+/**
+ * This function should not be called directly, use unzip_file instead. Attempts to unzip an archive using the PclZip library.
+ * Assumes that WP_Filesystem() has already been called and set up.
+ *
+ * @since 3.0.0
+ * @see unzip_file
+ * @access private
+ *
+ * @param string $file Full path and filename of zip archive
+ * @param string $to Full path on the filesystem to extract archive to
+ * @param array $needed_dirs A partial list of required folders needed to be created.
+ * @return mixed WP_Error on failure, True on success
+ */
+function _unzip_file_pclzip($file, $to, $needed_dirs = array()) {
+ global $wp_filesystem;
+
+ mbstring_binary_safe_encoding();