package com.eua.local.view.controller; import java.io.File; import java.io.FileInputStream; import java.io.FileOutputStream; import java.math.BigDecimal; import java.util.Random; import java.util.zip.ZipEntry; import java.util.zip.ZipOutputStream; import com.foundation.metadata.Attribute; import com.foundation.util.IManagedList; import com.foundation.util.ManagedList; import com.foundation.view.ControlDecoration; import com.foundation.view.IViewContext; import com.foundation.view.swt.DirectoryDialog; import com.foundation.view.swt.FileDialog; import com.common.comparison.Comparator; import com.common.debug.Debug; import com.common.io.StreamSupport; import com.common.io.SymmetricOutputStream; import com.common.security.ISymmetricAlgorithm; import com.common.security.Rijndael; import com.common.security.Sha1; import com.common.util.LiteHashSet; import com.common.util.StringSupport; import com.eua.controller.SettingsController; import com.eua.local.view.EncryptView; import com.eua.model.FileMetadata; /** * Copyright Declarative Engineering LLC 2009
*/ public class EncryptViewController extends AbstractViewController { private static final int METRIC_BYTES = 0; private static final int METRIC_KILO_BYTES = 1; private static final int METRIC_MEGA_BYTES = 2; private static final int METRIC_GIGA_BYTES = 3; //Note: The metrics must be in ascending sequential order starting with zero for both the options array and the names array.// private static final Integer[] METRIC_OPTIONS = new Integer[] {new Integer(METRIC_BYTES), new Integer(METRIC_KILO_BYTES), new Integer(METRIC_MEGA_BYTES), new Integer(METRIC_GIGA_BYTES)}; private static final String[] METRIC_OPTION_NAMES = new String[] {"Bytes", "Kilobytes", "Megabytes", "Gigabytes"}; public static final Attribute FILES = registerCollection(EncryptViewController.class, "files", AO_REFERENCED | AO_LAZY); public static final Attribute SELECTED_FILES = registerCollection(EncryptViewController.class, "selectedFiles", AO_REFERENCED | AO_LAZY); public static final Attribute SPLIT = registerAttribute(EncryptViewController.class, "split", Boolean.FALSE); public static final Attribute MAXIMUM_OUTPUT_SIZE = registerAttribute(EncryptViewController.class, "maximumOutputSize", new Long(1)); public static final Attribute MAXIMUM_OUTPUT_METRIC = registerAttribute(EncryptViewController.class, "maximumOutputMetric", new Integer(METRIC_MEGA_BYTES)); public static final Attribute PASSWORD = registerAttribute(EncryptViewController.class, "password"); public static final Attribute OUTPUT_DIRECTORY = registerAttribute(EncryptViewController.class, "outputDirectory", AO_LAZY); public static final Attribute OUTPUT_NAME = registerAttribute(EncryptViewController.class, "outputName", AO_LAZY); public static final Attribute FILE_METADATA_SET = registerAttribute(EncryptViewController.class, "fileMetadataSet", AO_REFERENCED | AO_LAZY); public static final Attribute INPUT_DIRECTORY = registerAttribute(EncryptViewController.class, "inputDirectory", AO_REFERENCED | AO_LAZY); /** * EncryptViewController constructor. * @param viewContext */ public EncryptViewController(IViewContext viewContext) { super(viewContext); }//EncryptViewController()// /* (non-Javadoc) * @see com.foundation.common.Entity#lazyLoadAttribute(int) */ protected Object lazyLoadAttribute(Attribute attributeNumber) { Object result = null; if(attributeNumber == FILES) { result = new ManagedList(10, 100); }//if// else if(attributeNumber == SELECTED_FILES) { result = new ManagedList(10, 100); }//else if// else if(attributeNumber == INPUT_DIRECTORY) { result = SettingsController.getSingleton().getInputDirectory(); if(result == null) { File desktop = new File(System.getProperty("user.home"), "desktop"); if(desktop.exists()) { result = desktop.getAbsolutePath(); }//if// else { result = System.getProperty("user.dir"); }//else// }//if// }//else if// else if(attributeNumber == OUTPUT_DIRECTORY) { result = SettingsController.getSingleton().getEncryptionOutputDirectory(); if(result == null) { File desktop = new File(System.getProperty("user.home"), "desktop"); if(desktop.exists()) { result = desktop.getAbsolutePath(); }//if// else { result = System.getProperty("user.dir"); }//else// }//if// }//else if// else if(attributeNumber == OUTPUT_NAME) { result = SettingsController.getSingleton().getEncryptedFileName(); if(result == null) { result = "encrypted"; }//if// }//else if// else if(attributeNumber == FILE_METADATA_SET) { result = new LiteHashSet(10, LiteHashSet.DEFAULT_LOAD_FACTOR, Comparator.getLogicalComparator(), LiteHashSet.STYLE_NO_DUPLICATES); }//else if// else { result = super.lazyLoadAttribute(attributeNumber); }//else// return result; }//lazyLoadAttribute()// /* (non-Javadoc) * @see com.foundation.controller.ViewController#getViewClass() */ protected Class getViewClass() { return EncryptView.class; }//getViewClass()// /* (non-Javadoc) * @see com.foundation.controller.ViewController#validate() */ public boolean validate() { boolean result = true; getDecorationManager().clearDecorations(); if(getSplit().booleanValue() && (getMaximumOutputSize() == null || getMaximumOutputSize().longValue() <= 0)) { getDecorationManager().addDecoration(this, MAXIMUM_OUTPUT_SIZE, new ControlDecoration(IMAGE_ERROR, "Missing output size.")); result = false; }//if// else if(getSplit().booleanValue() && getMaximumOutputMetric().intValue() == METRIC_BYTES && getMaximumOutputSize().longValue() < 1000) { getDecorationManager().addDecoration(this, MAXIMUM_OUTPUT_SIZE, new ControlDecoration(IMAGE_ERROR, "Maximum output size must be greater than 1000 if measured in bytes.")); result = false; }//else if// if(getPassword() == null || getPassword().length() == 0) { getDecorationManager().addDecoration(this, PASSWORD, new ControlDecoration(IMAGE_ERROR, "Password required.")); result = false; }//if// if(getOutputDirectory() == null || getOutputDirectory().trim().length() == 0) { getDecorationManager().addDecoration(this, OUTPUT_DIRECTORY, new ControlDecoration(IMAGE_ERROR, "Output directory required.")); result = false; }//if// if(getOutputName() == null || getOutputName().trim().length() == 0) { getDecorationManager().addDecoration(this, OUTPUT_NAME, new ControlDecoration(IMAGE_ERROR, "Output name required.")); result = false; }//if// getDecorationManager().applyDecorationChanges(); return result; }//validate()// /** * Cancels the log-in process and closes the client. */ public void doCancel() { close(); }//doCancel()// /** * Initiates the validation process and opens the next view upon sucsessful validation. */ public void doEncrypt() { synchronize(); if(validate()) { if(getFiles().getSize() > 0 && getPassword() != null) { BigDecimal totalSize = new BigDecimal(0); BigDecimal maximumOutputSize = getSplit().booleanValue() ? new BigDecimal(getMaximumOutputSize().longValue()) : null; int currentFileIndex = 0; FileMetadata nextFile = (FileMetadata) getFiles().getFirst(); FileInputStream fin = null; FileOutputStream fout = null; byte[] buffer = new byte[100000]; //100k ISymmetricAlgorithm algorithm = new Rijndael(new Random(StringSupport.hash(getPassword()))); SymmetricOutputStream sout = null; ZipOutputStream zout = null; File outputFile = new File(getOutputDirectory(), getOutputName() + ".enc"); try { fin = new FileInputStream(new File(nextFile.getPath(), nextFile.getName())); //Calculate the maximum output size of each split segment in terms of bytes.// if(maximumOutputSize != null) { switch(getMaximumOutputMetric().intValue()) { case METRIC_GIGA_BYTES: { maximumOutputSize = maximumOutputSize.multiply(new BigDecimal(1073741824)); //1024^3 break; }//case// case METRIC_MEGA_BYTES: { maximumOutputSize = maximumOutputSize.multiply(new BigDecimal(1048576)); //1024^2 break; }//case// case METRIC_KILO_BYTES: { maximumOutputSize = maximumOutputSize.multiply(new BigDecimal(1024)); break; }//case// default: { //Do nothing.// break; }//default// }//switch// }//if// //Calculate the total size of all the input content.// for(int index = 0; index < getFiles().getSize(); index++) { FileMetadata next = (FileMetadata) getFiles().get(index); totalSize = totalSize.add(new BigDecimal(next.getLength().longValue())); }//for// fout = new FileOutputStream(outputFile); //Write the magic number which is used when reading to verify that the file is generated by this same tool.// StreamSupport.writeInt(MAGIC_NUMBER, fout); //Write the version number of the tool being used to generate the encrypted data.// StreamSupport.writeInt(VERSION, fout); //Create the symmetric encryption output stream.// sout = new SymmetricOutputStream(fout, algorithm); sout.encrypt(true); //Create the zip output stream to ensure all encrypted content is first compressed.// zout = new ZipOutputStream(sout); zout.putNextEntry(new ZipEntry("main")); //Write the number of input files.// StreamSupport.writeInt(getFiles().getSize(), zout); //Write each file name and size.// for(int index = 0; index < getFiles().getSize(); index++) { FileMetadata next = (FileMetadata) getFiles().get(index); StreamSupport.writeString(next.getName(), zout); StreamSupport.writeLong(next.getLength().longValue(), zout); }//for// //Write the file data as long as we have an input stream.// while(fin != null) { int readCount = 0; //Read in a whole buffer of content.// while(readCount < buffer.length && fin != null) { readCount += StreamSupport.readBytes(fin, buffer, readCount, buffer.length - readCount); //If we couldn't fill the buffer (due to not enough bytes in the input stream), then open a stream on the next file.// if(readCount != buffer.length) { fin.close(); //Increment to the next file and open an input stream.// if(++currentFileIndex != getFiles().getSize()) { nextFile = (FileMetadata) getFiles().get(currentFileIndex); fin = new FileInputStream(new File(nextFile.getPath(), nextFile.getName())); }//if// else { fin = null; nextFile = null; }//else// }//if// }//while// zout.write(buffer, 0, readCount); }//while// zout.closeEntry(); zout.flush(); zout.finish(); sout.encrypt(false); sout.flush(); zout.close(); sout.close(); fout.close(); //If the output should be split then split the generated file now.// if(getSplit().booleanValue() && maximumOutputSize.compareTo(new BigDecimal(outputFile.length())) < 0) { int outputFileIndex = 0; BigDecimal bufferSize = new BigDecimal(buffer.length); fin = new FileInputStream(outputFile); fout = null; //Keep reading and writing until the input stream is empty.// while(fin != null) { BigDecimal writeSize = new BigDecimal(0); if(fout != null) { fout.close(); }//if// fout = new FileOutputStream(outputFile.getAbsolutePath() + "." + ++outputFileIndex); //Keep writing to the current file until we can't write any more.// while(fin != null && writeSize.compareTo(maximumOutputSize) < 0) { int readCount; BigDecimal readLength = maximumOutputSize.subtract(writeSize); //If the read length is greater than the buffer size then use the buffer size.// if(readLength.compareTo(bufferSize) > 0) { readLength = bufferSize; }//if// readCount = StreamSupport.readBytes(fin, buffer, 0, readLength.intValue()); fout.write(buffer, 0, readCount); writeSize = writeSize.add(new BigDecimal(readCount)); //Check to see if the input stream is empty.// if(readCount != readLength.intValue()) { fin.close(); fin = null; }//if// }//while// }//while// fout.close(); fout = new FileOutputStream(outputFile); //Write the magic number which is used when reading to verify that the file is generated by this same tool.// StreamSupport.writeInt(SEQUENCE_MAGIC_NUMBER, fout); //Write the version number of the tool being used to generate the encrypted data.// StreamSupport.writeInt(SEQUENCE_VERSION, fout); //Write the number of files in the sequence.// StreamSupport.writeInt(outputFileIndex, fout); fout.close(); fout = null; displayInformation("Encryption Successful", "The content was successfully encrypted and split. All results placed in \"" + outputFile.getParentFile().getCanonicalPath() + "\"."); }//if// else { displayInformation("Encryption Successful", "The content was successfully encrypted to \"" + outputFile.getCanonicalPath() + "\"."); }//else// SettingsController.getSingleton().setEncryptionSettings(getOutputDirectory(), getOutputName(), getInputDirectory()); }//try// catch(Throwable e) { Debug.log(e); }//catch// finally { if(fin != null) { try {fin.close();}catch(Throwable e) {} }//if// if(fout != null) { try {fout.close();}catch(Throwable e) {} }//if// }//finally// }//if// }//if// }//doEncrypt()// /** * Changes the output directory by opening a dialog for the user to choose a location. */ public void doChangeOutputDirectory() { DirectoryDialog dialog = new DirectoryDialog(getView(), 0); String directory; dialog.setMessage("Choose the output directory."); if((directory = dialog.open()) != null) { setOutputDirectory(directory); }//if// }//doChangeOutputDirectory()// /** * Adds files to the collection to be encrypted. */ public void doAddFiles() { FileDialog dialog = new FileDialog(getView(), FileDialog.STYLE_MULTI); String path; dialog.setFilterPath(getInputDirectory()); dialog.setText("Select the files you wish to encrypt."); if((path = dialog.open()) != null) { String[] fileNames = dialog.getFileNames(); File pathFile = new File(path); //Get the actual path. Doing this because the dialog's result isn't a directory path, but rather a path to the first selected file.// path = pathFile.getParent(); setInputDirectory(path); for(int index = 0; index < fileNames.length; index++) { FileMetadata fileMetadata = new FileMetadata(fileNames[index], path); if(getFileMetadataSet().add(fileMetadata) != -1) { getFiles().add(fileMetadata); }//if// }//for// }//if// }//doAddFiles()// /** * Removes files from the collection to be encrypted. */ public void doRemoveFiles() { if(getSelectedFiles().getSize() > 0) { getFiles().removeAll(getSelectedFiles()); getSelectedFiles().removeAll(); }//if// }//doRemoveFiles()// /** * Gets the files value. * @return The files value. */ public IManagedList getFiles() { return (IManagedList) getAttributeValue(FILES); }//getFiles()// /** * Gets the selectedFiles value. * @return The selectedFiles value. */ public IManagedList getSelectedFiles() { return (IManagedList) getAttributeValue(SELECTED_FILES); }//getSelectedFiles()// /** * Gets the split value. * @return The split value. */ public Boolean getSplit() { return (Boolean) getAttributeValue(SPLIT); }//getSplit()// /** * Sets the split value. * @param split The split value. */ public void setSplit(Boolean split) { setAttributeValue(SPLIT, split); }//setSplit()// /** * Gets the maximumOutputSize value. * @return The maximumOutputSize value. */ public Long getMaximumOutputSize() { return (Long) getAttributeValue(MAXIMUM_OUTPUT_SIZE); }//getMaximumOutputSize()// /** * Sets the maximumOutputSize value. * @param maximumOutputSize The maximumOutputSize value. */ public void setMaximumOutputSize(Long maximumOutputSize) { setAttributeValue(MAXIMUM_OUTPUT_SIZE, maximumOutputSize); }//setMaximumOutputSize()// /** * Gets the maximumOutputMetric value. * @return The maximumOutputMetric value. */ public Integer getMaximumOutputMetric() { return (Integer) getAttributeValue(MAXIMUM_OUTPUT_METRIC); }//getMaximumOutputMetric()// /** * Sets the metric for the maximum output value. * @param maximumOutputMetric The metric for the maximum output value. */ public void setMaximumOutputMetric(Integer maximumOutputMetric) { setAttributeValue(MAXIMUM_OUTPUT_METRIC, maximumOutputMetric); }//setMaximumOutputMetric()// /** * Gets the set of metrics allowed for the maximum output metric. * @return The metrics allowed. */ public Integer[] getMetrics() { return METRIC_OPTIONS; }//getMetrics()// /** * Gets the metric name for the given metric identifier. * @param metric The metric number. * @return The name for the metric. */ public String getMetricName(Integer metric) { return METRIC_OPTION_NAMES[metric.intValue()]; }//getMetricName()// /** * Gets the password value. * @return The password value. */ public String getPassword() { return (String) getAttributeValue(PASSWORD); }//getPassword()// /** * Sets the password value. * @param password The password value. */ public void setPassword(String password) { setAttributeValue(PASSWORD, password); }//setPassword()// /** * Gets the outputDirectory value. * @return The outputDirectory value. */ public String getOutputDirectory() { return (String) getAttributeValue(OUTPUT_DIRECTORY); }//getOutputDirectory()// /** * Sets the outputDirectory value. * @param outputDirectory The outputDirectory value. */ public void setOutputDirectory(String outputDirectory) { setAttributeValue(OUTPUT_DIRECTORY, outputDirectory); }//setOutputDirectory()// /** * Gets the outputName value. * @return The outputName value. */ public String getOutputName() { return (String) getAttributeValue(OUTPUT_NAME); }//getOutputName()// /** * Sets the outputName value. * @param outputName The outputName value. */ public void setOutputName(String outputName) { setAttributeValue(OUTPUT_NAME, outputName); }//setOutputName()// /** * Gets the fileMetadataSet value. * @return The fileMetadataSet value. */ protected LiteHashSet getFileMetadataSet() { return (LiteHashSet) getAttributeValue(FILE_METADATA_SET); }//getFileMetadataSet()// /** * Gets the last used directory for selecting files to encrypt. * @return The input directory path. */ public String getInputDirectory() { return (String) getAttributeValue(INPUT_DIRECTORY); }//getInputDirectory()// /** * Sets the last used directory for selecting files to encrypt. * @param inputDirectory The input directory path. */ public void setInputDirectory(String inputDirectory) { setAttributeValue(INPUT_DIRECTORY, inputDirectory); }//setInputDirectory()// }//EncryptViewController//