Search in sources :

Example 11 with DialogAction

use of com.mucommander.ui.dialog.DialogAction in project mucommander by mucommander.

the class FileCollisionDialog method init.

private void init(int collisionType, AbstractFile sourceFile, AbstractFile destFile, boolean multipleFilesMode, boolean allowRename) {
    // Init choices
    List<DialogAction> actionChoices = new ArrayList<>();
    actionChoices.add(FileCollisionAction.CANCEL);
    if (multipleFilesMode) {
        actionChoices.add(FileCollisionAction.SKIP);
    }
    // Add 'overwrite' / 'overwrite if older' / 'resume' actions only for 'destination file already exists' collision type
    if (collisionType == FileCollisionChecker.DESTINATION_FILE_ALREADY_EXISTS && !destFile.isDirectory()) {
        actionChoices.add(FileCollisionAction.OVERWRITE);
        if (sourceFile != null) {
            actionChoices.add(FileCollisionAction.OVERWRITE_IF_OLDER);
            actionChoices.add(FileCollisionAction.OVERWRITE_IF_SIZE_DIFFERS);
            // Give resume option only if destination file is smaller than source file
            long destSize = destFile.getSize();
            long sourceSize = sourceFile.getSize();
            if (destSize != -1 && (sourceSize == -1 || destSize < sourceSize)) {
                actionChoices.add(FileCollisionAction.RESUME);
            }
            if (allowRename) {
                actionChoices.add(FileCollisionAction.RENAME);
            }
        }
    }
    // Init UI
    String desc = null;
    switch(collisionType) {
        case FileCollisionChecker.DESTINATION_FILE_ALREADY_EXISTS:
            desc = Translator.get("file_exists_in_destination");
            break;
        case FileCollisionChecker.SAME_SOURCE_AND_DESTINATION:
            desc = Translator.get("same_source_destination");
            break;
        case FileCollisionChecker.SOURCE_PARENT_OF_DESTINATION:
            desc = Translator.get("source_parent_of_destination");
            break;
    }
    YBoxPanel yPanel = new YBoxPanel();
    if (desc != null) {
        yPanel.add(new InformationPane(desc, null, Font.PLAIN, InformationPane.QUESTION_ICON));
        yPanel.addSpace(10);
    }
    // Add a separator before file details
    yPanel.add(new JSeparator());
    XAlignedComponentPanel tfPanel = new XAlignedComponentPanel(10);
    // If collision type is 'same source and destination' no need to show both source and destination
    if (collisionType == FileCollisionChecker.SAME_SOURCE_AND_DESTINATION) {
        addFileDetails(tfPanel, sourceFile, Translator.get("name"));
    } else {
        if (sourceFile != null) {
            addFileDetails(tfPanel, sourceFile, Translator.get("source"));
        }
        addFileDetails(tfPanel, destFile, Translator.get("destination"));
    }
    yPanel.add(tfPanel);
    // Add a separator after file details
    yPanel.add(new JSeparator());
    init(yPanel, actionChoices, 3);
    // TODO below there's workaround to accommodate texts within buttons - any idea to how to make it better?
    // override to avoid FocusDialog#pack making the dialog box too small for some buttons
    // so they won't display full texts (observe when Spanish lang pack is chosen - a lot of them have ellipsis)
    setMinimumSize(null);
    setMaximumSize(null);
    // 'Apply to all' is available only for 'destination file already exists' collision type
    if (multipleFilesMode && collisionType == FileCollisionChecker.DESTINATION_FILE_ALREADY_EXISTS) {
        applyToAllCheckBox = new JCheckBox(Translator.get("apply_to_all"));
        addComponent(applyToAllCheckBox);
    }
    // Send a system notification if a notifier is available and enabled
    if (NotifierProvider.isAvailable() && NotifierProvider.getNotifier().isEnabled()) {
        NotifierProvider.displayBackgroundNotification(NotificationType.JOB_ERROR, getTitle(), desc);
    }
}
Also used : JCheckBox(javax.swing.JCheckBox) XAlignedComponentPanel(com.mucommander.commons.util.ui.layout.XAlignedComponentPanel) YBoxPanel(com.mucommander.commons.util.ui.layout.YBoxPanel) DialogAction(com.mucommander.ui.dialog.DialogAction) ArrayList(java.util.ArrayList) InformationPane(com.mucommander.ui.layout.InformationPane) JSeparator(javax.swing.JSeparator)

Example 12 with DialogAction

use of com.mucommander.ui.dialog.DialogAction in project mucommander by mucommander.

the class MoveJob method processFile.

// //////////////////////////////////
// TransferFileJob implementation //
// //////////////////////////////////
/**
 * Moves recursively the given file or folder.
 *
 * @param file the file or folder to move
 * @param recurseParams destination folder where the given file will be moved (null for top level files)
 *
 * @return <code>true</code> if the file has been moved completely (copied + deleted).
 */
@Override
protected boolean processFile(AbstractFile file, Object recurseParams) {
    // Stop if interrupted
    if (getState() == FileJobState.INTERRUPTED)
        return false;
    // Destination folder
    AbstractFile destFolder = recurseParams == null ? baseDestFolder : (AbstractFile) recurseParams;
    // the renamed file (the search results may reside in different sub-folders)
    if (renameMode && SearchFile.SCHEMA.equals(destFolder.getURL().getScheme()))
        destFolder = file.getParent();
    // Is current file at the base folder level ?
    boolean isFileInBaseFolder = files.indexOf(file) != -1;
    // Determine filename in destination
    String destFileName = isFileInBaseFolder && newName != null ? newName : file.getName();
    // Create destination AbstractFile instance
    AbstractFile destFile = createDestinationFile(file, destFolder, destFileName);
    if (destFile == null)
        return false;
    /*
        // Do not follow symlink, simply delete it and return
        if(file.isSymlink()) {
            do {		// Loop for retry
                try  {
                    file.delete();
                    return true;
                }
                catch(IOException e) {
                    LOGGER.debug("IOException caught", e);

                    int ret = showErrorDialog(errorDialogTitle, Translator.get("cannot_delete_file", file.getAbsolutePath()));
                    // Retry loops
                    if(ret==FileJobAction.RETRY)
                        continue;
                    // Cancel, skip or close dialog returns false
                    return false;
                }
            } while(true);
        }
        */
    destFile = checkForCollision(file, destFolder, destFile, renameMode);
    if (destFile == null)
        return false;
    // I/O bound checks and ends up throwing an exception which also comes at a cost.
    if (!append && file.getURL().schemeEquals(destFile.getURL()) && file.isFileOperationSupported(FileOperation.RENAME)) {
        try {
            file.renameTo(destFile);
            return true;
        } catch (IOException e) {
            // Fail silently: renameTo might fail under normal conditions, for instance for local files which are
            // not located on the same volume.
            LOGGER.debug("Failed to rename " + file + " into " + destFile + " (not necessarily an error)", e);
        }
    }
    // Move the directory and all its children recursively, by copying files to the destination and then deleting them.
    if (file.isDirectory()) {
        // create the destination folder if it doesn't exist
        if (!(destFile.exists() && destFile.isDirectory())) {
            do {
                // Loop for retry
                try {
                    destFile.mkdir();
                } catch (IOException e) {
                    DialogAction ret = showErrorDialog(errorDialogTitle, Translator.get("cannot_create_folder", destFile.getAbsolutePath()));
                    // Retry loops
                    if (ret == FileJobAction.RETRY)
                        continue;
                    // Cancel, skip or close dialog returns false
                    return false;
                }
                break;
            } while (true);
        }
        // move each file in this folder recursively
        do {
            // Loop for retry
            try {
                AbstractFile[] subFiles = file.ls();
                boolean isFolderEmpty = true;
                for (AbstractFile subFile : subFiles) {
                    // Return now if the job was interrupted, so that we do not attempt to delete this folder
                    if (getState() == FileJobState.INTERRUPTED)
                        return false;
                    // Notify job that we're starting to process this file (needed for recursive calls to processFile)
                    nextFile(subFile);
                    if (!processFile(subFile, destFile))
                        isFolderEmpty = false;
                }
                // Only when finished with folder, set destination folder's date to match the original folder one
                if (destFile.isFileOperationSupported(FileOperation.CHANGE_DATE)) {
                    try {
                        destFile.changeDate(file.getDate());
                    } catch (IOException e) {
                        LOGGER.debug("failed to change the date of " + destFile, e);
                    // Fail silently
                    }
                }
                // If one file failed to be moved, return false (failure) since this folder could not be moved totally
                if (!isFolderEmpty)
                    return false;
            } catch (IOException e) {
                // file.ls() failed
                DialogAction ret = showErrorDialog(errorDialogTitle, Translator.get("cannot_read_folder", file.getName()));
                // Retry loops
                if (ret == FileJobAction.RETRY)
                    continue;
                // Cancel, skip or close dialog returns false
                return false;
            }
            break;
        } while (true);
        // Return now if the job was interrupted, so that we do not attempt to delete this folder
        if (getState() == FileJobState.INTERRUPTED)
            return false;
        // finally, delete the empty folder
        do {
            // Loop for retry
            try {
                file.delete();
                return true;
            } catch (IOException e) {
                DialogAction ret = showErrorDialog(errorDialogTitle, Translator.get("cannot_delete_folder", file.getAbsolutePath()));
                // Retry loops
                if (ret == FileJobAction.RETRY)
                    continue;
                // Cancel, skip or close dialog returns false
                return false;
            }
        } while (true);
    } else // File is a regular file, move it by copying it to the destination and then deleting it
    {
        // try the hard way by copying the file first, and then deleting the source file.
        if (tryCopyFile(file, destFile, append, errorDialogTitle) && getState() != FileJobState.INTERRUPTED) {
            // Delete the source file
            do {
                // Loop for retry
                try {
                    file.delete();
                    // All OK
                    return true;
                } catch (IOException e) {
                    LOGGER.debug("IOException caught", e);
                    DialogAction ret = showErrorDialog(errorDialogTitle, Translator.get("cannot_delete_file", file.getAbsolutePath()));
                    // Retry loops
                    if (ret == FileJobAction.RETRY)
                        continue;
                    // Cancel, skip or close dialog returns false
                    return false;
                }
            } while (true);
        }
        return false;
    }
}
Also used : AbstractFile(com.mucommander.commons.file.AbstractFile) DialogAction(com.mucommander.ui.dialog.DialogAction) IOException(java.io.IOException)

Example 13 with DialogAction

use of com.mucommander.ui.dialog.DialogAction 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 14 with DialogAction

use of com.mucommander.ui.dialog.DialogAction in project mucommander by mucommander.

the class ArchiveJob method processFile.

// //////////////////////////////////
// TransferFileJob implementation //
// //////////////////////////////////
@Override
protected boolean processFile(AbstractFile file, Object recurseParams) {
    if (getState() == FileJobState.INTERRUPTED)
        return false;
    String filePath = file.getAbsolutePath(false);
    String entryRelativePath = filePath.substring(baseFolderPath.length() + 1, filePath.length());
    // Process current file
    do {
        // Loop for retry
        try {
            if (file.isDirectory() && !file.isSymlink()) {
                // Create new directory entry in archive file
                archiver.createEntry(entryRelativePath, file);
                // Recurse on files
                AbstractFile[] subFiles = file.ls();
                boolean folderComplete = true;
                for (int i = 0; i < subFiles.length && getState() != FileJobState.INTERRUPTED; i++) {
                    // Notify job that we're starting to process this file (needed for recursive calls to processFile)
                    nextFile(subFiles[i]);
                    if (!processFile(subFiles[i], null))
                        folderComplete = false;
                }
                return folderComplete;
            } else {
                InputStream in = setCurrentInputStream(file.getInputStream());
                // written to the archive OutputStream, this would cause ZipOutputStream to deadlock.
                synchronized (ioLock) {
                    // Create a new file entry in archive and copy the current file
                    StreamUtils.copyStream(in, archiver.createEntry(entryRelativePath, file));
                    in.close();
                }
                return true;
            }
        }// Catch Exception rather than IOException as ZipOutputStream has been seen throwing NullPointerException
         catch (Exception e) {
            // In this case, the exception should not be interpreted as an error.
            if (getState() == FileJobState.INTERRUPTED)
                return false;
            LOGGER.debug("Caught IOException", e);
            DialogAction ret = showErrorDialog(Translator.get("pack_dialog.error_title"), Translator.get("error_while_transferring", file.getAbsolutePath()));
            // Retry loops
            if (ret == FileJobAction.RETRY) {
                // Reset processed bytes currentFileByteCounter
                resetCurrentFileByteCounter();
                continue;
            }
            // Cancel, skip or close dialog return false
            return false;
        }
    } while (true);
}
Also used : AbstractFile(com.mucommander.commons.file.AbstractFile) InputStream(java.io.InputStream) DialogAction(com.mucommander.ui.dialog.DialogAction) IOException(java.io.IOException)

Example 15 with DialogAction

use of com.mucommander.ui.dialog.DialogAction in project mucommander by mucommander.

the class ArchiveJob method jobStarted.

// //////////////////////
// Overridden methods //
// //////////////////////
/**
 * Overriden method to initialize the archiver and handle the case where the destination file already exists.
 */
@Override
protected void jobStarted() {
    super.jobStarted();
    // Check for file collisions, i.e. if the file already exists in the destination
    int collision = FileCollisionChecker.checkForCollision(null, destFile);
    if (collision != FileCollisionChecker.NO_COLLISION) {
        // File already exists in destination, ask the user what to do (cancel, overwrite,...) but
        // do not offer the multiple files mode options such as 'skip' and 'apply to all'.
        DialogAction choice = waitForUserResponse(new FileCollisionDialog(getProgressDialog(), getMainFrame(), collision, null, destFile, false, false));
        // Overwrite file
        if (choice == FileCollisionDialog.FileCollisionAction.OVERWRITE) {
        // Do nothing, simply continue and file will be overwritten
        } else // 'Cancel' or close dialog interrupts the job
        {
            interrupt();
            return;
        }
    }
    // Loop for retry
    do {
        try {
            // Tries to get an Archiver instance.
            this.archiver = Archiver.getArchiver(destFile, archiveFormat);
            this.archiver.setComment(archiveComment);
            break;
        } catch (Exception e) {
            DialogAction choice = showErrorDialog(Translator.get("pack_dialog.error_title"), Translator.get("cannot_write_file", destFile.getName()), Arrays.asList(FileJobAction.CANCEL, FileJobAction.RETRY));
            // Retry loops
            if (choice == FileJobAction.RETRY)
                continue;
            // 'Cancel' or close dialog interrupts the job
            interrupt();
            return;
        }
    } while (true);
}
Also used : FileCollisionDialog(com.mucommander.ui.dialog.file.FileCollisionDialog) DialogAction(com.mucommander.ui.dialog.DialogAction) IOException(java.io.IOException)

Aggregations

DialogAction (com.mucommander.ui.dialog.DialogAction)22 IOException (java.io.IOException)13 AbstractFile (com.mucommander.commons.file.AbstractFile)10 FileCollisionDialog (com.mucommander.ui.dialog.file.FileCollisionDialog)7 QuestionDialog (com.mucommander.ui.dialog.QuestionDialog)6 JFileChooser (javax.swing.JFileChooser)3 InformationPane (com.mucommander.ui.layout.InformationPane)2 MainFrame (com.mucommander.ui.main.MainFrame)2 WarnUserException (com.mucommander.viewer.WarnUserException)2 Cursor (java.awt.Cursor)2 Frame (java.awt.Frame)2 InputStream (java.io.InputStream)2 ArrayList (java.util.ArrayList)2 JCheckBox (javax.swing.JCheckBox)2 VersionChecker (com.mucommander.VersionChecker)1 CredentialsMapping (com.mucommander.auth.CredentialsMapping)1 AuthException (com.mucommander.commons.file.AuthException)1 AuthenticationType (com.mucommander.commons.file.AuthenticationType)1 FileURL (com.mucommander.commons.file.FileURL)1 UnsupportedFileOperationException (com.mucommander.commons.file.UnsupportedFileOperationException)1