]> source.dussan.org Git - poi.git/commitdiff
#52991: Unexpected end of ZLIB input stream on embedded OLE extraction from PPT
authorMaxim Valyanskiy <maxcom@apache.org>
Tue, 27 Mar 2012 08:32:47 +0000 (08:32 +0000)
committerMaxim Valyanskiy <maxcom@apache.org>
Tue, 27 Mar 2012 08:32:47 +0000 (08:32 +0000)
git-svn-id: https://svn.apache.org/repos/asf/poi/trunk@1305778 13f79535-47bb-0310-9956-ffa450edef68

src/java/org/apache/poi/util/BoundedInputStream.java [new file with mode: 0644]
src/scratchpad/src/org/apache/poi/hslf/record/ExOleObjStg.java

diff --git a/src/java/org/apache/poi/util/BoundedInputStream.java b/src/java/org/apache/poi/util/BoundedInputStream.java
new file mode 100644 (file)
index 0000000..7812116
--- /dev/null
@@ -0,0 +1,230 @@
+/*
+ * 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.util;
+
+import java.io.IOException;
+import java.io.InputStream;
+
+/**
+ * This is a stream that will only supply bytes up to a certain length - if its
+ * position goes above that, it will stop.
+ * <p>
+ * This is useful to wrap ServletInputStreams. The ServletInputStream will block
+ * if you try to read content from it that isn't there, because it doesn't know
+ * whether the content hasn't arrived yet or whether the content has finished.
+ * So, one of these, initialized with the Content-length sent in the
+ * ServletInputStream's header, will stop it blocking, providing it's been sent
+ * with a correct content length.
+ *
+ * @version $Id$
+ * @since Commons IO 2.0
+ */
+public class BoundedInputStream extends InputStream {
+
+    /** the wrapped input stream */
+    private final InputStream in;
+
+    /** the max length to provide */
+    private final long max;
+
+    /** the number of bytes already returned */
+    private long pos = 0;
+
+    /** the marked position */
+    private long mark = -1;
+
+    /** flag if close shoud be propagated */
+    private boolean propagateClose = true;
+
+    /**
+     * Creates a new <code>BoundedInputStream</code> that wraps the given input
+     * stream and limits it to a certain size.
+     *
+     * @param in The wrapped input stream
+     * @param size The maximum number of bytes to return
+     */
+    public BoundedInputStream(InputStream in, long size) {
+        // Some badly designed methods - eg the servlet API - overload length
+        // such that "-1" means stream finished
+        this.max = size;
+        this.in = in;
+    }
+
+    /**
+     * Creates a new <code>BoundedInputStream</code> that wraps the given input
+     * stream and is unlimited.
+     *
+     * @param in The wrapped input stream
+     */
+    public BoundedInputStream(InputStream in) {
+        this(in, -1);
+    }
+
+    /**
+     * Invokes the delegate's <code>read()</code> method if
+     * the current position is less than the limit.
+     * @return the byte read or -1 if the end of stream or
+     * the limit has been reached.
+     * @throws IOException if an I/O error occurs
+     */
+    @Override
+    public int read() throws IOException {
+        if (max>=0 && pos==max) {
+            return -1;
+        }
+        int result = in.read();
+        pos++;
+        return result;
+    }
+
+    /**
+     * Invokes the delegate's <code>read(byte[])</code> method.
+     * @param b the buffer to read the bytes into
+     * @return the number of bytes read or -1 if the end of stream or
+     * the limit has been reached.
+     * @throws IOException if an I/O error occurs
+     */
+    @Override
+    public int read(byte[] b) throws IOException {
+        return this.read(b, 0, b.length);
+    }
+
+    /**
+     * Invokes the delegate's <code>read(byte[], int, int)</code> method.
+     * @param b the buffer to read the bytes into
+     * @param off The start offset
+     * @param len The number of bytes to read
+     * @return the number of bytes read or -1 if the end of stream or
+     * the limit has been reached.
+     * @throws IOException if an I/O error occurs
+     */
+    @Override
+    public int read(byte[] b, int off, int len) throws IOException {
+        if (max>=0 && pos>=max) {
+            return -1;
+        }
+        long maxRead = max>=0 ? Math.min(len, max-pos) : len;
+        int bytesRead = in.read(b, off, (int)maxRead);
+
+        if (bytesRead==-1) {
+            return -1;
+        }
+
+        pos+=bytesRead;
+        return bytesRead;
+    }
+
+    /**
+     * Invokes the delegate's <code>skip(long)</code> method.
+     * @param n the number of bytes to skip
+     * @return the actual number of bytes skipped
+     * @throws IOException if an I/O error occurs
+     */
+    @Override
+    public long skip(long n) throws IOException {
+        long toSkip = max>=0 ? Math.min(n, max-pos) : n;
+        long skippedBytes = in.skip(toSkip);
+        pos+=skippedBytes;
+        return skippedBytes;
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public int available() throws IOException {
+        if (max>=0 && pos>=max) {
+            return 0;
+        }
+        return in.available();
+    }
+
+    /**
+     * Invokes the delegate's <code>toString()</code> method.
+     * @return the delegate's <code>toString()</code>
+     */
+    @Override
+    public String toString() {
+        return in.toString();
+    }
+
+    /**
+     * Invokes the delegate's <code>close()</code> method
+     * if {@link #isPropagateClose()} is <code>true</code>.
+     * @throws IOException if an I/O error occurs
+     */
+    @Override
+    public void close() throws IOException {
+        if (propagateClose) {
+            in.close();
+        }
+    }
+
+    /**
+     * Invokes the delegate's <code>reset()</code> method.
+     * @throws IOException if an I/O error occurs
+     */
+    @Override
+    public synchronized void reset() throws IOException {
+        in.reset();
+        pos = mark;
+    }
+
+    /**
+     * Invokes the delegate's <code>mark(int)</code> method.
+     * @param readlimit read ahead limit
+     */
+    @Override
+    public synchronized void mark(int readlimit) {
+        in.mark(readlimit);
+        mark = pos;
+    }
+
+    /**
+     * Invokes the delegate's <code>markSupported()</code> method.
+     * @return true if mark is supported, otherwise false
+     */
+    @Override
+    public boolean markSupported() {
+        return in.markSupported();
+    }
+
+    /**
+     * Indicates whether the {@link #close()} method
+     * should propagate to the underling {@link InputStream}.
+     *
+     * @return <code>true</code> if calling {@link #close()}
+     * propagates to the <code>close()</code> method of the
+     * underlying stream or <code>false</code> if it does not.
+     */
+    public boolean isPropagateClose() {
+        return propagateClose;
+    }
+
+    /**
+     * Set whether the {@link #close()} method
+     * should propagate to the underling {@link InputStream}.
+     *
+     * @param propagateClose <code>true</code> if calling
+     * {@link #close()} propagates to the <code>close()</code>
+     * method of the underlying stream or
+     * <code>false</code> if it does not.
+     */
+    public void setPropagateClose(boolean propagateClose) {
+        this.propagateClose = propagateClose;
+    }
+}
index 7e6a7593baffaff79001dd62b8b06d7e8d9f4627..c3997dc5862ff4d5bada0b0929c82e686d92e9ba 100644 (file)
@@ -22,6 +22,7 @@ import java.util.zip.InflaterInputStream;
 import java.util.zip.DeflaterOutputStream;
 import java.util.Hashtable;
 
+import org.apache.poi.util.BoundedInputStream;
 import org.apache.poi.util.LittleEndian;
 
 /**
@@ -97,8 +98,10 @@ public class ExOleObjStg extends RecordAtom implements PositionDependentRecord,
      */
     public InputStream getData() {
         if (isCompressed()) {
+            int size = LittleEndian.getInt(_data);
+
             InputStream compressedStream = new ByteArrayInputStream(_data, 4, _data.length);
-            return new InflaterInputStream(compressedStream);
+            return new BoundedInputStream(new InflaterInputStream(compressedStream), size);
         } else {
             return new ByteArrayInputStream(_data, 0, _data.length);
         }