Java tutorial
/* * Copyright (C) 2010-2013, Martin Goellnitz * * 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 2 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, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA, 02110-1301, USA */ package jfs.sync.encryption; import java.io.IOException; import java.io.InputStream; import java.io.ObjectInputStream; import java.io.OutputStream; import java.security.InvalidKeyException; import java.security.NoSuchAlgorithmException; import java.util.Map; import javax.crypto.Cipher; import javax.crypto.NoSuchPaddingException; import jfs.conf.JFSConfig; import jfs.conf.JFSLog; import jfs.conf.JFSText; import jfs.sync.JFSFile; import jfs.sync.JFSProgress; import jfs.sync.util.SecurityUtils; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; public class JFSEncryptedFile extends JFSFile { private static Log log = LogFactory.getLog(JFSEncryptedFile.class); /** * duplication to avoid constant casts */ private AbstractFileProducer fileProducer; private FileInfo fileInfo; /** * If the file is a directory this points to all files (and directories) within the directory. Null for all non * directories. */ private JFSFile[] list = null; /** The last input stream opened for this file. */ private InputStream in = null; /** The last output stream opened for this file. */ private OutputStream out = null; /** * Creates a new local JFS file object. * * @param fileProducer * The assigned file producer. * @param cipherSpec * JCE cipher specification. * @param relativePath * The relative path of the JFS file starting from the root JFS file. */ JFSEncryptedFile(AbstractFileProducer fileProducer, String relativePath) { super(fileProducer, relativePath); // super has a somewhat buggy normalization of filename only dealing with local file separator definitions this.relativePath = this.relativePath.replace('\\', '/'); this.relativePath = this.relativePath.replace("/", fileProducer.getSeparator()); this.fileProducer = fileProducer; fileInfo = fileProducer.getFileInfo(getRelativePath()); if (log.isDebugEnabled()) { log.debug("('" + getRelativePath() + "') name=" + fileInfo.getPath()); } // if }// JFSEncryptedFile() private Cipher getCipher(int cipherMode) { try { String cipherSpec = ((AbstractFileProducer) getFileProducer()).storageAccess.getCipherSpec(); byte[] credentials = ((AbstractFileProducer) getFileProducer()).storageAccess .getFileCredentials(getRelativePath()); return SecurityUtils.getCipher(cipherSpec, cipherMode, credentials); } catch (NoSuchAlgorithmException nsae) { log.error("getCipher() No Such Algorhithm"); } catch (NoSuchPaddingException nspe) { log.error("getCipher() No Such Padding"); } catch (InvalidKeyException ike) { log.error("getCipher() Invalid Key " + ike.getLocalizedMessage()); } // try/catch return null; } // getCipher() /** * @see JFSFile#getName() */ @Override public final String getName() { if (log.isDebugEnabled()) { log.debug("getName('" + getRelativePath() + "') " + fileInfo.getName()); } // if return fileInfo.getName(); } /** * @see JFSFile#getPath() */ @Override public final String getPath() { return fileProducer.getRootPath() + getRelativePath(); } /** * @see JFSFile#isDirectory() */ @Override public final boolean isDirectory() { return fileInfo.isDirectory(); } /** * @see JFSFile#canRead() */ @Override public final boolean canRead() { return fileInfo.isCanRead(); } /** * @see JFSFile#canWrite() */ @Override public final boolean canWrite() { return fileInfo.isCanWrite(); } /** * @see JFSFile#getLength() */ @Override public final long getLength() { if (fileInfo.getSize() < 0) { try { // TODO: move this to storage layer? InputStream fis = fileProducer.getInputStream(getRelativePath()); ObjectInputStream ois = new ObjectInputStream(fis); JFSEncryptedStream.readMarker(ois); fileInfo.setSize(JFSEncryptedStream.readLength(ois)); if (log.isDebugEnabled()) { log.debug("getLength(" + getRelativePath() + ") detected plain text length " + fileInfo.getSize()); } // if ois.close(); } catch (Exception e) { // TODO: what to do now?!?!?! log.error("getLength() could not detect plain text length for " + getPath(), e); } // try/catch } // if return fileInfo.getSize(); } // getLength() /** * @see JFSFile#getLastModified() */ @Override public final long getLastModified() { if (log.isDebugEnabled()) { log.debug("lastModified('" + getRelativePath() + "') " + fileInfo.getModificationDate()); } // if // strange enough, directories need to be 0 in all cases return isDirectory() ? 0 : fileInfo.getModificationDate(); } /** * @see JFSFile#getList() */ @Override public final JFSFile[] getList() { if (list == null) { String[] files = fileProducer.list(getRelativePath()); if (files != null) { list = new JFSFile[files.length]; for (int i = 0; i < files.length; i++) { // asFolder parameter doesn't to anything if (log.isDebugEnabled()) { log.debug("getList() " + i + " " + files[i]); } // if list[i] = fileProducer.getJfsFile(getRelativePath() + fileProducer.getSeparator() + files[i], false); } // for } else { list = new JFSFile[0]; } } if (log.isDebugEnabled()) { log.debug("getList() " + list.length); } // if return list; } /** * @see JFSFile#exists() */ @Override public final boolean exists() { if (log.isDebugEnabled()) { log.debug("exists('" + getRelativePath() + "') " + fileInfo.isExists()); } // if return fileInfo.isExists(); } /** * @see JFSFile#mkdir() */ @Override public final boolean mkdir() { boolean success = fileProducer.createDirectory(getRelativePath()); if (success) { fileInfo.setDirectory(true); } // if return success; } // mkdir() /** * @see JFSFile#setLastModified(long) */ @Override public final boolean setLastModified(long time) { boolean success = fileProducer.setLastModified(getRelativePath(), time); if (log.isDebugEnabled()) { log.debug("setLastModified('" + getRelativePath() + "') setting modification date: " + success); } // if if (success) { fileInfo.setModificationDate(time); } // if return success; } /** * @see JFSFile#setReadOnly() */ @Override public final boolean setReadOnly() { boolean success = true; if (JFSConfig.getInstance().isSetCanWrite()) { success = fileProducer.setReadOnly(getRelativePath()); if (success) { fileInfo.setCanWrite(false); } // if } // if return success; } // setReadOnly() /** * @see JFSFile#delete() */ @Override public final boolean delete() { return fileProducer.delete(getRelativePath()); } /** * @see JFSFile#getInputStream() */ @Override protected InputStream getInputStream() { try { InputStream stream = fileProducer.getInputStream(getRelativePath()); return JFSEncryptedStream.createInputStream(stream, getLength(), getCipher(Cipher.DECRYPT_MODE)); } catch (IOException ioe) { log.error("getInputStream() I/O Exception " + ioe.getLocalizedMessage()); return null; } // try/catch } // getInputStream() /** * @see JFSFile#getOutputStream() */ @Override protected OutputStream getOutputStream() { String p = getRelativePath(); out = null; long l = getLength(); if (log.isDebugEnabled()) { log.debug("getOutputStream() opening '" + p + "' with length " + l); } // if try { int idx = p.lastIndexOf('.'); long compressionLimit = Long.MAX_VALUE; if (idx > 0) { idx++; String extension = p.substring(idx).toLowerCase(); Map<String, Long> compressedExtensions = ((JFSEncryptedFileProducer) fileProducer) .getCompressionLevels(); if (compressedExtensions.containsKey(extension)) { compressionLimit = compressedExtensions.get(extension); if (log.isInfoEnabled()) { log.info("getOutputStream() compression limit " + compressionLimit + " set for " + getName()); } // if } // if } // if out = JFSEncryptedStream.createOutputStream(compressionLimit, fileProducer.getOutputStream(p), l, getCipher(Cipher.ENCRYPT_MODE)); } catch (IOException e) { log.error("getOutputStream()", e); } // try/catch return out; } // getOutputStream() /** * @see JFSFile#closeInputStream() */ @Override protected void closeInputStream() { JFSText t = JFSText.getInstance(); try { if (in != null) { in.close(); in = null; } // if } catch (IOException e) { JFSLog.getErr().getStream().println(t.get("error.io") + " " + e); } // try/catch } // closeInputStream() /** * @see JFSFile#closeOutputStream() */ @Override protected void closeOutputStream() { JFSText t = JFSText.getInstance(); try { if (out != null) { if (log.isDebugEnabled()) { log.debug("closeOutputStream() closing " + getPath()); } // if out.flush(); out.close(); out = null; } // if } catch (IOException e) { JFSLog.getErr().getStream().println(t.get("error.io") + " " + e); } // try/catch } // closeOutputStream() /** * @see JFSFile#preCopyTgt(JFSFile) */ @Override protected boolean preCopyTgt(JFSFile srcFile) { fileInfo.setSize(srcFile.getLength()); return true; } /** * @see JFSFile#preCopySrc(JFSFile) */ @Override protected boolean preCopySrc(JFSFile tgtFile) { return true; } /** * @see JFSFile#postCopyTgt(JFSFile) */ @Override protected boolean postCopyTgt(JFSFile srcFile) { boolean success = true; // Set last modified and read-only only when file is no directory: if (!JFSProgress.getInstance().isCanceled() && !srcFile.isDirectory()) { // Just to work on the same file info fileInfo = fileProducer.getFileInfo(relativePath); fileInfo.setExists(true); fileInfo.setSize(srcFile.getLength()); success = success && setLastModified(srcFile.getLastModified()); // set last modified has to implicitly if (!success) { fileProducer.flush(fileInfo); } // if if (!srcFile.canWrite()) { success = success && setReadOnly(); } // if } // if return success; } /** * @see JFSFile#postCopySrc(JFSFile) */ @Override protected boolean postCopySrc(JFSFile tgtFile) { if (log.isInfoEnabled()) { log.info("postCopySrc() free memory " + Runtime.getRuntime().freeMemory()); } // if return true; } // postCopySrc() /** * @see JFSFile#flush() */ @Override public boolean flush() { return true; } @Override protected void finalize() throws Throwable { super.finalize(); if (log.isInfoEnabled()) { log.info("finalize() free memory " + Runtime.getRuntime().freeMemory()); } // if } // finalize() } // JFSEncryptedFile()