From b9a6d0a2e037552a78bc1fef8a2465ac8b4e01b9 Mon Sep 17 00:00:00 2001 From: Maxim Valyanskiy Date: Tue, 27 Mar 2012 08:32:47 +0000 Subject: [PATCH] #52991: Unexpected end of ZLIB input stream on embedded OLE extraction from PPT git-svn-id: https://svn.apache.org/repos/asf/poi/trunk@1305778 13f79535-47bb-0310-9956-ffa450edef68 --- .../apache/poi/util/BoundedInputStream.java | 230 ++++++++++++++++++ .../apache/poi/hslf/record/ExOleObjStg.java | 5 +- 2 files changed, 234 insertions(+), 1 deletion(-) create mode 100644 src/java/org/apache/poi/util/BoundedInputStream.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 index 000000000..78121163b --- /dev/null +++ b/src/java/org/apache/poi/util/BoundedInputStream.java @@ -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. + *

+ * 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 BoundedInputStream 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 BoundedInputStream 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 read() 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 read(byte[]) 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 read(byte[], int, int) 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 skip(long) 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 toString() method. + * @return the delegate's toString() + */ + @Override + public String toString() { + return in.toString(); + } + + /** + * Invokes the delegate's close() method + * if {@link #isPropagateClose()} is true. + * @throws IOException if an I/O error occurs + */ + @Override + public void close() throws IOException { + if (propagateClose) { + in.close(); + } + } + + /** + * Invokes the delegate's reset() method. + * @throws IOException if an I/O error occurs + */ + @Override + public synchronized void reset() throws IOException { + in.reset(); + pos = mark; + } + + /** + * Invokes the delegate's mark(int) method. + * @param readlimit read ahead limit + */ + @Override + public synchronized void mark(int readlimit) { + in.mark(readlimit); + mark = pos; + } + + /** + * Invokes the delegate's markSupported() 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 true if calling {@link #close()} + * propagates to the close() method of the + * underlying stream or false if it does not. + */ + public boolean isPropagateClose() { + return propagateClose; + } + + /** + * Set whether the {@link #close()} method + * should propagate to the underling {@link InputStream}. + * + * @param propagateClose true if calling + * {@link #close()} propagates to the close() + * method of the underlying stream or + * false if it does not. + */ + public void setPropagateClose(boolean propagateClose) { + this.propagateClose = propagateClose; + } +} diff --git a/src/scratchpad/src/org/apache/poi/hslf/record/ExOleObjStg.java b/src/scratchpad/src/org/apache/poi/hslf/record/ExOleObjStg.java index 7e6a7593b..c3997dc58 100644 --- a/src/scratchpad/src/org/apache/poi/hslf/record/ExOleObjStg.java +++ b/src/scratchpad/src/org/apache/poi/hslf/record/ExOleObjStg.java @@ -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); }