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;
}
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();
}
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));
}
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());
}
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));
}
Aggregations