org.pgptool.gui.ui.encryptone.EncryptOnePm.java Source code

Java tutorial

Introduction

Here is the source code for org.pgptool.gui.ui.encryptone.EncryptOnePm.java

Source

/*******************************************************************************
 * PGPTool is a desktop application for pgp encryption/decryption
 * Copyright (C) 2017 Sergey Karpushin
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>
 *******************************************************************************/
package org.pgptool.gui.ui.encryptone;

import static org.pgptool.gui.app.Messages.text;

import java.awt.Desktop;
import java.awt.event.ActionEvent;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.io.File;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashSet;
import java.util.List;
import java.util.Optional;
import java.util.Set;

import javax.swing.Action;
import javax.swing.JFileChooser;
import javax.swing.JOptionPane;
import javax.swing.SwingUtilities;
import javax.swing.filechooser.FileFilter;
import javax.swing.filechooser.FileNameExtensionFilter;

import org.apache.commons.io.FilenameUtils;
import org.apache.log4j.Logger;
import org.pgptool.gui.app.EntryPoint;
import org.pgptool.gui.app.MessageSeverity;
import org.pgptool.gui.app.Messages;
import org.pgptool.gui.bkgoperation.ProgressHandler;
import org.pgptool.gui.bkgoperation.UserRequestedCancellationException;
import org.pgptool.gui.configpairs.api.ConfigPairs;
import org.pgptool.gui.decryptedlist.api.DecryptedFile;
import org.pgptool.gui.decryptedlist.api.MonitoringDecryptedFilesService;
import org.pgptool.gui.encryption.api.EncryptionService;
import org.pgptool.gui.encryption.api.KeyRingService;
import org.pgptool.gui.encryption.api.dto.Key;
import org.pgptool.gui.encryptionparams.api.EncryptionParamsStorage;
import org.pgptool.gui.filecomparison.ChecksumCalcInputStreamSupervisor;
import org.pgptool.gui.filecomparison.ChecksumCalcInputStreamSupervisorImpl;
import org.pgptool.gui.filecomparison.ChecksumCalcOutputStreamSupervisor;
import org.pgptool.gui.filecomparison.ChecksumCalcOutputStreamSupervisorImpl;
import org.pgptool.gui.filecomparison.Fingerprint;
import org.pgptool.gui.filecomparison.MessageDigestFactory;
import org.pgptool.gui.tools.FileUtilsEx;
import org.pgptool.gui.ui.decryptone.DecryptOnePm;
import org.pgptool.gui.ui.keyslist.ComparatorKeyByNameImpl;
import org.pgptool.gui.ui.tools.ListChangeListenerAnyEventImpl;
import org.pgptool.gui.ui.tools.ProgressHandlerPmMixinImpl;
import org.pgptool.gui.ui.tools.UiUtils;
import org.pgptool.gui.ui.tools.browsefs.ExistingFileChooserDialog;
import org.pgptool.gui.ui.tools.browsefs.SaveFileChooserDialog;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;
import org.summerb.utils.DeepCopy;

import com.google.common.base.Preconditions;

import ru.skarpushin.swingpm.base.PresentationModelBase;
import ru.skarpushin.swingpm.modelprops.ModelProperty;
import ru.skarpushin.swingpm.modelprops.ModelPropertyAccessor;
import ru.skarpushin.swingpm.modelprops.lists.ModelListProperty;
import ru.skarpushin.swingpm.modelprops.lists.ModelMultSelInListProperty;
import ru.skarpushin.swingpm.tools.actions.LocalizedAction;
import ru.skarpushin.swingpm.valueadapters.ValueAdapterHolderImpl;
import ru.skarpushin.swingpm.valueadapters.ValueAdapterReadonlyImpl;

public class EncryptOnePm extends PresentationModelBase implements InitializingBean {
    private static Logger log = Logger.getLogger(EncryptOnePm.class);

    private static final String ENCRYPTED_FILE_EXTENSION = "pgp";
    private static final String SOURCE_FOLDER = "EncryptOnePm.SOURCE_FOLDER";

    @Autowired
    private ConfigPairs appProps;
    @Autowired
    private EncryptionParamsStorage encryptionParamsStorage;
    @Autowired
    private MessageDigestFactory messageDigestFactory;
    @Autowired
    private MonitoringDecryptedFilesService monitoringDecryptedFilesService;

    @Autowired
    // @Resource(name = "keyRingService")
    private KeyRingService keyRingService;
    @Autowired
    // @Resource(name = "encryptionService")
    private EncryptionService encryptionService;

    private EncryptOneHost host;

    private ModelProperty<String> sourceFile;
    private ModelProperty<Boolean> isUseSameFolder;
    private ModelProperty<String> targetFile;
    private ModelProperty<Boolean> targetFileEnabled;
    private ModelMultSelInListProperty<Key> selectedRecipients;
    private ModelListProperty<Key> availabileRecipients;
    private ModelProperty<Boolean> isNoPrivateKeysSelected;
    private ModelProperty<Boolean> isDeleteSourceAfter;
    private ModelProperty<Boolean> isOpenTargetFolderAfter;

    private ExistingFileChooserDialog sourceFileChooser;
    private SaveFileChooserDialog targetFileChooser;
    private EncryptionDialogParameters encryptionDialogParameters;

    private ModelProperty<Boolean> isProgressVisible;
    private ModelProperty<Integer> progressValue;
    private ModelProperty<String> progressNote;
    private ModelProperty<Boolean> isDisableControls;
    private ProgressHandler progressHandler;
    private Thread operationThread;

    @Override
    public void afterPropertiesSet() throws Exception {
    }

    public boolean init(EncryptOneHost host, String optionalSource) {
        Preconditions.checkArgument(host != null);
        this.host = host;

        if (!doWeHaveKeysToEncryptWith()) {
            return false;
        }

        initModelProperties();

        if (optionalSource == null) {
            if (askUserForSourceFile() == null) {
                return false;
            }
        } else {
            sourceFile.setValueByOwner(optionalSource);
        }

        return true;
    }

    private String askUserForSourceFile() {
        String selectedSourceFile;
        if ((selectedSourceFile = getSourceFileChooser().askUserForFile()) == null) {
            return null;
        }
        sourceFile.setValueByOwner(selectedSourceFile);
        return selectedSourceFile;
    }

    public SaveFileChooserDialog getTargetFileChooser() {
        if (targetFileChooser == null) {
            targetFileChooser = new SaveFileChooserDialog(findRegisteredWindowIfAny(), "action.chooseTargetFile",
                    "action.choose", appProps, "EncryptionTargetChooser") {
                @Override
                protected String onDialogClosed(String filePathName, JFileChooser ofd) {
                    String ret = super.onDialogClosed(filePathName, ofd);
                    if (ret != null) {
                        targetFile.setValueByOwner(ret);
                    }
                    return ret;
                }

                @Override
                protected void onFileChooserPostConstrct(JFileChooser ofd) {
                    ofd.setAcceptAllFileFilterUsed(false);
                    ofd.addChoosableFileFilter(new FileNameExtensionFilter("GPG Files (.pgp)", "pgp"));
                    // NOTE: Should we support other extensions?....
                    ofd.addChoosableFileFilter(ofd.getAcceptAllFileFilter());
                    ofd.setFileFilter(ofd.getChoosableFileFilters()[0]);
                }

                @Override
                protected void suggestTarget(JFileChooser ofd) {
                    String sourceFileStr = sourceFile.getValue();
                    if (StringUtils.hasText(targetFile.getValue())) {
                        use(ofd, targetFile.getValue());
                    } else if (encryptionDialogParameters != null
                            && encryptionDialogParameters.getTargetFile() != null) {
                        if (encryptionDialogParameters.getSourceFile().equals(sourceFile.getValue())) {
                            use(ofd, encryptionDialogParameters.getTargetFile());
                        } else {
                            use(ofd, madeUpTargetFileName(sourceFile.getValue(), FilenameUtils
                                    .getFullPathNoEndSeparator(encryptionDialogParameters.getTargetFile())));
                        }
                    } else if (StringUtils.hasText(sourceFileStr) && new File(sourceFileStr).exists()) {
                        String basePath = FilenameUtils.getFullPathNoEndSeparator(sourceFileStr);
                        ofd.setCurrentDirectory(new File(basePath));
                        ofd.setSelectedFile(new File(madeUpTargetFileName(sourceFileStr, basePath)));
                    }
                }

                private void use(JFileChooser ofd, String filePathName) {
                    ofd.setCurrentDirectory(new File(FilenameUtils.getFullPathNoEndSeparator(filePathName)));
                    ofd.setSelectedFile(new File(filePathName));
                }
            };
        }
        return targetFileChooser;
    }

    private boolean doWeHaveKeysToEncryptWith() {
        if (!keyRingService.readKeys().isEmpty()) {
            return true;
        }
        UiUtils.messageBox(text("phrase.noKeysForEncryption"), text("term.attention"), MessageSeverity.WARNING);
        host.getActionToOpenCertificatesList().actionPerformed(null);
        if (keyRingService.readKeys().isEmpty()) {
            return false;
        }
        return true;
    }

    private void initModelProperties() {
        sourceFile = new ModelProperty<>(this, new ValueAdapterHolderImpl<>(), "sourceFile");
        sourceFile.getModelPropertyAccessor().addPropertyChangeListener(onSourceFileModified);

        isUseSameFolder = new ModelProperty<>(this, new ValueAdapterHolderImpl<>(true), "saveToSameFolder");
        isUseSameFolder.getModelPropertyAccessor().addPropertyChangeListener(onUseSameFolderChanged);
        targetFile = new ModelProperty<>(this, new ValueAdapterHolderImpl<>(), "targetFile");
        targetFile.getModelPropertyAccessor().addPropertyChangeListener(onTargetFileChanged);
        targetFileEnabled = new ModelProperty<>(this, new ValueAdapterHolderImpl<>(), "targetFile");

        List<Key> allKeys = keyRingService.readKeys();
        allKeys.sort(new ComparatorKeyByNameImpl());
        availabileRecipients = new ModelListProperty<Key>(this, new ValueAdapterReadonlyImpl<List<Key>>(allKeys),
                "availabileRecipients");
        selectedRecipients = new ModelMultSelInListProperty<Key>(this,
                new ValueAdapterHolderImpl<List<Key>>(new ArrayList<Key>()), "projects", availabileRecipients);
        selectedRecipients.getModelMultSelInListPropertyAccessor()
                .addListExEventListener(onRecipientsSelectionChanged);
        isNoPrivateKeysSelected = new ModelProperty<>(this, new ValueAdapterHolderImpl<>(),
                "isNoPrivateKeysSelected");
        onRecipientsSelectionChanged.onListChanged();

        isDeleteSourceAfter = new ModelProperty<>(this, new ValueAdapterHolderImpl<>(false), "deleteSourceAfter");
        isOpenTargetFolderAfter = new ModelProperty<>(this, new ValueAdapterHolderImpl<>(false),
                "openTargetFolder");

        onUseSameFolderChanged.propertyChange(null);

        isDisableControls = new ModelProperty<>(this, new ValueAdapterHolderImpl<>(false), "isDisableControls");
        isProgressVisible = new ModelProperty<>(this, new ValueAdapterHolderImpl<>(false), "isProgressVisible");
        progressValue = new ModelProperty<>(this, new ValueAdapterHolderImpl<>(0), "progressValue");
        progressNote = new ModelProperty<>(this, new ValueAdapterHolderImpl<>(""), "progressNote");
        progressHandler = new ProgressHandlerPmMixinImpl(isProgressVisible, progressValue, progressNote);
    }

    private PropertyChangeListener onTargetFileChanged = new PropertyChangeListener() {
        @Override
        public void propertyChange(PropertyChangeEvent evt) {
            refreshPrimaryOperationAvailability();
        }
    };

    public ExistingFileChooserDialog getSourceFileChooser() {
        if (sourceFileChooser == null) {
            sourceFileChooser = new ExistingFileChooserDialog(findRegisteredWindowIfAny(), appProps,
                    SOURCE_FOLDER) {
                @Override
                protected void doFileChooserPostConstruct(JFileChooser ofd) {
                    super.doFileChooserPostConstruct(ofd);
                    ofd.setDialogTitle(Messages.get("phrase.selectFileToEncrypt"));

                    ofd.setAcceptAllFileFilterUsed(false);
                    ofd.addChoosableFileFilter(notEncryptedFiles);
                    ofd.addChoosableFileFilter(ofd.getAcceptAllFileFilter());
                    ofd.setFileFilter(ofd.getChoosableFileFilters()[0]);
                }

                private FileFilter notEncryptedFiles = new FileFilter() {
                    @Override
                    public boolean accept(File f) {
                        return !DecryptOnePm.isItLooksLikeYourSourceFile(f.getAbsolutePath());
                    }

                    @Override
                    public String getDescription() {
                        return text("phrase.allExceptEncrypted");
                    }
                };
            };
        }
        return sourceFileChooser;
    }

    private PropertyChangeListener onSourceFileModified = new PropertyChangeListener() {
        @Override
        public void propertyChange(PropertyChangeEvent evt) {
            log.debug("Source changed to : " + sourceFile.getValue());

            refreshPrimaryOperationAvailability();

            if (!StringUtils.hasText(sourceFile.getValue())) {
                return;
            }

            encryptionDialogParameters = encryptionParamsStorage.findParamsBasedOnSourceFile(sourceFile.getValue(),
                    true);
            if (encryptionDialogParameters != null) {
                useSugestedParameters(encryptionDialogParameters);
            } else {
                selectSelfAsRecipient();
            }
        }

        private void selectSelfAsRecipient() {
            selectedRecipients.getList().clear();
            for (Key key : availabileRecipients.getList()) {
                if (!key.getKeyData().isCanBeUsedForDecryption()) {
                    continue;
                }
                selectedRecipients.getList().add(key);
            }
        }

        private void useSugestedParameters(EncryptionDialogParameters params) {
            useSuggestedTargetFile(params);

            // NOTE: MAGIC: We need to set it AFTER we set targetFolder. Because
            // then isUseSameFolder onChange handler will not open folder
            // selection
            // dialog
            isUseSameFolder.setValueByOwner(params.isUseSameFolder());
            isDeleteSourceAfter.setValueByOwner(params.isDeleteSourceFile());
            isOpenTargetFolderAfter.setValueByOwner(params.isOpenTargetFolder());

            Set<String> missedKeys = preselectRecipients(new HashSet<>(params.getRecipientsKeysIds()));
            notifyUserOfMissingKeysIfAny(missedKeys);
        }

        private void useSuggestedTargetFile(EncryptionDialogParameters params) {
            if (!params.isUseSameFolder()) {
                if (params.getSourceFile().equals(sourceFile.getValue())) {
                    targetFile.setValueByOwner(params.getTargetFile());
                } else {
                    // NOTE: Assuming that params fully valid and target file is
                    // provided
                    targetFile.setValueByOwner(madeUpTargetFileName(sourceFile.getValue(),
                            FilenameUtils.getFullPathNoEndSeparator(params.getTargetFile())));
                }
            } else {
                targetFile.setValueByOwner("");
            }
        }

        private Set<String> preselectRecipients(Set<String> recipientsKeysIds) {
            selectedRecipients.getList().clear();
            Set<String> missedKeys = new HashSet<>();
            for (String keyId : recipientsKeysIds) {
                Optional<Key> key = availabileRecipients.getList().stream()
                        .filter(x -> x.getKeyData().isHasAlternativeId(keyId)).findFirst();
                if (key.isPresent()) {
                    selectedRecipients.getList().add(key.get());
                } else {
                    missedKeys.add(keyId);
                }
            }
            return missedKeys;
        }

        private void notifyUserOfMissingKeysIfAny(Set<String> missedKeys) {
            if (CollectionUtils.isEmpty(missedKeys)) {
                return;
            }

            UiUtils.messageBox(findRegisteredWindowIfAny(),
                    text("error.notAllRecipientsAvailable", Arrays.asList(missedKeys)), text("term.attention"),
                    JOptionPane.WARNING_MESSAGE);
        }
    };

    private PropertyChangeListener onUseSameFolderChanged = new PropertyChangeListener() {
        @Override
        public void propertyChange(PropertyChangeEvent evt) {
            boolean result = !isUseSameFolder.getValue();
            actionBrowseTarget.setEnabled(result);
            targetFileEnabled.setValueByOwner(result);
            // NOTE: MAGIC: This event might be triggered when using suggested
            // parameters. So if value is already provided for target file then
            // we'll not show file chooser
            refreshPrimaryOperationAvailability();
            if (result && !StringUtils.hasText(targetFile.getValue())) {
                // NOTE: We need to let event processing go otherwise it all be
                // messed up
                SwingUtilities.invokeLater(new Runnable() {
                    @Override
                    public void run() {
                        getTargetFileChooser().askUserForFile();
                        refreshPrimaryOperationAvailability();
                    }
                });
            }
        }
    };

    protected void refreshPrimaryOperationAvailability() {
        boolean result = true;
        result &= !selectedRecipients.getList().isEmpty();
        result &= StringUtils.hasText(sourceFile.getValue()) && new File(sourceFile.getValue()).exists();
        result &= isUseSameFolder.getValue() || StringUtils.hasText(targetFile.getValue());
        actionDoOperation.setEnabled(result);
    }

    private ListChangeListenerAnyEventImpl<Key> onRecipientsSelectionChanged = new ListChangeListenerAnyEventImpl<Key>() {
        @Override
        public void onListChanged() {
            refreshPrimaryOperationAvailability();

            isNoPrivateKeysSelected.setValueByOwner(selectedRecipients.getList().size() == 0 || selectedRecipients
                    .getList().stream().filter(x -> x.getKeyData().isCanBeUsedForDecryption()).count() == 0);
        }
    };

    @SuppressWarnings("serial")
    protected final Action actionDoOperation = new LocalizedAction("action.encrypt") {
        @Override
        public void actionPerformed(ActionEvent e) {
            actionDoOperation.setEnabled(false);
            isDisableControls.setValueByOwner(true);
            operationThread = new Thread(operationWorker);
            operationThread.start();
        }
    };

    private Runnable operationWorker = new Runnable() {
        @Override
        public void run() {
            String targetFileName = getEffectiveTargetFileName();

            Fingerprint source;
            Fingerprint target;

            try {
                ChecksumCalcInputStreamSupervisor inputStreamSupervisor = new ChecksumCalcInputStreamSupervisorImpl(
                        messageDigestFactory);
                ChecksumCalcOutputStreamSupervisor outputStreamSupervisor = new ChecksumCalcOutputStreamSupervisorImpl(
                        messageDigestFactory);

                FileUtilsEx.baitAndSwitch(targetFileName,
                        x -> encryptionService.encrypt(sourceFile.getValue(), x, selectedRecipients.getList(),
                                progressHandler, inputStreamSupervisor, outputStreamSupervisor));

                source = inputStreamSupervisor.getFingerprint();
                target = outputStreamSupervisor.getFingerprint();

                log.debug("Encryption completed: " + targetFileName);
            } catch (UserRequestedCancellationException ce) {
                host.handleClose();
                return;
            } catch (Throwable t) {
                log.error("Failed to encrypt", t);
                EntryPoint.reportExceptionToUser("error.failedToEncryptFile", t);
                actionDoOperation.setEnabled(true);
                isDisableControls.setValueByOwner(false);
                return;
            }

            // Delete source if asked
            if (isDeleteSourceAfter.getValue()) {
                try {
                    Preconditions.checkState(new File(sourceFile.getValue()).delete(),
                            "File.delete() returned false");
                } catch (Throwable t) {
                    EntryPoint.reportExceptionToUser("error.encryptOkButCantDeleteSource", t);
                }
            } else {
                updateBaselineFingerprintsIfApplicable(sourceFile.getValue(), targetFileName, source, target);
            }

            // Open target folder
            if (isOpenTargetFolderAfter.getValue()) {
                askOperSystemToBrowseForFolder(targetFileName);
            } else {
                // Or show confirmation
                UiUtils.messageBox(text("phrase.encryptionSuccess"), text("term.success"), MessageSeverity.INFO);
            }

            // Remember parameters
            encryptionParamsStorage.persistDialogParametersForCurrentInputs(buildEncryptionDialogParameters(),
                    true);

            // close window
            host.handleClose();
        }

        private void updateBaselineFingerprintsIfApplicable(String decryptedFileName, String encryptedFileName,
                Fingerprint source, Fingerprint target) {
            DecryptedFile decryptedMonitored = monitoringDecryptedFilesService
                    .findByDecryptedFile(decryptedFileName);
            if (decryptedMonitored == null) {
                return;
            }

            DecryptedFile newDecryptedFile = DeepCopy.copy(decryptedMonitored);
            newDecryptedFile.setDecryptedFileFingerprint(source);
            newDecryptedFile.setEncryptedFileFingerprint(target);
            monitoringDecryptedFilesService.addOrUpdate(newDecryptedFile);
        }

        private void askOperSystemToBrowseForFolder(String targetFileName) {
            try {
                Desktop.getDesktop().browse(new File(targetFileName).getParentFile().toURI());
            } catch (Throwable t) {
                EntryPoint.reportExceptionToUser("error.encryptOkButCantBrowseForFolder", t);
            }
        }

        private String getEffectiveTargetFileName() {
            if (!StringUtils.hasText(targetFile.getValue()) || isUseSameFolder.getValue()) {
                isUseSameFolder.setValueByOwner(true);
                return madeUpTargetFileName(sourceFile.getValue(),
                        FilenameUtils.getFullPathNoEndSeparator(sourceFile.getValue()));
            }

            String targetFileName = targetFile.getValue();
            File parentFolder = new File(targetFileName).getParentFile();
            Preconditions.checkState(parentFolder.exists() || parentFolder.mkdirs(),
                    "Failed to ensure all parents directories created");
            return targetFileName;
        }

        private EncryptionDialogParameters buildEncryptionDialogParameters() {
            EncryptionDialogParameters ret = new EncryptionDialogParameters();
            ret.setSourceFile(sourceFile.getValue());
            ret.setUseSameFolder(isUseSameFolder.getValue());
            ret.setTargetFile(targetFile.getValue());
            ret.setDeleteSourceFile(isDeleteSourceAfter.getValue());
            ret.setOpenTargetFolder(isOpenTargetFolderAfter.getValue());
            ret.setRecipientsKeysIds(new ArrayList<>(selectedRecipients.getList().size()));
            for (Key key : selectedRecipients.getList()) {
                ret.getRecipientsKeysIds().add(key.getKeyInfo().getKeyId());
            }
            return ret;
        }
    };

    private String madeUpTargetFileName(String sourceFileName, String targetBasedPath) {
        File fileSource = new File(sourceFileName);
        String fileNameOnlyWoPathAndExtension = fileSource.getName();
        return targetBasedPath + File.separator + fileNameOnlyWoPathAndExtension + "." + ENCRYPTED_FILE_EXTENSION;
    }

    @SuppressWarnings("serial")
    protected final Action actionCancel = new LocalizedAction("action.cancel") {
        @Override
        public void actionPerformed(ActionEvent e) {
            if (operationThread != null && operationThread.isAlive()) {
                operationThread.interrupt();
            } else {
                host.handleClose();
            }
        }
    };

    @SuppressWarnings("serial")
    protected final Action actionBrowseSource = new LocalizedAction("action.browse") {
        @Override
        public void actionPerformed(ActionEvent e) {
            askUserForSourceFile();
        }
    };

    @SuppressWarnings("serial")
    protected final Action actionBrowseTarget = new LocalizedAction("action.browse") {
        @Override
        public void actionPerformed(ActionEvent e) {
            getTargetFileChooser().askUserForFile();
            refreshPrimaryOperationAvailability();
        }
    };

    public ModelPropertyAccessor<Boolean> getIsDeleteSourceAfter() {
        return isDeleteSourceAfter.getModelPropertyAccessor();
    }

    public ModelPropertyAccessor<Boolean> getIsOpenTargetFolderAfter() {
        return isOpenTargetFolderAfter.getModelPropertyAccessor();
    }

    public ModelPropertyAccessor<Boolean> getIsUseSameFolder() {
        return isUseSameFolder.getModelPropertyAccessor();
    }

    public ModelMultSelInListProperty<Key> getSelectedRecipients() {
        // NOTE: I did sort of exception here. Instead of providing accessor I'm
        // prvoding model property itself so that JCheckList can bind directly to list
        // of checked recipients
        // TODO: This doesn't look right actually, I'd say it's better to refactor it
        // and incorporate changes into swingpm if possible
        return selectedRecipients;
    }

    public ModelPropertyAccessor<String> getSourceFile() {
        return sourceFile.getModelPropertyAccessor();
    }

    public ModelPropertyAccessor<String> getTargetFile() {
        return targetFile.getModelPropertyAccessor();
    }

    public ModelPropertyAccessor<Boolean> getTargetFileEnabled() {
        return targetFileEnabled.getModelPropertyAccessor();
    }

    public static boolean isItLooksLikeYourSourceFile(String file) {
        // NOTE: As of know it's fairly simple -- like if it's not for
        // decryption than it's opposite. Later on we might want to revisit this
        // logic.
        return new File(file).exists() && !DecryptOnePm.isItLooksLikeYourSourceFile(file);
    }

    public ModelPropertyAccessor<Boolean> getIsProgressVisible() {
        return isProgressVisible.getModelPropertyAccessor();
    }

    public ModelPropertyAccessor<Integer> getProgressValue() {
        return progressValue.getModelPropertyAccessor();
    }

    public ModelPropertyAccessor<String> getProgressNote() {
        return progressNote.getModelPropertyAccessor();
    }

    public ModelPropertyAccessor<Boolean> getIsDisableControls() {
        return isDisableControls.getModelPropertyAccessor();
    }

    public ModelPropertyAccessor<Boolean> getIsNoPrivateKeysSelected() {
        return isNoPrivateKeysSelected.getModelPropertyAccessor();
    }
}