Search in sources :

Example 6 with ArchiveEntry

use of com.mucommander.commons.file.archive.ArchiveEntry 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 7 with ArchiveEntry

use of com.mucommander.commons.file.archive.ArchiveEntry 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)

Example 8 with ArchiveEntry

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

the class GzipArchiveFile method getEntryIterator.

// //////////////////////////////////////
// AbstractArchiveFile implementation //
// //////////////////////////////////////
@Override
public ArchiveEntryIterator getEntryIterator() throws IOException {
    String extension = getCustomExtension() != null ? getCustomExtension() : getExtension();
    String name = getName();
    if (extension != null) {
        // Remove the 'gz' or 'tgz' extension from the entry's name
        extension = extension.toLowerCase();
        int extensionIndex = name.toLowerCase().lastIndexOf("." + extension);
        if (extensionIndex > -1)
            name = name.substring(0, extensionIndex);
        if (extension.equals("tgz") || extension.equals("tar.gz"))
            name += ".tar";
    }
    return new SingleArchiveEntryIterator(new ArchiveEntry("/" + name, false, getDate(), -1, true));
}
Also used : SingleArchiveEntryIterator(com.mucommander.commons.file.archive.SingleArchiveEntryIterator) ArchiveEntry(com.mucommander.commons.file.archive.ArchiveEntry)

Example 9 with ArchiveEntry

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

the class TarArchiveFile method getEntryInputStream.

@Override
public InputStream getEntryInputStream(ArchiveEntry entry, ArchiveEntryIterator entryIterator) throws IOException, UnsupportedFileOperationException {
    if (entry.isDirectory())
        throw new IOException();
    // the more expensive if the TAR archive is GZipped.
    if (entryIterator != null && (entryIterator instanceof TarEntryIterator)) {
        ArchiveEntry currentEntry = ((TarEntryIterator) entryIterator).getCurrentEntry();
        if (currentEntry.equals(entry)) {
            // we don't want the TarArchiveInputStream to be closed when the caller closes the entry's stream.
            return new FilterInputStream(((TarEntryIterator) entryIterator).getTarInputStream()) {

                @Override
                public void close() throws IOException {
                // No-op
                }
            };
        }
    // This is not the one, look for the entry from the beginning of the archive
    }
    TarArchiveInputStream tin = new TarArchiveInputStream(getInputStream());
    TarArchiveEntry tarEntry;
    String targetPath = entry.getPath();
    // Iterate through the archive until we've found the entry
    while ((tarEntry = tin.getNextTarEntry()) != null) {
        if (tarEntry.getName().equals(targetPath)) {
            // That's the one, return it
            return tin;
        }
    }
    throw new IOException("Unknown TAR entry: " + entry.getName());
}
Also used : TarArchiveInputStream(org.apache.commons.compress.archivers.tar.TarArchiveInputStream) FilterInputStream(java.io.FilterInputStream) TarArchiveEntry(org.apache.commons.compress.archivers.tar.TarArchiveEntry) ArchiveEntry(com.mucommander.commons.file.archive.ArchiveEntry) IOException(java.io.IOException) TarArchiveEntry(org.apache.commons.compress.archivers.tar.TarArchiveEntry)

Example 10 with ArchiveEntry

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

the class XzArchiveFile method getEntryIterator.

// //////////////////////////////////////
// AbstractArchiveFile implementation //
// //////////////////////////////////////
@Override
public ArchiveEntryIterator getEntryIterator() throws IOException {
    String extension = getCustomExtension() != null ? getCustomExtension() : getExtension();
    String name = getName();
    if (extension != null) {
        // Remove the 'xz' or 'txz' extension from the entry's name
        extension = extension.toLowerCase();
        int extensionIndex = name.toLowerCase().lastIndexOf("." + extension);
        if (extensionIndex > -1)
            name = name.substring(0, extensionIndex);
        if (extension.equals("txz") || extension.equals("tar.xz"))
            name += ".tar";
    }
    return new SingleArchiveEntryIterator(new ArchiveEntry("/" + name, false, getDate(), -1, true));
}
Also used : SingleArchiveEntryIterator(com.mucommander.commons.file.archive.SingleArchiveEntryIterator) ArchiveEntry(com.mucommander.commons.file.archive.ArchiveEntry)

Aggregations

ArchiveEntry (com.mucommander.commons.file.archive.ArchiveEntry)13 IOException (java.io.IOException)4 AbstractArchiveFile (com.mucommander.commons.file.archive.AbstractArchiveFile)3 SingleArchiveEntryIterator (com.mucommander.commons.file.archive.SingleArchiveEntryIterator)3 Vector (java.util.Vector)3 AbstractFile (com.mucommander.commons.file.AbstractFile)2 AbstractArchiveEntryFile (com.mucommander.commons.file.archive.AbstractArchiveEntryFile)2 ArchiveEntryIterator (com.mucommander.commons.file.archive.ArchiveEntryIterator)2 CopyJob (com.mucommander.job.impl.CopyJob)2 TransferFileJob (com.mucommander.job.impl.TransferFileJob)2 UnpackJob (com.mucommander.job.impl.UnpackJob)2 TarArchiveEntry (org.apache.commons.compress.archivers.tar.TarArchiveEntry)2 com.mucommander.commons.file (com.mucommander.commons.file)1 SimpleFilePermissions (com.mucommander.commons.file.SimpleFilePermissions)1 WrapperArchiveEntryIterator (com.mucommander.commons.file.archive.WrapperArchiveEntryIterator)1 IInArchive (com.mucommander.commons.file.archive.sevenzip.provider.SevenZip.Archive.IInArchive)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 FilterInputStream (java.io.FilterInputStream)1