Search in sources :

Example 1 with ArchiveEntryIterator

use of com.mucommander.commons.file.archive.ArchiveEntryIterator in project mucommander by mucommander.

the class ZipArchiveFile method getEntryIterator.

// ////////////////////////////////////////
// AbstractROArchiveFile implementation //
// ////////////////////////////////////////
@Override
public synchronized ArchiveEntryIterator getEntryIterator() throws IOException, UnsupportedFileOperationException {
    // If the underlying AbstractFile has random read access, use our own ZipFile implementation to read entries
    if (file.isFileOperationSupported(FileOperation.RANDOM_READ_FILE)) {
        checkZipFile();
        final Iterator<ZipEntry> iterator = zipFile.getEntries();
        return new ArchiveEntryIterator() {

            @Override
            public ArchiveEntry nextEntry() throws IOException {
                ZipEntry entry;
                if (!iterator.hasNext() || (entry = iterator.next()) == null)
                    return null;
                return createArchiveEntry(entry);
            }
        };
    } else // If the underlying AbstractFile doesn't have random read access, use java.util.zip.ZipInputStream to
    // read the entries. This is much slower than the former method as the file cannot be sought through and needs
    // to be traversed.
    {
        return new JavaUtilZipEntryIterator(new ZipInputStream(file.getInputStream()));
    }
}
Also used : ZipInputStream(java.util.zip.ZipInputStream) ZipEntry(com.mucommander.commons.file.archive.zip.provider.ZipEntry) ArchiveEntryIterator(com.mucommander.commons.file.archive.ArchiveEntryIterator)

Example 2 with ArchiveEntryIterator

use of com.mucommander.commons.file.archive.ArchiveEntryIterator in project mucommander by mucommander.

the class UnpackJob method processFile.

/**
 * Unpacks the given archive file. If the file is a directory, its children will be processed recursively.
 * If the file is not an archive file nor a directory, it is not processed and <code>false</code> is returned.
 *
 * @param file          the file to unpack
 * @param recurseParams unused
 * @return <code>true</code> if the file has been processed successfully
 */
@Override
protected boolean processFile(AbstractFile file, Object recurseParams) {
    // Stop if interrupted
    if (getState() == FileJobState.INTERRUPTED)
        return false;
    // Destination folder
    AbstractFile destFolder = baseDestFolder;
    // If the file is a directory, process its children recursively
    if (file.isDirectory()) {
        do {
            // Loop for retries
            try {
                // List files inside archive file (can throw an IOException)
                AbstractFile[] archiveFiles = getCurrentFile().ls();
                // Recurse on zip's contents
                for (int j = 0; j < archiveFiles.length && getState() != FileJobState.INTERRUPTED; j++) {
                    // Notify job that we're starting to process this file (needed for recursive calls to processFile)
                    nextFile(archiveFiles[j]);
                    // Recurse
                    processFile(archiveFiles[j], destFolder);
                }
                // Return true when complete
                return true;
            } catch (IOException e) {
                // File could not be uncompressed properly
                DialogAction ret = showErrorDialog(errorDialogTitle, Translator.get("cannot_read_file", getCurrentFilename()));
                // Retry loops
                if (ret == FileJobAction.RETRY)
                    continue;
                // cancel, skip or close dialog will simply return false
                return false;
            }
        } while (true);
    }
    // Abort if the file is neither an archive file nor a directory
    if (!file.isArchive())
        return false;
    // 'Cast' the file as an archive file
    AbstractArchiveFile archiveFile = file.getAncestor(AbstractArchiveFile.class);
    String destSeparator = destFolder.getSeparator();
    // Unpack the archive, copying entries one by one, in the iterator's order
    try (ArchiveEntryIterator iterator = archiveFile.getEntryIterator()) {
        ArchiveEntry entry;
        while ((entry = iterator.nextEntry()) != null && getState() != FileJobState.INTERRUPTED) {
            String entryPath = entry.getPath();
            boolean processEntry = false;
            if (selectedEntries == null) {
                // Entries are processed
                processEntry = true;
            } else {
                // We need to determine if the entry should be processed or not
                // Process this entry if the selectedEntries set contains this entry, or a parent of this entry
                int nbSelectedEntries = selectedEntries.size();
                for (int i = 0; i < nbSelectedEntries; i++) {
                    ArchiveEntry selectedEntry = selectedEntries.get(i);
                    // selectedEntry is a parent of the current entry.
                    if (selectedEntry.isDirectory()) {
                        if (entryPath.startsWith(selectedEntry.getPath())) {
                            processEntry = true;
                            break;
                        // Note: we can't remove selectedEntryPath from the set, we still need it
                        }
                    } else if (entryPath.equals(selectedEntry.getPath())) {
                        // If the (regular file) entry is in the set, remove it as we no longer need it (will speed up
                        // subsequent searches)
                        processEntry = true;
                        selectedEntries.remove(i);
                        break;
                    }
                }
            }
            if (!processEntry)
                continue;
            DefaultMutableTreeNode entryNode = archiveFile.getArchiveEntryNode(entryPath);
            if (entryNode != null) {
                ArchiveEntry archiveEntry = (ArchiveEntry) entryNode.getUserObject();
                if (archiveEntry.isSymbolicLink()) {
                    Files.createSymbolicLink(FileSystems.getDefault().getPath(destFolder.getPath(), entry.getName()), FileSystems.getDefault().getPath(entry.getLinkTarget()));
                    continue;
                }
            }
            // Resolve the entry file
            AbstractFile entryFile = archiveFile.getArchiveEntryFile(entryPath);
            // Notify the job that we're starting to process this file
            nextFile(entryFile);
            // Figure out the destination file's path, relatively to the base destination folder
            String relDestPath = baseArchiveDepth == 0 ? entry.getPath() : PathUtils.removeLeadingFragments(entry.getPath(), "/", baseArchiveDepth);
            if (newName != null)
                relDestPath = newName + (PathUtils.getDepth(relDestPath, "/") <= 1 ? "" : "/" + PathUtils.removeLeadingFragments(relDestPath, "/", 1));
            if (!"/".equals(destSeparator))
                relDestPath = relDestPath.replace("/", destSeparator);
            // Create destination AbstractFile instance
            AbstractFile destFile = destFolder.getChild(relDestPath);
            // Check for ZipSlip (see https://snyk.io/research/zip-slip-vulnerability)
            do {
                if (destFolder.isParentOf(destFile))
                    break;
                DialogAction ret = showErrorDialog(errorDialogTitle, Translator.get("unpack.entry_out_of_target_dir", destFile.getName()));
                // Retry loops
                if (ret == FileJobAction.RETRY)
                    continue;
                // Cancel, skip or close dialog returns false
                return false;
            } while (true);
            // Check if the file does not already exist in the destination
            destFile = checkForCollision(entryFile, destFolder, destFile, false);
            if (destFile == null) {
                // A collision occurred and either the file was skipped, or the user cancelled the job
                continue;
            }
            // If the entry is a directory ...
            if (entryFile.isDirectory()) {
                // Create the directory in the destination, if it doesn't already exist
                if (!(destFile.exists() && destFile.isDirectory())) {
                    // Loop for retry
                    do {
                        try {
                            // Use mkdirs() instead of mkdir() to create any parent folder that doesn't exist yet
                            destFile.mkdirs();
                        } catch (IOException e) {
                            // Unable to create folder
                            DialogAction ret = showErrorDialog(errorDialogTitle, Translator.get("cannot_create_folder", entryFile.getName()));
                            // Retry loops
                            if (ret == FileJobAction.RETRY)
                                continue;
                            // Cancel or close dialog return false
                            return false;
                        // Skip continues
                        }
                        break;
                    } while (true);
                }
            } else // The entry is a regular file, copy it
            {
                // Create the file's parent directory(s) if it doesn't already exist
                AbstractFile destParentFile = destFile.getParent();
                if (!destParentFile.exists()) {
                    // Use mkdirs() instead of mkdir() to create any parent folder that doesn't exist yet
                    destParentFile.mkdirs();
                }
                // some archive file implementations (such as TAR) can speed things by an order of magnitude.
                if (!tryCopyFile(new ProxiedEntryFile(entryFile, entry, archiveFile, iterator), destFile, append, errorDialogTitle))
                    return false;
            }
        }
        return true;
    } catch (IOException e) {
        showErrorDialog(errorDialogTitle, Translator.get("cannot_read_file", archiveFile.getName()));
    }
    return false;
}
Also used : AbstractArchiveFile(com.mucommander.commons.file.archive.AbstractArchiveFile) DefaultMutableTreeNode(javax.swing.tree.DefaultMutableTreeNode) DialogAction(com.mucommander.ui.dialog.DialogAction) ArchiveEntry(com.mucommander.commons.file.archive.ArchiveEntry) IOException(java.io.IOException) ArchiveEntryIterator(com.mucommander.commons.file.archive.ArchiveEntryIterator)

Example 3 with ArchiveEntryIterator

use of com.mucommander.commons.file.archive.ArchiveEntryIterator in project mucommander by mucommander.

the class ArArchiveFile method getEntryInputStream.

@Override
public InputStream getEntryInputStream(ArchiveEntry entry, ArchiveEntryIterator entryIterator) throws IOException, UnsupportedFileOperationException {
    InputStream in = getInputStream();
    ArchiveEntryIterator iterator = new ArArchiveEntryIterator(in);
    ArchiveEntry currentEntry;
    while ((currentEntry = iterator.nextEntry()) != null) {
        if (currentEntry.getName().equals(entry.getName())) {
            LOGGER.trace("found entry {}", entry.getName());
            return new BoundedInputStream(in, entry.getSize(), false);
        }
    }
    // Entry not found, should not normally happen
    LOGGER.info("Warning: entry not found, throwing IOException");
    throw new IOException();
}
Also used : BoundedInputStream(com.mucommander.commons.io.BoundedInputStream) InputStream(java.io.InputStream) BoundedInputStream(com.mucommander.commons.io.BoundedInputStream) ArchiveEntry(com.mucommander.commons.file.archive.ArchiveEntry) ArchiveEntryIterator(com.mucommander.commons.file.archive.ArchiveEntryIterator) IOException(java.io.IOException)

Aggregations

ArchiveEntryIterator (com.mucommander.commons.file.archive.ArchiveEntryIterator)3 ArchiveEntry (com.mucommander.commons.file.archive.ArchiveEntry)2 IOException (java.io.IOException)2 AbstractArchiveFile (com.mucommander.commons.file.archive.AbstractArchiveFile)1 ZipEntry (com.mucommander.commons.file.archive.zip.provider.ZipEntry)1 BoundedInputStream (com.mucommander.commons.io.BoundedInputStream)1 DialogAction (com.mucommander.ui.dialog.DialogAction)1 InputStream (java.io.InputStream)1 ZipInputStream (java.util.zip.ZipInputStream)1 DefaultMutableTreeNode (javax.swing.tree.DefaultMutableTreeNode)1