poi-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From kiwiwi...@apache.org
Subject svn commit: r1830061 [1/2] - in /poi/trunk/src: examples/src/org/apache/poi/xssf/usermodel/examples/ java/org/apache/poi/ ooxml/java/org/apache/poi/ ooxml/java/org/apache/poi/openxml4j/exceptions/ ooxml/java/org/apache/poi/openxml4j/opc/ ooxml/java/org...
Date Wed, 25 Apr 2018 10:03:40 GMT
Author: kiwiwings
Date: Wed Apr 25 10:03:39 2018
New Revision: 1830061

URL: http://svn.apache.org/viewvc?rev=1830061&view=rev
Log:
Bug 62187 - commit Commons Compress unrelated changes

Added:
    poi/trunk/src/ooxml/java/org/apache/poi/openxml4j/util/ZipArchiveFakeEntry.java   (with props)
    poi/trunk/src/ooxml/java/org/apache/poi/openxml4j/util/ZipArchiveThresholdInputStream.java   (with props)
Removed:
    poi/trunk/src/ooxml/java/org/apache/poi/openxml4j/opc/CompressionOption.java
    poi/trunk/src/ooxml/testcases/org/apache/poi/openxml4j/opc/TestZipPackage.java
Modified:
    poi/trunk/src/examples/src/org/apache/poi/xssf/usermodel/examples/BigGridDemo.java
    poi/trunk/src/java/org/apache/poi/UnsupportedFileFormatException.java
    poi/trunk/src/ooxml/java/org/apache/poi/POIXMLFactory.java
    poi/trunk/src/ooxml/java/org/apache/poi/openxml4j/exceptions/NotOfficeXmlFileException.java
    poi/trunk/src/ooxml/java/org/apache/poi/openxml4j/opc/OPCPackage.java
    poi/trunk/src/ooxml/java/org/apache/poi/openxml4j/opc/PackagePart.java
    poi/trunk/src/ooxml/java/org/apache/poi/openxml4j/opc/PackageRelationshipCollection.java
    poi/trunk/src/ooxml/java/org/apache/poi/openxml4j/opc/ZipPackage.java
    poi/trunk/src/ooxml/java/org/apache/poi/openxml4j/opc/ZipPackagePart.java
    poi/trunk/src/ooxml/java/org/apache/poi/openxml4j/opc/internal/ZipContentTypeManager.java
    poi/trunk/src/ooxml/java/org/apache/poi/openxml4j/opc/internal/ZipHelper.java
    poi/trunk/src/ooxml/java/org/apache/poi/openxml4j/opc/internal/marshallers/ZipPackagePropertiesMarshaller.java
    poi/trunk/src/ooxml/java/org/apache/poi/openxml4j/opc/internal/marshallers/ZipPartMarshaller.java
    poi/trunk/src/ooxml/java/org/apache/poi/openxml4j/util/ZipEntrySource.java
    poi/trunk/src/ooxml/java/org/apache/poi/openxml4j/util/ZipFileZipEntrySource.java
    poi/trunk/src/ooxml/java/org/apache/poi/openxml4j/util/ZipInputStreamZipEntrySource.java
    poi/trunk/src/ooxml/java/org/apache/poi/openxml4j/util/ZipSecureFile.java
    poi/trunk/src/ooxml/java/org/apache/poi/poifs/crypt/temp/AesZipFileZipEntrySource.java
    poi/trunk/src/ooxml/java/org/apache/poi/poifs/crypt/temp/EncryptedTempData.java
    poi/trunk/src/ooxml/java/org/apache/poi/xssf/dev/XSSFDump.java
    poi/trunk/src/ooxml/java/org/apache/poi/xssf/streaming/SXSSFWorkbook.java
    poi/trunk/src/ooxml/testcases/org/apache/poi/openxml4j/opc/TestPackage.java
    poi/trunk/src/ooxml/testcases/org/apache/poi/openxml4j/opc/ZipFileAssert.java
    poi/trunk/src/ooxml/testcases/org/apache/poi/openxml4j/opc/internal/marshallers/TestZipPackagePropertiesMarshaller.java
    poi/trunk/src/ooxml/testcases/org/apache/poi/poifs/crypt/TestDecryptor.java
    poi/trunk/src/ooxml/testcases/org/apache/poi/xssf/streaming/TestSXSSFWorkbook.java

Modified: poi/trunk/src/examples/src/org/apache/poi/xssf/usermodel/examples/BigGridDemo.java
URL: http://svn.apache.org/viewvc/poi/trunk/src/examples/src/org/apache/poi/xssf/usermodel/examples/BigGridDemo.java?rev=1830061&r1=1830060&r2=1830061&view=diff
==============================================================================
--- poi/trunk/src/examples/src/org/apache/poi/xssf/usermodel/examples/BigGridDemo.java (original)
+++ poi/trunk/src/examples/src/org/apache/poi/xssf/usermodel/examples/BigGridDemo.java Wed Apr 25 10:03:39 2018
@@ -74,12 +74,11 @@ import org.apache.poi.xssf.usermodel.XSS
  * </p>
  * See <a "http://poi.apache.org/spreadsheet/how-to.html#sxssf">
  *     http://poi.apache.org/spreadsheet/how-to.html#sxssf</a>.
-
- *
- * @author Yegor Kozlov
  */
-public class BigGridDemo {
+public final class BigGridDemo {
     private static final String XML_ENCODING = "UTF-8";
+
+    private BigGridDemo() {}
     
     public static void main(String[] args) throws Exception {
 
@@ -229,17 +228,17 @@ public class BigGridDemo {
         private final Writer _out;
         private int _rownum;
 
-        public SpreadsheetWriter(Writer out){
+        SpreadsheetWriter(Writer out){
             _out = out;
         }
 
-        public void beginSheet() throws IOException {
+        void beginSheet() throws IOException {
             _out.write("<?xml version=\"1.0\" encoding=\""+XML_ENCODING+"\"?>" +
                     "<worksheet xmlns=\"http://schemas.openxmlformats.org/spreadsheetml/2006/main\">" );
             _out.write("<sheetData>\n");
         }
 
-        public void endSheet() throws IOException {
+        void endSheet() throws IOException {
             _out.write("</sheetData>");
             _out.write("</worksheet>");
         }
@@ -249,7 +248,7 @@ public class BigGridDemo {
          *
          * @param rownum 0-based row number
          */
-        public void insertRow(int rownum) throws IOException {
+        void insertRow(int rownum) throws IOException {
             _out.write("<row r=\""+(rownum+1)+"\">\n");
             this._rownum = rownum;
         }
@@ -257,7 +256,7 @@ public class BigGridDemo {
         /**
          * Insert row end marker
          */
-        public void endRow() throws IOException {
+        void endRow() throws IOException {
             _out.write("</row>\n");
         }
 

Modified: poi/trunk/src/java/org/apache/poi/UnsupportedFileFormatException.java
URL: http://svn.apache.org/viewvc/poi/trunk/src/java/org/apache/poi/UnsupportedFileFormatException.java?rev=1830061&r1=1830060&r2=1830061&view=diff
==============================================================================
--- poi/trunk/src/java/org/apache/poi/UnsupportedFileFormatException.java (original)
+++ poi/trunk/src/java/org/apache/poi/UnsupportedFileFormatException.java Wed Apr 25 10:03:39 2018
@@ -23,7 +23,11 @@ package org.apache.poi;
 public abstract class UnsupportedFileFormatException extends IllegalArgumentException {
     private static final long serialVersionUID = -8281969197282030046L;
 
-    public UnsupportedFileFormatException(String s) {
+    protected UnsupportedFileFormatException(String s) {
 		super(s);
 	}
+
+    protected UnsupportedFileFormatException(String message, Throwable cause) {
+        super(message, cause);
+    }
 }
\ No newline at end of file

Modified: poi/trunk/src/ooxml/java/org/apache/poi/POIXMLFactory.java
URL: http://svn.apache.org/viewvc/poi/trunk/src/ooxml/java/org/apache/poi/POIXMLFactory.java?rev=1830061&r1=1830060&r2=1830061&view=diff
==============================================================================
--- poi/trunk/src/ooxml/java/org/apache/poi/POIXMLFactory.java (original)
+++ poi/trunk/src/ooxml/java/org/apache/poi/POIXMLFactory.java Wed Apr 25 10:03:39 2018
@@ -60,7 +60,7 @@ public abstract class POIXMLFactory {
                 return createDocumentPart(cls, ORPHAN_PART, new Object[]{part});
             }
         } catch (Exception e) {
-            throw new POIXMLException(e);
+            throw new POIXMLException((e.getCause() != null ? e.getCause() : e).getMessage(), e);
         }
     }
     

Modified: poi/trunk/src/ooxml/java/org/apache/poi/openxml4j/exceptions/NotOfficeXmlFileException.java
URL: http://svn.apache.org/viewvc/poi/trunk/src/ooxml/java/org/apache/poi/openxml4j/exceptions/NotOfficeXmlFileException.java?rev=1830061&r1=1830060&r2=1830061&view=diff
==============================================================================
--- poi/trunk/src/ooxml/java/org/apache/poi/openxml4j/exceptions/NotOfficeXmlFileException.java (original)
+++ poi/trunk/src/ooxml/java/org/apache/poi/openxml4j/exceptions/NotOfficeXmlFileException.java Wed Apr 25 10:03:39 2018
@@ -26,4 +26,8 @@ public class NotOfficeXmlFileException e
     public NotOfficeXmlFileException(String message) {
         super(message);
     }
+
+    public NotOfficeXmlFileException(String message, Throwable cause) {
+        super(message, cause);
+    }
 }

Modified: poi/trunk/src/ooxml/java/org/apache/poi/openxml4j/opc/OPCPackage.java
URL: http://svn.apache.org/viewvc/poi/trunk/src/ooxml/java/org/apache/poi/openxml4j/opc/OPCPackage.java?rev=1830061&r1=1830060&r2=1830061&view=diff
==============================================================================
--- poi/trunk/src/ooxml/java/org/apache/poi/openxml4j/opc/OPCPackage.java (original)
+++ poi/trunk/src/ooxml/java/org/apache/poi/openxml4j/opc/OPCPackage.java Wed Apr 25 10:03:39 2018
@@ -76,12 +76,12 @@ public abstract class OPCPackage impleme
 	/**
 	 * Package access.
 	 */
-	private PackageAccess packageAccess;
+	private final PackageAccess packageAccess;
 
 	/**
 	 * Package parts collection.
 	 */
-	protected PackagePartCollection partList;
+	private PackagePartCollection partList;
 
 	/**
 	 * Package relationships.
@@ -91,17 +91,17 @@ public abstract class OPCPackage impleme
 	/**
 	 * Part marshallers by content type.
 	 */
-	protected Map<ContentType, PartMarshaller> partMarshallers;
+	protected final Map<ContentType, PartMarshaller> partMarshallers = new HashMap<>(5);
 
 	/**
 	 * Default part marshaller.
 	 */
-	protected PartMarshaller defaultPartMarshaller;
+	protected final PartMarshaller defaultPartMarshaller = new DefaultMarshaller();
 
 	/**
 	 * Part unmarshallers by content type.
 	 */
-	protected Map<ContentType, PartUnmarshaller> partUnmarshallers;
+	protected final Map<ContentType, PartUnmarshaller> partUnmarshallers = new HashMap<>(2);
 
 	/**
 	 * Core package properties.
@@ -138,41 +138,27 @@ public abstract class OPCPackage impleme
 		if (getClass() != ZipPackage.class) {
 			throw new IllegalArgumentException("PackageBase may not be subclassed");
 		}
-		init();
 		this.packageAccess = access;
-	}
 
-	/**
-	 * Initialize the package instance.
-	 */
-	private void init() {
-		this.partMarshallers = new HashMap<>(5);
-		this.partUnmarshallers = new HashMap<>(2);
+		final ContentType contentType = newCorePropertiesPart();
+		// TODO Delocalize specialized marshallers
+		this.partUnmarshallers.put(contentType, new PackagePropertiesUnmarshaller());
+		this.partMarshallers.put(contentType, new ZipPackagePropertiesMarshaller());
+	}
 
+	private static ContentType newCorePropertiesPart() {
 		try {
-			// Add 'default' unmarshaller
-			this.partUnmarshallers.put(new ContentType(
-					ContentTypes.CORE_PROPERTIES_PART),
-					new PackagePropertiesUnmarshaller());
-
-			// Add default marshaller
-			this.defaultPartMarshaller = new DefaultMarshaller();
-			// TODO Delocalize specialized marshallers
-			this.partMarshallers.put(new ContentType(
-					ContentTypes.CORE_PROPERTIES_PART),
-					new ZipPackagePropertiesMarshaller());
+			return new ContentType(ContentTypes.CORE_PROPERTIES_PART);
 		} catch (InvalidFormatException e) {
 			// Should never happen
 			throw new OpenXML4JRuntimeException(
-					"Package.init() : this exception should never happen, " +
-					"if you read this message please send a mail to the developers team. : " +
-					e.getMessage(),
-					e
+				"Package.init() : this exception should never happen, " +
+				"if you read this message please send a mail to the developers team. : " +
+				e.getMessage(), e
 			);
 		}
 	}
 
-
 	/**
 	 * Open a package with read/write permission.
 	 *
@@ -625,7 +611,8 @@ public abstract class OPCPackage impleme
 				return null;
 			}
 		}
-		return getPartImpl(partName);
+
+		return partList.get(partName);
 	}
 
 	/**
@@ -737,25 +724,16 @@ public abstract class OPCPackage impleme
 
 			// Check rule M4.1 -> A format consumer shall consider more than
 			// one core properties relationship for a package to be an error
-		   // (We just log it and move on, as real files break this!)
+		    // (We just log it and move on, as real files break this!)
 			boolean hasCorePropertiesPart = false;
 			boolean needCorePropertiesPart = true;
 
-			PackagePart[] parts = this.getPartsImpl();
-			this.partList = new PackagePartCollection();
-			for (PackagePart part : parts) {
-				if (partList.containsKey(part._partName)) {
-					throw new InvalidFormatException(
-							"A part with the name '" +
-							part._partName +
-						        "' already exist : Packages shall not contain equivalent " +
-						        "part names and package implementers shall neither create " +
-					        	"nor recognize packages with equivalent part names. [M1.12]");
-				}
+			partList = getPartsImpl();
+			for (PackagePart part : new ArrayList<>(partList.sortedValues())) {
+			    part.loadRelationships();
 
 				// Check OPC compliance rule M4.1
-				if (part.getContentType().equals(
-						ContentTypes.CORE_PROPERTIES_PART)) {
+				if (ContentTypes.CORE_PROPERTIES_PART.equals(part.getContentType())) {
 					if (!hasCorePropertiesPart) {
 						hasCorePropertiesPart = true;
 					} else {
@@ -768,11 +746,10 @@ public abstract class OPCPackage impleme
 				PartUnmarshaller partUnmarshaller = partUnmarshallers.get(part._contentType);
 
 				if (partUnmarshaller != null) {
-					UnmarshallContext context = new UnmarshallContext(this,
-							part._partName);
+					UnmarshallContext context = new UnmarshallContext(this, part._partName);
 					try {
-						PackagePart unmarshallPart = partUnmarshaller
-								.unmarshall(context, part.getInputStream());
+						PackagePart unmarshallPart = partUnmarshaller.unmarshall(context, part.getInputStream());
+						partList.remove(part.getPartName());
 						partList.put(unmarshallPart._partName, unmarshallPart);
 
 						// Core properties case-- use first CoreProperties part we come across
@@ -790,12 +767,6 @@ public abstract class OPCPackage impleme
 					} catch (InvalidOperationException invoe) {
 						throw new InvalidFormatException(invoe.getMessage(), invoe);
 					}
-				} else {
-					try {
-						partList.put(part._partName, part);
-					} catch (InvalidOperationException e) {
-						throw new InvalidFormatException(e.getMessage(), e);
-					}
 				}
 			}
 		}
@@ -1457,6 +1428,7 @@ public abstract class OPCPackage impleme
         }
 	}
 
+
 	/* Accesseurs */
 
 	/**
@@ -1576,20 +1548,11 @@ public abstract class OPCPackage impleme
 			throws IOException;
 
 	/**
-	 * Get the package part mapped to the specified URI.
-	 *
-	 * @param partName
-	 *            The URI of the part to retrieve.
-	 * @return The package part located by the specified URI, else <b>null</b>.
-	 */
-	protected abstract PackagePart getPartImpl(PackagePartName partName);
-
-	/**
 	 * Get all parts link to the package.
 	 *
 	 * @return A list of the part owned by the package.
 	 */
-	protected abstract PackagePart[] getPartsImpl()
+	protected abstract PackagePartCollection getPartsImpl()
 			throws InvalidFormatException;
 
     /**

Modified: poi/trunk/src/ooxml/java/org/apache/poi/openxml4j/opc/PackagePart.java
URL: http://svn.apache.org/viewvc/poi/trunk/src/ooxml/java/org/apache/poi/openxml4j/opc/PackagePart.java?rev=1830061&r1=1830060&r2=1830061&view=diff
==============================================================================
--- poi/trunk/src/ooxml/java/org/apache/poi/openxml4j/opc/PackagePart.java (original)
+++ poi/trunk/src/ooxml/java/org/apache/poi/openxml4j/opc/PackagePart.java Wed Apr 25 10:03:39 2018
@@ -106,8 +106,9 @@ public abstract class PackagePart implem
 		_isRelationshipPart = this._partName.isRelationshipPartURI();
 
 		// Load relationships if any
-		if (loadRelationships)
+		if (loadRelationships) {
 			loadRelationships();
+		}
 	}
 
 	/**
@@ -558,7 +559,7 @@ public abstract class PackagePart implem
 	 * @throws InvalidFormatException
 	 *             Throws if
 	 */
-	private void loadRelationships() throws InvalidFormatException {
+	/* package */ void loadRelationships() throws InvalidFormatException {
 		if (this._relationships == null && !this._isRelationshipPart) {
 			this.throwExceptionIfRelationship();
 			_relationships = new PackageRelationshipCollection(this);

Modified: poi/trunk/src/ooxml/java/org/apache/poi/openxml4j/opc/PackageRelationshipCollection.java
URL: http://svn.apache.org/viewvc/poi/trunk/src/ooxml/java/org/apache/poi/openxml4j/opc/PackageRelationshipCollection.java?rev=1830061&r1=1830060&r2=1830061&view=diff
==============================================================================
--- poi/trunk/src/ooxml/java/org/apache/poi/openxml4j/opc/PackageRelationshipCollection.java (original)
+++ poi/trunk/src/ooxml/java/org/apache/poi/openxml4j/opc/PackageRelationshipCollection.java Wed Apr 25 10:03:39 2018
@@ -45,12 +45,12 @@ public final class PackageRelationshipCo
     /**
      * Package relationships ordered by ID.
      */
-    private TreeMap<String, PackageRelationship> relationshipsByID;
+    private final TreeMap<String, PackageRelationship> relationshipsByID = new TreeMap<>();
 
     /**
      * Package relationships ordered by type.
      */
-    private TreeMap<String, PackageRelationship> relationshipsByType;
+    private final TreeMap<String, PackageRelationship> relationshipsByType = new TreeMap<>();
 
     /**
      * A lookup of internal relationships to avoid
@@ -88,8 +88,6 @@ public final class PackageRelationshipCo
      * Constructor.
      */
     PackageRelationshipCollection() {
-        relationshipsByID = new TreeMap<>();
-        relationshipsByType = new TreeMap<>();
     }
 
     /**
@@ -149,8 +147,6 @@ public final class PackageRelationshipCo
      */
     public PackageRelationshipCollection(OPCPackage container, PackagePart part)
             throws InvalidFormatException {
-        this();
-
         if (container == null)
             throw new IllegalArgumentException("container needs to be specified");
 

Modified: poi/trunk/src/ooxml/java/org/apache/poi/openxml4j/opc/ZipPackage.java
URL: http://svn.apache.org/viewvc/poi/trunk/src/ooxml/java/org/apache/poi/openxml4j/opc/ZipPackage.java?rev=1830061&r1=1830060&r2=1830061&view=diff
==============================================================================
--- poi/trunk/src/ooxml/java/org/apache/poi/openxml4j/opc/ZipPackage.java (original)
+++ poi/trunk/src/ooxml/java/org/apache/poi/openxml4j/opc/ZipPackage.java Wed Apr 25 10:03:39 2018
@@ -17,17 +17,23 @@
 
 package org.apache.poi.openxml4j.opc;
 
+import static org.apache.poi.openxml4j.opc.ContentTypes.RELATIONSHIPS_PART;
+import static org.apache.poi.openxml4j.opc.internal.ContentTypeManager.CONTENT_TYPES_PART_NAME;
+
 import java.io.File;
 import java.io.FileInputStream;
 import java.io.FileNotFoundException;
 import java.io.IOException;
 import java.io.InputStream;
 import java.io.OutputStream;
-import java.util.Enumeration;
+import java.util.Collections;
+import java.util.List;
+import java.util.stream.Collectors;
 import java.util.zip.ZipEntry;
 import java.util.zip.ZipFile;
 import java.util.zip.ZipOutputStream;
 
+import org.apache.poi.UnsupportedFileFormatException;
 import org.apache.poi.openxml4j.exceptions.InvalidFormatException;
 import org.apache.poi.openxml4j.exceptions.InvalidOperationException;
 import org.apache.poi.openxml4j.exceptions.NotOfficeXmlFileException;
@@ -41,10 +47,10 @@ import org.apache.poi.openxml4j.opc.inte
 import org.apache.poi.openxml4j.opc.internal.ZipContentTypeManager;
 import org.apache.poi.openxml4j.opc.internal.ZipHelper;
 import org.apache.poi.openxml4j.opc.internal.marshallers.ZipPartMarshaller;
+import org.apache.poi.openxml4j.util.ZipArchiveThresholdInputStream;
 import org.apache.poi.openxml4j.util.ZipEntrySource;
 import org.apache.poi.openxml4j.util.ZipFileZipEntrySource;
 import org.apache.poi.openxml4j.util.ZipInputStreamZipEntrySource;
-import org.apache.poi.openxml4j.util.ZipSecureFile.ThresholdInputStream;
 import org.apache.poi.util.IOUtils;
 import org.apache.poi.util.POILogFactory;
 import org.apache.poi.util.POILogger;
@@ -95,12 +101,12 @@ public final class ZipPackage extends OP
      */
     ZipPackage(InputStream in, PackageAccess access) throws IOException {
         super(access);
-        ThresholdInputStream zis = ZipHelper.openZipStream(in);
+        ZipArchiveThresholdInputStream zis = ZipHelper.openZipStream(in);
         try {
             this.zipArchive = new ZipInputStreamZipEntrySource(zis);
         } catch (final IOException e) {
             IOUtils.closeQuietly(zis);
-            throw new IOException("Failed to read zip entry source", e);
+            throw e;
         }
     }
 
@@ -138,6 +144,9 @@ public final class ZipPackage extends OP
             if (access == PackageAccess.WRITE) {
                 throw new InvalidOperationException("Can't open the specified file: '" + file + "'", e);
             }
+            if ("java.util.zip.ZipException: archive is not a ZIP archive".equals(e.getMessage())) {
+                throw new NotOfficeXmlFileException("archive is not a ZIP archive", e);
+            }
             LOG.log(POILogger.ERROR, "Error in zip file "+file+" - falling back to stream processing (i.e. ignoring zip central directory)");
             ze = openZipEntrySourceStream(file);
         }
@@ -159,19 +168,19 @@ public final class ZipPackage extends OP
         try {
             // read from the file input stream
             return openZipEntrySourceStream(fis);
+        } catch (final InvalidOperationException|UnsupportedFileFormatException e) {
+            // abort: close the zip input stream
+            IOUtils.closeQuietly(fis);
+            throw e;
         } catch (final Exception e) {
             // abort: close the file input stream
             IOUtils.closeQuietly(fis);
-            if (e instanceof InvalidOperationException) {
-                throw (InvalidOperationException)e;
-            } else {
-                throw new InvalidOperationException("Failed to read the file input stream from file: '" + file + "'", e);
-            }
+            throw new InvalidOperationException("Failed to read the file input stream from file: '" + file + "'", e);
         }
     }
     
     private static ZipEntrySource openZipEntrySourceStream(FileInputStream fis) throws InvalidOperationException {
-        final ThresholdInputStream zis;
+        final ZipArchiveThresholdInputStream zis;
         // Acquire a resource that is needed to read the next level of openZipEntrySourceStream
         try {
             // open the zip input stream
@@ -185,18 +194,18 @@ public final class ZipPackage extends OP
         try {
             // read from the zip input stream
             return openZipEntrySourceStream(zis);
+        } catch (final InvalidOperationException|UnsupportedFileFormatException e) {
+            // abort: close the zip input stream
+            IOUtils.closeQuietly(zis);
+            throw e;
         } catch (final Exception e) {
             // abort: close the zip input stream
             IOUtils.closeQuietly(zis);
-            if (e instanceof InvalidOperationException) {
-                throw (InvalidOperationException)e;
-            } else {
-                throw new InvalidOperationException("Failed to read the zip entry source stream", e);
-            }
+            throw new InvalidOperationException("Failed to read the zip entry source stream", e);
         }
     }
     
-    private static ZipEntrySource openZipEntrySourceStream(ThresholdInputStream zis) throws InvalidOperationException {
+    private static ZipEntrySource openZipEntrySourceStream(ZipArchiveThresholdInputStream zis) throws InvalidOperationException {
         // Acquire the final level resource. If this is acquired successfully, the zip package was read successfully from the input stream
         try {
             // open the zip entry source stream
@@ -224,149 +233,115 @@ public final class ZipPackage extends OP
     /**
      * Retrieves the parts from this package. We assume that the package has not
      * been yet inspect to retrieve all the parts, this method will open the
-     * archive and look for all parts contain inside it. If the package part
-     * list is not empty, it will be emptied.
+     * archive and look for all parts contain inside it.
      *
      * @return All parts contain in this package.
      * @throws InvalidFormatException if the package is not valid.
      */
     @Override
-    protected PackagePart[] getPartsImpl() throws InvalidFormatException {
-        if (this.partList == null) {
-            // The package has just been created, we create an empty part
-            // list.
-            this.partList = new PackagePartCollection();
-        }
+    protected PackagePartCollection getPartsImpl() throws InvalidFormatException {
+        final PackagePartCollection newPartList = new PackagePartCollection();
 
-        if (this.zipArchive == null) {
-            return this.partList.sortedValues().toArray(
-                    new PackagePart[this.partList.size()]);
+        if (zipArchive == null) {
+            return newPartList;
         }
 
         // First we need to parse the content type part
-        Enumeration<? extends ZipEntry> entries = this.zipArchive.getEntries();
-        while (entries.hasMoreElements()) {
-            ZipEntry entry = entries.nextElement();
-            if (entry.getName().equalsIgnoreCase(
-                    ContentTypeManager.CONTENT_TYPES_PART_NAME)) {
-                try {
-                    this.contentTypeManager = new ZipContentTypeManager(
-                            getZipArchive().getInputStream(entry), this);
-                } catch (IOException e) {
-                    throw new InvalidFormatException(e.getMessage(), e);
-                }
-                break;
+        final ZipEntry contentTypeEntry =
+                zipArchive.getEntry(CONTENT_TYPES_PART_NAME);
+        if (contentTypeEntry != null) {
+            try {
+                this.contentTypeManager = new ZipContentTypeManager(
+                        zipArchive.getInputStream(contentTypeEntry), this);
+            } catch (IOException e) {
+                throw new InvalidFormatException(e.getMessage(), e);
             }
-        }
-
-        // At this point, we should have loaded the content type part
-        if (this.contentTypeManager == null) {
+        } else {
             // Is it a different Zip-based format?
-            int numEntries = 0;
-            boolean hasMimetype = false;
-            boolean hasSettingsXML = false;
-            entries = this.zipArchive.getEntries();
-            while (entries.hasMoreElements()) {
-                final ZipEntry entry = entries.nextElement();
-                final String name = entry.getName();
-                if (MIMETYPE.equals(name)) {
-                    hasMimetype = true;
-                }
-                if (SETTINGS_XML.equals(name)) {
-                    hasSettingsXML = true;
-                }
-                numEntries++;
-            }
+            final boolean hasMimetype = zipArchive.getEntry(MIMETYPE) != null;
+            final boolean hasSettingsXML = zipArchive.getEntry(SETTINGS_XML) != null;
             if (hasMimetype && hasSettingsXML) {
                 throw new ODFNotOfficeXmlFileException(
-                   "The supplied data appears to be in ODF (Open Document) Format. " +
-                   "Formats like these (eg ODS, ODP) are not supported, try Apache ODFToolkit");
+                        "The supplied data appears to be in ODF (Open Document) Format. " +
+                                "Formats like these (eg ODS, ODP) are not supported, try Apache ODFToolkit");
             }
-            if (numEntries == 0) {
+            if (!zipArchive.getEntries().hasMoreElements()) {
                 throw new NotOfficeXmlFileException(
-                   "No valid entries or contents found, this is not a valid OOXML " +
-                   "(Office Open XML) file");
+                        "No valid entries or contents found, this is not a valid OOXML " +
+                                "(Office Open XML) file");
             }
-
             // Fallback exception
             throw new InvalidFormatException(
-                "Package should contain a content type part [M1.13]");
+                    "Package should contain a content type part [M1.13]");
         }
 
         // Now create all the relationships
         // (Need to create relationships before other
         //  parts, otherwise we might create a part before
         //  its relationship exists, and then it won't tie up)
-        entries = this.zipArchive.getEntries();
-        while (entries.hasMoreElements()) {
-            ZipEntry entry = entries.nextElement();
-            PackagePartName partName = buildPartName(entry);
-            if(partName == null) {
-                continue;
-            }
+        final List<EntryTriple> entries =
+                Collections.list(zipArchive.getEntries()).stream()
+                        .map(zae -> new EntryTriple(zae, contentTypeManager))
+                        .filter(mm -> mm.partName != null)
+                        .sorted()
+                        .collect(Collectors.toList());
 
-            // Only proceed for Relationships at this stage
-            String contentType = contentTypeManager.getContentType(partName);
-            if (contentType != null && contentType.equals(ContentTypes.RELATIONSHIPS_PART)) {
-                try {
-                    PackagePart part = new ZipPackagePart(this, entry, partName, contentType);
-                    partList.put(partName, part);
-                } catch (InvalidOperationException e) {
-                    throw new InvalidFormatException(e.getMessage(), e);
-                }
-            }
+        for (final EntryTriple et : entries) {
+            et.register(newPartList);
         }
 
-        // Then we can go through all the other parts
-        entries = this.zipArchive.getEntries();
-        while (entries.hasMoreElements()) {
-            ZipEntry entry = entries.nextElement();
-            PackagePartName partName = buildPartName(entry);
-            if(partName == null) {
-                continue;
+        return newPartList;
+    }
+
+    private class EntryTriple implements Comparable<EntryTriple> {
+        final ZipEntry zipArchiveEntry;
+        final PackagePartName partName;
+        final String contentType;
+
+        EntryTriple(final ZipEntry zipArchiveEntry, final ContentTypeManager contentTypeManager) {
+            this.zipArchiveEntry = zipArchiveEntry;
+
+            final String entryName = zipArchiveEntry.getName();
+            PackagePartName ppn = null;
+            try {
+                // We get an error when we parse [Content_Types].xml
+                // because it's not a valid URI.
+                ppn = (CONTENT_TYPES_PART_NAME.equalsIgnoreCase(entryName)) ? null
+                    : PackagingURIHelper.createPartName(ZipHelper.getOPCNameFromZipItemName(entryName));
+            } catch (Exception e) {
+                // We assume we can continue, even in degraded mode ...
+                LOG.log(POILogger.WARN,"Entry " + entryName + " is not valid, so this part won't be add to the package.", e);
             }
 
-            String contentType = contentTypeManager.getContentType(partName);
-            if (contentType != null && contentType.equals(ContentTypes.RELATIONSHIPS_PART)) {
-                // Already handled
+            this.partName = ppn;
+            this.contentType = (ppn == null) ? null : contentTypeManager.getContentType(partName);
+        }
+
+        void register(final PackagePartCollection partList) throws InvalidFormatException {
+            if (contentType == null) {
+                throw new InvalidFormatException("The part " + partName.getURI().getPath() + " does not have any " +
+                        "content type ! Rule: Package require content types when retrieving a part from a package. [M.1.14]");
             }
-            else if (contentType != null) {
-                try {
-                    PackagePart part = new ZipPackagePart(this, entry, partName, contentType);
-                    partList.put(partName, part);
-                } catch (InvalidOperationException e) {
-                    throw new InvalidFormatException(e.getMessage(), e);
-                }
-            } else {
+
+            if (partList.containsKey(partName)) {
                 throw new InvalidFormatException(
-                    "The part " + partName.getURI().getPath()
-                    + " does not have any content type ! Rule: Package require content types when retrieving a part from a package. [M.1.14]");
+                    "A part with the name '"+partName+"' already exist : Packages shall not contain equivalent part names " +
+                    "and package implementers shall neither create nor recognize packages with equivalent part names. [M1.12]");
             }
-        }
-
-        return partList.sortedValues().toArray(new PackagePart[partList.size()]);
-    }
 
-    /**
-     * Builds a PackagePartName for the given ZipEntry,
-     *  or null if it's the content types / invalid part
-     */
-    private PackagePartName buildPartName(ZipEntry entry) {
-        try {
-            // We get an error when we parse [Content_Types].xml
-            // because it's not a valid URI.
-            if (entry.getName().equalsIgnoreCase(
-                    ContentTypeManager.CONTENT_TYPES_PART_NAME)) {
-                return null;
+            try {
+                partList.put(partName, new ZipPackagePart(ZipPackage.this, zipArchiveEntry, partName, contentType, false));
+            } catch (InvalidOperationException e) {
+                throw new InvalidFormatException(e.getMessage(), e);
             }
-            return PackagingURIHelper.createPartName(ZipHelper
-                    .getOPCNameFromZipItemName(entry.getName()));
-        } catch (Exception e) {
-            // We assume we can continue, even in degraded mode ...
-            LOG.log(POILogger.WARN,"Entry "
-                                      + entry.getName()
-                                      + " is not valid, so this part won't be add to the package.", e);
-            return null;
+        }
+
+        @Override
+        public int compareTo(EntryTriple o) {
+            final int contentTypeOrder1 = RELATIONSHIPS_PART.equals(contentType) ? -1 : 1;
+            final int contentTypeOrder2 = RELATIONSHIPS_PART.equals(o.contentType) ? -1 : 1;
+            final int cmpCT = Integer.compare(contentTypeOrder1, contentTypeOrder2);
+            return cmpCT != 0 ? cmpCT : partName.compareTo(o.partName);
         }
     }
 
@@ -494,21 +469,6 @@ public final class ZipPackage extends OP
 		}
 	}
 
-    /**
-     * Implement the getPart() method to retrieve a part from its URI in the
-     * current package
-     *
-     *
-     * @see #getPart(PackageRelationship)
-     */
-    @Override
-    protected PackagePart getPartImpl(PackagePartName partName) {
-        if (partList.containsKey(partName)) {
-            return partList.get(partName);
-        }
-        return null;
-    }
-
 	/**
 	 * Save this package into the specified stream
 	 *
@@ -523,13 +483,8 @@ public final class ZipPackage extends OP
 		// Check that the document was open in write mode
 		throwExceptionIfReadOnly();
 
-		final ZipOutputStream zos;
-		try {
-			if (!(outputStream instanceof ZipOutputStream)) {
-                zos = new ZipOutputStream(outputStream);
-            } else {
-                zos = (ZipOutputStream) outputStream;
-            }
+		try (final ZipOutputStream zos = (outputStream instanceof ZipOutputStream)
+                ? (ZipOutputStream) outputStream : new ZipOutputStream(outputStream)) {
 
 			// If the core properties part does not exist in the part list,
 			// we save it as well
@@ -574,20 +529,14 @@ public final class ZipPackage extends OP
 
 				final PackagePartName ppn = part.getPartName();
 				LOG.log(POILogger.DEBUG,"Save part '" + ZipHelper.getZipItemNameFromOPCName(ppn.getName()) + "'");
-				PartMarshaller marshaller = partMarshallers.get(part._contentType);
-				String errMsg = "The part " + ppn.getURI() + " failed to be saved in the stream with marshaller ";
+				final PartMarshaller marshaller = partMarshallers.get(part._contentType);
 
-				if (marshaller != null) {
-					if (!marshaller.marshall(part, zos)) {
-						throw new OpenXML4JException(errMsg + marshaller);
-					}
-				} else {
-					if (!defaultPartMarshaller.marshall(part, zos)) {
-                        throw new OpenXML4JException(errMsg + defaultPartMarshaller);
-                    }
-				}
+				final PartMarshaller pm = (marshaller != null) ? marshaller : defaultPartMarshaller;
+                if (!pm.marshall(part, zos)) {
+                    String errMsg = "The part " + ppn.getURI() + " failed to be saved in the stream with marshaller ";
+                    throw new OpenXML4JException(errMsg + pm);
+                }
 			}
-			zos.close();
 		} catch (OpenXML4JRuntimeException e) {
 			// no need to wrap this type of Exception
 			throw e;

Modified: poi/trunk/src/ooxml/java/org/apache/poi/openxml4j/opc/ZipPackagePart.java
URL: http://svn.apache.org/viewvc/poi/trunk/src/ooxml/java/org/apache/poi/openxml4j/opc/ZipPackagePart.java?rev=1830061&r1=1830060&r2=1830061&view=diff
==============================================================================
--- poi/trunk/src/ooxml/java/org/apache/poi/openxml4j/opc/ZipPackagePart.java (original)
+++ poi/trunk/src/ooxml/java/org/apache/poi/openxml4j/opc/ZipPackagePart.java Wed Apr 25 10:03:39 2018
@@ -25,6 +25,7 @@ import java.util.zip.ZipEntry;
 import org.apache.poi.openxml4j.exceptions.InvalidFormatException;
 import org.apache.poi.openxml4j.exceptions.InvalidOperationException;
 import org.apache.poi.openxml4j.exceptions.OpenXML4JException;
+import org.apache.poi.openxml4j.opc.internal.ContentType;
 import org.apache.poi.openxml4j.opc.internal.marshallers.ZipPartMarshaller;
 import org.apache.poi.util.NotImplemented;
 
@@ -48,11 +49,11 @@ public class ZipPackagePart extends Pack
 	 * @param container
 	 *            The container package.
 	 * @param partName
-	 *            Part name.
+	 *            The part name.
 	 * @param contentType
 	 *            Content type.
 	 * @throws InvalidFormatException
-	 *             Throws if the content of this part invalid.
+	 *             Throws if the content of this part is invalid.
 	 */
 	public ZipPackagePart(OPCPackage container, PackagePartName partName,
 			String contentType) throws InvalidFormatException {
@@ -73,10 +74,10 @@ public class ZipPackagePart extends Pack
 	 * @throws InvalidFormatException
 	 *             Throws if the content of this part is invalid.
 	 */
-	public ZipPackagePart(OPCPackage container, ZipEntry zipEntry,
-			PackagePartName partName, String contentType)
+	/* package */ ZipPackagePart(OPCPackage container, ZipEntry zipEntry,
+						  PackagePartName partName, String contentType, boolean loadRelationships)
 			throws InvalidFormatException {
-		super(container, partName, contentType);
+		super(container, partName, new ContentType(contentType), loadRelationships);
 		this.zipEntry = zipEntry;
 	}
 

Modified: poi/trunk/src/ooxml/java/org/apache/poi/openxml4j/opc/internal/ZipContentTypeManager.java
URL: http://svn.apache.org/viewvc/poi/trunk/src/ooxml/java/org/apache/poi/openxml4j/opc/internal/ZipContentTypeManager.java?rev=1830061&r1=1830060&r2=1830061&view=diff
==============================================================================
--- poi/trunk/src/ooxml/java/org/apache/poi/openxml4j/opc/internal/ZipContentTypeManager.java (original)
+++ poi/trunk/src/ooxml/java/org/apache/poi/openxml4j/opc/internal/ZipContentTypeManager.java Wed Apr 25 10:03:39 2018
@@ -57,26 +57,23 @@ public class ZipContentTypeManager exten
 	@SuppressWarnings("resource")
     @Override
 	public boolean saveImpl(Document content, OutputStream out) {
-		ZipOutputStream zos = null;
-		if (out instanceof ZipOutputStream)
-			zos = (ZipOutputStream) out;
-		else
-			zos = new ZipOutputStream(out);
+		final ZipOutputStream zos = (out instanceof ZipOutputStream)
+				? (ZipOutputStream) out : new ZipOutputStream(out);
 
 		ZipEntry partEntry = new ZipEntry(CONTENT_TYPES_PART_NAME);
 		try {
 			// Referenced in ZIP
 			zos.putNextEntry(partEntry);
-			// Saving data in the ZIP file
-			if (!StreamHelper.saveXmlInStream(content, zos)) {
-			    return false;
+			try {
+				// Saving data in the ZIP file
+				return StreamHelper.saveXmlInStream(content, zos);
+			} finally {
+				zos.closeEntry();
 			}
-			zos.closeEntry();
 		} catch (IOException ioe) {
 			logger.log(POILogger.ERROR, "Cannot write: " + CONTENT_TYPES_PART_NAME
 					+ " in Zip !", ioe);
 			return false;
 		}
-		return true;
 	}
 }

Modified: poi/trunk/src/ooxml/java/org/apache/poi/openxml4j/opc/internal/ZipHelper.java
URL: http://svn.apache.org/viewvc/poi/trunk/src/ooxml/java/org/apache/poi/openxml4j/opc/internal/ZipHelper.java?rev=1830061&r1=1830060&r2=1830061&view=diff
==============================================================================
--- poi/trunk/src/ooxml/java/org/apache/poi/openxml4j/opc/internal/ZipHelper.java (original)
+++ poi/trunk/src/ooxml/java/org/apache/poi/openxml4j/opc/internal/ZipHelper.java Wed Apr 25 10:03:39 2018
@@ -24,9 +24,7 @@ import java.io.IOException;
 import java.io.InputStream;
 import java.net.URI;
 import java.net.URISyntaxException;
-import java.util.Enumeration;
 import java.util.zip.ZipEntry;
-import java.util.zip.ZipFile;
 import java.util.zip.ZipInputStream;
 
 import org.apache.poi.openxml4j.exceptions.NotOfficeXmlFileException;
@@ -34,8 +32,8 @@ import org.apache.poi.openxml4j.exceptio
 import org.apache.poi.openxml4j.opc.PackageRelationship;
 import org.apache.poi.openxml4j.opc.PackageRelationshipTypes;
 import org.apache.poi.openxml4j.opc.ZipPackage;
+import org.apache.poi.openxml4j.util.ZipArchiveThresholdInputStream;
 import org.apache.poi.openxml4j.util.ZipSecureFile;
-import org.apache.poi.openxml4j.util.ZipSecureFile.ThresholdInputStream;
 import org.apache.poi.poifs.filesystem.FileMagic;
 import org.apache.poi.util.Internal;
 
@@ -73,24 +71,6 @@ public final class ZipHelper {
     }
 
     /**
-     * Retrieve the Zip entry of the content types part.
-     */
-    public static ZipEntry getContentTypeZipEntry(ZipPackage pkg) {
-        Enumeration<? extends ZipEntry> entries = pkg.getZipArchive().getEntries();
-
-        // Enumerate through the Zip entries until we find the one named
-        // '[Content_Types].xml'.
-        while (entries.hasMoreElements()) {
-            ZipEntry entry = entries.nextElement();
-            if (entry.getName().equals(
-                    ContentTypeManager.CONTENT_TYPES_PART_NAME)) {
-                return entry;
-            }
-        }
-        return null;
-    }
-
-    /**
      * Convert a zip name into an OPC name by adding a leading forward slash to
      * the specified item name.
      *
@@ -158,7 +138,7 @@ public final class ZipHelper {
      * Warning - this will consume the first few bytes of the stream,
      *  you should push-back or reset the stream after use!
      */
-    public static void verifyZipHeader(InputStream stream) throws NotOfficeXmlFileException, IOException {
+    private static void verifyZipHeader(InputStream stream) throws NotOfficeXmlFileException, IOException {
         InputStream is = FileMagic.prepareToCheckMagic(stream);
         FileMagic fm = FileMagic.valueOf(is);
 
@@ -191,14 +171,14 @@ public final class ZipHelper {
      * @return The zip stream freshly open.
      */
     @SuppressWarnings("resource")
-    public static ThresholdInputStream openZipStream(InputStream stream) throws IOException {
+    public static ZipArchiveThresholdInputStream openZipStream(InputStream stream) throws IOException {
         // Peek at the first few bytes to sanity check
         InputStream checkedStream = FileMagic.prepareToCheckMagic(stream);
         verifyZipHeader(checkedStream);
         
         // Open as a proper zip stream
         InputStream zis = new ZipInputStream(checkedStream);
-        return ZipSecureFile.addThreshold(zis);
+        return new ZipArchiveThresholdInputStream(zis);
     }
 
     /**
@@ -211,7 +191,7 @@ public final class ZipHelper {
      * @throws IOException if the zip file cannot be opened or closed to read the header signature
      * @throws NotOfficeXmlFileException if stream does not start with zip header signature
      */
-    public static ZipFile openZipFile(File file) throws IOException, NotOfficeXmlFileException {
+    public static ZipSecureFile openZipFile(File file) throws IOException, NotOfficeXmlFileException {
         if (!file.exists()) {
             throw new FileNotFoundException("File does not exist");
         }
@@ -235,7 +215,7 @@ public final class ZipHelper {
      *            The file path.
      * @return The zip archive freshly open.
      */
-    public static ZipFile openZipFile(String path) throws IOException {
+    public static ZipSecureFile openZipFile(String path) throws IOException {
         return openZipFile(new File(path));
     }
 }

Modified: poi/trunk/src/ooxml/java/org/apache/poi/openxml4j/opc/internal/marshallers/ZipPackagePropertiesMarshaller.java
URL: http://svn.apache.org/viewvc/poi/trunk/src/ooxml/java/org/apache/poi/openxml4j/opc/internal/marshallers/ZipPackagePropertiesMarshaller.java?rev=1830061&r1=1830060&r2=1830061&view=diff
==============================================================================
--- poi/trunk/src/ooxml/java/org/apache/poi/openxml4j/opc/internal/marshallers/ZipPackagePropertiesMarshaller.java (original)
+++ poi/trunk/src/ooxml/java/org/apache/poi/openxml4j/opc/internal/marshallers/ZipPackagePropertiesMarshaller.java Wed Apr 25 10:03:39 2018
@@ -49,15 +49,15 @@ public final class ZipPackagePropertiesM
 		try {
 			// Save in ZIP
 			zos.putNextEntry(ctEntry); // Add entry in ZIP
-			super.marshall(part, out); // Marshall the properties inside a XML
-			// Document
-			if (!StreamHelper.saveXmlInStream(xmlDoc, out)) {
-				return false;
+			try {
+				super.marshall(part, out); // Marshall the properties inside a XML
+				// Document
+				return StreamHelper.saveXmlInStream(xmlDoc, out);
+			} finally {
+				zos.closeEntry();
 			}
-			zos.closeEntry();
 		} catch (IOException e) {
 			throw new OpenXML4JException(e.getLocalizedMessage(), e);
 		}
-		return true;
 	}
 }

Modified: poi/trunk/src/ooxml/java/org/apache/poi/openxml4j/opc/internal/marshallers/ZipPartMarshaller.java
URL: http://svn.apache.org/viewvc/poi/trunk/src/ooxml/java/org/apache/poi/openxml4j/opc/internal/marshallers/ZipPartMarshaller.java?rev=1830061&r1=1830060&r2=1830061&view=diff
==============================================================================
--- poi/trunk/src/ooxml/java/org/apache/poi/openxml4j/opc/internal/marshallers/ZipPartMarshaller.java (original)
+++ poi/trunk/src/ooxml/java/org/apache/poi/openxml4j/opc/internal/marshallers/ZipPartMarshaller.java Wed Apr 25 10:03:39 2018
@@ -36,6 +36,7 @@ import org.apache.poi.openxml4j.opc.Targ
 import org.apache.poi.openxml4j.opc.internal.PartMarshaller;
 import org.apache.poi.openxml4j.opc.internal.ZipHelper;
 import org.apache.poi.util.DocumentHelper;
+import org.apache.poi.util.IOUtils;
 import org.apache.poi.util.POILogFactory;
 import org.apache.poi.util.POILogger;
 import org.apache.poi.xssf.usermodel.XSSFRelation;
@@ -47,7 +48,6 @@ import org.w3c.dom.Element;
  */
 public final class ZipPartMarshaller implements PartMarshaller {
 	private final static POILogger logger = POILogFactory.getLogger(ZipPartMarshaller.class);
-	private final static int READ_WRITE_FILE_BUFFER_SIZE = 8192;
 
 	/**
 	 * Save the specified part.
@@ -80,17 +80,11 @@ public final class ZipPartMarshaller imp
 			zos.putNextEntry(partEntry);
 
 			// Saving data in the ZIP file
-			InputStream ins = part.getInputStream();
-			byte[] buff = new byte[READ_WRITE_FILE_BUFFER_SIZE];
-			while (ins.available() > 0) {
-				int resultRead = ins.read(buff);
-				if (resultRead == -1) {
-					// End of file reached
-					break;
-				}
-				zos.write(buff, 0, resultRead);
+			try (final InputStream ins = part.getInputStream()) {
+				IOUtils.copy(ins, zos);
+			} finally {
+				zos.closeEntry();
 			}
-			zos.closeEntry();
 		} catch (IOException ioe) {
 			logger.log(POILogger.ERROR,"Cannot write: " + part.getPartName() + ": in ZIP",
 					ioe);
@@ -178,14 +172,14 @@ public final class ZipPartMarshaller imp
 				relPartName.getURI().toASCIIString()).getPath());
 		try {
 			zos.putNextEntry(ctEntry);
-			if (!StreamHelper.saveXmlInStream(xmlOutDoc, zos)) {
-				return false;
+			try {
+				return StreamHelper.saveXmlInStream(xmlOutDoc, zos);
+			} finally {
+				zos.closeEntry();
 			}
-			zos.closeEntry();
 		} catch (IOException e) {
 			logger.log(POILogger.ERROR,"Cannot create zip entry " + relPartName, e);
 			return false;
 		}
-		return true; // success
 	}
 }

Added: poi/trunk/src/ooxml/java/org/apache/poi/openxml4j/util/ZipArchiveFakeEntry.java
URL: http://svn.apache.org/viewvc/poi/trunk/src/ooxml/java/org/apache/poi/openxml4j/util/ZipArchiveFakeEntry.java?rev=1830061&view=auto
==============================================================================
--- poi/trunk/src/ooxml/java/org/apache/poi/openxml4j/util/ZipArchiveFakeEntry.java (added)
+++ poi/trunk/src/ooxml/java/org/apache/poi/openxml4j/util/ZipArchiveFakeEntry.java Wed Apr 25 10:03:39 2018
@@ -0,0 +1,53 @@
+/* ====================================================================
+   Licensed to the Apache Software Foundation (ASF) under one or more
+   contributor license agreements.  See the NOTICE file distributed with
+   this work for additional information regarding copyright ownership.
+   The ASF licenses this file to You under the Apache License, Version 2.0
+   (the "License"); you may not use this file except in compliance with
+   the License.  You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+   Unless required by applicable law or agreed to in writing, software
+   distributed under the License is distributed on an "AS IS" BASIS,
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+   See the License for the specific language governing permissions and
+   limitations under the License.
+==================================================================== */
+
+package org.apache.poi.openxml4j.util;
+
+import java.io.ByteArrayInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.zip.ZipEntry;
+
+import org.apache.poi.util.IOUtils;
+
+
+/**
+ * So we can close the real zip entry and still
+ *  effectively work with it.
+ * Holds the (decompressed!) data in memory, so
+ *  close this as soon as you can!
+ */
+/* package */ class ZipArchiveFakeEntry extends ZipEntry {
+    private final byte[] data;
+
+    ZipArchiveFakeEntry(ZipEntry entry, InputStream inp) throws IOException {
+        super(entry.getName());
+
+        final long entrySize = entry.getSize();
+
+        if (entrySize < -1 || entrySize>=Integer.MAX_VALUE) {
+            throw new IOException("ZIP entry size is too large or invalid");
+        }
+
+        // Grab the de-compressed contents for later
+        data = (entrySize == -1) ? IOUtils.toByteArray(inp) : IOUtils.toByteArray(inp, (int)entrySize);
+    }
+
+    public InputStream getInputStream() {
+        return new ByteArrayInputStream(data);
+    }
+}

Propchange: poi/trunk/src/ooxml/java/org/apache/poi/openxml4j/util/ZipArchiveFakeEntry.java
------------------------------------------------------------------------------
    svn:eol-style = native

Added: poi/trunk/src/ooxml/java/org/apache/poi/openxml4j/util/ZipArchiveThresholdInputStream.java
URL: http://svn.apache.org/viewvc/poi/trunk/src/ooxml/java/org/apache/poi/openxml4j/util/ZipArchiveThresholdInputStream.java?rev=1830061&view=auto
==============================================================================
--- poi/trunk/src/ooxml/java/org/apache/poi/openxml4j/util/ZipArchiveThresholdInputStream.java (added)
+++ poi/trunk/src/ooxml/java/org/apache/poi/openxml4j/util/ZipArchiveThresholdInputStream.java Wed Apr 25 10:03:39 2018
@@ -0,0 +1,243 @@
+/* ====================================================================
+   Licensed to the Apache Software Foundation (ASF) under one or more
+   contributor license agreements.  See the NOTICE file distributed with
+   this work for additional information regarding copyright ownership.
+   The ASF licenses this file to You under the Apache License, Version 2.0
+   (the "License"); you may not use this file except in compliance with
+   the License.  You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+   Unless required by applicable law or agreed to in writing, software
+   distributed under the License is distributed on an "AS IS" BASIS,
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+   See the License for the specific language governing permissions and
+   limitations under the License.
+==================================================================== */
+
+package org.apache.poi.openxml4j.util;
+
+import static org.apache.poi.openxml4j.util.ZipSecureFile.MAX_ENTRY_SIZE;
+import static org.apache.poi.openxml4j.util.ZipSecureFile.MIN_INFLATE_RATIO;
+
+import java.io.FilterInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.PushbackInputStream;
+import java.lang.reflect.Field;
+import java.security.AccessController;
+import java.security.PrivilegedAction;
+import java.util.Locale;
+import java.util.zip.InflaterInputStream;
+import java.util.zip.ZipEntry;
+import java.util.zip.ZipInputStream;
+
+import org.apache.poi.util.POILogFactory;
+import org.apache.poi.util.POILogger;
+import org.apache.poi.util.SuppressForbidden;
+
+public class ZipArchiveThresholdInputStream extends PushbackInputStream {
+    private static final POILogger LOG = POILogFactory.getLogger(ZipArchiveThresholdInputStream.class);
+
+    // don't alert for expanded sizes smaller than 100k
+    private static final long GRACE_ENTRY_SIZE = 100*1024L;
+
+    private static final String MAX_ENTRY_SIZE_MSG =
+        "Zip bomb detected! The file would exceed the max size of the expanded data in the zip-file.\n" +
+        "This may indicates that the file is used to inflate memory usage and thus could pose a security risk.\n" +
+        "You can adjust this limit via ZipSecureFile.setMaxEntrySize() if you need to work with files which are very large.\n" +
+        "Counter: %d, cis.counter: %d\n" +
+        "Limits: MAX_ENTRY_SIZE: %d, Entry: %s";
+
+    private static final String MIN_INFLATE_RATIO_MSG =
+        "Zip bomb detected! The file would exceed the max. ratio of compressed file size to the size of the expanded data.\n" +
+        "This may indicate that the file is used to inflate memory usage and thus could pose a security risk.\n" +
+        "You can adjust this limit via ZipSecureFile.setMinInflateRatio() if you need to work with files which exceed this limit.\n" +
+        "Counter: %d, cis.counter: %d, ratio: %f\n" +
+        "Limits: MIN_INFLATE_RATIO: %f, Entry: %s";
+
+    private static final String SECURITY_BLOCKED =
+        "SecurityManager doesn't allow manipulation via reflection for zipbomb detection - continue with original input stream";
+
+    /**
+     * the reference to the current entry is only used for a more detailed log message in case of an error
+     */
+    private ZipEntry entry;
+
+    private long counter;
+    private long markPos;
+    private final ZipArchiveThresholdInputStream cis;
+    private boolean guardState = true;
+
+
+    public ZipArchiveThresholdInputStream(final InputStream zipIS) throws IOException {
+        super(zipIS);
+        if (zipIS instanceof InflaterInputStream) {
+            cis = AccessController.doPrivileged(inject(zipIS));
+        } else {
+            // the inner stream is a ZipFileInputStream, i.e. the data wasn't compressed
+            cis = null;
+        }
+    }
+
+    private ZipArchiveThresholdInputStream(InputStream is, ZipArchiveThresholdInputStream cis) {
+        super(is);
+        this.cis = cis;
+    }
+
+    @SuppressForbidden
+    private static PrivilegedAction<ZipArchiveThresholdInputStream> inject(final InputStream zipIS) {
+        return () -> {
+            try {
+                final Field f = FilterInputStream.class.getDeclaredField("in");
+                f.setAccessible(true);
+                final InputStream oldInner = (InputStream)f.get(zipIS);
+                final ZipArchiveThresholdInputStream inner = new ZipArchiveThresholdInputStream(oldInner, null);
+                f.set(zipIS, inner);
+                return inner;
+            } catch (Exception ex) {
+                LOG.log(POILogger.WARN, SECURITY_BLOCKED, ex);
+            }
+            return null;
+        };
+    }
+
+    @Override
+    public int read() throws IOException {
+        int b = in.read();
+        if (b > -1) {
+            advance(1);
+        }
+        return b;
+    }
+
+    @Override
+    public int read(byte b[], int off, int len) throws IOException {
+        int cnt = in.read(b, off, len);
+        if (cnt > -1) {
+            advance(cnt);
+        }
+        return cnt;
+    }
+
+    @Override
+    public long skip(long n) throws IOException {
+        long s = in.skip(n);
+        counter += s;
+        return s;
+    }
+
+    @Override
+    public synchronized void reset() throws IOException {
+        counter = markPos;
+        super.reset();
+    }
+
+    /**
+     * De-/activate threshold check.
+     * A disabled guard might make sense, when POI is processing its own temporary data (see #59743)
+     *
+     * @param guardState {@code true} (= default) enables the threshold check
+     */
+    public void setGuardState(boolean guardState) {
+        this.guardState = guardState;
+    }
+
+    public void advance(int advance) throws IOException {
+        counter += advance;
+
+        if (!guardState) {
+            return;
+        }
+
+        final String entryName = entry == null ? "not set" : entry.getName();
+        final long cisCount = (cis == null ? 0 : cis.counter);
+
+        // check the file size first, in case we are working on uncompressed streams
+        if(counter > MAX_ENTRY_SIZE) {
+            throw new IOException(String.format(Locale.ROOT, MAX_ENTRY_SIZE_MSG, counter, cisCount, MAX_ENTRY_SIZE, entryName));
+        }
+
+        // no expanded size?
+        if (cis == null) {
+            return;
+        }
+
+        // don't alert for small expanded size
+        if (counter <= GRACE_ENTRY_SIZE) {
+            return;
+        }
+
+        double ratio = (double)cis.counter/(double)counter;
+        if (ratio >= MIN_INFLATE_RATIO) {
+            return;
+        }
+
+        // one of the limits was reached, report it
+        throw new IOException(String.format(Locale.ROOT, MIN_INFLATE_RATIO_MSG, counter, cisCount, ratio, MIN_INFLATE_RATIO, entryName));
+    }
+
+    public ZipEntry getNextEntry() throws IOException {
+        if (!(in instanceof ZipInputStream)) {
+            throw new UnsupportedOperationException("underlying stream is not a ZipInputStream");
+        }
+        counter = 0;
+        return ((ZipInputStream)in).getNextEntry();
+    }
+
+    public void closeEntry() throws IOException {
+        if (!(in instanceof ZipInputStream)) {
+            throw new UnsupportedOperationException("underlying stream is not a ZipInputStream");
+        }
+        counter = 0;
+        ((ZipInputStream)in).closeEntry();
+    }
+
+    @Override
+    public void unread(int b) throws IOException {
+        if (!(in instanceof PushbackInputStream)) {
+            throw new UnsupportedOperationException("underlying stream is not a PushbackInputStream");
+        }
+        if (--counter < 0) {
+            counter = 0;
+        }
+        ((PushbackInputStream)in).unread(b);
+    }
+
+    @Override
+    public void unread(byte[] b, int off, int len) throws IOException {
+        if (!(in instanceof PushbackInputStream)) {
+            throw new UnsupportedOperationException("underlying stream is not a PushbackInputStream");
+        }
+        counter -= len;
+        if (--counter < 0) {
+            counter = 0;
+        }
+        ((PushbackInputStream)in).unread(b, off, len);
+    }
+
+    @Override
+    @SuppressForbidden("just delegating")
+    public int available() throws IOException {
+        return in.available();
+    }
+
+    @Override
+    public boolean markSupported() {
+        return in.markSupported();
+    }
+
+    @Override
+    public synchronized void mark(int readlimit) {
+        markPos = counter;
+        in.mark(readlimit);
+    }
+
+    /**
+     * Sets the zip entry for a detailed logging
+     * @param entry the entry
+     */
+    void setEntry(ZipEntry entry) {
+        this.entry = entry;
+    }
+}
\ No newline at end of file

Propchange: poi/trunk/src/ooxml/java/org/apache/poi/openxml4j/util/ZipArchiveThresholdInputStream.java
------------------------------------------------------------------------------
    svn:eol-style = native

Modified: poi/trunk/src/ooxml/java/org/apache/poi/openxml4j/util/ZipEntrySource.java
URL: http://svn.apache.org/viewvc/poi/trunk/src/ooxml/java/org/apache/poi/openxml4j/util/ZipEntrySource.java?rev=1830061&r1=1830060&r2=1830061&view=diff
==============================================================================
--- poi/trunk/src/ooxml/java/org/apache/poi/openxml4j/util/ZipEntrySource.java (original)
+++ poi/trunk/src/ooxml/java/org/apache/poi/openxml4j/util/ZipEntrySource.java Wed Apr 25 10:03:39 2018
@@ -33,23 +33,32 @@ public interface ZipEntrySource extends
 	/**
 	 * Returns an Enumeration of all the Entries
 	 */
-	public Enumeration<? extends ZipEntry> getEntries();
-	
+	Enumeration<? extends ZipEntry> getEntries();
+
+	/**
+	 * Return an entry by its path
+	 * @param path the path in unix-notation
+	 * @return the entry or {@code null} if not found
+	 *
+	 * @since POI 4.0.0
+	 */
+	ZipEntry getEntry(String path);
+
 	/**
 	 * Returns an InputStream of the decompressed 
 	 *  data that makes up the entry
 	 */
-	public InputStream getInputStream(ZipEntry entry) throws IOException;
+	InputStream getInputStream(ZipEntry entry) throws IOException;
 	
 	/**
 	 * Indicates we are done with reading, and 
 	 *  resources may be freed
 	 */
 	@Override
-	public void close() throws IOException;
+	void close() throws IOException;
 	
 	/**
 	 * Has close been called already?
 	 */
-	public boolean isClosed();
+	boolean isClosed();
 }

Modified: poi/trunk/src/ooxml/java/org/apache/poi/openxml4j/util/ZipFileZipEntrySource.java
URL: http://svn.apache.org/viewvc/poi/trunk/src/ooxml/java/org/apache/poi/openxml4j/util/ZipFileZipEntrySource.java?rev=1830061&r1=1830060&r2=1830061&view=diff
==============================================================================
--- poi/trunk/src/ooxml/java/org/apache/poi/openxml4j/util/ZipFileZipEntrySource.java (original)
+++ poi/trunk/src/ooxml/java/org/apache/poi/openxml4j/util/ZipFileZipEntrySource.java Wed Apr 25 10:03:39 2018
@@ -16,6 +16,9 @@
 ==================================================================== */
 package org.apache.poi.openxml4j.util;
 
+import static org.apache.commons.collections4.IteratorUtils.asIterable;
+import static org.apache.commons.collections4.IteratorUtils.asIterator;
+
 import java.io.IOException;
 import java.io.InputStream;
 import java.util.Enumeration;
@@ -33,16 +36,20 @@ public class ZipFileZipEntrySource imple
       this.zipArchive = zipFile;
    }
 
+   @Override
    public void close() throws IOException {
       if(zipArchive != null) {
          zipArchive.close();
       }
       zipArchive = null;
    }
+
+   @Override
    public boolean isClosed() {
        return (zipArchive == null);
    }
 
+   @Override
    public Enumeration<? extends ZipEntry> getEntries() {
       if (zipArchive == null)
          throw new IllegalStateException("Zip File is closed");
@@ -50,10 +57,30 @@ public class ZipFileZipEntrySource imple
       return zipArchive.entries();
    }
 
+   @Override
    public InputStream getInputStream(ZipEntry entry) throws IOException {
       if (zipArchive == null)
          throw new IllegalStateException("Zip File is closed");
       
       return zipArchive.getInputStream(entry);
    }
+
+   @Override
+   public ZipEntry getEntry(final String path) {
+      String normalizedPath = path.replace('\\', '/');
+
+      final ZipEntry entry = zipArchive.getEntry(normalizedPath);
+      if (entry != null) {
+         return entry;
+      }
+
+      // the opc spec allows case-insensitive filename matching (see #49609)
+      for (final ZipEntry ze : asIterable(asIterator(zipArchive.entries()))) {
+         if (normalizedPath.equalsIgnoreCase(ze.getName().replace('\\','/'))) {
+            return ze;
+         }
+      }
+
+      return null;
+   }
 }

Modified: poi/trunk/src/ooxml/java/org/apache/poi/openxml4j/util/ZipInputStreamZipEntrySource.java
URL: http://svn.apache.org/viewvc/poi/trunk/src/ooxml/java/org/apache/poi/openxml4j/util/ZipInputStreamZipEntrySource.java?rev=1830061&r1=1830060&r2=1830061&view=diff
==============================================================================
--- poi/trunk/src/ooxml/java/org/apache/poi/openxml4j/util/ZipInputStreamZipEntrySource.java (original)
+++ poi/trunk/src/ooxml/java/org/apache/poi/openxml4j/util/ZipInputStreamZipEntrySource.java Wed Apr 25 10:03:39 2018
@@ -16,16 +16,14 @@
 ==================================================================== */
 package org.apache.poi.openxml4j.util;
 
-import java.io.ByteArrayInputStream;
-import java.io.ByteArrayOutputStream;
 import java.io.IOException;
 import java.io.InputStream;
-import java.util.ArrayList;
 import java.util.Enumeration;
-import java.util.Iterator;
+import java.util.HashMap;
+import java.util.Map;
 import java.util.zip.ZipEntry;
 
-import org.apache.poi.openxml4j.util.ZipSecureFile.ThresholdInputStream;
+import org.apache.commons.collections4.IteratorUtils;
 
 /**
  * Provides a way to get at all the ZipEntries
@@ -36,7 +34,7 @@ import org.apache.poi.openxml4j.util.Zip
  *  done, to free up that memory!
  */
 public class ZipInputStreamZipEntrySource implements ZipEntrySource {
-	private ArrayList<FakeZipEntry> zipEntries;
+	private final Map<String, ZipArchiveFakeEntry> zipEntries = new HashMap<>();
 	
 	/**
 	 * Reads all the entries from the ZipInputStream 
@@ -44,100 +42,53 @@ public class ZipInputStreamZipEntrySourc
 	 * We'll then eat lots of memory, but be able to
 	 *  work with the entries at-will.
 	 */
-	public ZipInputStreamZipEntrySource(ThresholdInputStream inp) throws IOException {
-		zipEntries = new ArrayList<>();
-		
-		boolean going = true;
-		while(going) {
-			ZipEntry zipEntry = inp.getNextEntry();
-			if(zipEntry == null) {
-				going = false;
-			} else {
-				FakeZipEntry entry = new FakeZipEntry(zipEntry, inp);
-				inp.closeEntry();
-				
-				zipEntries.add(entry);
+	public ZipInputStreamZipEntrySource(ZipArchiveThresholdInputStream inp) throws IOException {
+		for (;;) {
+			final ZipEntry zipEntry = inp.getNextEntry();
+			if (zipEntry == null) {
+				break;
 			}
+			zipEntries.put(zipEntry.getName(), new ZipArchiveFakeEntry(zipEntry, inp));
 		}
 		inp.close();
 	}
 
+	@Override
 	public Enumeration<? extends ZipEntry> getEntries() {
-		return new EntryEnumerator();
+		return IteratorUtils.asEnumeration(zipEntries.values().iterator());
 	}
-	
+
+	@Override
 	public InputStream getInputStream(ZipEntry zipEntry) {
-	    assert (zipEntry instanceof FakeZipEntry);
-		FakeZipEntry entry = (FakeZipEntry)zipEntry;
-		return entry.getInputStream();
+	    assert (zipEntry instanceof ZipArchiveFakeEntry);
+		return ((ZipArchiveFakeEntry)zipEntry).getInputStream();
 	}
-	
+
+	@Override
 	public void close() {
 		// Free the memory
-		zipEntries = null;
+		zipEntries.clear();
 	}
+
+	@Override
 	public boolean isClosed() {
-	    return (zipEntries == null);
+	    return zipEntries.isEmpty();
 	}
-	
-	/**
-	 * Why oh why oh why are Iterator and Enumeration
-	 *  still not compatible?
-	 */
-	private class EntryEnumerator implements Enumeration<ZipEntry> {
-		private Iterator<? extends ZipEntry> iterator;
-		
-		private EntryEnumerator() {
-			iterator = zipEntries.iterator();
-		}
-		
-		public boolean hasMoreElements() {
-			return iterator.hasNext();
-		}
 
-		public ZipEntry nextElement() {
-			return iterator.next();
+	@Override
+	public ZipEntry getEntry(final String path) {
+		final String normalizedPath = path.replace('\\', '/');
+		final ZipEntry ze = zipEntries.get(normalizedPath);
+		if (ze != null) {
+			return ze;
 		}
-	}
 
-	/**
-	 * So we can close the real zip entry and still
-	 *  effectively work with it.
-	 * Holds the (decompressed!) data in memory, so
-	 *  close this as soon as you can! 
-	 */
-	public static class FakeZipEntry extends ZipEntry {
-		private byte[] data;
-		
-		public FakeZipEntry(ZipEntry entry, InputStream inp) throws IOException {
-			super(entry.getName());
-			
-			// Grab the de-compressed contents for later
-            ByteArrayOutputStream baos;
-
-            long entrySize = entry.getSize();
-
-            if (entrySize !=-1) {
-                if (entrySize>=Integer.MAX_VALUE) {
-                    throw new IOException("ZIP entry size is too large");
-                }
-
-                baos = new ByteArrayOutputStream((int) entrySize);
-            } else {
-    			baos = new ByteArrayOutputStream();
-            }
-
-			byte[] buffer = new byte[4096];
-			int read = 0;
-			while( (read = inp.read(buffer)) != -1 ) {
-				baos.write(buffer, 0, read);
+		for (final Map.Entry<String, ZipArchiveFakeEntry> fze : zipEntries.entrySet()) {
+			if (normalizedPath.equalsIgnoreCase(fze.getKey())) {
+				return fze.getValue();
 			}
-			
-			data = baos.toByteArray();
-		}
-		
-		public InputStream getInputStream() {
-			return new ByteArrayInputStream(data);
 		}
+
+		return null;
 	}
 }

Modified: poi/trunk/src/ooxml/java/org/apache/poi/openxml4j/util/ZipSecureFile.java
URL: http://svn.apache.org/viewvc/poi/trunk/src/ooxml/java/org/apache/poi/openxml4j/util/ZipSecureFile.java?rev=1830061&r1=1830060&r2=1830061&view=diff
==============================================================================
--- poi/trunk/src/ooxml/java/org/apache/poi/openxml4j/util/ZipSecureFile.java (original)
+++ poi/trunk/src/ooxml/java/org/apache/poi/openxml4j/util/ZipSecureFile.java Wed Apr 25 10:03:39 2018
@@ -18,43 +18,27 @@
 package org.apache.poi.openxml4j.util;
 
 import java.io.File;
-import java.io.FilterInputStream;
 import java.io.IOException;
-import java.io.InputStream;
-import java.io.PushbackInputStream;
-import java.lang.reflect.Field;
-import java.security.AccessController;
-import java.security.PrivilegedAction;
-import java.util.zip.InflaterInputStream;
 import java.util.zip.ZipEntry;
-import java.util.zip.ZipException;
 import java.util.zip.ZipFile;
-import java.util.zip.ZipInputStream;
 
-import org.apache.poi.util.POILogFactory;
-import org.apache.poi.util.POILogger;
-import org.apache.poi.util.SuppressForbidden;
 
 /**
  * This class wraps a {@link ZipFile} in order to check the
  * entries for <a href="https://en.wikipedia.org/wiki/Zip_bomb">zip bombs</a>
- * while reading the archive.
- * If a {@link ZipInputStream} is directly used, the wrapper
- * can be applied via {@link #addThreshold(InputStream)}.
+ * while reading the archive.<p>
+ *
  * The alert limits can be globally defined via {@link #setMaxEntrySize(long)}
  * and {@link #setMinInflateRatio(double)}.
  */
 public class ZipSecureFile extends ZipFile {
-    private static final POILogger LOG = POILogFactory.getLogger(ZipSecureFile.class);
+    /* package */ static double MIN_INFLATE_RATIO = 0.01d;
+    /* package */ static long MAX_ENTRY_SIZE = 0xFFFFFFFFL;
     
-    private static double MIN_INFLATE_RATIO = 0.01d;
-    private static long MAX_ENTRY_SIZE = 0xFFFFFFFFL;
-    
-    // don't alert for expanded sizes smaller than 100k
-    private final static long GRACE_ENTRY_SIZE = 100*1024L;
-
-    // The default maximum size of extracted text 
+    // The default maximum size of extracted text
     private static long MAX_TEXT_SIZE = 10*1024*1024L;
+
+    private final String fileName;
     
     /**
      * Sets the ratio between de- and inflated bytes to detect zipbomb.
@@ -134,16 +118,14 @@ public class ZipSecureFile extends ZipFi
         return MAX_TEXT_SIZE;
     }
 
-    public ZipSecureFile(File file, int mode) throws ZipException, IOException {
-        super(file, mode);
-    }
-
-    public ZipSecureFile(File file) throws ZipException, IOException {
+    public ZipSecureFile(File file) throws IOException {
         super(file);
+        this.fileName = file.getAbsolutePath();
     }
 
-    public ZipSecureFile(String name) throws ZipException, IOException {
+    public ZipSecureFile(String name) throws IOException {
         super(name);
+        this.fileName = new File(name).getAbsolutePath();
     }
 
     /**
@@ -156,176 +138,22 @@ public class ZipSecureFile extends ZipFi
      * @param entry the zip file entry
      * @return the input stream for reading the contents of the specified
      * zip file entry.
-     * @throws ZipException if a ZIP format error has occurred
      * @throws IOException if an I/O error has occurred
      * @throws IllegalStateException if the zip file has been closed
      */
     @Override
     @SuppressWarnings("resource")
-    public InputStream getInputStream(ZipEntry entry) throws IOException {
-        InputStream zipIS = super.getInputStream(entry);
-        return addThreshold(zipIS);
-    }
-
-    public static ThresholdInputStream addThreshold(final InputStream zipIS) throws IOException {
-        ThresholdInputStream newInner;
-        if (zipIS instanceof InflaterInputStream) {
-            newInner = AccessController.doPrivileged(new PrivilegedAction<ThresholdInputStream>() { // NOSONAR
-                @Override
-                @SuppressForbidden("TODO: Fix this to not use reflection (it will break in Java 9)! " +
-                        "Better would be to wrap *before* instead of trying to insert wrapper afterwards.")
-                public ThresholdInputStream run() {
-                    try {
-                        Field f = FilterInputStream.class.getDeclaredField("in");
-                        f.setAccessible(true);
-                        InputStream oldInner = (InputStream)f.get(zipIS);
-                        ThresholdInputStream newInner2 = new ThresholdInputStream(oldInner, null);
-                        f.set(zipIS, newInner2);
-                        return newInner2;
-                    } catch (Exception ex) {
-                        LOG.log(POILogger.WARN, "SecurityManager doesn't allow manipulation via reflection for zipbomb detection - continue with original input stream", ex);
-                    }
-                    return null;
-                }
-            });
-        } else {
-            // the inner stream is a ZipFileInputStream, i.e. the data wasn't compressed
-            newInner = null;
-        }
-
-        return new ThresholdInputStream(zipIS, newInner);
+    public ZipArchiveThresholdInputStream getInputStream(ZipEntry entry) throws IOException {
+        ZipArchiveThresholdInputStream zatis = new ZipArchiveThresholdInputStream(super.getInputStream(entry));
+        zatis.setEntry(entry);
+        return zatis;
     }
 
-    public static class ThresholdInputStream extends PushbackInputStream {
-        long counter;
-        long markPos;
-        ThresholdInputStream cis;
-
-        public ThresholdInputStream(InputStream is, ThresholdInputStream cis) {
-            super(is);
-            this.cis = cis;
-        }
-
-        @Override
-        public int read() throws IOException {
-            int b = in.read();
-            if (b > -1) {
-                advance(1);
-            }
-            return b;
-        }
-
-        @Override
-        public int read(byte b[], int off, int len) throws IOException {
-            int cnt = in.read(b, off, len);
-            if (cnt > -1) {
-                advance(cnt);
-            }
-            return cnt;
-        }
-
-        @Override
-        public long skip(long n) throws IOException {
-            long s = in.skip(n);
-            counter += s;
-            return s;
-        }
-
-        @Override
-        public synchronized void reset() throws IOException {
-            counter = markPos;
-            super.reset();
-        }
-
-        public void advance(int advance) throws IOException {
-            counter += advance;
-            
-            // check the file size first, in case we are working on uncompressed streams
-            if(counter > MAX_ENTRY_SIZE) {
-                throw new IOException("Zip bomb detected! The file would exceed the max size of the expanded data in the zip-file. "
-                        + "This may indicates that the file is used to inflate memory usage and thus could pose a security risk. "
-                        + "You can adjust this limit via ZipSecureFile.setMaxEntrySize() if you need to work with files which are very large. "
-                        + "Counter: " + counter + ", cis.counter: " + (cis == null ? 0 : cis.counter)
-                        + "Limits: MAX_ENTRY_SIZE: " + MAX_ENTRY_SIZE);
-            }
-
-            // no expanded size?
-            if (cis == null) {
-                return;
-            }
-            
-            // don't alert for small expanded size
-            if (counter <= GRACE_ENTRY_SIZE) {
-                return;
-            }
-
-            double ratio = (double)cis.counter/(double)counter;
-            if (ratio >= MIN_INFLATE_RATIO) {
-                return;
-            }
-
-            // one of the limits was reached, report it
-            throw new IOException("Zip bomb detected! The file would exceed the max. ratio of compressed file size to the size of the expanded data.\n"
-                    + "This may indicate that the file is used to inflate memory usage and thus could pose a security risk.\n"
-                    + "You can adjust this limit via ZipSecureFile.setMinInflateRatio() if you need to work with files which exceed this limit.\n"
-                    + "Counter: " + counter + ", cis.counter: " + cis.counter + ", ratio: " + ratio + "\n"
-                    + "Limits: MIN_INFLATE_RATIO: " + MIN_INFLATE_RATIO);
-        }
-
-        public ZipEntry getNextEntry() throws IOException {
-            if (!(in instanceof ZipInputStream)) {
-                throw new UnsupportedOperationException("underlying stream is not a ZipInputStream");
-            }
-            counter = 0;
-            return ((ZipInputStream)in).getNextEntry();
-        }
-
-        public void closeEntry() throws IOException {
-            if (!(in instanceof ZipInputStream)) {
-                throw new UnsupportedOperationException("underlying stream is not a ZipInputStream");
-            }
-            counter = 0;
-            ((ZipInputStream)in).closeEntry();
-        }
-
-        @Override
-        public void unread(int b) throws IOException {
-            if (!(in instanceof PushbackInputStream)) {
-                throw new UnsupportedOperationException("underlying stream is not a PushbackInputStream");
-            }
-            if (--counter < 0) {
-                counter = 0;
-            }
-            ((PushbackInputStream)in).unread(b);
-        }
-
-        @Override
-        public void unread(byte[] b, int off, int len) throws IOException {
-            if (!(in instanceof PushbackInputStream)) {
-                throw new UnsupportedOperationException("underlying stream is not a PushbackInputStream");
-            }
-            counter -= len;
-            if (--counter < 0) {
-                counter = 0;
-            }
-            ((PushbackInputStream)in).unread(b, off, len);
-        }
-
-        @Override
-        @SuppressForbidden("just delegating")
-        public int available() throws IOException {
-            return in.available();
-        }
-
-        @Override
-        public boolean markSupported() {
-            return in.markSupported();
-        }
-
-        @Override
-        public synchronized void mark(int readlimit) {
-            markPos = counter;
-            in.mark(readlimit);
-        }
+    /**
+     * Returns the path name of the ZIP file.
+     * @return the path name of the ZIP file
+     */
+    public String getName() {
+        return fileName;
     }
 }

Modified: poi/trunk/src/ooxml/java/org/apache/poi/poifs/crypt/temp/AesZipFileZipEntrySource.java
URL: http://svn.apache.org/viewvc/poi/trunk/src/ooxml/java/org/apache/poi/poifs/crypt/temp/AesZipFileZipEntrySource.java?rev=1830061&r1=1830060&r2=1830061&view=diff
==============================================================================
--- poi/trunk/src/ooxml/java/org/apache/poi/poifs/crypt/temp/AesZipFileZipEntrySource.java (original)
+++ poi/trunk/src/ooxml/java/org/apache/poi/poifs/crypt/temp/AesZipFileZipEntrySource.java Wed Apr 25 10:03:39 2018
@@ -53,15 +53,17 @@ import org.apache.poi.util.TempFile;
  * sensitive data is not stored in raw format on disk.
  */
 @Beta
-public class AesZipFileZipEntrySource implements ZipEntrySource {
+public final class AesZipFileZipEntrySource implements ZipEntrySource {
     private static final POILogger LOG = POILogFactory.getLogger(AesZipFileZipEntrySource.class);
+
+    private static final String PADDING = "PKCS5Padding";
     
     private final File tmpFile;
     private final ZipFile zipFile;
     private final Cipher ci;
     private boolean closed;
     
-    public AesZipFileZipEntrySource(File tmpFile, Cipher ci) throws IOException {
+    private AesZipFileZipEntrySource(File tmpFile, Cipher ci) throws IOException {
         this.tmpFile = tmpFile;
         this.zipFile = new ZipFile(tmpFile);
         this.ci = ci;
@@ -76,7 +78,12 @@ public class AesZipFileZipEntrySource im
     public Enumeration<? extends ZipEntry> getEntries() {
         return zipFile.entries();
     }
-    
+
+    @Override
+    public ZipEntry getEntry(String path) {
+        return zipFile.getEntry(path);
+    }
+
     @Override
     public InputStream getInputStream(ZipEntry entry) throws IOException {
         InputStream is = zipFile.getInputStream(entry);
@@ -106,14 +113,14 @@ public class AesZipFileZipEntrySource im
         sr.nextBytes(ivBytes);
         sr.nextBytes(keyBytes);
         final File tmpFile = TempFile.createTempFile("protectedXlsx", ".zip");
-        copyToFile(is, tmpFile, CipherAlgorithm.aes128, keyBytes, ivBytes);
+        copyToFile(is, tmpFile, keyBytes, ivBytes);
         IOUtils.closeQuietly(is);
-        return fileToSource(tmpFile, CipherAlgorithm.aes128, keyBytes, ivBytes);
+        return fileToSource(tmpFile, keyBytes, ivBytes);
     }
 
-    private static void copyToFile(InputStream is, File tmpFile, CipherAlgorithm cipherAlgorithm, byte keyBytes[], byte ivBytes[]) throws IOException, GeneralSecurityException {
-        SecretKeySpec skeySpec = new SecretKeySpec(keyBytes, cipherAlgorithm.jceId);
-        Cipher ciEnc = CryptoFunctions.getCipher(skeySpec, cipherAlgorithm, ChainingMode.cbc, ivBytes, Cipher.ENCRYPT_MODE, "PKCS5Padding");
+    private static void copyToFile(InputStream is, File tmpFile, byte keyBytes[], byte ivBytes[]) throws IOException, GeneralSecurityException {
+        SecretKeySpec skeySpec = new SecretKeySpec(keyBytes, CipherAlgorithm.aes128.jceId);
+        Cipher ciEnc = CryptoFunctions.getCipher(skeySpec, CipherAlgorithm.aes128, ChainingMode.cbc, ivBytes, Cipher.ENCRYPT_MODE, PADDING);
         
         ZipInputStream zis = new ZipInputStream(is);
         FileOutputStream fos = new FileOutputStream(tmpFile);
@@ -146,9 +153,9 @@ public class AesZipFileZipEntrySource im
         zis.close();
     }
 
-    private static AesZipFileZipEntrySource fileToSource(File tmpFile, CipherAlgorithm cipherAlgorithm, byte keyBytes[], byte ivBytes[]) throws ZipException, IOException {
-        SecretKeySpec skeySpec = new SecretKeySpec(keyBytes, cipherAlgorithm.jceId);
-        Cipher ciDec = CryptoFunctions.getCipher(skeySpec, cipherAlgorithm, ChainingMode.cbc, ivBytes, Cipher.DECRYPT_MODE, "PKCS5Padding");
+    private static AesZipFileZipEntrySource fileToSource(File tmpFile, byte keyBytes[], byte ivBytes[]) throws ZipException, IOException {
+        SecretKeySpec skeySpec = new SecretKeySpec(keyBytes, CipherAlgorithm.aes128.jceId);
+        Cipher ciDec = CryptoFunctions.getCipher(skeySpec, CipherAlgorithm.aes128, ChainingMode.cbc, ivBytes, Cipher.DECRYPT_MODE, PADDING);
         return new AesZipFileZipEntrySource(tmpFile, ciDec);
     }
     

Modified: poi/trunk/src/ooxml/java/org/apache/poi/poifs/crypt/temp/EncryptedTempData.java
URL: http://svn.apache.org/viewvc/poi/trunk/src/ooxml/java/org/apache/poi/poifs/crypt/temp/EncryptedTempData.java?rev=1830061&r1=1830060&r2=1830061&view=diff
==============================================================================
--- poi/trunk/src/ooxml/java/org/apache/poi/poifs/crypt/temp/EncryptedTempData.java (original)
+++ poi/trunk/src/ooxml/java/org/apache/poi/poifs/crypt/temp/EncryptedTempData.java Wed Apr 25 10:03:39 2018
@@ -48,6 +48,7 @@ public class EncryptedTempData {
     private static POILogger LOG = POILogFactory.getLogger(EncryptedTempData.class);
  
     private final static CipherAlgorithm cipherAlgorithm = CipherAlgorithm.aes128;
+    private final static String PADDING = "PKCS5Padding";
     private final SecretKeySpec skeySpec;
     private final byte[] ivBytes;
     private final File tempFile;
@@ -63,12 +64,12 @@ public class EncryptedTempData {
     }
 
     public OutputStream getOutputStream() throws IOException {
-        Cipher ciEnc = CryptoFunctions.getCipher(skeySpec, cipherAlgorithm, ChainingMode.cbc, ivBytes, Cipher.ENCRYPT_MODE, null);
+        Cipher ciEnc = CryptoFunctions.getCipher(skeySpec, cipherAlgorithm, ChainingMode.cbc, ivBytes, Cipher.ENCRYPT_MODE, PADDING);
         return new CipherOutputStream(new FileOutputStream(tempFile), ciEnc);
     }
 
     public InputStream getInputStream() throws IOException {
-        Cipher ciDec = CryptoFunctions.getCipher(skeySpec, cipherAlgorithm, ChainingMode.cbc, ivBytes, Cipher.DECRYPT_MODE, null);
+        Cipher ciDec = CryptoFunctions.getCipher(skeySpec, cipherAlgorithm, ChainingMode.cbc, ivBytes, Cipher.DECRYPT_MODE, PADDING);
         return new CipherInputStream(new FileInputStream(tempFile), ciDec);
     }
     

Modified: poi/trunk/src/ooxml/java/org/apache/poi/xssf/dev/XSSFDump.java
URL: http://svn.apache.org/viewvc/poi/trunk/src/ooxml/java/org/apache/poi/xssf/dev/XSSFDump.java?rev=1830061&r1=1830060&r2=1830061&view=diff
==============================================================================
--- poi/trunk/src/ooxml/java/org/apache/poi/xssf/dev/XSSFDump.java (original)
+++ poi/trunk/src/ooxml/java/org/apache/poi/xssf/dev/XSSFDump.java Wed Apr 25 10:03:39 2018
@@ -24,9 +24,9 @@ import java.io.FileOutputStream;
 import java.io.OutputStream;
 import java.util.Enumeration;
 import java.util.zip.ZipEntry;
-import java.util.zip.ZipFile;
 
 import org.apache.poi.openxml4j.opc.internal.ZipHelper;
+import org.apache.poi.openxml4j.util.ZipSecureFile;
 import org.apache.poi.util.DocumentHelper;
 import org.apache.poi.util.IOUtils;
 import org.apache.xmlbeans.XmlException;
@@ -41,14 +41,13 @@ import org.w3c.dom.Document;
  */
 public final class XSSFDump {
 
+    private XSSFDump() {}
+
     public static void main(String[] args) throws Exception {
-        for (int i = 0; i < args.length; i++) {
-            System.out.println("Dumping " + args[i]);
-            ZipFile zip = ZipHelper.openZipFile(args[i]);
-            try {
+        for (String arg : args) {
+            System.out.println("Dumping " + arg);
+            try (ZipSecureFile zip = ZipHelper.openZipFile(arg)) {
                 dump(zip);
-            } finally {
-                zip.close();
             }
         }
     }
@@ -72,7 +71,7 @@ public final class XSSFDump {
     }
     
 
-    public static void dump(ZipFile zip) throws Exception {
+    public static void dump(ZipSecureFile zip) throws Exception {
         String zipname = zip.getName();
         int sep = zipname.lastIndexOf('.');
         File root = new File(zipname.substring(0, sep));
@@ -90,8 +89,7 @@ public final class XSSFDump {
             }
 
             File f = new File(root, entry.getName());
-            OutputStream out = new FileOutputStream(f);
-            try {
+            try (final OutputStream out = new FileOutputStream(f)) {
                 if (entry.getName().endsWith(".xml") || entry.getName().endsWith(".vml") || entry.getName().endsWith(".rels")) {
                     try {
                         Document doc = DocumentHelper.readDocument(zip.getInputStream(entry));
@@ -106,8 +104,6 @@ public final class XSSFDump {
                 } else {
                     IOUtils.copy(zip.getInputStream(entry), out);
                 }
-            } finally {
-                out.close();
             }
         }
     }



---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@poi.apache.org
For additional commands, e-mail: commits-help@poi.apache.org


Mime
View raw message