incubator-odf-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From robw...@apache.org
Subject svn commit: r1374762 [2/4] - in /incubator/odf/trunk: odfdom/ odfdom/src/main/java/org/odftoolkit/odfdom/pkg/ odfdom/src/main/java/org/odftoolkit/odfdom/pkg/manifest/ odfdom/src/test/java/org/odftoolkit/odfdom/doc/ odfdom/src/test/java/org/odftoolkit/o...
Date Sun, 19 Aug 2012 15:01:10 GMT
Modified: incubator/odf/trunk/odfdom/src/main/java/org/odftoolkit/odfdom/pkg/OdfPackage.java
URL: http://svn.apache.org/viewvc/incubator/odf/trunk/odfdom/src/main/java/org/odftoolkit/odfdom/pkg/OdfPackage.java?rev=1374762&r1=1374761&r2=1374762&view=diff
==============================================================================
--- incubator/odf/trunk/odfdom/src/main/java/org/odftoolkit/odfdom/pkg/OdfPackage.java (original)
+++ incubator/odf/trunk/odfdom/src/main/java/org/odftoolkit/odfdom/pkg/OdfPackage.java Sun Aug 19 15:01:09 2012
@@ -37,10 +37,6 @@ import java.io.PipedOutputStream;
 import java.io.UnsupportedEncodingException;
 import java.lang.reflect.Constructor;
 import java.net.URI;
-import java.security.InvalidKeyException;
-import java.security.MessageDigest;
-import java.security.NoSuchAlgorithmException;
-import java.security.SecureRandom;
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.HashMap;
@@ -48,46 +44,31 @@ import java.util.HashSet;
 import java.util.Iterator;
 import java.util.List;
 import java.util.Map;
+import java.util.Map.Entry;
 import java.util.Set;
 import java.util.StringTokenizer;
 import java.util.TreeSet;
-import java.util.Map.Entry;
 import java.util.logging.Level;
 import java.util.logging.Logger;
 import java.util.regex.Pattern;
 import java.util.zip.CRC32;
-import java.util.zip.Deflater;
-import java.util.zip.Inflater;
 import java.util.zip.ZipEntry;
-import java.util.zip.ZipException;
-import java.util.zip.ZipFile;
 import java.util.zip.ZipOutputStream;
-
-import javax.crypto.Cipher;
-import javax.crypto.Mac;
-import javax.crypto.spec.IvParameterSpec;
-import javax.crypto.spec.SecretKeySpec;
 import javax.xml.parsers.DocumentBuilder;
 import javax.xml.parsers.DocumentBuilderFactory;
 import javax.xml.parsers.ParserConfigurationException;
-import javax.xml.parsers.SAXParser;
-import javax.xml.parsers.SAXParserFactory;
 import javax.xml.transform.TransformerConfigurationException;
 import javax.xml.transform.TransformerException;
 import javax.xml.transform.URIResolver;
-
 import org.apache.xerces.dom.DOMXSImplementationSourceImpl;
-import org.odftoolkit.odfdom.doc.OdfDocument;
+import javax.xml.parsers.SAXParser;
+import javax.xml.parsers.SAXParserFactory;
 import org.odftoolkit.odfdom.doc.OdfDocument.OdfMediaType;
-import org.odftoolkit.odfdom.pkg.manifest.AlgorithmElement;
-import org.odftoolkit.odfdom.pkg.manifest.EncryptionDataElement;
-import org.odftoolkit.odfdom.pkg.manifest.FileEntryElement;
-import org.odftoolkit.odfdom.pkg.manifest.KeyDerivationElement;
-import org.odftoolkit.odfdom.pkg.manifest.ManifestElement;
+import org.odftoolkit.odfdom.doc.OdfDocument;
+import org.odftoolkit.odfdom.pkg.manifest.Algorithm;
+import org.odftoolkit.odfdom.pkg.manifest.EncryptionData;
+import org.odftoolkit.odfdom.pkg.manifest.KeyDerivation;
 import org.odftoolkit.odfdom.pkg.manifest.OdfFileEntry;
-import org.odftoolkit.odfdom.pkg.manifest.OdfManifestDom;
-import org.odftoolkit.odfdom.pkg.manifest.StartKeyGenerationElement;
-import org.odftoolkit.odfdom.type.Base64Binary;
 import org.w3c.dom.Document;
 import org.w3c.dom.ls.DOMImplementationLS;
 import org.w3c.dom.ls.LSOutput;
@@ -96,6 +77,7 @@ import org.xml.sax.EntityResolver;
 import org.xml.sax.ErrorHandler;
 import org.xml.sax.InputSource;
 import org.xml.sax.SAXException;
+import org.xml.sax.SAXParseException;
 import org.xml.sax.XMLReader;
 
 /**
@@ -130,41 +112,23 @@ public class OdfPackage implements Close
 	private Map<String, ZipEntry> mZipEntries;
 	private HashMap<String, ZipEntry> mOriginalZipEntries;
 	private Map<String, OdfFileEntry> mManifestEntries;
-	// All opened documents from the same package are cached (including the root
-	// document)
+	// All opened documents from the same package are cached (including the root document)
 	private Map<String, OdfPackageDocument> mPkgDocuments;
 	// Three different incarnations of a package file/data
 	// save() will check 1) mPkgDoms, 2) if not check mMemoryFileCache
 	private HashMap<String, Document> mPkgDoms;
 	private HashMap<String, byte[]> mMemoryFileCache;
-
 	private ErrorHandler mErrorHandler;
-	private String mManifestVersion;
-	private OdfManifestDom mManifestDom;
-	private String oldPwd;
-	private String newPwd;
+        private String mManifestVersion;
 
 	/* Commonly used files within the ODF Package */
 	public enum OdfFile {
 
-		/**
-		 * The image directory is not defined by the OpenDocument standard,
-		 * nevertheless the most spread ODF application OpenOffice.org is using
-		 * the directory named "Pictures".
-		 */
+		/** The image directory is not defined by the OpenDocument standard, nevertheless the most spread ODF application OpenOffice.org is using the directory named "Pictures". */
 		IMAGE_DIRECTORY("Pictures"),
-		/**
-		 * The "META-INF/manifest.xml" file is defined by the ODF 1.2 part 3
-		 * Package specification. This manifest is the 'content table' of the
-		 * ODF package and describes the file entries of the ZIP including
-		 * directories, but should not contain empty directories.
-		 */
+		/** The "META-INF/manifest.xml" file is defined by the ODF 1.2 part 3 Package specification. This manifest is the 'content table' of the ODF package and describes the file entries of the ZIP including directories, but should not contain empty directories.*/
 		MANIFEST("META-INF/manifest.xml"),
-		/**
-		 * The "mimetype" file is defined by the ODF 1.2 part 3 Package
-		 * specification. It contains the mediatype string of the root document
-		 * and must be the first file in the ZIP and must not be compressed.
-		 */
+		/** The "mimetype" file is defined by the ODF 1.2 part 3 Package specification. It contains the mediatype string of the root document and must be the first file in the ZIP and must not be compressed. */
 		MEDIA_TYPE("mimetype");
 		private final String internalPath;
 
@@ -179,8 +143,9 @@ public class OdfPackage implements Close
 
 	static {
 		mCompressedFileTypes = new HashSet<String>();
-		String[] typelist = new String[] { "jpg", "gif", "png", "zip", "rar", "jpeg", "mpe", "mpg", "mpeg", "mpeg4", "mp4", "7z", "ari", "arj", "jar", "gz",
-				"tar", "war", "mov", "avi" };
+		String[] typelist = new String[]{"jpg", "gif", "png", "zip", "rar",
+			"jpeg", "mpe", "mpg", "mpeg", "mpeg4", "mp4", "7z", "ari",
+			"arj", "jar", "gz", "tar", "war", "mov", "avi"};
 		mCompressedFileTypes.addAll(Arrays.asList(typelist));
 	}
 
@@ -194,23 +159,21 @@ public class OdfPackage implements Close
 		mPkgDoms = new HashMap<String, Document>();
 		mMemoryFileCache = new HashMap<String, byte[]>();
 		mManifestEntries = new HashMap<String, OdfFileEntry>();
-		// specify whether validation should be enabled and what SAX
-		// ErrorHandler should be used.
+		// specify whether validation should be enabled and what SAX ErrorHandler should be used.
 		if (mErrorHandler == null) {
 			String errorHandlerProperty = System.getProperty("org.odftoolkit.odfdom.validation");
 			if (errorHandlerProperty != null) {
 				if (errorHandlerProperty.equalsIgnoreCase("true")) {
 					mErrorHandler = new DefaultErrorHandler();
-					Logger.getLogger(OdfPackage.class.getName()).info("Activated validation with default ErrorHandler!");
+					Logger.getLogger(OdfPackage.class.getName()).fine("Activated validation with default ErrorHandler!");
 				} else {
 					try {
-						Class cl = Class.forName(errorHandlerProperty);
-						Constructor ctor = cl.getDeclaredConstructor(new Class[] {});
+						Class<?> cl = Class.forName(errorHandlerProperty);
+						Constructor<?> ctor = cl.getDeclaredConstructor(new Class[]{});
 						mErrorHandler = (ErrorHandler) ctor.newInstance();
-						Logger.getLogger(OdfPackage.class.getName()).log(Level.CONFIG, "Activated validation with ErrorHandler:''{0}''!", errorHandlerProperty);
+						Logger.getLogger(OdfPackage.class.getName()).log(Level.FINE, "Activated validation with ErrorHandler:''{0}''!", errorHandlerProperty);
 					} catch (Exception ex) {
-						Logger.getLogger(OdfPackage.class.getName()).log(Level.SEVERE,
-								"Could not initiate validation with the given ErrorHandler: '" + errorHandlerProperty + "'", ex);
+						Logger.getLogger(OdfPackage.class.getName()).log(Level.SEVERE, "Could not initiate validation with the given ErrorHandler: '" + errorHandlerProperty + "'", ex);
 					}
 				}
 			}
@@ -219,73 +182,82 @@ public class OdfPackage implements Close
 
 	/**
 	 * Creates an OdfPackage from the OpenDocument provided by a File.
-	 * 
+	 *
 	 * <p>
 	 * OdfPackage relies on the file being available for read access over the
 	 * whole lifecycle of OdfPackage.
 	 * </p>
-	 * 
+	 *
 	 * @param pkgFile
 	 *            - a file representing the ODF document
-	 * @param baseURI
-	 *            defining the base URI of ODF package.
-	 * @param password
-	 *            defining the password of ODF package.
-	 * @param errorHandler
-	 *            - SAX ErrorHandler used for ODF validation
-	 * @see #getErrorHandler
-	 * @throws java.lang.Exception
-	 *             - if the package could not be created
+	 * @throws SAXException if there's an XML- or validation-related error while loading the package
+	 * @throws IOException if there's an I/O error while loading the package
+	 */
+	private OdfPackage(File pkgFile) throws SAXException, IOException {
+		this(pkgFile, getBaseURLFromFile(pkgFile), null);
+	}
+
+	/**
+	 * Creates an OdfPackage from the OpenDocument provided by a File.
+	 *
+	 * <p>
+	 * OdfPackage relies on the file being available for read access over the
+	 * whole lifecycle of OdfPackage.
+	 * </p>
+	 *
+	 * @param pkgFile a file representing the ODF document
+	 * @param baseURI defining the base URI of ODF package.
+	 * @param errorHandler - SAX ErrorHandler used for ODF validation
+	 * @throws SAXException if there's an XML- or validation-related error while loading the package
+	 * @throws IOException if there's an I/O error while loading the package
+	 *
 	 * @see #getErrorHandler*
 	 */
-	private OdfPackage(File pkgFile, String baseURI, String password, ErrorHandler errorHandler) throws Exception {
+	private OdfPackage(File pkgFile, String baseURI, ErrorHandler errorHandler) throws SAXException, IOException {
 		this();
-		mBaseURI = getBaseURLFromFile(pkgFile);
-		mErrorHandler = errorHandler;
-		oldPwd = password;
-		newPwd = oldPwd;
-		initializeZip(pkgFile);
+		if (errorHandler != null) {
+			mErrorHandler = errorHandler;
+		}
+		mBaseURI = baseURI;
+		InputStream packageStream = new FileInputStream(pkgFile);
+		try {
+			initializeZip(packageStream);
+		} finally {
+			close(packageStream);
+		}
 	}
 
 	/**
 	 * Creates an OdfPackage from the OpenDocument provided by a InputStream.
-	 * 
-	 * <p>
-	 * Since an InputStream does not provide the arbitrary (non sequentiell)
+	 *
+	 * <p>Since an InputStream does not provide the arbitrary (non sequentiell)
 	 * read access needed by OdfPackage, the InputStream is cached. This usually
-	 * takes more time compared to the other constructors.
-	 * </p>
-	 * 
-	 * @param packageStream
-	 *            - an inputStream representing the ODF package
-	 * @param baseURI
-	 *            defining the base URI of ODF package.
-	 * @param password
-	 *            defining the password of ODF package.
-	 * @param errorHandler
-	 *            - SAX ErrorHandler used for ODF validation
-	 * @see #getErrorHandler
-	 * @throws java.lang.Exception
-	 *             - if the package could not be created
+	 * takes more time compared to the other constructors. </p>
+	 *
+	 * @param packageStream - an inputStream representing the ODF package
+	 * @param baseURI defining the base URI of ODF package.
+	 * @param errorHandler - SAX ErrorHandler used for ODF validation
+	 * @throws SAXException if there's an XML- or validation-related error while loading the package
+	 * @throws IOException if there's an I/O error while loading the package
+	 *
 	 * @see #getErrorHandler*
 	 */
-	private OdfPackage(InputStream packageStream, String baseURI, String password, ErrorHandler errorHandler) throws Exception {
+	private OdfPackage(InputStream packageStream, String baseURI, ErrorHandler errorHandler)
+			throws SAXException, IOException {
 		this(); // calling private constructor
 		mErrorHandler = errorHandler;
 		mBaseURI = baseURI;
-		oldPwd = password;
-		newPwd = oldPwd;
 		initializeZip(packageStream);
 	}
 
 	/**
 	 * Loads an OdfPackage from the given documentURL.
-	 * 
+	 *
 	 * <p>
 	 * OdfPackage relies on the file being available for read access over the
 	 * whole lifecycle of OdfPackage.
 	 * </p>
-	 * 
+	 *
 	 * @param path
 	 *            - the documentURL to the ODF package
 	 * @return the OpenDocument document represented as an OdfPackage
@@ -294,97 +266,83 @@ public class OdfPackage implements Close
 	 */
 	public static OdfPackage loadPackage(String path) throws Exception {
 		File pkgFile = new File(path);
-		return new OdfPackage(pkgFile, getBaseURLFromFile(pkgFile), null, null);
+		return new OdfPackage(pkgFile, getBaseURLFromFile(pkgFile), null);
 	}
 
 	/**
 	 * Loads an OdfPackage from the OpenDocument provided by a File.
-	 * 
+	 *
 	 * <p>
 	 * OdfPackage relies on the file being available for read access over the
 	 * whole lifecycle of OdfPackage.
 	 * </p>
-	 * 
-	 * @param pkgFile
-	 *            - the ODF Package
+	 *
+	 * @param pkgFile - the ODF Package
 	 * @return the OpenDocument document represented as an OdfPackage
-	 * @throws java.lang.Exception
-	 *             - if the package could not be loaded
+	 * @throws java.lang.Exception - if the package could not be loaded
 	 */
 	public static OdfPackage loadPackage(File pkgFile) throws Exception {
-		return new OdfPackage(pkgFile, getBaseURLFromFile(pkgFile), null, null);
+		return new OdfPackage(pkgFile, getBaseURLFromFile(pkgFile), null);
 	}
 
 	/**
 	 * Creates an OdfPackage from the given InputStream.
-	 * 
+	 *
 	 * <p>
 	 * Since an InputStream does not provide the arbitrary (non sequentiell)
 	 * read access needed by OdfPackage, the InputStream is cached. This usually
 	 * takes more time compared to the other loadPackage methods.
 	 * </p>
-	 * 
+	 *
 	 * @param packageStream
 	 *            - an inputStream representing the ODF package
 	 * @return the OpenDocument document represented as an OdfPackage
 	 * @throws java.lang.Exception
 	 *             - if the package could not be loaded
 	 */
-	public static OdfPackage loadPackage(InputStream packageStream) throws Exception {
-		return new OdfPackage(packageStream, null, null, null);
+	public static OdfPackage loadPackage(InputStream packageStream)
+			throws Exception {
+		return new OdfPackage(packageStream, null, null);
 	}
 
 	/**
 	 * Creates an OdfPackage from the given InputStream.
-	 * 
-	 * <p>
-	 * OdfPackage relies on the file being available for read access over the
-	 * whole lifecycle of OdfPackage.
-	 * </p>
-	 * 
-	 * @param packageStream
-	 *            - an inputStream representing the ODF package
-	 * @param baseURI
-	 *            allows to explicitly set the base URI from the document, As
-	 *            the URL can not be derived from a stream. In addition it is
-	 *            possible to set the baseURI to any arbitrary URI, e.g. an URN.
-	 *            One usage of the baseURI to describe the source of validation
-	 *            exception thrown by the ErrorHandler.
-	 * @param errorHandler
-	 *            - SAX ErrorHandler used for ODF validation
-	 * @throws java.lang.Exception
-	 *             - if the package could not be created
+	 *
+	 * <p>OdfPackage relies on the file being available for read access over
+	 * the whole lifecycle of OdfPackage.</p>
+	 *
+	 * @param packageStream - an inputStream representing the ODF package
+	 * @param baseURI allows to explicitly set the base URI from the document, As the URL can not be derived from a stream.
+	 * In addition it is possible to set the baseURI to any arbitrary URI, e.g. an URN.
+	 * One usage of the baseURI to describe the source of validation exception thrown by the ErrorHandler.
+	 * @param errorHandler - SAX ErrorHandler used for ODF validation
+	 * @throws SAXException if there's an XML- or validation-related error while loading the package
+	 * @throws IOException if there's an I/O error while loading the package
 	 * @see #getErrorHandler
 	 */
-	public static OdfPackage loadPackage(InputStream packageStream, String baseURI, ErrorHandler errorHandler) throws Exception {
-		return new OdfPackage(packageStream, baseURI, null, errorHandler);
+	public static OdfPackage loadPackage(InputStream packageStream, String baseURI, ErrorHandler errorHandler)
+			throws SAXException, IOException {
+		return new OdfPackage(packageStream, baseURI, errorHandler);
 	}
 
 	/**
 	 * Loads an OdfPackage from the given File.
-	 * 
-	 * <p>
-	 * OdfPackage relies on the file being available for read access over the
-	 * whole lifecycle of OdfPackage.
-	 * </p>
-	 * 
-	 * @param pkgFile
-	 *            - the ODF Package. A baseURL is being generated based on its
-	 *            location.
-	 * @param password
-	 *            - the ODF Package password.
-	 * @param errorHandler
-	 *            - SAX ErrorHandler used for ODF validation.
-	 * @throws java.lang.Exception
-	 *             - if the package could not be created
+	 *
+	 * <p>OdfPackage relies on the file being available for read access over
+	 * the whole lifecycle of OdfPackage.</p>
+	 * @param pkgFile - the ODF Package. A baseURL is being generated based on its location.
+	 * @param errorHandler - SAX ErrorHandler used for ODF validation.
+	 * @throws SAXException if there's an XML- or validation-related error while loading the package
+	 * @throws IOException if there's an I/O error while loading the package
 	 * @see #getErrorHandler
 	 */
-	public static OdfPackage loadPackage(File pkgFile, String password, ErrorHandler errorHandler) throws Exception {
-		return new OdfPackage(pkgFile, getBaseURLFromFile(pkgFile), password, errorHandler);
+	public static OdfPackage loadPackage(File pkgFile, ErrorHandler errorHandler)
+			throws SAXException, IOException {
+		return new OdfPackage(pkgFile, getBaseURLFromFile(pkgFile), errorHandler);
 	}
 
 	// Initialize using memory
-	private void initializeZip(InputStream odfStream) throws Exception {
+	private void initializeZip(InputStream odfStream) throws SAXException, IOException {
 		ByteArrayOutputStream tempBuf = new ByteArrayOutputStream();
 		StreamHelper.transformStream(odfStream, tempBuf);
 		byte[] mTempByteBuf = tempBuf.toByteArray();
@@ -400,20 +358,6 @@ public class OdfPackage implements Close
 		readZip();
 	}
 
-	// Initialize using ZipFile
-	private void initializeZip(File pkgFile) throws Exception {
-		try {
-			mZipFile = new ZipHelper(this, new ZipFile(pkgFile));
-		} catch (ZipException ze) {
-			OdfValidationException ve = new OdfValidationException(OdfPackageConstraint.PACKAGE_IS_NO_ZIP, getBaseURI());
-			if (mErrorHandler != null) {
-				mErrorHandler.fatalError(ve);
-			}
-			throw new IllegalArgumentException(ve);
-		}
-		readZip();
-	}
-
 	private void readZip() throws SAXException, IOException {
 		mZipEntries = new HashMap<String, ZipEntry>();
 		String firstEntryName = mZipFile.entriesToMap(mZipEntries);
@@ -430,8 +374,7 @@ public class OdfPackage implements Close
 			// initialize the package media type
 			initializeMediaType(firstEntryName);
 
-			// ToDo: Remove all META-INF/* files from the fileEntries of
-			// Manifest
+			// ToDo: Remove all META-INF/* files from the fileEntries of Manifest
 			mOriginalZipEntries = new HashMap<String, ZipEntry>();
 			mOriginalZipEntries.putAll(mZipEntries);
 			mZipEntries.remove(OdfPackage.OdfFile.MEDIA_TYPE.getPath());
@@ -443,14 +386,13 @@ public class OdfPackage implements Close
 			Iterator<String> zipPaths = mZipEntries.keySet().iterator();
 			while (zipPaths.hasNext()) {
 				String internalPath = zipPaths.next();
-				// every resource aside the /META-INF/manifest.xml (and
-				// META-INF/ directory)
+				// every resource aside the /META-INF/manifest.xml (and META-INF/ directory)
 				// and "mimetype" will be added as fileEntry
-				if (!internalPath.equals(OdfPackage.OdfFile.MANIFEST.getPath()) && !internalPath.equals("META-INF/")
+				if (!internalPath.equals(OdfPackage.OdfFile.MANIFEST.getPath())
+						&& !internalPath.equals("META-INF/")
 						&& !internalPath.equals(OdfPackage.OdfFile.MEDIA_TYPE.getPath())) {
 					// aside "mediatype" and "META-INF/manifest"
-					// add manifest entry as to be described by a
-					// <manifest:file-entry>
+					// add manifest entry as to be described by a <manifest:file-entry>
 					ensureFileEntryExistence(internalPath);
 				}
 			}
@@ -458,17 +400,17 @@ public class OdfPackage implements Close
 	}
 
 	/** Validates if all file entries exist in the ZIP and vice versa */
-	private void validateManifest() {
-		Set zipPaths = mZipEntries.keySet();
-		Set manifestPaths = mManifestEntries.keySet();
+	private void validateManifest() throws SAXException {
+		Set<String> zipPaths = mZipEntries.keySet();
+		Set<String> manifestPaths = mManifestEntries.keySet();
 		Set<String> sharedPaths = new HashSet<String>(zipPaths);
 		sharedPaths.retainAll(manifestPaths);
 
 		if (sharedPaths.size() < zipPaths.size()) {
 			Set<String> zipPathSuperset = new HashSet<String>(mZipEntries.keySet());
 			zipPathSuperset.removeAll(sharedPaths);
-			Set sortedSet = new TreeSet<String>(zipPathSuperset);
-			Iterator iter = sortedSet.iterator();
+			Set<String> sortedSet = new TreeSet<String>(zipPathSuperset);
+			Iterator<String> iter = sortedSet.iterator();
 			String documentURL = getBaseURI();
 			String internalPath;
 			while (iter.hasNext()) {
@@ -484,8 +426,7 @@ public class OdfPackage implements Close
 			// removing root directory
 			zipPathSubset.remove(SLASH);
 
-			// No directory are listed in a ZIP removing all directory with
-			// content
+			// No directory are listed in a ZIP removing all directory with content
 			Iterator<String> manifestOnlyPaths = zipPathSubset.iterator();
 			while (manifestOnlyPaths.hasNext()) {
 				String manifestOnlyPath = manifestOnlyPaths.next();
@@ -511,61 +452,46 @@ public class OdfPackage implements Close
 	}
 
 	/** Removes directories without a mimetype (all none documents) */
-	private void removeDirectory(String path) {
+	private void removeDirectory(String path) throws SAXException {
 		if (path.endsWith(SLASH)) {
 			// Check if it is a sub-document?
 			// Our assumption: it is a document if it has a mimetype...
 			String dirMimeType = mManifestEntries.get(path).getMediaTypeString();
 			if (dirMimeType == null || EMPTY_STRING.equals(dirMimeType)) {
 				logValidationWarning(OdfPackageConstraint.MANIFEST_LISTS_DIRECTORY, getBaseURI(), path);
-				OdfFileEntry manifestEntry = mManifestEntries.remove(path);
-				FileEntryElement manifestEle = manifestEntry.getOdfElement();
-				manifestEle.getParentNode().removeChild(manifestEle);
+				mManifestEntries.remove(path);
 			}
 		}
 	}
 
-	/**
-	 * Reads the uncompressed "mimetype" file, which contains the package
-	 * media/mimte type
-	 */
-	private void initializeMediaType(String firstEntryName) {
+	/** Reads the uncompressed "mimetype" file, which contains the package media/mimte type*/
+	private void initializeMediaType(String firstEntryName) throws SAXException, IOException {
 		ZipEntry mimetypeEntry = mZipEntries.get(OdfPackage.OdfFile.MEDIA_TYPE.getPath());
 		if (mimetypeEntry != null) {
 			if (mErrorHandler != null) {
 				validateMimeTypeEntry(mimetypeEntry, firstEntryName);
 			}
-			// get mediatype value of the root document/package from the
-			// mediatype file stream
+			// get mediatype value of the root document/package from the mediatype file stream
 			String entryMediaType = getMediaTypeFromEntry(mimetypeEntry);
-			// get mediatype value of the root document/package from the
-			// manifest.xml
+			// get mediatype value of the root document/package from the manifest.xml
 			String manifestMediaType = getMediaTypeFromManifest();
 			// if a valid mediatype was set by the "mimetype" file
 			if (entryMediaType != null && !entryMediaType.equals(EMPTY_STRING)) {
-				// the root document's mediatype is taken from the "mimetype"
-				// file
+				// the root document's mediatype is taken from the "mimetype" file
 				mMediaType = entryMediaType;
 				if (mErrorHandler != null) {
-					// if the "mediatype" does exist, the
-					// "/META-INF/manifest.xml" have to contain a MIMETYPE for
-					// the root document);
+					// if the "mediatype" does exist, the "/META-INF/manifest.xml" have to contain a MIMETYPE for the root document);
 					if (manifestMediaType != null && !manifestMediaType.equals(EMPTY_STRING)) {
 						// if the two media-types are inconsistent
 						if (!entryMediaType.equals(manifestMediaType)) {
-							logValidationError(OdfPackageConstraint.MIMETYPE_DIFFERS_FROM_PACKAGE, getBaseURI(), CONTROL_CHAR_PATTERN.matcher(mMediaType)
-									.replaceAll(EMPTY_STRING), manifestMediaType);
+							logValidationError(OdfPackageConstraint.MIMETYPE_DIFFERS_FROM_PACKAGE, getBaseURI(), CONTROL_CHAR_PATTERN.matcher(mMediaType).replaceAll(EMPTY_STRING), manifestMediaType);
 						}
-					} else { // if "mimetype" file exists, there have to be a
-								// mimetype in the manifest.xml for the root
-								// document (see ODF 1.2 part 3)
-						logValidationError(OdfPackageConstraint.MIMETYPE_WITHOUT_MANIFEST_MEDIATYPE, getBaseURI(), CONTROL_CHAR_PATTERN.matcher(mMediaType)
-								.replaceAll(EMPTY_STRING), manifestMediaType);
+					} else { // if "mimetype" file exists, there have to be a mimetype in the manifest.xml for the root document (see ODF 1.2 part 3)
+						logValidationError(OdfPackageConstraint.MIMETYPE_WITHOUT_MANIFEST_MEDIATYPE, getBaseURI(), CONTROL_CHAR_PATTERN.matcher(mMediaType).replaceAll(EMPTY_STRING), manifestMediaType);
 					}
 				}
 			} else { // if there is no media-type was set by the "mimetype" file
-				// try as fall-back the mediatype of the root document from the
-				// manifest.xml
+				// try as fall-back the mediatype of the root document from the manifest.xml
 				if (manifestMediaType != null && !manifestMediaType.equals(EMPTY_STRING)) {
 					// and used as fall-back for the mediatype of the package
 					mMediaType = manifestMediaType;
@@ -574,8 +500,7 @@ public class OdfPackage implements Close
 		} else {
 			String manifestMediaType = getMediaTypeFromManifest();
 			if (manifestMediaType != null && !manifestMediaType.equals(EMPTY_STRING)) {
-				// if not mimetype file exists, the root document mediaType from
-				// the manifest.xml is taken
+				// if not mimetype file exists, the root document mediaType from the manifest.xml is taken
 				mMediaType = manifestMediaType;
 			}
 			if (mErrorHandler != null) {
@@ -584,7 +509,7 @@ public class OdfPackage implements Close
 		}
 	}
 
-	private void validateMimeTypeEntry(ZipEntry mimetypeEntry, String firstEntryName) {
+	private void validateMimeTypeEntry(ZipEntry mimetypeEntry, String firstEntryName) throws SAXException {
 
 		if (mimetypeEntry.getMethod() != ZipEntry.STORED) {
 			logValidationError(OdfPackageConstraint.MIMETYPE_IS_COMPRESSED, getBaseURI());
@@ -608,37 +533,61 @@ public class OdfPackage implements Close
 	}
 
 	/** @returns the media type of the root document from the manifest.xml */
-	private String getMediaTypeFromEntry(ZipEntry mimetypeEntry) {
+	private String getMediaTypeFromEntry(ZipEntry mimetypeEntry) throws SAXException, IOException {
 		String entryMediaType = null;
 		ByteArrayOutputStream out = new ByteArrayOutputStream();
 		try {
 			StreamHelper.transformStream(mZipFile.getInputStream(mimetypeEntry), out);
 			entryMediaType = new String(out.toByteArray(), 0, out.size(), "UTF-8");
-		} catch (Exception ex) {
-			Logger.getLogger(OdfPackage.class.getName()).log(Level.SEVERE, null, ex);
+		} catch (IOException ex) {
+			handleIOException(ex, false);
 		} finally {
-			if (out != null) {
-				try {
-					out.close();
-				} catch (IOException ex) {
-					Logger.getLogger(OdfPackage.class.getName()).log(Level.SEVERE, null, ex);
+			close(out);
+		}
+		return entryMediaType;
+	}
+
+	private void close(Closeable closeable) throws SAXException, IOException {
+		if (closeable != null) {
+			try {
+				closeable.close();
+			} catch (IOException ioe) {
+				//Warning only. This is usually just logged.
+				//Allow user to throw an exception all the same
+				handleIOException(ioe, true);
+			}
+		}
+	}
+
+	private void handleIOException(IOException ex, boolean warningOnly) throws SAXException, IOException{
+		if (mErrorHandler != null) {
+			SAXParseException se = new SAXParseException(ex.getMessage(), null, ex);
+			try {
+				if (warningOnly) {
+					mErrorHandler.warning(se);
+				} else {
+					mErrorHandler.error(se);
+				}
+			} catch (SAXException e1) {
+				if (e1 == se) {
+					throw ex;
+					//We re-throw the original exception if the error handler
+					//just threw the SAXException we gave it.
+				} else {
+					throw e1; //Throw what the error handler threw.
 				}
-				out = null;
 			}
 		}
-		return entryMediaType;
+		throw ex; //No error handler? Just throw the original IOException
 	}
 
 	/**
-	 * Insert an Odf document into the package at the given path. The path has
-	 * to be a directory and will receive the MIME type of the
-	 * OdfPackageDocument.
-	 * 
-	 * @param doc
-	 *            the OdfPackageDocument to be inserted.
+	 * Insert an Odf document into the package at the given path.
+	 * The path has to be a directory and will receive the MIME type of the OdfPackageDocument.
+	 *
+	 * @param doc the OdfPackageDocument to be inserted.
 	 * @param internalPath
-	 *            path relative to the package root, where the document should
-	 *            be inserted.
+	 *		path relative to the package root, where the document should be inserted.
 	 */
 	void cacheDocument(OdfPackageDocument doc, String internalPath) {
 		internalPath = normalizeDirectoryPath(internalPath);
@@ -649,19 +598,16 @@ public class OdfPackage implements Close
 	/**
 	 * Set the baseURI for this ODF package. NOTE: Should only be set during
 	 * saving the package.
-	 * 
-	 * @param baseURI
-	 *            defining the location of the package
+	 * @param baseURI defining the location of the package
 	 */
 	void setBaseURI(String baseURI) {
 		mBaseURI = baseURI;
 	}
 
 	/**
-	 * @return The URI to the ODF package, usually the URL, where this ODF
-	 *         package is located. If the package has not URI NULL is returned.
-	 *         This is the case if the package was new created without an URI
-	 *         and not saved before.
+	 * @return The URI to the ODF package, usually the URL, where this ODF package is located.
+	 * If the package has not URI NULL is returned.
+	 * This is the case if the package was new created without an URI and not saved before.
 	 */
 	public String getBaseURI() {
 		return mBaseURI;
@@ -669,12 +615,10 @@ public class OdfPackage implements Close
 
 	/**
 	 * Returns on ODF documents based a given mediatype.
-	 * 
-	 * @param internalPath
-	 *            path relative to the package root, where the document should
-	 *            be inserted.
+	 *
+	 * @param internalPath path relative to the package root, where the document should be inserted.
 	 * @return The ODF document, which mediatype dependends on the parameter or
-	 *         NULL if media type were not supported.
+	 *	NULL if media type were not supported.
 	 */
 	public OdfPackageDocument loadDocument(String internalPath) {
 		OdfPackageDocument doc = getCachedDocument(internalPath);
@@ -691,10 +635,11 @@ public class OdfPackage implements Close
 					if (odfMediaType == null) {
 						return null;
 					}
-					// ToDo: Issue 265 - Remove dependency to higher layer by
-					// facotory
+					// ToDo: Issue 265 - Remove dependency to higher layer by factory
 					doc = OdfDocument.loadDocument(this, internalPath);
 				} catch (Exception ex) {
+					// ToDo: catching Exception, logging it and continuing is bad style.
+					//Refactor exception handling in higher layer, too.
 					Logger.getLogger(OdfPackageDocument.class.getName()).log(Level.SEVERE, null, ex);
 				}
 			}
@@ -703,12 +648,10 @@ public class OdfPackage implements Close
 	}
 
 	/**
-	 * @deprecated This method is only added temporary as workaround for the IBM
-	 *             fork using different DOC classes. Until the registering of
-	 *             DOC documents to the PKG layer has been finished.
+	 * @deprecated This method is only added temporary as workaround for the IBM fork using different DOC classes.
+	 * Until the registering of DOC documents to the PKG layer has been finished.
 	 * @param internalPath
-	 *            path relative to the package root, where the document should
-	 *            be inserted.
+	 *		path relative to the package root, where the document should be inserted.
 	 * @return an already open OdfPackageDocument via its path, otherwise NULL.
 	 */
 	@Deprecated
@@ -719,11 +662,9 @@ public class OdfPackage implements Close
 
 	/**
 	 * @param dom
-	 *            the DOM tree that has been parsed and should be added to the
-	 *            cache.
+	 *		the DOM tree that has been parsed and should be added to the cache.
 	 * @param internalPath
-	 *            path relative to the package root, where the XML of the DOM is
-	 *            located.
+	 *		path relative to the package root, where the XML of the DOM is located.
 	 * @return an already open OdfPackageDocument via its path, otherwise NULL.
 	 */
 	void cacheDom(Document dom, String internalPath) {
@@ -733,8 +674,7 @@ public class OdfPackage implements Close
 
 	/**
 	 * @param internalPath
-	 *            path relative to the package root, where the document should
-	 *            be inserted.
+	 *		path relative to the package root, where the document should be inserted.
 	 * @return an already open W3C XML Documenet via its path, otherwise NULL.
 	 */
 	Document getCachedDom(String internalPath) {
@@ -743,55 +683,44 @@ public class OdfPackage implements Close
 	}
 
 	/**
-	 * @return a map with all open W3C XML documents with their internal package
-	 *         path as key.
+	 * @return a map with all open W3C XML documents with their internal package path as key.
 	 */
 	Map<String, Document> getCachedDoms() {
 		return this.mPkgDoms;
 	}
 
 	/**
-	 * Removes a document from the package via its path. Independent if it was
-	 * already opened or not.
-	 * 
+	 * Removes a document from the package via its path. Independent if it was already opened or not.
 	 * @param internalPath
-	 *            path relative to the package root, where the document should
-	 *            be removed.
+	 *		path relative to the package root, where the document should be removed.
 	 */
 	public void removeDocument(String internalPath) {
 		// Note: the EMPTY String for rrot path will be exchanged to a SLASH
 		internalPath = normalizeDirectoryPath(internalPath);
-		try {
-			// get all files of the package
-			Set<String> allPackageFileNames = getFilePaths();
+		// get all files of the package
+		Set<String> allPackageFileNames = getFilePaths();
 
-			// If the document is the root document
-			// the "/" representing the root document is outside the
-			// manifest.xml in the API an empty path
-			// still normalizeDirectoryPath() already exchanged the EMPTY_STRING
-			// to SLASH
-			if (internalPath.equals(SLASH)) {
-				for (String entryName : allPackageFileNames) {
-					remove(entryName);
-				}
-				remove(SLASH);
-			} else {
-				// remove all the stream of the directory, such as pictures
-				List<String> directoryEntryNames = new ArrayList<String>();
-				for (String entryName : allPackageFileNames) {
-					if (entryName.startsWith(internalPath)) {
-						directoryEntryNames.add(entryName);
-					}
-				}
-				for (String entryName : directoryEntryNames) {
-					remove(entryName);
+		// If the document is the root document
+		// the "/" representing the root document is outside the manifest.xml in the API an empty path
+		// still normalizeDirectoryPath() already exchanged the EMPTY_STRING to SLASH
+		if (internalPath.equals(SLASH)) {
+			for (String entryName : allPackageFileNames) {
+				remove(entryName);
+			}
+			remove(SLASH);
+		} else {
+			//remove all the stream of the directory, such as pictures
+			List<String> directoryEntryNames = new ArrayList<String>();
+			for (String entryName : allPackageFileNames) {
+				if (entryName.startsWith(internalPath)) {
+					directoryEntryNames.add(entryName);
 				}
-				remove(internalPath);
 			}
-		} catch (Exception ex) {
-			Logger.getLogger(OdfPackageDocument.class.getName()).log(Level.SEVERE, null, ex);
+			for (String entryName : directoryEntryNames) {
+				remove(entryName);
+			}
+			remove(internalPath);
 		}
-
 	}
 
 	/** @return all currently opened OdfPackageDocument of this OdfPackage */
@@ -803,18 +732,11 @@ public class OdfPackage implements Close
 		return mPkgDocuments.get(OdfPackageDocument.ROOT_DOCUMENT_PATH);
 	}
 
-	public OdfManifestDom getManifestDom() {
-		return mManifestDom;
-	}
-
 	/**
-	 * Get the media type of the ODF file or document (ie. a directory). A
-	 * directory with a mediatype can be loaded as
-	 * <code>OdfPackageDocument</code>. Note: A directoy is represented by in
-	 * the package as directory with media type
-	 * 
-	 * @param internalPath
-	 *            within the package of the file or document.
+	 * Get the media type of the ODF file or document (ie. a directory).
+	 * A directory with a mediatype can be loaded as <code>OdfPackageDocument</code>.
+	 *  Note: A directoy is represented by in the package as directory with media type
+	 * @param internalPath within the package of the file or document.
 	 * @return the mediaType for the resource of the given path
 	 */
 	public String getMediaTypeString(String internalPath) {
@@ -846,7 +768,7 @@ public class OdfPackage implements Close
 	/**
 	 * Get the media type of the ODF package (equal to media type of ODF root
 	 * document)
-	 * 
+	 *
 	 * @return the mediaType string of this ODF package
 	 */
 	public String getMediaTypeString() {
@@ -856,7 +778,7 @@ public class OdfPackage implements Close
 	/**
 	 * Set the media type of the ODF package (equal to media type of ODF root
 	 * document)
-	 * 
+	 *
 	 * @param mediaType
 	 *            string of this ODF package
 	 */
@@ -865,10 +787,10 @@ public class OdfPackage implements Close
 	}
 
 	/**
-	 * 
+	 *
 	 * Get an OdfFileEntry for the internalPath NOTE: This method should be
 	 * better moved to a DOM inherited Manifest class
-	 * 
+	 *
 	 * @param internalPath
 	 *            The relative package path within the ODF package
 	 * @return The manifest file entry will be returned.
@@ -880,7 +802,7 @@ public class OdfPackage implements Close
 
 	/**
 	 * Get a OdfFileEntries from the manifest file (i.e. /META/manifest.xml")
-	 * 
+	 *
 	 * @return The paths of the manifest file entries will be returned.
 	 */
 	public Set<String> getFilePaths() {
@@ -888,9 +810,9 @@ public class OdfPackage implements Close
 	}
 
 	/**
-	 * 
+	 *
 	 * Check existence of a file in the package.
-	 * 
+	 *
 	 * @param internalPath
 	 *            The relative package documentURL within the ODF package
 	 * @return True if there is an entry and a file for the given documentURL
@@ -902,77 +824,69 @@ public class OdfPackage implements Close
 
 	/**
 	 * Save the package to given documentURL.
-	 * 
+	 *
 	 * @param odfPath
 	 *            - the path to the ODF package destination
-	 * @throws java.lang.Exception
+	 * @throws java.io.IOException
 	 *             - if the package could not be saved
 	 */
-	public void save(String odfPath) throws Exception {
+	public void save(String odfPath) throws IOException {
 		File f = new File(odfPath);
 		save(f);
 	}
 
 	/**
-	 * Save package to a given File. After saving it is still necessary to close
-	 * the package to have again full access about the file.
-	 * 
+	 * Save package to a given File.
+	 *
 	 * @param pkgFile
 	 *            - the File to save the ODF package to
-	 * @throws java.lang.Exception
+	 * @throws java.lang.IOException
 	 *             - if the package could not be saved
 	 */
-	public void save(File pkgFile) throws Exception {
+	public void save(File pkgFile) throws IOException {
 		String baseURL = getBaseURLFromFile(pkgFile);
-		// if (baseURL.equals(mBaseURI)) {
-		// // save to the same file: cache everything first
-		// // ToDo: (Issue 219 - PackageRefactoring) --maybe it's better to
-		// write to a new file and copy that
-		// // to the original one - would be less memory footprint
-		// cacheContent();
-		// }
+//		if (baseURL.equals(mBaseURI)) {
+//			// save to the same file: cache everything first
+//			// ToDo: (Issue 219 - PackageRefactoring) --maybe it's better to write to a new file and copy that
+//			// to the original one - would be less memory footprint
+//			cacheContent();
+//		}
 		FileOutputStream fos = new FileOutputStream(pkgFile);
-		save(fos, baseURL);
-	}
-
-	public void save(OutputStream odfStream) throws Exception {
-		save(odfStream, null);
+		try {
+			save(fos, baseURL);
+		} finally {
+			fos.close();
+		}
 	}
 
 	/**
-	 * Sets the password of this package. if password is not null, package will
-	 * be encrypted when save.
-	 * 
-	 * @param password
-	 *            password
-	 * @since 0.8.9
+	 * Saves the package to a given {@link OutputStream}. The given stream is not closed by this method.
+	 * @param odfStream the output stream
+	 * @throws IOException if an I/O error occurs while saving the package
 	 */
-	public void setPassword(String password) {
-		newPwd = password;
+	public void save(OutputStream odfStream) throws IOException {
+		save(odfStream, null);
 	}
 
 	/**
 	 * Save an ODF document to the OutputStream.
-	 * 
+	 *
 	 * @param odfStream
 	 *            - the OutputStream to insert content to
-	 * @param baseURL
-	 *            defining the location of the package
-	 * @throws java.lang.Exception
-	 *             - if the package could not be saved
+	 * @param baseURL defining the location of the package
+	 * @throws java.io.IOException if an I/O error occurs while saving the package
 	 */
-	private void save(OutputStream odfStream, String baseURL) {
+	private void save(OutputStream odfStream, String baseURL) throws IOException {
+		mBaseURI = baseURL;
+		OdfFileEntry rootEntry = mManifestEntries.get(SLASH);
+		if (rootEntry == null) {
+			rootEntry = new OdfFileEntry(SLASH, mMediaType);
+			mManifestEntries.put(SLASH, rootEntry);
+		} else {
+			rootEntry.setMediaTypeString(mMediaType);
+		}
+		ZipOutputStream zos = new ZipOutputStream(odfStream);
 		try {
-			mBaseURI = baseURL;
-			OdfFileEntry rootEntry = mManifestEntries.get(SLASH);
-			if (rootEntry == null) {
-				rootEntry = new OdfFileEntry(getManifestDom().getRootElement().newFileEntryElement(SLASH, mMediaType));
-				mManifestEntries.put(SLASH, rootEntry);
-			} else {
-				rootEntry.setMediaTypeString(mMediaType);
-			}
-			ZipOutputStream zos = new ZipOutputStream(odfStream);
-
 			// remove mediatype path and use it as first
 			this.mManifestEntries.remove(OdfFile.MEDIA_TYPE.getPath());
 			Iterator<String> it = mManifestEntries.keySet().iterator();
@@ -980,122 +894,68 @@ public class OdfPackage implements Close
 			boolean isFirstFile = true;
 			CRC32 crc = new CRC32();
 			long modTime = (new java.util.Date()).getTime();
-			byte[] data = null;
 			while (it.hasNext() || isFirstFile) {
-				try {
-					// ODF requires the "mimetype" file to be at first in the
-					// package
-					if (isFirstFile) {
-						isFirstFile = false;
-						// create "mimetype" from current attribute value
-						data = mMediaType.getBytes("UTF-8");
-						createZipEntry(OdfFile.MEDIA_TYPE.getPath(), data, zos, modTime, crc);
-					} else {
-						path = it.next();
-						// not interested to reuse previous mediaType nor
-						// manifest from ZIP
-						if (!path.endsWith(SLASH) && !path.equals(OdfPackage.OdfFile.MANIFEST.getPath())
-								&& !path.equals(OdfPackage.OdfFile.MEDIA_TYPE.getPath())) {
-							data = getBytes(path);
-							createZipEntry(path, data, zos, modTime, crc);
-						}
+				byte[] data = null;
+				// ODF requires the "mimetype" file to be at first in the package
+				if (isFirstFile) {
+					isFirstFile = false;
+					// create "mimetype" from current attribute value
+					data = mMediaType.getBytes("UTF-8");
+					createZipEntry(OdfFile.MEDIA_TYPE.getPath(), data, zos, modTime, crc);
+					// Create "META-INF/" directory
+					createZipEntry("META-INF/", null, zos, modTime, crc);
+					// Create "META-INF/manifest.xml" file
+					data = getBytes(OdfFile.MANIFEST.getPath());
+					createZipEntry(OdfFile.MANIFEST.getPath(), data, zos, modTime, crc);
+				} else {
+					path = it.next();
+					// not interested to reuse previous mediaType nor manifest from ZIP
+					if (!path.endsWith(SLASH) && !path.equals(OdfPackage.OdfFile.MANIFEST.getPath())
+							&& !path.equals(OdfPackage.OdfFile.MEDIA_TYPE.getPath())) {
+						data = getBytes(path);
+						createZipEntry(path, data, zos, modTime, crc);
 					}
-					data = null;
-				} catch (IOException ex) {
-					Logger.getLogger(OdfPackage.class.getName()).log(Level.SEVERE, null, ex);
 				}
 			}
-			// Create "META-INF/" directory
-			createZipEntry("META-INF/", null, zos, modTime, crc);
-			// Create "META-INF/manifest.xml" file
-			data = getBytes(OdfFile.MANIFEST.getPath());
-			createZipEntry(OdfFile.MANIFEST.getPath(), data, zos, modTime, crc);
+		} finally {
 			zos.close();
-			odfStream.flush();
-		} catch (IOException ex) {
-			Logger.getLogger(OdfPackage.class.getName()).log(Level.SEVERE, null, ex);
 		}
+		odfStream.flush();
 	}
 
-	private void createZipEntry(String path, byte[] data, ZipOutputStream zos, long modTime, CRC32 crc) {
-		ZipEntry ze = null;
-		try {
-			ze = mZipEntries.get(path);
-			if (ze == null) {
-				ze = new ZipEntry(path);
-			}
-			ze.setTime(modTime);
-			if (fileNeedsCompression(path)) {
-				ze.setMethod(ZipEntry.DEFLATED);
-			} else {
-				ze.setMethod(ZipEntry.STORED);
-			}
-			crc.reset();
-			if (data != null) {
-				OdfFileEntry fileEntry = mManifestEntries.get(path);
-				// encrypt file
-				if (data.length > 0 && fileNeedsEncryption(path)) {
-					data = encryptData(data, fileEntry);
-					// encrypted file entries shall be flagged as 'STORED'.
-					ze.setMethod(ZipEntry.STORED);
-					// the size of the encrypted file should replace the real
-					// size value.
-					ze.setCompressedSize(data.length);
-				} else {
-					if (fileEntry != null) {
-						fileEntry.setSize(null);
-						FileEntryElement fileEntryEle = fileEntry.getOdfElement();
-						EncryptionDataElement encryptionDataElement = OdfElement.findFirstChildNode(EncryptionDataElement.class, fileEntryEle);
-						while (encryptionDataElement != null) {
-							fileEntryEle.removeChild(encryptionDataElement);
-							encryptionDataElement = OdfElement.findFirstChildNode(EncryptionDataElement.class, fileEntryEle);
-						}
-					}
-					ze.setCompressedSize(-1);
-				}
-				ze.setSize(data.length);
-				crc.update(data);
-				ze.setCrc(crc.getValue());
-			} else {
-				ze.setSize(0);
-				ze.setCrc(0);
-				ze.setCompressedSize(-1);
-			}
-			zos.putNextEntry(ze);
-			if (data != null) {
-				zos.write(data, 0, data.length);
-			}
-			zos.closeEntry();
-			mZipEntries.put(path, ze);
-		} catch (IOException ex) {
-			Logger.getLogger(OdfPackage.class.getName()).log(Level.SEVERE, null, ex);
+	private void createZipEntry(String path, byte[] data, ZipOutputStream zos, long modTime, CRC32 crc)
+			throws IOException {
+		ZipEntry ze = mZipEntries.get(path);
+		if (ze == null) {
+			ze = new ZipEntry(path);
+		}
+		ze.setTime(modTime);
+		if (fileNeedsCompression(path)) {
+			ze.setMethod(ZipEntry.DEFLATED);
+		} else {
+			ze.setMethod(ZipEntry.STORED);
 		}
-	}
-
-	/**
-	 * Determines if a file have to be encrypted.
-	 * 
-	 * @param internalPath
-	 *            the file location
-	 * @return true if the file needs encrypted, false, otherwise
-	 */
-	private boolean fileNeedsEncryption(String internalPath) {
-		if (newPwd != null) {
-			// ODF spec does not allow encrytion of "./mimetype" file
-			if (internalPath.endsWith(SLASH) || OdfFile.MANIFEST.getPath().equals(internalPath) || OdfPackage.OdfFile.MEDIA_TYPE.getPath().equals(internalPath)) {
-				return false;
-			}
-			return fileNeedsCompression(internalPath);
+		crc.reset();
+		if (data != null) {
+			ze.setSize(data.length);
+			crc.update(data);
+			ze.setCrc(crc.getValue());
 		} else {
-			return false;
+			ze.setSize(0);
+			ze.setCrc(0);
 		}
+		ze.setCompressedSize(-1);
+		zos.putNextEntry(ze);
+		if (data != null) {
+			zos.write(data, 0, data.length);
+		}
+		zos.closeEntry();
+		mZipEntries.put(path, ze);
 	}
 
 	/**
 	 * Determines if a file have to be compressed.
-	 * 
-	 * @param internalPath
-	 *            the file location
+	 * @param internalPath the file location
 	 * @return true if the file needs compression, false, otherwise
 	 */
 	private boolean fileNeedsCompression(String internalPath) {
@@ -1128,7 +988,8 @@ public class OdfPackage implements Close
 				mZipFile.close();
 			} catch (IOException ex) {
 				// log exception and continue
-				Logger.getLogger(OdfPackage.class.getName()).log(Level.INFO, null, ex);
+				Logger.getLogger(OdfPackage.class.getName()).log(Level.INFO,
+						null, ex);
 			}
 		}
 		// release all stuff - this class is impossible to use afterwards
@@ -1145,46 +1006,36 @@ public class OdfPackage implements Close
 	/**
 	 * Parse the Manifest file
 	 */
-
-	private void parseManifest() {
-		// ToDo: manifest.xml will be held in the future as DOM, now its
-		// being generated for each save()
+	private void parseManifest() throws SAXException, IOException {
+		InputStream is = null;
 		try {
-			mManifestDom = (OdfManifestDom) OdfFileDom.newFileDom(this, OdfFile.MANIFEST.getPath());
-			ManifestElement manifestEle = mManifestDom.getRootElement();
-			if (manifestEle != null) {
-				setManifestVersion(manifestEle.getVersionAttribute());
-			} else {
-				logValidationError(OdfPackageConstraint.MANIFEST_NOT_IN_PACKAGE, getBaseURI());
+			ZipEntry entry = null;
+			// loading the MANIFEST once from the ZIP, as it will never be cached, just once read
+			// during load (now) and on save serialized from file status (ie. mManifestEntries)
+			if ((entry = mZipEntries.get(OdfPackage.OdfFile.MANIFEST.internalPath)) != null) {
+				is = mZipFile.getInputStream(entry);
 			}
-			Map<String, OdfFileEntry> entries = getManifestEntries();
-			FileEntryElement fileEntryEle = OdfElement.findFirstChildNode(FileEntryElement.class, manifestEle);
-			while (fileEntryEle != null) {
-				String path = fileEntryEle.getFullPathAttribute();
-				if (path.equals(EMPTY_STRING)) {
-					if (getErrorHandler() != null) {
-						logValidationError(OdfPackageConstraint.MANIFEST_WITH_EMPTY_PATH, getBaseURI());
-					}
-				}
-				path = normalizePath(path);
-				OdfFileEntry currentFileEntry = entries.get(path);
-				if (currentFileEntry == null) {
-					currentFileEntry = new OdfFileEntry(fileEntryEle);
-				}
-				if (path != null) {
-					entries.put(path, currentFileEntry);
-				}
-				fileEntryEle = OdfElement.findNextChildNode(FileEntryElement.class, fileEntryEle);
+			if (is == null) {
+				logValidationError(OdfPackageConstraint.MANIFEST_NOT_IN_PACKAGE, getBaseURI());
+				return;
 			}
-			mMemoryFileCache.remove(OdfFile.MANIFEST.getPath());
-			mPkgDoms.put(OdfFile.MANIFEST.getPath(), mManifestDom);
-		} catch (Exception ex) {
-			Logger.getLogger(OdfPackage.class.getName()).log(Level.SEVERE, null, ex);
+			XMLReader xmlReader = getXMLReader();
+			xmlReader.setEntityResolver(getEntityResolver());
+			xmlReader.setContentHandler(new OdfManifestSaxHandler(this));
+			InputSource ins = new InputSource(is);
+			String uri = mBaseURI + SLASH + OdfPackage.OdfFile.MANIFEST.internalPath;
+			ins.setSystemId(uri);
+			xmlReader.parse(ins);
+			// ToDo: manifest.xml will be held in the future as DOM, now its being generated for each save()
+			mMemoryFileCache.remove(OdfPackage.OdfFile.MANIFEST.internalPath);
+		} catch (IOException ioe) {
+			handleIOException(ioe, false);
+		} finally {
+			close(is);
 		}
 	}
 
-
-	XMLReader getXMLReader() throws ParserConfigurationException, SAXException {
+	XMLReader getXMLReader() throws SAXException {
 		// create sax parser
 		SAXParserFactory saxFactory = new org.apache.xerces.jaxp.SAXParserFactoryImpl();
 		saxFactory.setNamespaceAware(true);
@@ -1195,16 +1046,19 @@ public class OdfPackage implements Close
 			Logger.getLogger(OdfPackage.class.getName()).log(Level.SEVERE, null, ex);
 		}
 
-		SAXParser parser = saxFactory.newSAXParser();
+		SAXParser parser;
+		try {
+			parser = saxFactory.newSAXParser();
+		} catch (ParserConfigurationException pce) {
+			//Re-throw as SAXException in order not to introduce too many checked exceptions
+			throw new SAXException(pce);
+		}
 		XMLReader xmlReader = parser.getXMLReader();
-		// More details at
-		// http://xerces.apache.org/xerces2-j/features.html#namespaces
+		// More details at http://xerces.apache.org/xerces2-j/features.html#namespaces
 		xmlReader.setFeature("http://xml.org/sax/features/namespaces", true);
-		// More details at
-		// http://xerces.apache.org/xerces2-j/features.html#namespace-prefixes
+		// More details at http://xerces.apache.org/xerces2-j/features.html#namespace-prefixes
 		xmlReader.setFeature("http://xml.org/sax/features/namespace-prefixes", true);
-		// More details at
-		// http://xerces.apache.org/xerces2-j/features.html#xmlns-uris
+		// More details at http://xerces.apache.org/xerces2-j/features.html#xmlns-uris
 		xmlReader.setFeature("http://xml.org/sax/features/xmlns-uris", true);
 		return xmlReader;
 	}
@@ -1212,7 +1066,6 @@ public class OdfPackage implements Close
 	// Add the given path and all its subdirectories to the internalPath list
 	// to be written later to the manifest
 	private void createSubEntries(String internalPath) {
-		ManifestElement manifestEle = getManifestDom().getRootElement();
 		StringTokenizer tok = new StringTokenizer(internalPath, SLASH);
 		if (tok.countTokens() > 1) {
 			String path = EMPTY_STRING;
@@ -1223,7 +1076,7 @@ public class OdfPackage implements Close
 					path = path + directory + SLASH;
 					OdfFileEntry fileEntry = mManifestEntries.get(path);
 					if (fileEntry == null) {
-						mManifestEntries.put(path, new OdfFileEntry(manifestEle.newFileEntryElement(path, null)));
+						mManifestEntries.put(path, new OdfFileEntry(path, null));
 					}
 				}
 			}
@@ -1232,12 +1085,11 @@ public class OdfPackage implements Close
 
 	/**
 	 * Insert DOM tree into OdfPackage. An existing file will be replaced.
-	 * 
+	 *
 	 * @param fileDOM
 	 *            - XML DOM tree to be inserted as file.
 	 * @param internalPath
-	 *            - relative documentURL where the DOM tree should be inserted
-	 *            as XML file
+	 *            - relative documentURL where the DOM tree should be inserted as XML file
 	 * @param mediaType
 	 *            - media type of stream. Set to null if unknown
 	 * @throws java.lang.Exception
@@ -1253,42 +1105,35 @@ public class OdfPackage implements Close
 		} else {
 			mPkgDoms.put(internalPath, fileDOM);
 		}
-		if (!internalPath.endsWith(OdfFile.MANIFEST.internalPath)) {
-			updateFileEntry(ensureFileEntryExistence(internalPath), mediaType);
-		}
+		updateFileEntry(ensureFileEntryExistence(internalPath), mediaType);
 		// remove byte array version of new DOM
 		mMemoryFileCache.remove(internalPath);
 	}
 
 	/**
-	 * Embed an OdfPackageDocument to the current OdfPackage. All the file
-	 * entries of child document will be inserted.
-	 * 
-	 * @param sourceDocument
-	 *            the OdfPackageDocument to be embedded.
-	 * @param internalPath
-	 *            path to the directory the ODF document should be inserted
-	 *            (relative to ODF package root).
+	 * Embed an OdfPackageDocument to the current OdfPackage.
+	 * All the file entries of child document will be inserted.
+	 * @param sourceDocument the OdfPackageDocument to be embedded.
+	 * @param internalPath path to the directory the ODF document should be inserted (relative to ODF package root).
 	 */
 	public void insertDocument(OdfPackageDocument sourceDocument, String internalPath) {
 		internalPath = normalizeDirectoryPath(internalPath);
 		// opened DOM of descendant Documents will be flashed to the their pkg
 		flushDoms(sourceDocument);
 
-		// Gets the OdfDocument's manifest entry info, no matter it is a
-		// independent document or an embeddedDocument.
+		// Gets the OdfDocument's manifest entry info, no matter it is a independent document or an embeddedDocument.
 		Map<String, OdfFileEntry> entryMapToCopy;
 		if (sourceDocument.isRootDocument()) {
 			entryMapToCopy = sourceDocument.getPackage().getManifestEntries();
 		} else {
-			entryMapToCopy = sourceDocument.getPackage().getSubDirectoryEntries(this, sourceDocument.getDocumentPath());
+			entryMapToCopy = sourceDocument.getPackage().getSubDirectoryEntries(sourceDocument.getDocumentPath());
 		}
-		// insert to package and add it to the Manifest
+		//insert to package and add it to the Manifest
 		internalPath = sourceDocument.setDocumentPath(internalPath);
 		String documentDirectory = null;
-		if (internalPath.equals(SLASH)) {
+		if(internalPath.equals(SLASH)){
 			documentDirectory = EMPTY_STRING;
-		} else {
+		}else{
 			documentDirectory = internalPath;
 		}
 		Set<String> entryNameList = entryMapToCopy.keySet();
@@ -1308,17 +1153,16 @@ public class OdfPackage implements Close
 						String packagePath = documentDirectory + entry.getPath();
 						insert(sourceDocument.getPackage().getInputStream(entryName), packagePath, entry.getMediaTypeString());
 					}
-				} catch (Exception ex) {
+				} catch (IOException ex) {
+					//Catching IOException should be fine here: in-memory operations only
 					Logger.getLogger(OdfPackage.class.getName()).log(Level.SEVERE, null, ex);
 				}
 			}
 		}
-		// make sure the media type of embedded Document is right set.
-		ManifestElement manifestEle = mManifestDom.getRootElement();
-		OdfFileEntry embedDocumentRootEntry = new OdfFileEntry(manifestEle.newFileEntryElement(internalPath, sourceDocument.getMediaTypeString()));
+		//make sure the media type of embedded Document is right set.
+		OdfFileEntry embedDocumentRootEntry = new OdfFileEntry(internalPath, sourceDocument.getMediaTypeString());
 		mManifestEntries.put(internalPath, embedDocumentRootEntry);
-		// the new document will be attached to its new package (it has been
-		// inserted to)
+		// the new document will be attached to its new package (it has been inserted to)
 		sourceDocument.setPackage(this);
 		cacheDocument(sourceDocument, internalPath);
 	}
@@ -1326,17 +1170,15 @@ public class OdfPackage implements Close
 	/**
 	 * Insert all open DOMs of XML files beyond parent document to the package.
 	 * The XML files will be updated in the package after calling save.
-	 * 
-	 * @param parentDocument
-	 *            the document, which XML files shall be serialized
+	 *
+	 * @param parentDocument the document, which XML files shall be serialized
 	 */
 	void flushDoms(OdfPackageDocument parentDocument) {
 		OdfPackage pkg = parentDocument.getPackage();
 		if (parentDocument.isRootDocument()) {
 			// for every parsed XML file (DOM)
 			for (String xmlFilePath : pkg.getCachedDoms().keySet()) {
-				// insert it to the package (serializing and caching it till
-				// final save)
+				// insert it to the package (serializing and caching it till final save)
 				pkg.insert(pkg.getCachedDom(xmlFilePath), xmlFilePath, "text/xml");
 			}
 		} else {
@@ -1346,8 +1188,7 @@ public class OdfPackage implements Close
 			for (String xmlFilePath : pkg.getCachedDoms().keySet()) {
 				// if the file is within the given document
 				if (xmlFilePath.startsWith(parentDocumentPath)) {
-					// insert it to the package (serializing and caching it till
-					// final save)
+					// insert it to the package (serializing and caching it till final save)
 					pkg.insert(pkg.getCachedDom(xmlFilePath), xmlFilePath, "text/xml");
 				}
 			}
@@ -1355,21 +1196,22 @@ public class OdfPackage implements Close
 	}
 
 	/** Get all the file entries from a sub directory */
-	private Map<String, OdfFileEntry> getSubDirectoryEntries(OdfPackage destinationPackage, String directory) {
+	private Map<String, OdfFileEntry> getSubDirectoryEntries(String directory) {
 		directory = normalizeDirectoryPath(directory);
 		Map<String, OdfFileEntry> subEntries = new HashMap<String, OdfFileEntry>();
 		Map<String, OdfFileEntry> allEntries = getManifestEntries();
 		Set<String> rootEntryNameSet = getFilePaths();
-		ManifestElement manifestEle = destinationPackage.getManifestDom().getRootElement();
 		for (String entryName : rootEntryNameSet) {
 			if (entryName.startsWith(directory)) {
 				String newEntryName = entryName.substring(directory.length());
 				if (newEntryName.length() == 0) {
-					newEntryName = SLASH;
+					continue;
 				}
 				OdfFileEntry srcFileEntry = allEntries.get(entryName);
-				OdfFileEntry newFileEntry = new OdfFileEntry(manifestEle.newFileEntryElement(newEntryName, srcFileEntry.getMediaTypeString()));
+				OdfFileEntry newFileEntry = new OdfFileEntry();
 				newFileEntry.setEncryptionData(srcFileEntry.getEncryptionData());
+				newFileEntry.setMediaTypeString(srcFileEntry.getMediaTypeString());
+				newFileEntry.setPath(newEntryName);
 				newFileEntry.setSize(srcFileEntry.getSize());
 				subEntries.put(entryName, newFileEntry);
 			}
@@ -1379,56 +1221,42 @@ public class OdfPackage implements Close
 
 	/**
 	 * Method returns the paths of all document within the package.
-	 * 
-	 * @return A set of paths of all documents of the package, including the
-	 *         root document.
+	 *
+	 * @return A set of paths of all documents of the package, including the root document.
 	 */
 	public Set<String> getDocumentPaths() {
 		return getDocumentPaths(null, null);
 	}
 
 	/**
-	 * Method returns the paths of all document within the package matching the
-	 * given criteria.
-	 * 
-	 * @param mediaTypeString
-	 *            limits the desired set of document paths to documents of the
-	 *            given mediaType
-	 * @return A set of paths of all documents of the package, including the
-	 *         root document, that match the given parameter.
+	 * Method returns the paths of all document within the package matching the given criteria.
+	 *
+	 * @param mediaTypeString limits the desired set of document paths to documents of the given mediaType
+	 * @return A set of paths of all documents of the package, including the root document, that match the given parameter.
 	 */
 	public Set<String> getDocumentPaths(String mediaTypeString) {
 		return getDocumentPaths(mediaTypeString, null);
 	}
 
 	/**
-	 * Method returns the paths of all document within the package matching the
-	 * given criteria.
-	 * 
-	 * @param mediaTypeString
-	 *            limits the desired set of document paths to documents of the
-	 *            given mediaType
-	 * @param subDirectory
-	 *            limits the desired set document paths to those documents below
-	 *            of this subdirectory
-	 * @return A set of paths of all documents of the package, including the
-	 *         root document, that match the given parameter.
+	 * Method returns the paths of all document within the package matching the given criteria.
+	 *
+	 * @param mediaTypeString limits the desired set of document paths to documents of the given mediaType
+	 * @param subDirectory limits the desired set document paths to those documents below of this subdirectory
+	 * @return A set of paths of all documents of the package, including the root document, that match the given parameter.
 	 */
 	Set<String> getDocumentPaths(String mediaTypeString, String subDirectory) {
 		Set<String> innerDocuments = new HashSet<String>();
 		Set<String> packageFilePaths = getFilePaths();
 		// check manifest for current embedded OdfPackageDocuments
 		for (String filePath : packageFilePaths) {
-			// check if a subdirectory was the criteria and if the files are
-			// beyond the given subdirectory
+			// check if a subdirectory was the criteria and if the files are beyond the given subdirectory
 			if (subDirectory == null || filePath.startsWith(subDirectory) && !filePath.equals(subDirectory)) {
-				// with documentURL is not empty and is a directory (ie. a
-				// potential document)
+				// with documentURL is not empty and is a directory (ie. a potential document)
 				if (filePath.length() > 1 && filePath.endsWith(SLASH)) {
 					String fileMediaType = getFileEntry(filePath).getMediaTypeString();
 					if (fileMediaType != null && !fileMediaType.equals(EMPTY_STRING)) {
-						// check if a certain mediaType was the critera and was
-						// matched
+						// check if a certain mediaType was the critera and was matched
 						if (mediaTypeString == null || mediaTypeString.equals(fileMediaType)) {
 							// only relative path is allowed as path
 							innerDocuments.add(filePath);
@@ -1441,21 +1269,21 @@ public class OdfPackage implements Close
 	}
 
 	/**
-	 * Adding a manifest:file-entry to be saved in manifest.xml. In addition,
-	 * sub directories will be added as well to the manifest.
+	 * Adding a manifest:file-entry to be saved in manifest.xml.
+	 * In addition, sub directories will be added as well to the manifest.
 	 */
 	private OdfFileEntry ensureFileEntryExistence(String internalPath) {
 		// if it is NOT the resource "/META-INF/manifest.xml"
 		OdfFileEntry fileEntry = null;
-		if (!OdfFile.MANIFEST.internalPath.equals(internalPath) || !internalPath.equals(EMPTY_STRING)) {
+		if (!OdfPackage.OdfFile.MANIFEST.internalPath.equals(internalPath) ||
+			!internalPath.equals(EMPTY_STRING)) {
 			if (mManifestEntries == null) {
 				mManifestEntries = new HashMap<String, OdfFileEntry>();
 			}
 			fileEntry = mManifestEntries.get(internalPath);
 			// for every new file entry
 			if (fileEntry == null) {
-				ManifestElement manifestEle = getManifestDom().getRootElement();
-				fileEntry = new OdfFileEntry(manifestEle.newFileEntryElement(internalPath, ""));
+				fileEntry = new OdfFileEntry(internalPath);
 				mManifestEntries.put(internalPath, fileEntry);
 				// creates recursive file entries for all sub directories
 				createSubEntries(internalPath);
@@ -1473,14 +1301,13 @@ public class OdfPackage implements Close
 		// reset encryption data (ODFDOM does not support encryption yet)
 		fileEntry.setEncryptionData(null);
 		// reset size to be unset
-		fileEntry.setSize(null);
+		fileEntry.setSize(-1);
 	}
 
 	/**
 	 * Gets org.w3c.dom.Document for XML file contained in package.
-	 * 
-	 * @param internalPath
-	 *            to a file within the Odf Package (eg. content.xml)
+	 *
+	 * @param internalPath to a file within the Odf Package (eg. content.xml)
 	 * @return an org.w3c.dom.Document
 	 * @throws SAXException
 	 * @throws ParserConfigurationException
@@ -1489,8 +1316,9 @@ public class OdfPackage implements Close
 	 * @throws TransformerConfigurationException
 	 * @throws TransformerException
 	 */
-	public Document getDom(String internalPath) throws SAXException, ParserConfigurationException, IllegalArgumentException, TransformerConfigurationException,
-			TransformerException, IOException {
+	public Document getDom(String internalPath) throws SAXException,
+			ParserConfigurationException, IllegalArgumentException,
+			TransformerConfigurationException, TransformerException, IOException {
 
 		Document doc = mPkgDoms.get(internalPath);
 		if (doc != null) {
@@ -1530,18 +1358,19 @@ public class OdfPackage implements Close
 	/**
 	 * Inserts an external file into an OdfPackage. An existing file will be
 	 * replaced.
-	 * 
+	 *
 	 * @param sourceURI
 	 *            - the source URI to the file to be inserted into the package.
 	 * @param internalPath
-	 *            - relative documentURL where the tree should be inserted as
-	 *            XML file
+	 *            - relative documentURL where the tree should be inserted as XML
+	 *            file
 	 * @param mediaType
 	 *            - media type of stream. Set to null if unknown
-	 * @throws java.lang.Exception
+	 * @throws java.io.IOException
 	 *             In case the file could not be saved
 	 */
-	public void insert(URI sourceURI, String internalPath, String mediaType) throws Exception {
+	public void insert(URI sourceURI, String internalPath, String mediaType)
+			throws IOException {
 		InputStream is = null;
 		if (sourceURI.isAbsolute()) {
 			// if the URI is absolute it can be converted to URL
@@ -1556,19 +1385,19 @@ public class OdfPackage implements Close
 	/**
 	 * Inserts InputStream into an OdfPackage. An existing file will be
 	 * replaced.
-	 * 
+	 *
 	 * @param fileStream
 	 *            - the stream of the file to be inserted into the ODF package.
 	 * @param internalPath
-	 *            - relative documentURL where the tree should be inserted as
-	 *            XML file
+	 *            - relative documentURL where the tree should be inserted as XML
+	 *            file
 	 * @param mediaType
 	 *            - media type of stream. Set to null if unknown
 	 */
-	public void insert(InputStream fileStream, String internalPath, String mediaType) throws Exception {
+	public void insert(InputStream fileStream, String internalPath, String mediaType) throws IOException {
 		internalPath = normalizeFilePath(internalPath);
 		if (fileStream == null) {
-			// adding a simple directory without MIMETYPE
+			//adding a simple directory without MIMETYPE
 			insert((byte[]) null, internalPath, mediaType);
 		} else {
 			ByteArrayOutputStream baos = new ByteArrayOutputStream();
@@ -1586,16 +1415,15 @@ public class OdfPackage implements Close
 
 	/**
 	 * Inserts a byte array into OdfPackage. An existing file will be replaced.
-	 * If the byte array is NULL a directory with the given mimetype will be
-	 * created.
-	 * 
+	 * If the byte array is NULL a directory with the given mimetype will be created.
+	 *
 	 * @param fileBytes
-	 *            - data of the file stream to be stored in package. If NULL a
-	 *            directory with the given mimetype will be created.
+	 *      - data of the file stream to be stored in package.
+	 *		If NULL a directory with the given mimetype will be created.
 	 * @param internalPath
-	 *            - path of the file or directory relative to the package root.
+	 *      - path of the file or directory relative to the package root.
 	 * @param mediaTypeString
-	 *            - media type of stream. If unknown null can be used.
+	 *      - media type of stream. If unknown null can be used.
 	 */
 	public void insert(byte[] fileBytes, String internalPath, String mediaTypeString) {
 		internalPath = normalizeFilePath(internalPath);
@@ -1603,14 +1431,14 @@ public class OdfPackage implements Close
 			try {
 				setMediaTypeString(new String(fileBytes, "UTF-8"));
 			} catch (UnsupportedEncodingException useEx) {
-				Logger.getLogger(OdfPackage.class.getName()).log(Level.SEVERE, "ODF file could not be created as string!", useEx);
+				Logger.getLogger(OdfPackage.class.getName()).log(Level.SEVERE,
+						"ODF file could not be created as string!", useEx);
 			}
 			return;
 		}
 		if (fileBytes != null) {
 			mMemoryFileCache.put(internalPath, fileBytes);
-			// as DOM would overwrite data cache, any existing DOM cache will be
-			// deleted
+			// as DOM would overwrite data cache, any existing DOM cache will be deleted
 			if (mPkgDoms.containsKey(internalPath)) {
 				mPkgDoms.remove(internalPath);
 			}
@@ -1618,19 +1446,113 @@ public class OdfPackage implements Close
 		updateFileEntry(ensureFileEntryExistence(internalPath), mediaTypeString);
 	}
 
-	// changed to package access as the manifest interiors are an implementation
-	// detail
+	// changed to package access as the manifest interiors are an implementation detail
 	Map<String, OdfFileEntry> getManifestEntries() {
 		return mManifestEntries;
 	}
 
 	/**
+	 * Get Manifest as String NOTE: This functionality should better be moved to
+	 * a DOM based Manifest class
+	 *
+	 * @return the /META-INF/manifest.xml as a String
+	 */
+	public String getManifestAsString() {
+		if (mManifestEntries == null) {
+			return null;
+		} else {
+			StringBuilder buf = new StringBuilder();
+			buf.append("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n");
+			buf.append("<manifest:manifest xmlns:manifest=\"urn:oasis:names:tc:opendocument:xmlns:manifest:1.0\"");
+                        if(mManifestVersion != null){
+                            buf.append(" manifest:version=\"" + mManifestVersion + "\"");
+                        }
+                        buf.append(" >\n");
+			Iterator<String> it = new TreeSet<String>(mManifestEntries.keySet()).iterator();
+			while (it.hasNext()) {
+				String key = it.next();
+				String s = null;
+				OdfFileEntry fileEntry = mManifestEntries.get(key);
+				if (fileEntry != null) {
+					s = fileEntry.getPath();
+					// only directories with a mimetype (documents) will be written into the manifest.xml
+					if (s != null && !s.endsWith(SLASH) || !fileEntry.getMediaTypeString().equals(EMPTY_STRING)) {
+						buf.append(" <manifest:file-entry");
+						if (s != null) {
+							buf.append(" manifest:full-path=\"");
+							buf.append(encodeXMLAttributes(s));
+							buf.append("\"");
+
+						}
+						s = fileEntry.getMediaTypeString();
+						buf.append(" manifest:media-type=\"");
+						buf.append(encodeXMLAttributes(s));
+						buf.append("\"");
+
+						int i = fileEntry.getSize();
+						if (i > 0) {
+							buf.append(" manifest:size=\"");
+							buf.append(i);
+							buf.append("\"");
+						}
+						EncryptionData enc = fileEntry.getEncryptionData();
+						if (enc != null) {
+							buf.append(">\n");
+							buf.append("  <manifest:encryption-data>\n");
+							Algorithm alg = enc.getAlgorithm();
+							if (alg != null) {
+								buf.append("   <manifest:algorithm");
+								s = alg.getName();
+								if (s != null) {
+									buf.append(" manifest:algorithm-name=\"");
+									buf.append(encodeXMLAttributes(s));
+									buf.append("\"");
+								}
+								s = alg.getInitializationVector();
+								if (s != null) {
+									buf.append(" manifest:initialization-vector=\"");
+									buf.append(encodeXMLAttributes(s));
+									buf.append("\"");
+								}
+								buf.append("/>\n");
+							}
+							KeyDerivation keyDerivation = enc.getKeyDerivation();
+							if (keyDerivation != null) {
+								buf.append("   <manifest:key-derivation");
+								s = keyDerivation.getName();
+								if (s != null) {
+									buf.append(" manifest:key-derivation-name=\"");
+									buf.append(encodeXMLAttributes(s));
+									buf.append("\"");
+								}
+								s = keyDerivation.getSalt();
+								if (s != null) {
+									buf.append(" manifest:salt=\"");
+									buf.append(encodeXMLAttributes(s));
+									buf.append("\"");
+								}
+								buf.append(" manifest:iteration-count=\"");
+								buf.append(keyDerivation.getIterationCount());
+								buf.append("\"/>\n");
+							}
+							buf.append("  </manifest:encryption-data>\n");
+							buf.append(" </<manifest:file-entry>\n");
+						} else {
+							buf.append("/>\n");
+						}
+					}
+				}
+			}
+			buf.append("</manifest:manifest>");
+			return buf.toString();
+		}
+	}
+
+	/**
 	 * Get package (sub-) content as byte array
-	 * 
-	 * @param internalPath
-	 *            relative documentURL to the package content
+	 *
+	 * @param internalPath relative documentURL to the package content
 	 * @return the unzipped package content as byte array
-	 * @throws java.lang.Exception
 	 */
 	public byte[] getBytes(String internalPath) {
 		// if path is null or empty return null
@@ -1651,11 +1573,29 @@ public class OdfPackage implements Close
 					return null;
 				}
 			}
+			// if the file is "/META-INF/manifest.xml"
+		} else if (internalPath.equals(OdfPackage.OdfFile.MANIFEST.internalPath)) {
+			if (mManifestEntries == null) {
+				// manifest was not present
+				return null;
+			}
+			String s = getManifestAsString();
+			if (s == null) {
+				return null;
+			} else {
+				try {
+					data = s.getBytes("UTF-8");
+				} catch (UnsupportedEncodingException ex) {
+					Logger.getLogger(OdfPackage.class.getName()).log(Level.SEVERE, null, ex);
+				}
+			}
+			// if the path is already loaded as DOM (highest priority)
 		} else if (mPkgDoms.get(internalPath) != null) {
 			data = flushDom(mPkgDoms.get(internalPath));
 			mMemoryFileCache.put(internalPath, data);
 			// if the path's file was cached to memory (second high priority)
-		} else if (mManifestEntries.containsKey(internalPath) && mMemoryFileCache.get(internalPath) != null) {
+		} else if (mManifestEntries.containsKey(internalPath)
+				&& mMemoryFileCache.get(internalPath) != null) {
 			data = mMemoryFileCache.get(internalPath);
 
 			// if the path's file was cached to disc (lowest priority)
@@ -1671,19 +1611,12 @@ public class OdfPackage implements Close
 						ByteArrayOutputStream out = new ByteArrayOutputStream();
 						StreamHelper.transformStream(inputStream, out);
 						data = out.toByteArray();
-						// decrypt data as needed
-						if (!(internalPath.equals(OdfFile.MEDIA_TYPE.getPath()) || internalPath.equals(OdfFile.MANIFEST.getPath()))) {
-							OdfFileEntry manifestEntry = getManifestEntries().get(internalPath);
-							EncryptionDataElement encryptionDataElement = manifestEntry.getEncryptionData();
-							if (encryptionDataElement != null) {
-								data = decryptData(data, manifestEntry, encryptionDataElement);
-							}
-						}
-						// store for further usage; do not care about manifest:
-						// that is handled exclusively
+						// store for further usage; do not care about manifest: that
+						// is handled exclusively
 						mMemoryFileCache.put(internalPath, data);
 					}
 				} catch (IOException ex) {
+					//Catching IOException here should be fine: in-memory operations only
 					Logger.getLogger(OdfPackage.class.getName()).log(Level.SEVERE, null, ex);
 				} finally {
 					try {
@@ -1699,232 +1632,10 @@ public class OdfPackage implements Close
 		return data;
 	}
 
-	// encrypt data and update manifest.
-	private byte[] encryptData(byte[] data, OdfFileEntry fileEntry) {
-		byte[] encryptedData = null;
-		try {
-			// 1.The original uncompressed, unencrypted size is
-			// contained in the manifest:size.
-			fileEntry.setSize(data.length);
-
-			// 2.Compress with the "deflate" algorithm
-			Deflater compresser = new Deflater(Deflater.DEFLATED, true);
-			compresser.setInput(data);
-			compresser.finish();
-			byte[] compressedData = new byte[data.length];
-			int compressedDataLength = compresser.deflate(compressedData);
-
-			// 3. The start key is generated: the byte sequence
-			// representing the password in UTF-8 is used to
-			// generate a 20-byte SHA1 digest.
-			byte[] passBytes = newPwd.getBytes("UTF-8");
-			MessageDigest md = MessageDigest.getInstance("SHA1");
-			passBytes = md.digest(passBytes);
-			// 4. Checksum specifies a digest in BASE64 encoding
-			// that can be used to detect password correctness. The
-			// digest is build from the compressed unencrypted file.
-			md.reset();
-			md.update(compressedData, 0, (compressedDataLength > 1024 ? 1024 : compressedDataLength));
-			byte[] checksumBytes = new byte[20];
-			md.digest(checksumBytes, 0, 20);
-
-			// 5. For each file, a 16-byte salt is generated by a random
-			// generator.
-			// The salt is a BASE64 encoded binary sequence.
-			SecureRandom secureRandom = SecureRandom.getInstance("SHA1PRNG");
-			byte[] salt = new byte[16];
-			secureRandom.nextBytes(salt);
-
-			// char passChars[] = new String(passBytes, "UTF-8").toCharArray();
-			/*
-			 * char passChars[] = new char[20]; for (int i = 0; i <
-			 * passBytes.length; i++) { passChars[i] = (char)
-			 * ((passBytes[i]+256)%256);
-			 * //System.out.println("passChars[i]:"+passChars
-			 * [i]+", passBytes[i]"+passBytes[i]); }
-			 * 
-			 * //char passChars[] = getChars(passBytes); // 6. The PBKDF2
-			 * algorithm based on the HMAC-SHA-1 function is used for the key
-			 * derivation. SecretKeyFactory factory =
-			 * SecretKeyFactory.getInstance("PBKDF2WithHmacSHA1"); // 7. The
-			 * salt is used together with the start key to derive a unique
-			 * 128-bit key for each file. // The default iteration count for the
-			 * algorithm is 1024. KeySpec spec = new PBEKeySpec(passChars, salt,
-			 * 1024, 128); SecretKey skey = factory.generateSecret(spec); byte[]
-			 * raw = skey.getEncoded(); // algorithm-name="Blowfish CFB"
-			 * SecretKeySpec skeySpec = new SecretKeySpec(raw, "Blowfish");
-			 */
-
-			byte[] dk = derivePBKDF2Key(passBytes, salt, 1024, 16);
-			SecretKeySpec key = new SecretKeySpec(dk, "Blowfish");
-			// 8.The files are encrypted: The random number
-			// generator is used to generate the 8-byte initialization vector
-			// for the
-			// algorithm. The derived key is used together with the
-			// initialization
-			// vector to encrypt the file using the Blowfish algorithm in cipher
-			// feedback
-			// CFB mode.
-			Cipher cipher = Cipher.getInstance("Blowfish/CFB/NoPadding");
-			// initialisation-vector specifies the byte-sequence used
-			// as an initialization vector to a encryption algorithm. The
-			// initialization vector is a BASE64 encoded binary sequence.
-			byte[] iv = new byte[8];
-			secureRandom.nextBytes(iv);
-			IvParameterSpec ivParameterSpec = new IvParameterSpec(iv);
-			cipher.init(Cipher.ENCRYPT_MODE, key, ivParameterSpec);
-			encryptedData = cipher.doFinal(compressedData, 0, compressedDataLength);
-
-			// 9.update file entry encryption data.
-			String checksum = new Base64Binary(checksumBytes).toString();
-			FileEntryElement fileEntryElement = fileEntry.getOdfElement();
-			EncryptionDataElement encryptionDataElement = OdfElement.findFirstChildNode(EncryptionDataElement.class, fileEntryElement);
-			if (encryptionDataElement != null) {
-				fileEntryElement.removeChild(encryptionDataElement);
-			}
-			encryptionDataElement = fileEntryElement.newEncryptionDataElement(checksum, "SHA1/1K");
-			String initialisationVector = new Base64Binary(iv).toString();
-			AlgorithmElement algorithmElement = OdfElement.findFirstChildNode(AlgorithmElement.class, encryptionDataElement);
-			if (algorithmElement != null) {
-				encryptionDataElement.removeChild(algorithmElement);
-			}
-			algorithmElement = encryptionDataElement.newAlgorithmElement("Blowfish CFB", initialisationVector);
-			String saltStr = new Base64Binary(salt).toString();
-			KeyDerivationElement keyDerivationElement = OdfElement.findFirstChildNode(KeyDerivationElement.class, encryptionDataElement);
-			if (keyDerivationElement != null) {
-				encryptionDataElement.removeChild(keyDerivationElement);
-			}
-			keyDerivationElement = encryptionDataElement.newKeyDerivationElement(1024, "PBKDF2", saltStr);
-			StartKeyGenerationElement startKeyGenerationElement = OdfElement.findFirstChildNode(StartKeyGenerationElement.class, encryptionDataElement);
-			if (startKeyGenerationElement != null) {
-				encryptionDataElement.removeChild(startKeyGenerationElement);
-			}
-			encryptionDataElement.newStartKeyGenerationElement("SHA1").setKeySizeAttribute(20);
-
-			// System.out.println("full-path=\""+ path +"\"");
-			// System.out.println("size=\""+ data.length +"\"");
-			// System.out.println("checksum=\""+ checksum +"\"");
-			// System.out.println("compressedData ="+compressedDataLength);
-
-		} catch (Exception e) {
-			// throws NoSuchAlgorithmException,
-			// InvalidKeySpecException, NoSuchPaddingException,
-			// InvalidKeyException,
-			// InvalidAlgorithmParameterException,
-			// IllegalBlockSizeException, BadPaddingException
-			Logger.getLogger(OdfPackage.class.getName()).log(Level.SEVERE, null, e);
-		}
-		return encryptedData;
-	}
-
-	private byte[] decryptData(byte[] data, OdfFileEntry manifestEntry, EncryptionDataElement encryptionDataElement) {
-		byte[] decompressData = null;
-		try {
-			KeyDerivationElement keyDerivationElement = OdfElement.findFirstChildNode(KeyDerivationElement.class, encryptionDataElement);
-			AlgorithmElement algorithmElement = OdfElement.findFirstChildNode(AlgorithmElement.class, encryptionDataElement);
-			String saltStr = keyDerivationElement.getSaltAttribute();
-			String ivStr = algorithmElement.getInitialisationVectorAttribute();
-			String checksum = encryptionDataElement.getChecksumAttribute();
-			byte[] salt = Base64Binary.valueOf(saltStr).getBytes();
-			byte[] iv = Base64Binary.valueOf(ivStr).getBytes();
-			byte[] passBytes = oldPwd.getBytes("UTF-8");
-			MessageDigest md = MessageDigest.getInstance("SHA-1");
-			passBytes = md.digest(passBytes);
-			/*
-			 * char passChars[] = new char[passBytes.length]; for(int i = 0;
-			 * i<passBytes.length; i++){ passChars[i] =
-			 * (char)(passBytes[i]|0xFF); } KeySpec spec = new
-			 * PBEKeySpec(passChars, salt, 1024, 128); SecretKeyFactory factory
-			 * = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA1"); SecretKey
-			 * skey = factory.generateSecret(spec); byte[] raw =
-			 * skey.getEncoded(); SecretKeySpec skeySpec = new
-			 * SecretKeySpec(raw, "Blowfish");
-			 */
-			byte[] dk = derivePBKDF2Key(passBytes, salt, 1024, 16);
-			SecretKeySpec key = new SecretKeySpec(dk, "Blowfish");
-
-			Cipher cipher = Cipher.getInstance("Blowfish/CFB/NoPadding");
-			IvParameterSpec ivParameterSpec = new IvParameterSpec(iv);
-			cipher.init(Cipher.DECRYPT_MODE, key, ivParameterSpec);
-			byte[] decryptedData = cipher.doFinal(data);
-
-			// valid checksum
-			md.reset();
-			md.update(decryptedData, 0, (decryptedData.length > 1024 ? 1024 : decryptedData.length));
-			byte[] checksumBytes = new byte[20];
-			md.digest(checksumBytes, 0, 20);
-			String newChecksum = new Base64Binary(checksumBytes).toString();
-			if (newChecksum.equals(checksum)) {
-				// decompress the bytes
-				Inflater decompresser = new Inflater(true);
-				decompresser.setInput(decryptedData);
-				decompressData = new byte[manifestEntry.getSize()];
-				decompresser.inflate(decompressData);
-				decompresser.end();
-			} else {
-				throw new OdfDecryptedException("The given password is wrong, please check it.");
-			}
-		} catch (Exception e) {
-			Logger.getLogger(OdfPackage.class.getName()).log(Level.SEVERE, null, e);
-		}
-		return decompressData;
-	}
-
-	// derive PBKDF2Key (reference http://www.ietf.org/rfc/rfc2898.txt)
-	byte[] derivePBKDF2Key(byte[] password, byte[] salt, int iterationCount, int keyLength) throws NoSuchAlgorithmException, InvalidKeyException {
-		SecretKeySpec keyspec = new SecretKeySpec(password, "HmacSHA1");
-		Mac hmac = Mac.getInstance("HmacSHA1");
-		hmac.init(keyspec);
-		// length in octets of HmacSHA1 function output, a positive integer
-		int hmacLen = hmac.getMacLength();
-		// let l be the number of hLen-octet blocks in the derived key, rounding
-		// up,
-		// l = CEIL (dkLen / hLen) Here, CEIL (x) is the smallest integer
-		// greater than, or equal to, x.
-		int l = (keyLength % hmacLen > 0) ? (keyLength / hmacLen + 1) : (keyLength / hmacLen);
-		// let r be the number of octets in the last block: r = dkLen - (l - 1)
-		// * hLen .
-		int r = keyLength - (l - 1) * hmacLen;
-		byte T[] = new byte[l * hmacLen];
-		int offset = 0;
-		// For each block of the derived key apply the function F defined below
-		// to the password P, the salt S, the iteration count c, and
-		// the block index to compute the block:
-		for (int i = 1; i <= l; i++) {
-			byte Ur[] = new byte[hmacLen];
-			byte Ui[] = new byte[salt.length + 4];
-			System.arraycopy(salt, 0, Ui, 0, salt.length);
-			// Here, INT (i) is a four-octet encoding of the integer i, most
-			// significant octet first.
-			Ui[salt.length + 0] = (byte) (i >>> 24);
-			Ui[salt.length + 1] = (byte) (i >>> 16);
-			Ui[salt.length + 2] = (byte) (i >>> 8);
-			Ui[salt.length + 3] = (byte) (i);
-			// U_1 \xor U_2 \xor ... \xor U_c
-			for (int j = 0; j < iterationCount; j++) {
-				Ui = hmac.doFinal(Ui);
-				// XOR
-				for (int k = 0; k < T.length; k++) {
-					Ur[k] ^= Ui[k];
-				}
-			}
-			System.arraycopy(Ur, 0, T, offset, hmacLen);
-			offset += hmacLen;
-		}
-		if (r < hmacLen) {
-			byte DK[] = new byte[keyLength];
-			System.arraycopy(T, 0, DK, 0, keyLength);
-			return DK;
-		}
-		return T;
-	}
-
 	// Serializes a DOM tree into a byte array.
-	// Providing the counterpart of the generic Namespace handling of
-	// OdfFileDom.
+	// Providing the counterpart of the generic Namespace handling of OdfFileDom.
 	private byte[] flushDom(Document dom) {
-		// if it is one of our DOM files we may flush all collected namespaces
-		// to the root element
+		// if it is one of our DOM files we may flush all collected namespaces to the root element
 		if (dom instanceof OdfFileDom) {
 			OdfFileDom odfDom = (OdfFileDom) dom;
 			Map<String, String> nsByUri = odfDom.getMapNamespacePrefixByUri();
@@ -1946,10 +1657,9 @@ public class OdfPackage implements Close
 	}
 
 	/**
-	 * Get the latest version of package content as InputStream, as it would be
-	 * saved. This might not be the original version once loaded from the
-	 * package.
-	 * 
+	 * Get the latest version of package content as InputStream, as it would be saved.
+	 * This might not be the original version once loaded from the package.
+	 *
 	 * @param internalPath
 	 *            of the desired stream.
 	 * @return Inputstream of the ODF file within the package for the given

[... 425 lines stripped ...]


Mime
View raw message