incubator-odf-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From liud...@apache.org
Subject svn commit: r1424353 [2/3] - in /incubator/odf/trunk: 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/odfdom/in...
Date Thu, 20 Dec 2012 07:33:34 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=1424353&r1=1424352&r2=1424353&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 Thu Dec 20 07:33:34 2012
@@ -37,6 +37,10 @@ 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;
@@ -52,24 +56,39 @@ 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 javax.xml.parsers.SAXParser;
-import javax.xml.parsers.SAXParserFactory;
-import org.odftoolkit.odfdom.doc.OdfDocument.OdfMediaType;
 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.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.pkg.manifest.OdfFileEntry;
+import org.odftoolkit.odfdom.pkg.manifest.OdfManifestDom;
+import org.odftoolkit.odfdom.pkg.manifest.StartKeyGenerationElement;
 import org.odftoolkit.odfdom.pkg.rdfa.Util;
+import org.odftoolkit.odfdom.type.Base64Binary;
 import org.w3c.dom.Document;
 import org.w3c.dom.ls.DOMImplementationLS;
 import org.w3c.dom.ls.LSOutput;
@@ -88,7 +107,7 @@ import org.xml.sax.XMLReader;
  * effect, when the OdfPackage is being made persisted by save().
  */
 public class OdfPackage implements Closeable {
-
+	
 	// Static parts of file references
 	private static final String DOUBLE_DOT = "..";
 	private static final String DOT = ".";
@@ -113,43 +132,59 @@ 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 String mManifestVersion;
+	private OdfManifestDom mManifestDom;
+	private String oldPwd;
+	private String newPwd;
+	
 	/* 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;
-
+		
 		OdfFile(String internalPath) {
 			this.internalPath = internalPath;
 		}
-
+		
 		public String getPath() {
 			return internalPath;
 		}
 	}
-
+	
 	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));
 	}
-
+	
 	/**
 	 * Creates the ODFPackage as an empty Package.
 	 */
@@ -160,19 +195,20 @@ 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()).fine("Activated validation with default ErrorHandler!");
+					Logger.getLogger(OdfPackage.class.getName()).info("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.FINE, "Activated validation with ErrorHandler:''{0}''!", errorHandlerProperty);
+						Logger.getLogger(OdfPackage.class.getName()).log(Level.CONFIG, "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);
 					}
@@ -180,85 +216,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
-	 * @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
-	 *
+	 * @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
 	 * @see #getErrorHandler*
 	 */
-	private OdfPackage(File pkgFile, String baseURI, ErrorHandler errorHandler) throws SAXException, IOException {
+	private OdfPackage(File pkgFile, String baseURI, String password, ErrorHandler errorHandler) throws Exception {
 		this();
-		if (errorHandler != null) {
-			mErrorHandler = errorHandler;
-		}
+		mBaseURI = getBaseURLFromFile(pkgFile);
+		mErrorHandler = errorHandler;
+		oldPwd = password;
+		newPwd = oldPwd;
 		mBaseURI = baseURI;
+		
 		InputStream packageStream = new FileInputStream(pkgFile);
 		try {
 			initializeZip(packageStream);
 		} finally {
 			close(packageStream);
 		}
+		
+		// initializeZip(pkgFile);
 	}
-
+	
 	/**
 	 * 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 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
-	 *
+	 * 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
 	 * @see #getErrorHandler*
 	 */
-	private OdfPackage(InputStream packageStream, String baseURI, ErrorHandler errorHandler)
-			throws SAXException, IOException {
+	private OdfPackage(InputStream packageStream, String baseURI, String password, ErrorHandler errorHandler) throws Exception {
 		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
@@ -267,83 +300,113 @@ 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);
+		return new OdfPackage(pkgFile, getBaseURLFromFile(pkgFile), null, 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);
+		return new OdfPackage(pkgFile, getBaseURLFromFile(pkgFile), null, 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);
+	public static OdfPackage loadPackage(InputStream packageStream) throws Exception {
+		return new OdfPackage(packageStream, null, 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 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
+	 * <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
 	 * @see #getErrorHandler
 	 */
-	public static OdfPackage loadPackage(InputStream packageStream, String baseURI, ErrorHandler errorHandler)
-			throws SAXException, IOException {
-		return new OdfPackage(packageStream, baseURI, errorHandler);
+	public static OdfPackage loadPackage(InputStream packageStream, String baseURI, ErrorHandler errorHandler) throws Exception {
+		return new OdfPackage(packageStream, baseURI, null, 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 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
+	 * <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 java.lang.Exception
+	 *             - if the package could not be created
 	 * @see #getErrorHandler
 	 */
-	public static OdfPackage loadPackage(File pkgFile, ErrorHandler errorHandler)
-			throws SAXException, IOException {
-		return new OdfPackage(pkgFile, getBaseURLFromFile(pkgFile), errorHandler);
+	public static OdfPackage loadPackage(File pkgFile, ErrorHandler errorHandler) throws Exception {
+		return new OdfPackage(pkgFile, getBaseURLFromFile(pkgFile), null, 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
+	 * @see #getErrorHandler
+	 */
+	public static OdfPackage loadPackage(File pkgFile, String password, ErrorHandler errorHandler) throws Exception {
+		return new OdfPackage(pkgFile, getBaseURLFromFile(pkgFile), password, errorHandler);
+	}
+	
 	// Initialize using memory
-	private void initializeZip(InputStream odfStream) throws SAXException, IOException {
+	private void initializeZip(InputStream odfStream) throws Exception {
 		ByteArrayOutputStream tempBuf = new ByteArrayOutputStream();
 		StreamHelper.transformStream(odfStream, tempBuf);
 		byte[] mTempByteBuf = tempBuf.toByteArray();
@@ -358,8 +421,24 @@ public class OdfPackage implements Close
 		mZipFile = new ZipHelper(this, mTempByteBuf);
 		readZip();
 	}
-
-	private void readZip() throws SAXException, IOException {
+	
+	// // 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 Exception {
 		mZipEntries = new HashMap<String, ZipEntry>();
 		String firstEntryName = mZipFile.entriesToMap(mZipEntries);
 		if (mZipEntries.isEmpty()) {
@@ -371,11 +450,12 @@ public class OdfPackage implements Close
 		} else {
 			// initialize the files of the package (fileEnties of Manifest)
 			parseManifest();
-
+			
 			// 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());
@@ -387,31 +467,35 @@ 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/")
-						&& !internalPath.equals(OdfPackage.OdfFile.MEDIA_TYPE.getPath())) {
+				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);
 				}
 			}
 		}
 	}
-
-	/** Validates if all file entries exist in the ZIP and vice versa */
+	
+	/**
+	 * Validates if all file entries exist in the ZIP and vice versa
+	 * 
+	 * @throws SAXException
+	 */
 	private void validateManifest() throws SAXException {
-		Set<String> zipPaths = mZipEntries.keySet();
-		Set<String> manifestPaths = mManifestEntries.keySet();
+		Set zipPaths = mZipEntries.keySet();
+		Set 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<String> sortedSet = new TreeSet<String>(zipPathSuperset);
-			Iterator<String> iter = sortedSet.iterator();
+			Set sortedSet = new TreeSet<String>(zipPathSuperset);
+			Iterator iter = sortedSet.iterator();
 			String documentURL = getBaseURI();
 			String internalPath;
 			while (iter.hasNext()) {
@@ -426,8 +510,9 @@ public class OdfPackage implements Close
 			zipPathSubset.removeAll(sharedPaths);
 			// 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();
@@ -451,8 +536,12 @@ public class OdfPackage implements Close
 			}
 		}
 	}
-
-	/** Removes directories without a mimetype (all none documents) */
+	
+	/**
+	 * Removes directories without a mimetype (all none documents)
+	 * 
+	 * @throws SAXException
+	 */
 	private void removeDirectory(String path) throws SAXException {
 		if (path.endsWith(SLASH)) {
 			// Check if it is a sub-document?
@@ -460,39 +549,54 @@ public class OdfPackage implements Close
 			String dirMimeType = mManifestEntries.get(path).getMediaTypeString();
 			if (dirMimeType == null || EMPTY_STRING.equals(dirMimeType)) {
 				logValidationWarning(OdfPackageConstraint.MANIFEST_LISTS_DIRECTORY, getBaseURI(), path);
-				mManifestEntries.remove(path);
+				OdfFileEntry manifestEntry = mManifestEntries.remove(path);
+				FileEntryElement manifestEle = manifestEntry.getOdfElement();
+				manifestEle.getParentNode().removeChild(manifestEle);
 			}
 		}
 	}
-
-	/** Reads the uncompressed "mimetype" file, which contains the package media/mimte type*/
-	private void initializeMediaType(String firstEntryName) throws SAXException, IOException {
+	
+	/**
+	 * Reads the uncompressed "mimetype" file, which contains the package
+	 * media/mimte type
+	 * 
+	 * @throws SAXException
+	 */
+	private void initializeMediaType(String firstEntryName) throws SAXException {
 		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);
 						}
-					} 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)
+					} 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;
@@ -501,7 +605,8 @@ 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) {
@@ -509,9 +614,9 @@ public class OdfPackage implements Close
 			}
 		}
 	}
-
+	
 	private void validateMimeTypeEntry(ZipEntry mimetypeEntry, String firstEntryName) throws SAXException {
-
+		
 		if (mimetypeEntry.getMethod() != ZipEntry.STORED) {
 			logValidationError(OdfPackageConstraint.MIMETYPE_IS_COMPRESSED, getBaseURI());
 		}
@@ -522,7 +627,7 @@ public class OdfPackage implements Close
 			logValidationError(OdfPackageConstraint.MIMETYPE_NOT_FIRST_IN_PACKAGE, getBaseURI());
 		}
 	}
-
+	
 	/** @returns the media type of the root document from the manifest.xml */
 	private String getMediaTypeFromManifest() {
 		OdfFileEntry rootDocumentEntry = mManifestEntries.get(SLASH);
@@ -532,94 +637,75 @@ public class OdfPackage implements Close
 			return null;
 		}
 	}
-
+	
 	/** @returns the media type of the root document from the manifest.xml */
-	private String getMediaTypeFromEntry(ZipEntry mimetypeEntry) throws SAXException, IOException {
+	private String getMediaTypeFromEntry(ZipEntry mimetypeEntry) {
 		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 (IOException ex) {
-			handleIOException(ex, false);
+		} catch (Exception ex) {
+			Logger.getLogger(OdfPackage.class.getName()).log(Level.SEVERE, null, ex);
 		} finally {
-			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.
+			if (out != null) {
+				try {
+					out.close();
+				} catch (IOException ex) {
+					Logger.getLogger(OdfPackage.class.getName()).log(Level.SEVERE, null, ex);
 				}
+				out = null;
 			}
 		}
-		throw ex; //No error handler? Just throw the original IOException
+		return entryMediaType;
 	}
-
+	
 	/**
-	 * 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);
 		updateFileEntry(ensureFileEntryExistence(internalPath), doc.getMediaTypeString());
 		mPkgDocuments.put(internalPath, doc);
 	}
-
+	
 	/**
 	 * 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;
 	}
-
+	
 	/**
 	 * 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);
@@ -636,23 +722,24 @@ public class OdfPackage implements Close
 					if (odfMediaType == null) {
 						return null;
 					}
-					// ToDo: Issue 265 - Remove dependency to higher layer by factory
+					// ToDo: Issue 265 - Remove dependency to higher layer by
+					// facotory
 					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);
 				}
 			}
 		}
 		return doc;
 	}
-
+	
 	/**
-	 * @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
@@ -660,84 +747,105 @@ public class OdfPackage implements Close
 		internalPath = normalizeDirectoryPath(internalPath);
 		return mPkgDocuments.get(internalPath);
 	}
-
+	
 	/**
 	 * @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) {
 		internalPath = normalizeFilePath(internalPath);
 		this.insert(dom, internalPath, null);
 	}
-
+	
 	/**
 	 * @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) {
 		internalPath = normalizeFilePath(internalPath);
 		return this.mPkgDoms.get(internalPath);
 	}
-
+	
 	/**
-	 * @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);
-		// 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);
+		try {
+			// 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);
+				}
+				remove(internalPath);
 			}
-			for (String entryName : directoryEntryNames) {
-				remove(entryName);
-			}
-			remove(internalPath);
+		} catch (Exception ex) {
+			Logger.getLogger(OdfPackageDocument.class.getName()).log(Level.SEVERE, null, ex);
 		}
+		
 	}
-
+	
 	/** @return all currently opened OdfPackageDocument of this OdfPackage */
 	Set<String> getCachedPackageDocuments() {
 		return mPkgDocuments.keySet();
 	}
-
+	
 	public OdfPackageDocument getRootDocument() {
 		return mPkgDocuments.get(OdfPackageDocument.ROOT_DOCUMENT_PATH);
 	}
-
-	/**
-	 * 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.
+	
+	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.
 	 * @return the mediaType for the resource of the given path
 	 */
 	public String getMediaTypeString(String internalPath) {
@@ -755,7 +863,7 @@ public class OdfPackage implements Close
 		}
 		return mediaType;
 	}
-
+	
 	private String getMediaTypeFromEntry(String internalPath) {
 		OdfFileEntry entry = getFileEntry(internalPath);
 		// if the document is not in the package, the return is NULL
@@ -765,33 +873,32 @@ public class OdfPackage implements Close
 			return null;
 		}
 	}
-
+	
 	/**
 	 * 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() {
 		return mMediaType;
 	}
-
+	
 	/**
 	 * Set the media type of the ODF package (equal to media type of ODF root
 	 * document)
-	 *
+	 * 
 	 * @param mediaType
 	 *            string of this ODF package
 	 */
 	void setMediaTypeString(String mediaType) {
 		mMediaType = mediaType;
 	}
-
+	
 	/**
-	 *
 	 * 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.
@@ -800,20 +907,19 @@ public class OdfPackage implements Close
 		internalPath = normalizeFilePath(internalPath);
 		return mManifestEntries.get(internalPath);
 	}
-
+	
 	/**
 	 * 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() {
 		return mManifestEntries.keySet();
 	}
-
+	
 	/**
-	 *
 	 * 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
@@ -822,66 +928,74 @@ public class OdfPackage implements Close
 		internalPath = normalizeFilePath(internalPath);
 		return mManifestEntries.containsKey(internalPath);
 	}
-
+	
 	/**
 	 * Save the package to given documentURL.
-	 *
+	 * 
 	 * @param odfPath
 	 *            - the path to the ODF package destination
-	 * @throws java.io.IOException
+	 * @throws java.lang.Exception
 	 *             - if the package could not be saved
 	 */
-	public void save(String odfPath) throws IOException {
+	public void save(String odfPath) throws Exception {
 		File f = new File(odfPath);
 		save(f);
 	}
-
+	
 	/**
-	 * Save package to a given File.
-	 *
+	 * Save package to a given File. After saving it is still necessary to close
+	 * the package to have again full access about the file.
+	 * 
 	 * @param pkgFile
 	 *            - the File to save the ODF package to
-	 * @throws java.lang.IOException
+	 * @throws java.lang.Exception
 	 *             - if the package could not be saved
 	 */
-	public void save(File pkgFile) throws IOException {
+	public void save(File pkgFile) throws Exception {
 		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);
-		try {
-			save(fos, baseURL);
-		} finally {
-			fos.close();
-		}
+		save(fos, baseURL);
 	}
-
+	
+	public void save(OutputStream odfStream) throws Exception {
+		save(odfStream, null);
+	}
+	
 	/**
-	 * 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
+	 * Sets the password of this package. if password is not null, package will
+	 * be encrypted when save.
+	 * 
+	 * @param password
+	 *            password
+	 * @since 0.8.9
 	 */
-	public void save(OutputStream odfStream) throws IOException {
-		save(odfStream, null);
+	public void setPassword(String password) {
+		newPwd = password;
 	}
-
+	
 	/**
 	 * 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.io.IOException if an I/O error occurs while saving the package
+	 * @param baseURL
+	 *            defining the location of the package
+	 * @throws java.lang.Exception
+	 *             - if the package could not be saved
 	 */
-	private void save(OutputStream odfStream, String baseURL) throws IOException {
+	private void save(OutputStream odfStream, String baseURL) throws Exception {
+		// try {
 		mBaseURI = baseURL;
 		OdfFileEntry rootEntry = mManifestEntries.get(SLASH);
 		if (rootEntry == null) {
-			rootEntry = new OdfFileEntry(SLASH, mMediaType);
+			rootEntry = new OdfFileEntry(getManifestDom().getRootElement().newFileEntryElement(SLASH, mMediaType));
 			mManifestEntries.put(SLASH, rootEntry);
 		} else {
 			rootEntry.setMediaTypeString(mMediaType);
@@ -895,38 +1009,50 @@ 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) {
-				byte[] data = null;
-				// ODF requires the "mimetype" file to be at first in the package
+				// 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);
-					// 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())) {
+					// 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();
-	}
-
-	private void createZipEntry(String path, byte[] data, ZipOutputStream zos, long modTime, CRC32 crc)
-			throws IOException {
-		ZipEntry ze = mZipEntries.get(path);
+		// } 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 = null;
+		// try {
+		ze = mZipEntries.get(path);
 		if (ze == null) {
 			ze = new ZipEntry(path);
 		}
@@ -938,30 +1064,76 @@ public class OdfPackage implements Close
 		}
 		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);
 		}
-		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);
+		// }
 	}
-
+	
+	/**
+	 * 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);
+		} else {
+			return false;
+		}
+	}
+	
 	/**
 	 * 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) {
 		boolean result = true;
-
+		
 		// ODF spec does not allow compression of "./mimetype" file
 		if (internalPath.equals(OdfPackage.OdfFile.MEDIA_TYPE.getPath())) {
 			return false;
@@ -975,7 +1147,41 @@ public class OdfPackage implements Close
 		}
 		return result;
 	}
-
+	
+	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.
+				}
+			}
+		}
+		throw ex; // No error handler? Just throw the original IOException
+	}
+	
 	/**
 	 * Close the OdfPackage after it is no longer needed. Even after saving it
 	 * is still necessary to close the package to have again full access about
@@ -989,8 +1195,7 @@ 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
@@ -1003,40 +1208,49 @@ public class OdfPackage implements Close
 		mBaseURI = null;
 		mResolver = null;
 	}
-
+	
 	/**
 	 * Parse the Manifest file
 	 */
-	private void parseManifest() throws SAXException, IOException {
-		InputStream is = null;
-		try {
-			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);
-			}
-			if (is == null) {
-				logValidationError(OdfPackageConstraint.MANIFEST_NOT_IN_PACKAGE, getBaseURI());
-				return;
-			}
-			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);
+	private void parseManifest() throws Exception {
+		// ToDo: manifest.xml will be held in the future as DOM, now its
+		// being generated for each save()
+		// 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());
 		}
+		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);
+		}
+		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 getXMLReader() throws SAXException {
+	
+	XMLReader getXMLReader() throws ParserConfigurationException, SAXException {
 		// create sax parser
 		SAXParserFactory saxFactory = new org.apache.xerces.jaxp.SAXParserFactoryImpl();
 		saxFactory.setNamespaceAware(true);
@@ -1046,27 +1260,25 @@ public class OdfPackage implements Close
 		} catch (Exception ex) {
 			Logger.getLogger(OdfPackage.class.getName()).log(Level.SEVERE, null, ex);
 		}
-
-		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);
-		}
+		
+		SAXParser parser = saxFactory.newSAXParser();
 		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;
 	}
-
+	
 	// 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;
@@ -1077,20 +1289,21 @@ public class OdfPackage implements Close
 					path = path + directory + SLASH;
 					OdfFileEntry fileEntry = mManifestEntries.get(path);
 					if (fileEntry == null) {
-						mManifestEntries.put(path, new OdfFileEntry(path, null));
+						mManifestEntries.put(path, new OdfFileEntry(manifestEle.newFileEntryElement(path, null)));
 					}
 				}
 			}
 		}
 	}
-
+	
 	/**
 	 * 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
@@ -1106,35 +1319,42 @@ public class OdfPackage implements Close
 		} else {
 			mPkgDoms.put(internalPath, fileDOM);
 		}
-		updateFileEntry(ensureFileEntryExistence(internalPath), mediaType);
+		if (!internalPath.endsWith(OdfFile.MANIFEST.internalPath)) {
+			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(sourceDocument.getDocumentPath());
+			entryMapToCopy = sourceDocument.getPackage().getSubDirectoryEntries(this, 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();
@@ -1154,32 +1374,35 @@ public class OdfPackage implements Close
 						String packagePath = documentDirectory + entry.getPath();
 						insert(sourceDocument.getPackage().getInputStream(entryName), packagePath, entry.getMediaTypeString());
 					}
-				} catch (IOException ex) {
-					//Catching IOException should be fine here: in-memory operations only
+				} catch (Exception ex) {
 					Logger.getLogger(OdfPackage.class.getName()).log(Level.SEVERE, null, ex);
 				}
 			}
 		}
-		//make sure the media type of embedded Document is right set.
-		OdfFileEntry embedDocumentRootEntry = new OdfFileEntry(internalPath, sourceDocument.getMediaTypeString());
+		// make sure the media type of embedded Document is right set.
+		ManifestElement manifestEle = mManifestDom.getRootElement();
+		OdfFileEntry embedDocumentRootEntry = new OdfFileEntry(manifestEle.newFileEntryElement(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);
 	}
-
+	
 	/**
 	 * 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 {
@@ -1189,75 +1412,89 @@ 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");
 				}
 			}
 		}
 	}
-
+	
 	/** Get all the file entries from a sub directory */
-	private Map<String, OdfFileEntry> getSubDirectoryEntries(String directory) {
+	private Map<String, OdfFileEntry> getSubDirectoryEntries(OdfPackage destinationPackage, 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) {
-					continue;
+					newEntryName = SLASH;
 				}
 				OdfFileEntry srcFileEntry = allEntries.get(entryName);
-				OdfFileEntry newFileEntry = new OdfFileEntry();
+				OdfFileEntry newFileEntry = new OdfFileEntry(manifestEle.newFileEntryElement(newEntryName, srcFileEntry.getMediaTypeString()));
 				newFileEntry.setEncryptionData(srcFileEntry.getEncryptionData());
-				newFileEntry.setMediaTypeString(srcFileEntry.getMediaTypeString());
-				newFileEntry.setPath(newEntryName);
 				newFileEntry.setSize(srcFileEntry.getSize());
 				subEntries.put(entryName, newFileEntry);
 			}
 		}
 		return subEntries;
 	}
-
+	
 	/**
 	 * 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);
@@ -1268,23 +1505,25 @@ public class OdfPackage implements Close
 		}
 		return innerDocuments;
 	}
-
+	
 	/**
-	 * 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 (!OdfPackage.OdfFile.MANIFEST.internalPath.equals(internalPath) ||
-			!internalPath.equals(EMPTY_STRING)) {
+		if (!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) {
-				fileEntry = new OdfFileEntry(internalPath);
+				ManifestElement manifestEle = getManifestDom().getRootElement();
+				if (manifestEle == null)
+					return null;
+				fileEntry = new OdfFileEntry(manifestEle.newFileEntryElement(internalPath, ""));
 				mManifestEntries.put(internalPath, fileEntry);
 				// creates recursive file entries for all sub directories
 				createSubEntries(internalPath);
@@ -1292,7 +1531,7 @@ public class OdfPackage implements Close
 		}
 		return fileEntry;
 	}
-
+	
 	/**
 	 * update file entry setting.
 	 */
@@ -1302,13 +1541,14 @@ public class OdfPackage implements Close
 		// reset encryption data (ODFDOM does not support encryption yet)
 		fileEntry.setEncryptionData(null);
 		// reset size to be unset
-		fileEntry.setSize(-1);
+		fileEntry.setSize(null);
 	}
-
+	
 	/**
 	 * 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
@@ -1317,61 +1557,58 @@ 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) {
 			return doc;
 		}
-
+		
 		InputStream is = getInputStream(internalPath);
-
+		
 		// We depend on Xerces. So we just go ahead and create a Xerces DBF,
 		// without
 		// forcing everything else to do so.
 		DocumentBuilderFactory factory = new org.apache.xerces.jaxp.DocumentBuilderFactoryImpl();
 		factory.setNamespaceAware(true);
 		factory.setValidating(false);
-
+		
 		DocumentBuilder builder = factory.newDocumentBuilder();
 		builder.setEntityResolver(getEntityResolver());
-
+		
 		String uri = getBaseURI() + internalPath;
-
+		
 		if (mErrorHandler != null) {
 			builder.setErrorHandler(mErrorHandler);
 		}
-
+		
 		InputSource ins = new InputSource(is);
 		ins.setSystemId(uri);
-
+		
 		doc = builder.parse(ins);
-
+		
 		if (doc != null) {
 			mPkgDoms.put(internalPath, doc);
 			mMemoryFileCache.remove(internalPath);
 		}
 		return doc;
 	}
-
+	
 	/**
 	 * 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.io.IOException
+	 * @throws java.lang.Exception
 	 *             In case the file could not be saved
 	 */
-	public void insert(URI sourceURI, String internalPath, String mediaType)
-			throws IOException {
+	public void insert(URI sourceURI, String internalPath, String mediaType) throws Exception {
 		InputStream is = null;
 		if (sourceURI.isAbsolute()) {
 			// if the URI is absolute it can be converted to URL
@@ -1382,23 +1619,23 @@ public class OdfPackage implements Close
 		}
 		insert(is, internalPath, mediaType);
 	}
-
+	
 	/**
 	 * 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 IOException {
+	public void insert(InputStream fileStream, String internalPath, String mediaType) throws Exception {
 		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();
@@ -1413,18 +1650,19 @@ public class OdfPackage implements Close
 			insert(data, internalPath, mediaType);
 		}
 	}
-
+	
 	/**
 	 * 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);
@@ -1432,128 +1670,34 @@ 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);
 			}
 		}
 		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
@@ -1574,31 +1718,13 @@ 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)
 		}
 		// if not available, check if file exists in ZIP
@@ -1612,12 +1738,19 @@ public class OdfPackage implements Close
 						ByteArrayOutputStream out = new ByteArrayOutputStream();
 						StreamHelper.transformStream(inputStream, out);
 						data = out.toByteArray();
-						// store for further usage; do not care about manifest: that
-						// is handled exclusively
+						// 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
 						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 {
@@ -1632,11 +1765,232 @@ 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();

[... 660 lines stripped ...]


Mime
View raw message