From odf-commits-return-763-apmail-incubator-odf-commits-archive=incubator.apache.org@incubator.apache.org Wed Jul 11 01:03:58 2012
Return-Path:
X-Original-To: apmail-incubator-odf-commits-archive@minotaur.apache.org
Delivered-To: apmail-incubator-odf-commits-archive@minotaur.apache.org
Received: from mail.apache.org (hermes.apache.org [140.211.11.3])
by minotaur.apache.org (Postfix) with SMTP id 641BADBE8
for ; Wed, 11 Jul 2012 01:03:58 +0000 (UTC)
Received: (qmail 91969 invoked by uid 500); 11 Jul 2012 01:03:58 -0000
Delivered-To: apmail-incubator-odf-commits-archive@incubator.apache.org
Received: (qmail 91945 invoked by uid 500); 11 Jul 2012 01:03:58 -0000
Mailing-List: contact odf-commits-help@incubator.apache.org; run by ezmlm
Precedence: bulk
List-Help:
List-Unsubscribe:
List-Post:
List-Id:
Reply-To: odf-dev@incubator.apache.org
Delivered-To: mailing list odf-commits@incubator.apache.org
Received: (qmail 91936 invoked by uid 99); 11 Jul 2012 01:03:58 -0000
Received: from nike.apache.org (HELO nike.apache.org) (192.87.106.230)
by apache.org (qpsmtpd/0.29) with ESMTP; Wed, 11 Jul 2012 01:03:58 +0000
X-ASF-Spam-Status: No, hits=-2000.0 required=5.0
tests=ALL_TRUSTED
X-Spam-Check-By: apache.org
Received: from [140.211.11.4] (HELO eris.apache.org) (140.211.11.4)
by apache.org (qpsmtpd/0.29) with ESMTP; Wed, 11 Jul 2012 01:03:47 +0000
Received: from eris.apache.org (localhost [127.0.0.1])
by eris.apache.org (Postfix) with ESMTP id A026023888E4;
Wed, 11 Jul 2012 01:03:25 +0000 (UTC)
Content-Type: text/plain; charset="utf-8"
MIME-Version: 1.0
Content-Transfer-Encoding: 7bit
Subject: svn commit: r1359972 [2/3] - 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: Wed, 11 Jul 2012 01:03:24 -0000
To: odf-commits@incubator.apache.org
From: robweir@apache.org
X-Mailer: svnmailer-1.0.8-patched
Message-Id: <20120711010325.A026023888E4@eris.apache.org>
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=1359972&r1=1359971&r2=1359972&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 Wed Jul 11 01:03:23 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;
@@ -44,31 +48,46 @@ 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 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.type.Base64Binary;
import org.w3c.dom.Document;
import org.w3c.dom.ls.DOMImplementationLS;
import org.w3c.dom.ls.LSOutput;
@@ -77,7 +96,6 @@ 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;
/**
@@ -112,23 +130,41 @@ public class OdfPackage implements Close
private Map mZipEntries;
private HashMap mOriginalZipEntries;
private Map 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 mPkgDocuments;
// Three different incarnations of a package file/data
// save() will check 1) mPkgDoms, 2) if not check mMemoryFileCache
private HashMap mPkgDoms;
private HashMap 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;
@@ -143,9 +179,8 @@ public class OdfPackage implements Close
static {
mCompressedFileTypes = new HashSet();
- 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));
}
@@ -159,21 +194,23 @@ public class OdfPackage implements Close
mPkgDoms = new HashMap();
mMemoryFileCache = new HashMap();
mManifestEntries = new HashMap();
- // 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);
+ Logger.getLogger(OdfPackage.class.getName()).log(Level.SEVERE,
+ "Could not initiate validation with the given ErrorHandler: '" + errorHandlerProperty + "'", ex);
}
}
}
@@ -182,82 +219,73 @@ public class OdfPackage implements Close
/**
* Creates an OdfPackage from the OpenDocument provided by a File.
- *
+ *
*
* OdfPackage relies on the file being available for read access over the
* whole lifecycle of OdfPackage.
*
- *
+ *
* @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.
- *
- *
- * OdfPackage relies on the file being available for read access over the
- * whole lifecycle of OdfPackage.
- *
- *
- * @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 = baseURI;
- InputStream packageStream = new FileInputStream(pkgFile);
- try {
- initializeZip(packageStream);
- } finally {
- close(packageStream);
- }
+ mBaseURI = getBaseURLFromFile(pkgFile);
+ mErrorHandler = errorHandler;
+ oldPwd = password;
+ newPwd = oldPwd;
+ initializeZip(pkgFile);
}
/**
* Creates an OdfPackage from the OpenDocument provided by a InputStream.
- *
- * Since an InputStream does not provide the arbitrary (non sequentiell)
+ *
+ *
+ * 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.
- *
- * @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.
+ *
+ *
+ * @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.
- *
+ *
*
* OdfPackage relies on the file being available for read access over the
* whole lifecycle of OdfPackage.
*
- *
+ *
* @param path
* - the documentURL to the ODF package
* @return the OpenDocument document represented as an OdfPackage
@@ -266,83 +294,97 @@ 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.
- *
+ *
*
* OdfPackage relies on the file being available for read access over the
* whole lifecycle of OdfPackage.
*
- *
- * @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.
- *
+ *
*
* 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.
*
- *
+ *
* @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.
- *
- * OdfPackage relies on the file being available for read access over
- * the whole lifecycle of OdfPackage.
- *
- * @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
+ *
+ *
+ * OdfPackage relies on the file being available for read access over the
+ * whole lifecycle of OdfPackage.
+ *
+ *
+ * @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.
- *
- * OdfPackage relies on the file being available for read access over
- * the whole lifecycle of OdfPackage.
- * @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
+ *
+ *
+ * OdfPackage relies on the file being available for read access over the
+ * whole lifecycle of OdfPackage.
+ *
+ *
+ * @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, ErrorHandler errorHandler)
- throws SAXException, IOException {
- return new OdfPackage(pkgFile, getBaseURLFromFile(pkgFile), errorHandler);
+ 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,6 +400,20 @@ 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 firstEntryName = mZipFile.entriesToMap(mZipEntries);
@@ -374,7 +430,8 @@ 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();
mOriginalZipEntries.putAll(mZipEntries);
mZipEntries.remove(OdfPackage.OdfFile.MEDIA_TYPE.getPath());
@@ -386,13 +443,14 @@ public class OdfPackage implements Close
Iterator 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
+ // add manifest entry as to be described by a
+ //
ensureFileEntryExistence(internalPath);
}
}
@@ -400,17 +458,17 @@ public class OdfPackage implements Close
}
/** Validates if all file entries exist in the ZIP and vice versa */
- private void validateManifest() throws SAXException {
- Set zipPaths = mZipEntries.keySet();
- Set manifestPaths = mManifestEntries.keySet();
+ private void validateManifest() {
+ Set zipPaths = mZipEntries.keySet();
+ Set manifestPaths = mManifestEntries.keySet();
Set sharedPaths = new HashSet(zipPaths);
sharedPaths.retainAll(manifestPaths);
if (sharedPaths.size() < zipPaths.size()) {
Set zipPathSuperset = new HashSet(mZipEntries.keySet());
zipPathSuperset.removeAll(sharedPaths);
- Set sortedSet = new TreeSet(zipPathSuperset);
- Iterator iter = sortedSet.iterator();
+ Set sortedSet = new TreeSet(zipPathSuperset);
+ Iterator iter = sortedSet.iterator();
String documentURL = getBaseURI();
String internalPath;
while (iter.hasNext()) {
@@ -426,7 +484,8 @@ 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 manifestOnlyPaths = zipPathSubset.iterator();
while (manifestOnlyPaths.hasNext()) {
String manifestOnlyPath = manifestOnlyPaths.next();
@@ -452,46 +511,61 @@ public class OdfPackage implements Close
}
/** Removes directories without a mimetype (all none documents) */
- private void removeDirectory(String path) throws SAXException {
+ private void removeDirectory(String path) {
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);
- 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
+ */
+ private void initializeMediaType(String firstEntryName) {
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;
@@ -500,7 +574,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,7 +584,7 @@ public class OdfPackage implements Close
}
}
- private void validateMimeTypeEntry(ZipEntry mimetypeEntry, String firstEntryName) throws SAXException {
+ private void validateMimeTypeEntry(ZipEntry mimetypeEntry, String firstEntryName) {
if (mimetypeEntry.getMethod() != ZipEntry.STORED) {
logValidationError(OdfPackageConstraint.MIMETYPE_IS_COMPRESSED, getBaseURI());
@@ -533,61 +608,37 @@ public class OdfPackage implements Close
}
/** @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);
@@ -598,16 +649,19 @@ 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;
@@ -615,10 +669,12 @@ 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);
@@ -635,11 +691,10 @@ 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);
}
}
@@ -648,10 +703,12 @@ 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
@@ -662,9 +719,11 @@ 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) {
@@ -674,7 +733,8 @@ 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) {
@@ -683,44 +743,55 @@ 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 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 allPackageFileNames = getFilePaths();
+ try {
+ // get all files of the package
+ Set 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 directoryEntryNames = new ArrayList();
- for (String entryName : allPackageFileNames) {
- if (entryName.startsWith(internalPath)) {
- directoryEntryNames.add(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 directoryEntryNames = new ArrayList();
+ 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 */
@@ -732,11 +803,18 @@ 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 OdfPackageDocument.
- * 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
+ * OdfPackageDocument. 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) {
@@ -768,7 +846,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() {
@@ -778,7 +856,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
*/
@@ -787,10 +865,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.
@@ -802,7 +880,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 getFilePaths() {
@@ -810,9 +888,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
@@ -824,69 +902,77 @@ public class OdfPackage implements Close
/**
* 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 {
- 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);
+ private void save(OutputStream odfStream, String baseURL) {
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 it = mManifestEntries.keySet().iterator();
@@ -894,68 +980,122 @@ 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
- 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);
+ 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);
+ }
}
+ data = null;
+ } catch (IOException ex) {
+ Logger.getLogger(OdfPackage.class.getName()).log(Level.SEVERE, null, ex);
}
}
- } finally {
+ // 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);
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)
- 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);
+ 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);
}
- crc.reset();
- if (data != null) {
- ze.setSize(data.length);
- crc.update(data);
- ze.setCrc(crc.getValue());
+ }
+
+ /**
+ * 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 {
- ze.setSize(0);
- ze.setCrc(0);
- }
- ze.setCompressedSize(-1);
- zos.putNextEntry(ze);
- if (data != null) {
- zos.write(data, 0, data.length);
+ return false;
}
- 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) {
@@ -988,8 +1128,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
@@ -1006,36 +1145,46 @@ public class OdfPackage implements Close
/**
* Parse the Manifest file
*/
- private void parseManifest() throws SAXException, IOException {
- InputStream is = null;
+
+ private void parseManifest() {
+ // ToDo: manifest.xml will be held in the future as DOM, now its
+ // being generated for each save()
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) {
+ 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());
- 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);
+ Map 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,19 +1195,16 @@ public class OdfPackage implements Close
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;
}
@@ -1066,6 +1212,7 @@ 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;
@@ -1076,7 +1223,7 @@ 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)));
}
}
}
@@ -1085,11 +1232,12 @@ 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
@@ -1105,35 +1253,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 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 entryNameList = entryMapToCopy.keySet();
@@ -1153,16 +1308,17 @@ 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);
}
@@ -1170,15 +1326,17 @@ 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 {
@@ -1188,7 +1346,8 @@ 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");
}
}
@@ -1196,22 +1355,21 @@ public class OdfPackage implements Close
}
/** Get all the file entries from a sub directory */
- private Map getSubDirectoryEntries(String directory) {
+ private Map getSubDirectoryEntries(OdfPackage destinationPackage, String directory) {
directory = normalizeDirectoryPath(directory);
Map subEntries = new HashMap();
Map allEntries = getManifestEntries();
Set 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);
}
@@ -1221,42 +1379,56 @@ 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 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 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 getDocumentPaths(String mediaTypeString, String subDirectory) {
Set innerDocuments = new HashSet();
Set 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);
@@ -1269,21 +1441,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 (!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();
}
fileEntry = mManifestEntries.get(internalPath);
// for every new file entry
if (fileEntry == null) {
- fileEntry = new OdfFileEntry(internalPath);
+ ManifestElement manifestEle = getManifestDom().getRootElement();
+ fileEntry = new OdfFileEntry(manifestEle.newFileEntryElement(internalPath, ""));
mManifestEntries.put(internalPath, fileEntry);
// creates recursive file entries for all sub directories
createSubEntries(internalPath);
@@ -1301,13 +1473,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
@@ -1316,9 +1489,8 @@ 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) {
@@ -1358,19 +1530,18 @@ 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.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
@@ -1385,19 +1556,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 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();
@@ -1415,15 +1586,16 @@ 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);
@@ -1431,14 +1603,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);
}
@@ -1446,113 +1618,19 @@ 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 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("\n");
- buf.append("\n");
- Iterator it = new TreeSet(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(" 0) {
- buf.append(" manifest:size=\"");
- buf.append(i);
- buf.append("\"");
- }
- EncryptionData enc = fileEntry.getEncryptionData();
- if (enc != null) {
- buf.append(">\n");
- buf.append(" \n");
- Algorithm alg = enc.getAlgorithm();
- if (alg != null) {
- buf.append(" \n");
- }
- KeyDerivation keyDerivation = enc.getKeyDerivation();
- if (keyDerivation != null) {
- buf.append(" \n");
- }
- buf.append(" \n");
- buf.append(" \n");
- } else {
- buf.append("/>\n");
- }
- }
- }
- }
- buf.append("");
- 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
@@ -1573,29 +1651,11 @@ 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)
@@ -1611,12 +1671,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,10 +1699,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();
+ 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 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 nsByUri = odfDom.getMapNamespacePrefixByUri();
@@ -1657,9 +1946,10 @@ 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
@@ -1674,7 +1964,7 @@ public class OdfPackage implements Close
// ZipFile class.
[... 423 lines stripped ...]