#59893 - Forbid calls to InputStream.available

git-svn-id: https://svn.apache.org/repos/asf/poi/trunk@1830400 13f79535-47bb-0310-9956-ffa450edef68
This commit is contained in:
Andreas Beeker 2018-04-27 21:38:19 +00:00
parent 2574fd2d3d
commit e0f7014503
22 changed files with 646 additions and 580 deletions

View File

@ -17,15 +17,17 @@
package org.apache.poi.poifs.poibrowser; package org.apache.poi.poifs.poibrowser;
import java.io.*; import java.io.IOException;
import org.apache.poi.poifs.filesystem.*;
import org.apache.poi.poifs.filesystem.DocumentInputStream;
import org.apache.poi.poifs.filesystem.POIFSDocumentPath;
import org.apache.poi.util.IOUtils; import org.apache.poi.util.IOUtils;
/** /**
* <p>Describes the most important (whatever that is) features of a * <p>Describes the most important (whatever that is) features of a
* {@link POIFSDocumentPath}.</p> * {@link POIFSDocumentPath}.</p>
*/ */
public class DocumentDescriptor class DocumentDescriptor
{ {
//arbitrarily selected; may need to increase //arbitrarily selected; may need to increase
@ -54,26 +56,20 @@ public class DocumentDescriptor
public DocumentDescriptor(final String name, public DocumentDescriptor(final String name,
final POIFSDocumentPath path, final POIFSDocumentPath path,
final DocumentInputStream stream, final DocumentInputStream stream,
final int nrOfBytes) final int nrOfBytes) {
{
this.name = name; this.name = name;
this.path = path; this.path = path;
this.stream = stream; this.stream = stream;
try try {
{ if (stream.markSupported()) {
size = stream.available();
if (stream.markSupported())
{
stream.mark(nrOfBytes); stream.mark(nrOfBytes);
final byte[] b = IOUtils.safelyAllocate(nrOfBytes, MAX_RECORD_LENGTH); bytes = IOUtils.toByteArray(stream, nrOfBytes, MAX_RECORD_LENGTH);
final int read = stream.read(b, 0, Math.min(size, b.length));
bytes = new byte[read];
System.arraycopy(b, 0, bytes, 0, read);
stream.reset(); stream.reset();
} else {
bytes = new byte[0];
} }
} size = bytes.length + stream.available();
catch (IOException ex) } catch (IOException ex) {
{
System.out.println(ex); System.out.println(ex);
} }
} }

View File

@ -175,10 +175,12 @@ public class VariantSupport extends Variant {
try { try {
typedPropertyValue.readValue(lei); typedPropertyValue.readValue(lei);
} catch ( UnsupportedOperationException exc ) { } catch ( UnsupportedOperationException exc ) {
int propLength = Math.min( length, lei.available() ); try {
final byte[] v = IOUtils.safelyAllocate(propLength, MAX_RECORD_LENGTH); final byte[] v = IOUtils.toByteArray(lei, length, MAX_RECORD_LENGTH);
lei.readFully(v, 0, propLength);
throw new ReadingNotSupportedException( type, v ); throw new ReadingNotSupportedException( type, v );
} catch (IOException e) {
throw new RuntimeException(e);
}
} }
switch ( (int) type ) { switch ( (int) type ) {

View File

@ -17,14 +17,13 @@
package org.apache.poi.hssf.record; package org.apache.poi.hssf.record;
import java.io.ByteArrayInputStream;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
import org.apache.poi.util.HexDump; import org.apache.poi.util.HexDump;
import org.apache.poi.util.LittleEndian; import org.apache.poi.util.LittleEndian;
import org.apache.poi.util.LittleEndianByteArrayInputStream;
import org.apache.poi.util.LittleEndianByteArrayOutputStream; import org.apache.poi.util.LittleEndianByteArrayOutputStream;
import org.apache.poi.util.LittleEndianInputStream;
import org.apache.poi.util.RecordFormatException; import org.apache.poi.util.RecordFormatException;
/** /**
@ -85,8 +84,7 @@ public final class ObjRecord extends Record implements Cloneable {
*/ */
subrecords = new ArrayList<>(); subrecords = new ArrayList<>();
ByteArrayInputStream bais = new ByteArrayInputStream(subRecordData); LittleEndianByteArrayInputStream subRecStream = new LittleEndianByteArrayInputStream(subRecordData);
LittleEndianInputStream subRecStream = new LittleEndianInputStream(bais);
CommonObjectDataSubRecord cmo = (CommonObjectDataSubRecord)SubRecord.createSubRecord(subRecStream, 0); CommonObjectDataSubRecord cmo = (CommonObjectDataSubRecord)SubRecord.createSubRecord(subRecStream, 0);
subrecords.add(cmo); subrecords.add(cmo);
while (true) { while (true) {
@ -96,7 +94,7 @@ public final class ObjRecord extends Record implements Cloneable {
break; break;
} }
} }
int nRemainingBytes = bais.available(); final int nRemainingBytes = subRecordData.length-subRecStream.getReadIndex();
if (nRemainingBytes > 0) { if (nRemainingBytes > 0) {
// At present (Oct-2008), most unit test samples have (subRecordData.length % 2 == 0) // At present (Oct-2008), most unit test samples have (subRecordData.length % 2 == 0)
_isPaddedToQuadByteMultiple = subRecordData.length % MAX_PAD_ALIGNMENT == 0; _isPaddedToQuadByteMultiple = subRecordData.length % MAX_PAD_ALIGNMENT == 0;

View File

@ -32,6 +32,7 @@ import org.apache.poi.util.LittleEndian;
import org.apache.poi.util.LittleEndianConsts; import org.apache.poi.util.LittleEndianConsts;
import org.apache.poi.util.LittleEndianInput; import org.apache.poi.util.LittleEndianInput;
import org.apache.poi.util.RecordFormatException; import org.apache.poi.util.RecordFormatException;
import org.apache.poi.util.SuppressForbidden;
public final class Biff8DecryptingStream implements BiffHeaderInput, LittleEndianInput { public final class Biff8DecryptingStream implements BiffHeaderInput, LittleEndianInput {
@ -39,7 +40,6 @@ public final class Biff8DecryptingStream implements BiffHeaderInput, LittleEndia
//arbitrarily selected; may need to increase //arbitrarily selected; may need to increase
private static final int MAX_RECORD_LENGTH = 100_000; private static final int MAX_RECORD_LENGTH = 100_000;
private final EncryptionInfo info;
private ChunkedCipherInputStream ccis; private ChunkedCipherInputStream ccis;
private final byte buffer[] = new byte[LittleEndianConsts.LONG_SIZE]; private final byte buffer[] = new byte[LittleEndianConsts.LONG_SIZE];
private boolean shouldSkipEncryptionOnCurrentRecord; private boolean shouldSkipEncryptionOnCurrentRecord;
@ -55,8 +55,7 @@ public final class Biff8DecryptingStream implements BiffHeaderInput, LittleEndia
((PushbackInputStream)stream).unread(initialBuf); ((PushbackInputStream)stream).unread(initialBuf);
} }
this.info = info; Decryptor dec = info.getDecryptor();
Decryptor dec = this.info.getDecryptor();
dec.setChunkSize(RC4_REKEYING_INTERVAL); dec.setChunkSize(RC4_REKEYING_INTERVAL);
ccis = (ChunkedCipherInputStream)dec.getDataStream(stream, Integer.MAX_VALUE, 0); ccis = (ChunkedCipherInputStream)dec.getDataStream(stream, Integer.MAX_VALUE, 0);
@ -69,6 +68,7 @@ public final class Biff8DecryptingStream implements BiffHeaderInput, LittleEndia
} }
@Override @Override
@SuppressForbidden("just delegating")
public int available() { public int available() {
return ccis.available(); return ccis.available();
} }

View File

@ -97,7 +97,7 @@ public abstract class ChunkedCipherInputStream extends LittleEndianInputStream {
private int read(byte[] b, int off, int len, boolean readPlain) throws IOException { private int read(byte[] b, int off, int len, boolean readPlain) throws IOException {
int total = 0; int total = 0;
if (available() <= 0) { if (remainingBytes() <= 0) {
return -1; return -1;
} }
@ -112,7 +112,7 @@ public abstract class ChunkedCipherInputStream extends LittleEndianInputStream {
} }
} }
int count = (int)(chunk.length - (pos & chunkMask)); int count = (int)(chunk.length - (pos & chunkMask));
int avail = available(); int avail = remainingBytes();
if (avail == 0) { if (avail == 0) {
return total; return total;
} }
@ -133,7 +133,7 @@ public abstract class ChunkedCipherInputStream extends LittleEndianInputStream {
} }
@Override @Override
public long skip(final long n) throws IOException { public long skip(final long n) {
long start = pos; long start = pos;
long skip = Math.min(remainingBytes(), n); long skip = Math.min(remainingBytes(), n);
@ -169,7 +169,7 @@ public abstract class ChunkedCipherInputStream extends LittleEndianInputStream {
} }
@Override @Override
public synchronized void reset() throws IOException { public synchronized void reset() {
throw new UnsupportedOperationException(); throw new UnsupportedOperationException();
} }
@ -193,7 +193,7 @@ public abstract class ChunkedCipherInputStream extends LittleEndianInputStream {
} }
final int todo = (int)Math.min(size, chunk.length); final int todo = (int)Math.min(size, chunk.length);
int readBytes = 0, totalBytes = 0; int readBytes, totalBytes = 0;
do { do {
readBytes = super.read(plain, totalBytes, todo-totalBytes); readBytes = super.read(plain, totalBytes, todo-totalBytes);
totalBytes += Math.max(0, readBytes); totalBytes += Math.max(0, readBytes);
@ -211,10 +211,6 @@ public abstract class ChunkedCipherInputStream extends LittleEndianInputStream {
/** /**
* Helper function for overriding the cipher invocation, i.e. XOR doesn't use a cipher * Helper function for overriding the cipher invocation, i.e. XOR doesn't use a cipher
* and uses it's own implementation * and uses it's own implementation
*
* @throws BadPaddingException
* @throws IllegalBlockSizeException
* @throws ShortBufferException
*/ */
protected int invokeCipher(int totalBytes, boolean doFinal) throws GeneralSecurityException { protected int invokeCipher(int totalBytes, boolean doFinal) throws GeneralSecurityException {
if (doFinal) { if (doFinal) {

View File

@ -273,11 +273,9 @@ public class POIFSReader
while (listeners.hasNext()) while (listeners.hasNext())
{ {
POIFSReaderListener listener = listeners.next(); POIFSReaderListener listener = listeners.next();
try (DocumentInputStream dis = new DocumentInputStream(document)) {
listener.processPOIFSReaderEvent( listener.processPOIFSReaderEvent(new POIFSReaderEvent(dis, path, name));
new POIFSReaderEvent( }
new DocumentInputStream(document), path,
name));
} }
} }
else else

View File

@ -138,7 +138,7 @@ public final class ODocumentInputStream extends DocumentInputStream {
if (atEOD()) { if (atEOD()) {
return EOF; return EOF;
} }
int limit = Math.min(available(), len); int limit = Math.min(_document_size - _current_offset, len);
readFully(b, off, limit); readFully(b, off, limit);
return limit; return limit;
} }

View File

@ -28,6 +28,7 @@ import java.awt.image.AffineTransformOp;
import java.awt.image.BufferedImage; import java.awt.image.BufferedImage;
import java.awt.image.RescaleOp; import java.awt.image.RescaleOp;
import java.io.ByteArrayInputStream; import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException; import java.io.IOException;
import java.io.InputStream; import java.io.InputStream;
import java.util.Iterator; import java.util.Iterator;
@ -39,6 +40,7 @@ import javax.imageio.ImageTypeSpecifier;
import javax.imageio.stream.ImageInputStream; import javax.imageio.stream.ImageInputStream;
import javax.imageio.stream.MemoryCacheImageInputStream; import javax.imageio.stream.MemoryCacheImageInputStream;
import org.apache.poi.util.IOUtils;
import org.apache.poi.util.POILogFactory; import org.apache.poi.util.POILogFactory;
import org.apache.poi.util.POILogger; import org.apache.poi.util.POILogger;
@ -69,20 +71,24 @@ public class BitmapImageRenderer implements ImageRenderer {
* @return the bufferedImage or null, if there was no image reader for this content type * @return the bufferedImage or null, if there was no image reader for this content type
* @throws IOException thrown if there was an error while processing the image * @throws IOException thrown if there was an error while processing the image
*/ */
private static BufferedImage readImage(InputStream data, String contentType) throws IOException { private static BufferedImage readImage(final InputStream data, final String contentType) throws IOException {
IOException lastException = null; IOException lastException = null;
BufferedImage img = null; BufferedImage img = null;
if (data.markSupported()) {
data.mark(data.available()); final ByteArrayInputStream bis;
if (data instanceof ByteArrayInputStream) {
bis = (ByteArrayInputStream)data;
} else {
ByteArrayOutputStream bos = new ByteArrayOutputStream(0x3FFFF);
IOUtils.copy(data, bos);
bis = new ByteArrayInputStream(bos.toByteArray());
} }
// currently don't use FileCacheImageInputStream, // currently don't use FileCacheImageInputStream,
// because of the risk of filling the file handles (see #59166) // because of the risk of filling the file handles (see #59166)
ImageInputStream iis = new MemoryCacheImageInputStream(data); ImageInputStream iis = new MemoryCacheImageInputStream(bis);
try { try {
iis = new MemoryCacheImageInputStream(data);
iis.mark();
Iterator<ImageReader> iter = ImageIO.getImageReaders(iis); Iterator<ImageReader> iter = ImageIO.getImageReaders(iis);
while (img==null && iter.hasNext()) { while (img==null && iter.hasNext()) {
ImageReader reader = iter.next(); ImageReader reader = iter.next();
@ -90,21 +96,11 @@ public class BitmapImageRenderer implements ImageRenderer {
// 0:default mode, 1:fallback mode // 0:default mode, 1:fallback mode
for (int mode=0; img==null && mode<3; mode++) { for (int mode=0; img==null && mode<3; mode++) {
lastException = null; lastException = null;
try { if (mode > 0) {
iis.reset(); bis.reset();
} catch (IOException e) {
if (data.markSupported()) {
data.reset();
data.mark(data.available());
iis.close(); iis.close();
iis = new MemoryCacheImageInputStream(data); iis = new MemoryCacheImageInputStream(bis);
} else {
// can't restore the input stream, so we need to stop processing here
lastException = e;
break;
} }
}
iis.mark();
try { try {

View File

@ -116,21 +116,57 @@ public final class IOUtils {
* @return A byte array with the read bytes. * @return A byte array with the read bytes.
* @throws IOException If reading data fails or EOF is encountered too early for the given length. * @throws IOException If reading data fails or EOF is encountered too early for the given length.
*/ */
public static byte[] toByteArray(InputStream stream, int length) throws IOException { public static byte[] toByteArray(InputStream stream, final int length) throws IOException {
ByteArrayOutputStream baos = new ByteArrayOutputStream(length == Integer.MAX_VALUE ? 4096 : length); return toByteArray(stream, length, Integer.MAX_VALUE);
}
/**
* Reads up to {@code length} bytes from the input stream, and returns the bytes read.
*
* @param stream The byte stream of data to read.
* @param length The maximum length to read, use {@link Integer#MAX_VALUE} to read the stream
* until EOF
* @param maxLength if the input is equal to/longer than {@code maxLength} bytes,
* then throw an {@link IOException} complaining about the length.
* use {@link Integer#MAX_VALUE} to disable the check
* @return A byte array with the read bytes.
* @throws IOException If reading data fails or EOF is encountered too early for the given length.
*/
public static byte[] toByteArray(InputStream stream, final long length, final int maxLength) throws IOException {
if (length < 0L || maxLength < 0L) {
throw new RecordFormatException("Can't allocate an array of length < 0");
}
if (length > (long)Integer.MAX_VALUE) {
throw new RecordFormatException("Can't allocate an array > "+Integer.MAX_VALUE);
}
if (BYTE_ARRAY_MAX_OVERRIDE > 0) {
if (length > BYTE_ARRAY_MAX_OVERRIDE) {
throwRFE(length, BYTE_ARRAY_MAX_OVERRIDE);
}
} else if (length > maxLength) {
throwRFE(length, maxLength);
}
final int len = Math.min((int)length, maxLength);
ByteArrayOutputStream baos = new ByteArrayOutputStream(len == Integer.MAX_VALUE ? 4096 : len);
byte[] buffer = new byte[4096]; byte[] buffer = new byte[4096];
int totalBytes = 0, readBytes; int totalBytes = 0, readBytes;
do { do {
readBytes = stream.read(buffer, 0, Math.min(buffer.length, length-totalBytes)); readBytes = stream.read(buffer, 0, Math.min(buffer.length, len-totalBytes));
totalBytes += Math.max(readBytes,0); totalBytes += Math.max(readBytes,0);
if (readBytes > 0) { if (readBytes > 0) {
baos.write(buffer, 0, readBytes); baos.write(buffer, 0, readBytes);
} }
} while (totalBytes < length && readBytes > -1); } while (totalBytes < len && readBytes > -1);
if (length != Integer.MAX_VALUE && totalBytes < length) { if (maxLength != Integer.MAX_VALUE && totalBytes == maxLength) {
throw new IOException("unexpected EOF"); throw new IOException("MaxLength ("+maxLength+") reached - stream seems to be invalid.");
}
if (len != Integer.MAX_VALUE && totalBytes < len) {
throw new EOFException("unexpected EOF");
} }
return baos.toByteArray(); return baos.toByteArray();
@ -350,19 +386,19 @@ public final class IOUtils {
* *
* @param inp The {@link InputStream} which provides the data * @param inp The {@link InputStream} which provides the data
* @param out The {@link OutputStream} to write the data to * @param out The {@link OutputStream} to write the data to
* @return the amount of bytes copied
*
* @throws IOException If copying the data fails. * @throws IOException If copying the data fails.
*/ */
public static void copy(InputStream inp, OutputStream out) throws IOException { public static long copy(InputStream inp, OutputStream out) throws IOException {
byte[] buff = new byte[4096]; final byte[] buff = new byte[4096];
int count; long totalCount = 0;
while ((count = inp.read(buff)) != -1) { for (int count; (count = inp.read(buff)) != -1; totalCount += count) {
if (count < -1) {
throw new RecordFormatException("Can't have read < -1 bytes");
}
if (count > 0) { if (count > 0) {
out.write(buff, 0, count); out.write(buff, 0, count);
} }
} }
return totalCount;
} }
/** /**
@ -370,16 +406,18 @@ public final class IOUtils {
* *
* @param srcStream The {@link InputStream} which provides the data * @param srcStream The {@link InputStream} which provides the data
* @param destFile The file where the data should be stored * @param destFile The file where the data should be stored
* @return the amount of bytes copied
*
* @throws IOException If the target directory does not exist and cannot be created * @throws IOException If the target directory does not exist and cannot be created
* or if copying the data fails. * or if copying the data fails.
*/ */
public static void copy(InputStream srcStream, File destFile) throws IOException { public static long copy(InputStream srcStream, File destFile) throws IOException {
File destDirectory = destFile.getParentFile(); File destDirectory = destFile.getParentFile();
if (!(destDirectory.exists() || destDirectory.mkdirs())) { if (!(destDirectory.exists() || destDirectory.mkdirs())) {
throw new RuntimeException("Can't create destination directory: "+destDirectory); throw new RuntimeException("Can't create destination directory: "+destDirectory);
} }
try (OutputStream destStream = new FileOutputStream(destFile)) { try (OutputStream destStream = new FileOutputStream(destFile)) {
IOUtils.copy(srcStream, destStream); return IOUtils.copy(srcStream, destStream);
} }
} }

View File

@ -30,6 +30,7 @@ import java.util.Date;
import org.apache.poi.POIXMLProperties.CoreProperties; import org.apache.poi.POIXMLProperties.CoreProperties;
import org.apache.poi.openxml4j.util.Nullable; import org.apache.poi.openxml4j.util.Nullable;
import org.apache.poi.util.IOUtils;
import org.apache.poi.util.LocaleUtil; import org.apache.poi.util.LocaleUtil;
import org.apache.poi.xssf.XSSFTestDataSamples; import org.apache.poi.xssf.XSSFTestDataSamples;
import org.apache.poi.xssf.usermodel.XSSFWorkbook; import org.apache.poi.xssf.usermodel.XSSFWorkbook;
@ -249,20 +250,18 @@ public final class TestPOIXMLProperties {
// Adding / changing // Adding / changing
ByteArrayInputStream imageData = new ByteArrayInputStream(new byte[1]); ByteArrayInputStream imageData = new ByteArrayInputStream(new byte[1]);
assertEquals(1, imageData.available());
noThumbProps.setThumbnail("Testing.png", imageData); noThumbProps.setThumbnail("Testing.png", imageData);
assertNotNull(noThumbProps.getThumbnailPart()); assertNotNull(noThumbProps.getThumbnailPart());
assertEquals("/Testing.png", noThumbProps.getThumbnailFilename()); assertEquals("/Testing.png", noThumbProps.getThumbnailFilename());
assertNotNull(noThumbProps.getThumbnailImage()); assertNotNull(noThumbProps.getThumbnailImage());
assertEquals(1, noThumbProps.getThumbnailImage().available()); assertEquals(1, IOUtils.toByteArray(noThumbProps.getThumbnailImage()).length);
imageData = new ByteArrayInputStream(new byte[2]); imageData = new ByteArrayInputStream(new byte[2]);
assertEquals(2, imageData.available());
noThumbProps.setThumbnail("Testing2.png", imageData); noThumbProps.setThumbnail("Testing2.png", imageData);
assertNotNull(noThumbProps.getThumbnailPart()); assertNotNull(noThumbProps.getThumbnailPart());
assertEquals("/Testing.png", noThumbProps.getThumbnailFilename()); assertEquals("/Testing.png", noThumbProps.getThumbnailFilename());
assertNotNull(noThumbProps.getThumbnailImage()); assertNotNull(noThumbProps.getThumbnailImage());
assertEquals(2, noThumbProps.getThumbnailImage().available()); assertEquals(2, IOUtils.toByteArray(noThumbProps.getThumbnailImage()).length);
} }
private static String zeroPad(long i) { private static String zeroPad(long i) {

View File

@ -16,6 +16,7 @@
==================================================================== */ ==================================================================== */
package org.apache.poi.openxml4j.util; package org.apache.poi.openxml4j.util;
import org.apache.poi.util.IOUtils;
import org.apache.poi.xssf.XSSFTestDataSamples; import org.apache.poi.xssf.XSSFTestDataSamples;
import org.junit.Test; import org.junit.Test;
@ -39,8 +40,9 @@ public class TestZipSecureFile {
while (entries.hasMoreElements()) { while (entries.hasMoreElements()) {
ZipEntry entry = entries.nextElement(); ZipEntry entry = entries.nextElement();
InputStream inputStream = secureFile.getInputStream(entry); try (InputStream inputStream = secureFile.getInputStream(entry)) {
assertTrue(inputStream.available() > 0); assertTrue(IOUtils.toByteArray(inputStream).length > 0);
}
} }
} }
} }

View File

@ -24,7 +24,6 @@ import static org.junit.Assert.fail;
import java.io.ByteArrayInputStream; import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream; import java.io.ByteArrayOutputStream;
import java.io.File; import java.io.File;
import java.io.FileInputStream;
import java.io.IOException; import java.io.IOException;
import java.io.InputStream; import java.io.InputStream;
import java.security.GeneralSecurityException; import java.security.GeneralSecurityException;
@ -39,58 +38,47 @@ import org.apache.poi.poifs.filesystem.DirectoryNode;
import org.apache.poi.poifs.filesystem.NPOIFSFileSystem; import org.apache.poi.poifs.filesystem.NPOIFSFileSystem;
import org.apache.poi.poifs.filesystem.POIFSFileSystem; import org.apache.poi.poifs.filesystem.POIFSFileSystem;
import org.apache.poi.util.IOUtils; import org.apache.poi.util.IOUtils;
import org.apache.poi.xssf.XSSFTestDataSamples;
import org.junit.Assume; import org.junit.Assume;
import org.junit.Test; import org.junit.Test;
public class TestDecryptor { public class TestDecryptor {
private static final POIDataSamples samples = POIDataSamples.getPOIFSInstance();
@Test @Test
public void passwordVerification() throws IOException, GeneralSecurityException { public void passwordVerification() throws IOException, GeneralSecurityException {
POIFSFileSystem fs = new POIFSFileSystem(POIDataSamples.getPOIFSInstance().openResourceAsStream("protect.xlsx")); try (InputStream is = samples.openResourceAsStream("protect.xlsx");
POIFSFileSystem fs = new POIFSFileSystem(is)) {
EncryptionInfo info = new EncryptionInfo(fs); EncryptionInfo info = new EncryptionInfo(fs);
Decryptor d = Decryptor.getInstance(info); Decryptor d = Decryptor.getInstance(info);
assertTrue(d.verifyPassword(Decryptor.DEFAULT_PASSWORD)); assertTrue(d.verifyPassword(Decryptor.DEFAULT_PASSWORD));
}
fs.close();
} }
@Test @Test
public void decrypt() throws IOException, GeneralSecurityException { public void decrypt() throws IOException, GeneralSecurityException {
POIFSFileSystem fs = new POIFSFileSystem(POIDataSamples.getPOIFSInstance().openResourceAsStream("protect.xlsx")); try (InputStream is = samples.openResourceAsStream("protect.xlsx");
POIFSFileSystem fs = new POIFSFileSystem(is)) {
EncryptionInfo info = new EncryptionInfo(fs); EncryptionInfo info = new EncryptionInfo(fs);
Decryptor d = Decryptor.getInstance(info); Decryptor d = Decryptor.getInstance(info);
d.verifyPassword(Decryptor.DEFAULT_PASSWORD); d.verifyPassword(Decryptor.DEFAULT_PASSWORD);
zipOk(fs.getRoot(), d); zipOk(fs.getRoot(), d);
}
fs.close();
} }
@Test @Test
public void agile() throws IOException, GeneralSecurityException { public void agile() throws IOException, GeneralSecurityException {
POIFSFileSystem fs = new POIFSFileSystem(POIDataSamples.getPOIFSInstance().openResourceAsStream("protected_agile.docx")); try (InputStream is = samples.openResourceAsStream("protected_agile.docx");
POIFSFileSystem fs = new POIFSFileSystem(is)) {
EncryptionInfo info = new EncryptionInfo(fs); EncryptionInfo info = new EncryptionInfo(fs);
assertTrue(info.getVersionMajor() == 4 && info.getVersionMinor() == 4); assertTrue(info.getVersionMajor() == 4 && info.getVersionMinor() == 4);
Decryptor d = Decryptor.getInstance(info); Decryptor d = Decryptor.getInstance(info);
assertTrue(d.verifyPassword(Decryptor.DEFAULT_PASSWORD)); assertTrue(d.verifyPassword(Decryptor.DEFAULT_PASSWORD));
zipOk(fs.getRoot(), d); zipOk(fs.getRoot(), d);
}
fs.close();
} }
private void zipOk(DirectoryNode root, Decryptor d) throws IOException, GeneralSecurityException { private void zipOk(DirectoryNode root, Decryptor d) throws IOException, GeneralSecurityException {
ZipInputStream zin = new ZipInputStream(d.getDataStream(root)); try (ZipInputStream zin = new ZipInputStream(d.getDataStream(root))) {
while (true) { while (true) {
ZipEntry entry = zin.getNextEntry(); ZipEntry entry = zin.getNextEntry();
@ -101,34 +89,30 @@ public class TestDecryptor {
if (entry.isDirectory()) { if (entry.isDirectory()) {
continue; continue;
} }
zin.skip(entry.getSize()); assertEquals(entry.getSize() - 1, zin.skip(entry.getSize() - 1));
byte buf[] = new byte[10]; byte buf[] = new byte[10];
int readBytes = zin.read(buf); int readBytes = zin.read(buf);
// zin.available() doesn't work for entries // zin.available() doesn't work for entries
assertEquals("size failed for "+entry.getName(), -1, readBytes); assertEquals("size failed for " + entry.getName(), 1, readBytes);
}
} }
zin.close();
} }
@Test @Test
public void dataLength() throws Exception { public void dataLength() throws Exception {
POIFSFileSystem fs = new POIFSFileSystem(POIDataSamples.getPOIFSInstance().openResourceAsStream("protected_agile.docx")); try (InputStream fsIs = samples.openResourceAsStream("protected_agile.docx");
POIFSFileSystem fs = new POIFSFileSystem(fsIs)) {
EncryptionInfo info = new EncryptionInfo(fs); EncryptionInfo info = new EncryptionInfo(fs);
Decryptor d = Decryptor.getInstance(info); Decryptor d = Decryptor.getInstance(info);
d.verifyPassword(Decryptor.DEFAULT_PASSWORD); d.verifyPassword(Decryptor.DEFAULT_PASSWORD);
InputStream is = d.getDataStream(fs); try (InputStream is = d.getDataStream(fs)) {
long len = d.getLength(); long len = d.getLength();
assertEquals(12810, len); assertEquals(12810, len);
byte[] buf = new byte[(int) len]; byte[] buf = new byte[(int) len];
assertEquals(12810, is.read(buf));
is.read(buf);
ZipInputStream zin = new ZipInputStream(new ByteArrayInputStream(buf)); ZipInputStream zin = new ZipInputStream(new ByteArrayInputStream(buf));
@ -138,8 +122,8 @@ public class TestDecryptor {
break; break;
} }
while (zin.available()>0) { IOUtils.toByteArray(zin);
zin.skip(zin.available()); }
} }
} }
} }
@ -148,7 +132,7 @@ public class TestDecryptor {
public void bug57080() throws Exception { public void bug57080() throws Exception {
// the test file contains a wrong ole entry size, produced by extenxls // the test file contains a wrong ole entry size, produced by extenxls
// the fix limits the available size and tries to read all entries // the fix limits the available size and tries to read all entries
File f = POIDataSamples.getPOIFSInstance().getFile("extenxls_pwd123.xlsx"); File f = samples.getFile("extenxls_pwd123.xlsx");
try (NPOIFSFileSystem fs = new NPOIFSFileSystem(f, true)) { try (NPOIFSFileSystem fs = new NPOIFSFileSystem(f, true)) {
EncryptionInfo info = new EncryptionInfo(fs); EncryptionInfo info = new EncryptionInfo(fs);
@ -174,14 +158,12 @@ public class TestDecryptor {
@Test @Test
public void test58616() throws IOException, GeneralSecurityException { public void test58616() throws IOException, GeneralSecurityException {
FileInputStream fis = new FileInputStream(XSSFTestDataSamples.getSampleFile("58616.xlsx")); try (InputStream is = POIDataSamples.getSpreadSheetInstance().openResourceAsStream("58616.xlsx");
POIFSFileSystem pfs = new POIFSFileSystem(fis); POIFSFileSystem pfs = new POIFSFileSystem(is)) {
EncryptionInfo info = new EncryptionInfo(pfs); EncryptionInfo info = new EncryptionInfo(pfs);
Decryptor dec = Decryptor.getInstance(info); Decryptor dec = Decryptor.getInstance(info);
//dec.verifyPassword(null); dec.getDataStream(pfs).close();
dec.getDataStream(pfs); }
pfs.close();
fis.close();
} }
@Test @Test
@ -189,19 +171,12 @@ public class TestDecryptor {
int maxKeyLen = Cipher.getMaxAllowedKeyLength("AES"); int maxKeyLen = Cipher.getMaxAllowedKeyLength("AES");
Assume.assumeTrue("Please install JCE Unlimited Strength Jurisdiction Policy files for AES 256", maxKeyLen == 2147483647); Assume.assumeTrue("Please install JCE Unlimited Strength Jurisdiction Policy files for AES 256", maxKeyLen == 2147483647);
InputStream is = POIDataSamples.getPOIFSInstance().openResourceAsStream("60320-protected.xlsx"); try (InputStream is = samples.openResourceAsStream("60320-protected.xlsx");
POIFSFileSystem fs = new POIFSFileSystem(is); POIFSFileSystem fs = new POIFSFileSystem(is)) {
is.close();
EncryptionInfo info = new EncryptionInfo(fs); EncryptionInfo info = new EncryptionInfo(fs);
Decryptor d = Decryptor.getInstance(info); Decryptor d = Decryptor.getInstance(info);
assertTrue(d.verifyPassword("Test001!!"));
boolean b = d.verifyPassword("Test001!!");
assertTrue(b);
zipOk(fs.getRoot(), d); zipOk(fs.getRoot(), d);
}
fs.close();
} }
} }

View File

@ -36,18 +36,20 @@ import javax.crypto.Cipher;
import org.apache.poi.POIDataSamples; import org.apache.poi.POIDataSamples;
import org.apache.poi.openxml4j.opc.ContentTypes; import org.apache.poi.openxml4j.opc.ContentTypes;
import org.apache.poi.openxml4j.opc.OPCPackage; import org.apache.poi.openxml4j.opc.OPCPackage;
import org.apache.poi.openxml4j.opc.PackageAccess;
import org.apache.poi.poifs.crypt.agile.AgileDecryptor; import org.apache.poi.poifs.crypt.agile.AgileDecryptor;
import org.apache.poi.poifs.crypt.agile.AgileEncryptionHeader; import org.apache.poi.poifs.crypt.agile.AgileEncryptionHeader;
import org.apache.poi.poifs.crypt.agile.AgileEncryptionVerifier; import org.apache.poi.poifs.crypt.agile.AgileEncryptionVerifier;
import org.apache.poi.poifs.filesystem.DirectoryNode; import org.apache.poi.poifs.filesystem.DirectoryNode;
import org.apache.poi.poifs.filesystem.DocumentEntry;
import org.apache.poi.poifs.filesystem.DocumentNode; import org.apache.poi.poifs.filesystem.DocumentNode;
import org.apache.poi.poifs.filesystem.Entry; import org.apache.poi.poifs.filesystem.Entry;
import org.apache.poi.poifs.filesystem.NPOIFSFileSystem; import org.apache.poi.poifs.filesystem.NPOIFSFileSystem;
import org.apache.poi.poifs.filesystem.POIFSFileSystem; import org.apache.poi.poifs.filesystem.POIFSFileSystem;
import org.apache.poi.util.BoundedInputStream;
import org.apache.poi.util.IOUtils; import org.apache.poi.util.IOUtils;
import org.apache.poi.util.TempFile; import org.apache.poi.util.TempFile;
import org.apache.poi.xwpf.usermodel.XWPFDocument; import org.apache.poi.xwpf.usermodel.XWPFDocument;
import org.apache.poi.xwpf.usermodel.XWPFParagraph;
import org.junit.Assume; import org.junit.Assume;
import org.junit.Ignore; import org.junit.Ignore;
import org.junit.Test; import org.junit.Test;
@ -59,35 +61,37 @@ public class TestEncryptor {
// ... at least the output can be opened in Excel Viewer // ... at least the output can be opened in Excel Viewer
String password = "pass"; String password = "pass";
InputStream is = POIDataSamples.getSpreadSheetInstance().openResourceAsStream("SimpleMultiCell.xlsx"); final byte[] payloadExpected;
ByteArrayOutputStream payloadExpected = new ByteArrayOutputStream(); try (InputStream is = POIDataSamples.getSpreadSheetInstance().openResourceAsStream("SimpleMultiCell.xlsx")) {
IOUtils.copy(is, payloadExpected); payloadExpected = IOUtils.toByteArray(is);
is.close(); }
POIFSFileSystem fs = new POIFSFileSystem(); ByteArrayOutputStream bos = new ByteArrayOutputStream();
try (POIFSFileSystem fs = new POIFSFileSystem()) {
EncryptionInfo ei = new EncryptionInfo(EncryptionMode.binaryRC4); EncryptionInfo ei = new EncryptionInfo(EncryptionMode.binaryRC4);
Encryptor enc = ei.getEncryptor(); Encryptor enc = ei.getEncryptor();
enc.confirmPassword(password); enc.confirmPassword(password);
OutputStream os = enc.getDataStream(fs.getRoot()); try (OutputStream os = enc.getDataStream(fs.getRoot())) {
payloadExpected.writeTo(os); os.write(payloadExpected);
os.close(); }
ByteArrayOutputStream bos = new ByteArrayOutputStream();
fs.writeFilesystem(bos); fs.writeFilesystem(bos);
}
fs = new POIFSFileSystem(new ByteArrayInputStream(bos.toByteArray())); final byte[] payloadActual;
ei = new EncryptionInfo(fs); try (POIFSFileSystem fs = new POIFSFileSystem(new ByteArrayInputStream(bos.toByteArray()))) {
EncryptionInfo ei = new EncryptionInfo(fs);
Decryptor dec = ei.getDecryptor(); Decryptor dec = ei.getDecryptor();
boolean b = dec.verifyPassword(password); boolean b = dec.verifyPassword(password);
assertTrue(b); assertTrue(b);
ByteArrayOutputStream payloadActual = new ByteArrayOutputStream(); try (InputStream is = dec.getDataStream(fs.getRoot())) {
is = dec.getDataStream(fs.getRoot()); payloadActual = IOUtils.toByteArray(is);
IOUtils.copy(is,payloadActual); }
is.close(); }
assertArrayEquals(payloadExpected.toByteArray(), payloadActual.toByteArray()); assertArrayEquals(payloadExpected, payloadActual);
} }
@Test @Test
@ -97,43 +101,47 @@ public class TestEncryptor {
File file = POIDataSamples.getDocumentInstance().getFile("bug53475-password-is-pass.docx"); File file = POIDataSamples.getDocumentInstance().getFile("bug53475-password-is-pass.docx");
String pass = "pass"; String pass = "pass";
NPOIFSFileSystem nfs = new NPOIFSFileSystem(file);
final byte[] payloadExpected, encPackExpected;
final long decPackLenExpected;
final EncryptionInfo infoExpected;
final Decryptor decExpected;
try (NPOIFSFileSystem nfs = new NPOIFSFileSystem(file, true)) {
// Check the encryption details // Check the encryption details
EncryptionInfo infoExpected = new EncryptionInfo(nfs); infoExpected = new EncryptionInfo(nfs);
Decryptor decExpected = Decryptor.getInstance(infoExpected); decExpected = Decryptor.getInstance(infoExpected);
boolean passed = decExpected.verifyPassword(pass); boolean passed = decExpected.verifyPassword(pass);
assertTrue("Unable to process: document is encrypted", passed); assertTrue("Unable to process: document is encrypted", passed);
// extract the payload // extract the payload
InputStream is = decExpected.getDataStream(nfs); try (InputStream is = decExpected.getDataStream(nfs)) {
byte payloadExpected[] = IOUtils.toByteArray(is); payloadExpected = IOUtils.toByteArray(is);
is.close(); }
long decPackLenExpected = decExpected.getLength(); decPackLenExpected = decExpected.getLength();
assertEquals(decPackLenExpected, payloadExpected.length); assertEquals(decPackLenExpected, payloadExpected.length);
is = nfs.getRoot().createDocumentInputStream(Decryptor.DEFAULT_POIFS_ENTRY); final DirectoryNode root = nfs.getRoot();
is = new BoundedInputStream(is, is.available()-16); // ignore padding block final DocumentEntry entry = (DocumentEntry)root.getEntry(Decryptor.DEFAULT_POIFS_ENTRY);
byte encPackExpected[] = IOUtils.toByteArray(is); try (InputStream is = root.createDocumentInputStream(entry)) {
is.close(); // ignore padding block
encPackExpected = IOUtils.toByteArray(is, entry.getSize()-16);
// listDir(nfs.getRoot(), "orig", ""); }
}
nfs.close();
// check that same verifier/salt lead to same hashes // check that same verifier/salt lead to same hashes
byte verifierSaltExpected[] = infoExpected.getVerifier().getSalt(); final byte verifierSaltExpected[] = infoExpected.getVerifier().getSalt();
byte verifierExpected[] = decExpected.getVerifier(); final byte verifierExpected[] = decExpected.getVerifier();
byte keySalt[] = infoExpected.getHeader().getKeySalt(); final byte keySalt[] = infoExpected.getHeader().getKeySalt();
byte keySpec[] = decExpected.getSecretKey().getEncoded(); final byte keySpec[] = decExpected.getSecretKey().getEncoded();
byte integritySalt[] = decExpected.getIntegrityHmacKey(); final byte integritySalt[] = decExpected.getIntegrityHmacKey();
// the hmacs of the file always differ, as we use PKCS5-padding to pad the bytes // the hmacs of the file always differ, as we use PKCS5-padding to pad the bytes
// whereas office just uses random bytes // whereas office just uses random bytes
// byte integrityHash[] = d.getIntegrityHmacValue(); // byte integrityHash[] = d.getIntegrityHmacValue();
POIFSFileSystem fs = new POIFSFileSystem(); final EncryptionInfo infoActual = new EncryptionInfo(
EncryptionInfo infoActual = new EncryptionInfo(
EncryptionMode.agile EncryptionMode.agile
, infoExpected.getVerifier().getCipherAlgorithm() , infoExpected.getVerifier().getCipherAlgorithm()
, infoExpected.getVerifier().getHashAlgorithm() , infoExpected.getVerifier().getHashAlgorithm()
@ -145,35 +153,37 @@ public class TestEncryptor {
Encryptor e = Encryptor.getInstance(infoActual); Encryptor e = Encryptor.getInstance(infoActual);
e.confirmPassword(pass, keySpec, keySalt, verifierExpected, verifierSaltExpected, integritySalt); e.confirmPassword(pass, keySpec, keySalt, verifierExpected, verifierSaltExpected, integritySalt);
OutputStream os = e.getDataStream(fs);
IOUtils.copy(new ByteArrayInputStream(payloadExpected), os);
os.close();
ByteArrayOutputStream bos = new ByteArrayOutputStream(); ByteArrayOutputStream bos = new ByteArrayOutputStream();
try (POIFSFileSystem fs = new POIFSFileSystem()) {
try (OutputStream os = e.getDataStream(fs)) {
os.write(payloadExpected);
}
fs.writeFilesystem(bos); fs.writeFilesystem(bos);
fs.close(); }
nfs = new NPOIFSFileSystem(new ByteArrayInputStream(bos.toByteArray())); final EncryptionInfo infoActual2;
infoActual = new EncryptionInfo(nfs.getRoot()); final byte[] payloadActual, encPackActual;
Decryptor decActual = Decryptor.getInstance(infoActual); final long decPackLenActual;
passed = decActual.verifyPassword(pass); try (NPOIFSFileSystem nfs = new NPOIFSFileSystem(new ByteArrayInputStream(bos.toByteArray()))) {
infoActual2 = new EncryptionInfo(nfs.getRoot());
Decryptor decActual = Decryptor.getInstance(infoActual2);
boolean passed = decActual.verifyPassword(pass);
assertTrue("Unable to process: document is encrypted", passed); assertTrue("Unable to process: document is encrypted", passed);
// extract the payload // extract the payload
is = decActual.getDataStream(nfs); try (InputStream is = decActual.getDataStream(nfs)) {
byte payloadActual[] = IOUtils.toByteArray(is); payloadActual = IOUtils.toByteArray(is);
is.close(); }
long decPackLenActual = decActual.getLength(); decPackLenActual = decActual.getLength();
is = nfs.getRoot().createDocumentInputStream(Decryptor.DEFAULT_POIFS_ENTRY); final DirectoryNode root = nfs.getRoot();
is = new BoundedInputStream(is, is.available()-16); // ignore padding block final DocumentEntry entry = (DocumentEntry)root.getEntry(Decryptor.DEFAULT_POIFS_ENTRY);
byte encPackActual[] = IOUtils.toByteArray(is); try (InputStream is = root.createDocumentInputStream(entry)) {
is.close(); // ignore padding block
encPackActual = IOUtils.toByteArray(is, entry.getSize()-16);
// listDir(nfs.getRoot(), "copy", ""); }
}
nfs.close();
AgileEncryptionHeader aehExpected = (AgileEncryptionHeader)infoExpected.getHeader(); AgileEncryptionHeader aehExpected = (AgileEncryptionHeader)infoExpected.getHeader();
AgileEncryptionHeader aehActual = (AgileEncryptionHeader)infoActual.getHeader(); AgileEncryptionHeader aehActual = (AgileEncryptionHeader)infoActual.getHeader();
@ -186,32 +196,33 @@ public class TestEncryptor {
@Test @Test
public void standardEncryption() throws Exception { public void standardEncryption() throws Exception {
File file = POIDataSamples.getDocumentInstance().getFile("bug53475-password-is-solrcell.docx"); File file = POIDataSamples.getDocumentInstance().getFile("bug53475-password-is-solrcell.docx");
String pass = "solrcell"; final String pass = "solrcell";
NPOIFSFileSystem nfs = new NPOIFSFileSystem(file); final byte[] payloadExpected;
final EncryptionInfo infoExpected;
final Decryptor d;
try (NPOIFSFileSystem nfs = new NPOIFSFileSystem(file, true)) {
// Check the encryption details // Check the encryption details
EncryptionInfo infoExpected = new EncryptionInfo(nfs); infoExpected = new EncryptionInfo(nfs);
Decryptor d = Decryptor.getInstance(infoExpected); d = Decryptor.getInstance(infoExpected);
boolean passed = d.verifyPassword(pass); boolean passed = d.verifyPassword(pass);
assertTrue("Unable to process: document is encrypted", passed); assertTrue("Unable to process: document is encrypted", passed);
// extract the payload // extract the payload
ByteArrayOutputStream bos = new ByteArrayOutputStream(); try (InputStream is = d.getDataStream(nfs)) {
InputStream is = d.getDataStream(nfs); payloadExpected = IOUtils.toByteArray(is);
IOUtils.copy(is, bos); }
is.close(); }
nfs.close();
byte payloadExpected[] = bos.toByteArray();
// check that same verifier/salt lead to same hashes // check that same verifier/salt lead to same hashes
byte verifierSaltExpected[] = infoExpected.getVerifier().getSalt(); final byte verifierSaltExpected[] = infoExpected.getVerifier().getSalt();
byte verifierExpected[] = d.getVerifier(); final byte verifierExpected[] = d.getVerifier();
byte keySpec[] = d.getSecretKey().getEncoded(); final byte keySpec[] = d.getSecretKey().getEncoded();
byte keySalt[] = infoExpected.getHeader().getKeySalt(); final byte keySalt[] = infoExpected.getHeader().getKeySalt();
EncryptionInfo infoActual = new EncryptionInfo( final EncryptionInfo infoActual = new EncryptionInfo(
EncryptionMode.standard EncryptionMode.standard
, infoExpected.getVerifier().getCipherAlgorithm() , infoExpected.getVerifier().getCipherAlgorithm()
, infoExpected.getVerifier().getHashAlgorithm() , infoExpected.getVerifier().getHashAlgorithm()
@ -220,7 +231,7 @@ public class TestEncryptor {
, infoExpected.getVerifier().getChainingMode() , infoExpected.getVerifier().getChainingMode()
); );
Encryptor e = Encryptor.getInstance(infoActual); final Encryptor e = Encryptor.getInstance(infoActual);
e.confirmPassword(pass, keySpec, keySalt, verifierExpected, verifierSaltExpected, null); e.confirmPassword(pass, keySpec, keySalt, verifierExpected, verifierSaltExpected, null);
assertArrayEquals(infoExpected.getVerifier().getEncryptedVerifier(), infoActual.getVerifier().getEncryptedVerifier()); assertArrayEquals(infoExpected.getVerifier().getEncryptedVerifier(), infoActual.getVerifier().getEncryptedVerifier());
@ -229,7 +240,10 @@ public class TestEncryptor {
// now we use a newly generated salt/verifier and check // now we use a newly generated salt/verifier and check
// if the file content is still the same // if the file content is still the same
infoActual = new EncryptionInfo( final byte[] encBytes;
try (POIFSFileSystem fs = new POIFSFileSystem()) {
final EncryptionInfo infoActual2 = new EncryptionInfo(
EncryptionMode.standard EncryptionMode.standard
, infoExpected.getVerifier().getCipherAlgorithm() , infoExpected.getVerifier().getCipherAlgorithm()
, infoExpected.getVerifier().getHashAlgorithm() , infoExpected.getVerifier().getHashAlgorithm()
@ -238,36 +252,28 @@ public class TestEncryptor {
, infoExpected.getVerifier().getChainingMode() , infoExpected.getVerifier().getChainingMode()
); );
e = Encryptor.getInstance(infoActual); final Encryptor e2 = Encryptor.getInstance(infoActual2);
e.confirmPassword(pass); e2.confirmPassword(pass);
POIFSFileSystem fs = new POIFSFileSystem(); try (OutputStream os = e2.getDataStream(fs)) {
OutputStream os = e.getDataStream(fs); os.write(payloadExpected);
IOUtils.copy(new ByteArrayInputStream(payloadExpected), os); }
os.close();
bos.reset(); final ByteArrayOutputStream bos = new ByteArrayOutputStream(50000);
fs.writeFilesystem(bos); fs.writeFilesystem(bos);
encBytes = bos.toByteArray();
}
ByteArrayInputStream bis = new ByteArrayInputStream(bos.toByteArray()); final byte[] payloadActual;
try (NPOIFSFileSystem nfs = new NPOIFSFileSystem(new ByteArrayInputStream(encBytes))) {
final EncryptionInfo ei = new EncryptionInfo(nfs);
Decryptor d2 = Decryptor.getInstance(ei);
assertTrue("Unable to process: document is encrypted", d2.verifyPassword(pass));
// FileOutputStream fos = new FileOutputStream("encrypted.docx"); try (InputStream is = d2.getDataStream(nfs)) {
// IOUtils.copy(bis, fos); payloadActual = IOUtils.toByteArray(is);
// fos.close(); }
// bis.reset(); }
nfs = new NPOIFSFileSystem(bis);
infoExpected = new EncryptionInfo(nfs);
d = Decryptor.getInstance(infoExpected);
passed = d.verifyPassword(pass);
assertTrue("Unable to process: document is encrypted", passed);
bos.reset();
is = d.getDataStream(nfs);
IOUtils.copy(is, bos);
is.close();
nfs.close();
byte payloadActual[] = bos.toByteArray();
assertArrayEquals(payloadExpected, payloadActual); assertArrayEquals(payloadExpected, payloadActual);
} }
@ -281,8 +287,9 @@ public class TestEncryptor {
@Test @Test
public void encryptPackageWithoutCoreProperties() throws Exception { public void encryptPackageWithoutCoreProperties() throws Exception {
// Open our file without core properties // Open our file without core properties
File inp = POIDataSamples.getOpenXML4JInstance().getFile("OPCCompliance_NoCoreProperties.xlsx"); final byte[] encBytes;
OPCPackage pkg = OPCPackage.open(inp.getPath()); try (InputStream is = POIDataSamples.getOpenXML4JInstance().openResourceAsStream("OPCCompliance_NoCoreProperties.xlsx");
OPCPackage pkg = OPCPackage.open(is)) {
// It doesn't have any core properties yet // It doesn't have any core properties yet
assertEquals(0, pkg.getPartsByContentType(ContentTypes.CORE_PROPERTIES_PART).size()); assertEquals(0, pkg.getPartsByContentType(ContentTypes.CORE_PROPERTIES_PART).size());
@ -292,52 +299,51 @@ public class TestEncryptor {
// Encrypt it // Encrypt it
EncryptionInfo info = new EncryptionInfo(EncryptionMode.agile); EncryptionInfo info = new EncryptionInfo(EncryptionMode.agile);
NPOIFSFileSystem fs = new NPOIFSFileSystem();
Encryptor enc = info.getEncryptor(); Encryptor enc = info.getEncryptor();
enc.confirmPassword("password"); enc.confirmPassword("password");
OutputStream os = enc.getDataStream(fs);
try (NPOIFSFileSystem fs = new NPOIFSFileSystem()) {
try (OutputStream os = enc.getDataStream(fs)) {
pkg.save(os); pkg.save(os);
os.close(); }
pkg.revert();
// Save the resulting OLE2 document, and re-open it // Save the resulting OLE2 document, and re-open it
ByteArrayOutputStream baos = new ByteArrayOutputStream(); ByteArrayOutputStream baos = new ByteArrayOutputStream();
fs.writeFilesystem(baos); fs.writeFilesystem(baos);
fs.close(); encBytes = baos.toByteArray();
}
}
ByteArrayInputStream bais = new ByteArrayInputStream(baos.toByteArray());
NPOIFSFileSystem inpFS = new NPOIFSFileSystem(bais);
try (NPOIFSFileSystem inpFS = new NPOIFSFileSystem(new ByteArrayInputStream(encBytes))) {
// Check we can decrypt it // Check we can decrypt it
info = new EncryptionInfo(inpFS); EncryptionInfo info = new EncryptionInfo(inpFS);
Decryptor d = Decryptor.getInstance(info); Decryptor d = Decryptor.getInstance(info);
assertEquals(true, d.verifyPassword("password")); assertEquals(true, d.verifyPassword("password"));
OPCPackage inpPkg = OPCPackage.open(d.getDataStream(inpFS)); try (OPCPackage inpPkg = OPCPackage.open(d.getDataStream(inpFS))) {
// Check it now has empty core properties // Check it now has empty core properties
assertEquals(1, inpPkg.getPartsByContentType(ContentTypes.CORE_PROPERTIES_PART).size()); assertEquals(1, inpPkg.getPartsByContentType(ContentTypes.CORE_PROPERTIES_PART).size());
assertNotNull(inpPkg.getPackageProperties()); assertNotNull(inpPkg.getPackageProperties());
assertNotNull(inpPkg.getPackageProperties().getLanguageProperty()); assertNotNull(inpPkg.getPackageProperties().getLanguageProperty());
assertNull(inpPkg.getPackageProperties().getLanguageProperty().getValue()); assertNull(inpPkg.getPackageProperties().getLanguageProperty().getValue());
inpPkg.close(); }
inpFS.close(); }
} }
@Test @Test
@Ignore @Ignore
public void inPlaceRewrite() throws Exception { public void inPlaceRewrite() throws Exception {
File f = TempFile.createTempFile("protected_agile", ".docx"); File f = TempFile.createTempFile("protected_agile", ".docx");
// File f = new File("protected_agile.docx");
FileOutputStream fos = new FileOutputStream(f);
InputStream fis = POIDataSamples.getPOIFSInstance().openResourceAsStream("protected_agile.docx");
IOUtils.copy(fis, fos);
fis.close();
fos.close();
NPOIFSFileSystem fs = new NPOIFSFileSystem(f, false); try (FileOutputStream fos = new FileOutputStream(f);
InputStream fis = POIDataSamples.getPOIFSInstance().openResourceAsStream("protected_agile.docx")) {
IOUtils.copy(fis, fos);
}
try (NPOIFSFileSystem fs = new NPOIFSFileSystem(f, false)) {
// decrypt the protected file - in this case it was encrypted with the default password // decrypt the protected file - in this case it was encrypted with the default password
EncryptionInfo encInfo = new EncryptionInfo(fs); EncryptionInfo encInfo = new EncryptionInfo(fs);
@ -345,21 +351,23 @@ public class TestEncryptor {
boolean b = d.verifyPassword(Decryptor.DEFAULT_PASSWORD); boolean b = d.verifyPassword(Decryptor.DEFAULT_PASSWORD);
assertTrue(b); assertTrue(b);
try (InputStream docIS = d.getDataStream(fs);
XWPFDocument docx = new XWPFDocument(docIS)) {
// do some strange things with it ;) // do some strange things with it ;)
InputStream docIS = d.getDataStream(fs); XWPFParagraph p = docx.getParagraphArray(0);
XWPFDocument docx = new XWPFDocument(docIS); p.insertNewRun(0).setText("POI was here! All your base are belong to us!");
docx.getParagraphArray(0).insertNewRun(0).setText("POI was here! All your base are belong to us!"); p.insertNewRun(1).addBreak();
docx.getParagraphArray(0).insertNewRun(1).addBreak();
// and encrypt it again // and encrypt it again
Encryptor e = encInfo.getEncryptor(); Encryptor e = encInfo.getEncryptor();
e.confirmPassword("AYBABTU"); e.confirmPassword("AYBABTU");
docx.write(e.getDataStream(fs));
docx.close();
docIS.close();
docx.close(); try (OutputStream os = e.getDataStream(fs)) {
fs.close(); docx.write(os);
}
}
}
} }
@ -437,21 +445,23 @@ public class TestEncryptor {
// existing = getCipher(skey, header.getCipherAlgorithm(), header.getChainingMode(), header.getKeySalt(), encryptionMode, padding); // existing = getCipher(skey, header.getCipherAlgorithm(), header.getChainingMode(), header.getKeySalt(), encryptionMode, padding);
// } // }
InputStream is = POIDataSamples.getPOIFSInstance().openResourceAsStream("60320-protected.xlsx"); final EncryptionInfo infoOrig;
POIFSFileSystem fsOrig = new POIFSFileSystem(is); final byte[] zipInput, epOrigBytes;
is.close(); try (InputStream is = POIDataSamples.getPOIFSInstance().openResourceAsStream("60320-protected.xlsx");
EncryptionInfo infoOrig = new EncryptionInfo(fsOrig); POIFSFileSystem fsOrig = new POIFSFileSystem(is)) {
infoOrig = new EncryptionInfo(fsOrig);
Decryptor decOrig = infoOrig.getDecryptor(); Decryptor decOrig = infoOrig.getDecryptor();
boolean b = decOrig.verifyPassword("Test001!!"); boolean b = decOrig.verifyPassword("Test001!!");
assertTrue(b); assertTrue(b);
InputStream decIn = decOrig.getDataStream(fsOrig); try (InputStream decIn = decOrig.getDataStream(fsOrig)) {
byte[] zipInput = IOUtils.toByteArray(decIn); zipInput = IOUtils.toByteArray(decIn);
decIn.close(); }
InputStream epOrig = fsOrig.getRoot().createDocumentInputStream("EncryptedPackage"); try (InputStream epOrig = fsOrig.getRoot().createDocumentInputStream("EncryptedPackage")) {
// ignore the 16 padding bytes // ignore the 16 padding bytes
byte[] epOrigBytes = IOUtils.toByteArray(epOrig, 9400); epOrigBytes = IOUtils.toByteArray(epOrig, 9400);
epOrig.close(); }
}
EncryptionInfo eiNew = new EncryptionInfo(EncryptionMode.agile); EncryptionInfo eiNew = new EncryptionInfo(EncryptionMode.agile);
AgileEncryptionHeader aehHeader = (AgileEncryptionHeader)eiNew.getHeader(); AgileEncryptionHeader aehHeader = (AgileEncryptionHeader)eiNew.getHeader();
@ -472,26 +482,29 @@ public class TestEncryptor {
infoOrig.getVerifier().getSalt(), infoOrig.getVerifier().getSalt(),
infoOrig.getDecryptor().getIntegrityHmacKey() infoOrig.getDecryptor().getIntegrityHmacKey()
); );
NPOIFSFileSystem fsNew = new NPOIFSFileSystem();
OutputStream os = enc.getDataStream(fsNew); final byte[] epNewBytes;
final EncryptionInfo infoReload;
try (NPOIFSFileSystem fsNew = new NPOIFSFileSystem()) {
try (OutputStream os = enc.getDataStream(fsNew)) {
os.write(zipInput); os.write(zipInput);
os.close(); }
ByteArrayOutputStream bos = new ByteArrayOutputStream(); ByteArrayOutputStream bos = new ByteArrayOutputStream();
fsNew.writeFilesystem(bos); fsNew.writeFilesystem(bos);
fsNew.close();
NPOIFSFileSystem fsReload = new NPOIFSFileSystem(new ByteArrayInputStream(bos.toByteArray())); try (NPOIFSFileSystem fsReload = new NPOIFSFileSystem(new ByteArrayInputStream(bos.toByteArray()))) {
InputStream epReload = fsReload.getRoot().createDocumentInputStream("EncryptedPackage"); infoReload = new EncryptionInfo(fsReload);
byte[] epNewBytes = IOUtils.toByteArray(epReload, 9400); try (InputStream epReload = fsReload.getRoot().createDocumentInputStream("EncryptedPackage")) {
epReload.close(); epNewBytes = IOUtils.toByteArray(epReload, 9400);
}
}
}
assertArrayEquals(epOrigBytes, epNewBytes); assertArrayEquals(epOrigBytes, epNewBytes);
EncryptionInfo infoReload = new EncryptionInfo(fsOrig);
Decryptor decReload = infoReload.getDecryptor(); Decryptor decReload = infoReload.getDecryptor();
b = decReload.verifyPassword("Test001!!"); assertTrue(decReload.verifyPassword("Test001!!"));
assertTrue(b);
AgileEncryptionHeader aehOrig = (AgileEncryptionHeader)infoOrig.getHeader(); AgileEncryptionHeader aehOrig = (AgileEncryptionHeader)infoOrig.getHeader();
AgileEncryptionHeader aehReload = (AgileEncryptionHeader)infoReload.getHeader(); AgileEncryptionHeader aehReload = (AgileEncryptionHeader)infoReload.getHeader();
@ -529,7 +542,5 @@ public class TestEncryptor {
// assertArrayEquals(adOrig.getIntegrityHmacValue(), adReload.getIntegrityHmacValue()); // assertArrayEquals(adOrig.getIntegrityHmacValue(), adReload.getIntegrityHmacValue());
assertArrayEquals(adOrig.getSecretKey().getEncoded(), adReload.getSecretKey().getEncoded()); assertArrayEquals(adOrig.getSecretKey().getEncoded(), adReload.getSecretKey().getEncoded());
assertArrayEquals(adOrig.getVerifier(), adReload.getVerifier()); assertArrayEquals(adOrig.getVerifier(), adReload.getVerifier());
fsReload.close();
} }
} }

View File

@ -119,6 +119,9 @@ java.lang.Object#notifyAll()
@defaultMessage Don't interrupt threads use FutureUtils#cancel(Future<T>) instead @defaultMessage Don't interrupt threads use FutureUtils#cancel(Future<T>) instead
java.util.concurrent.Future#cancel(boolean) java.util.concurrent.Future#cancel(boolean)
@defaultMessage Don't use ...InputStream.available() as it gives wrong result for certain streams - use IOUtils.toByteArray to read the stream fully and then count the available bytes
java.io.InputStream#available()
@defaultMessage Unnecessary, inefficient, and confusing conversion of String.toString @defaultMessage Unnecessary, inefficient, and confusing conversion of String.toString
java.lang.String#toString() java.lang.String#toString()

View File

@ -171,7 +171,7 @@ public final class TextSpecInfoAtom extends RecordAtom {
public TextSpecInfoRun[] getTextSpecInfoRuns(){ public TextSpecInfoRun[] getTextSpecInfoRuns(){
LittleEndianByteArrayInputStream bis = new LittleEndianByteArrayInputStream(_data); // NOSONAR LittleEndianByteArrayInputStream bis = new LittleEndianByteArrayInputStream(_data); // NOSONAR
List<TextSpecInfoRun> lst = new ArrayList<>(); List<TextSpecInfoRun> lst = new ArrayList<>();
while (bis.available() > 0) { while (bis.getReadIndex() < _data.length) {
lst.add(new TextSpecInfoRun(bis)); lst.add(new TextSpecInfoRun(bis));
} }
return lst.toArray(new TextSpecInfoRun[lst.size()]); return lst.toArray(new TextSpecInfoRun[lst.size()]);

View File

@ -50,16 +50,16 @@ public class HwmfPicture {
final HwmfHeader header; final HwmfHeader header;
public HwmfPicture(InputStream inputStream) throws IOException { public HwmfPicture(InputStream inputStream) throws IOException {
BufferedInputStream bis = new BufferedInputStream(inputStream, 10000);
LittleEndianInputStream leis = new LittleEndianInputStream(bis); try (BufferedInputStream bis = new BufferedInputStream(inputStream, 10000);
LittleEndianInputStream leis = new LittleEndianInputStream(bis)) {
placeableHeader = HwmfPlaceableHeader.readHeader(leis); placeableHeader = HwmfPlaceableHeader.readHeader(leis);
header = new HwmfHeader(leis); header = new HwmfHeader(leis);
for (;;) { for (;;) {
if (leis.available() < 6) { long recordSize;
logger.log(POILogger.ERROR, "unexpected eof - wmf file was truncated"); int recordFunction;
break; try {
}
// recordSize in DWORDs // recordSize in DWORDs
long recordSizeLong = leis.readUInt()*2; long recordSizeLong = leis.readUInt()*2;
if (recordSizeLong > Integer.MAX_VALUE) { if (recordSizeLong > Integer.MAX_VALUE) {
@ -67,15 +67,21 @@ public class HwmfPicture {
} else if (recordSizeLong < 0L) { } else if (recordSizeLong < 0L) {
throw new RecordFormatException("record size can't be < 0"); throw new RecordFormatException("record size can't be < 0");
} }
int recordSize = (int)recordSizeLong; recordSize = (int)recordSizeLong;
int recordFunction = leis.readShort(); recordFunction = leis.readShort();
} catch (Exception e) {
logger.log(POILogger.ERROR, "unexpected eof - wmf file was truncated");
break;
}
// 4 bytes (recordSize) + 2 bytes (recordFunction) // 4 bytes (recordSize) + 2 bytes (recordFunction)
int consumedSize = 6; int consumedSize = 6;
HwmfRecordType wrt = HwmfRecordType.getById(recordFunction); HwmfRecordType wrt = HwmfRecordType.getById(recordFunction);
if (wrt == null) { if (wrt == null) {
throw new IOException("unexpected record type: "+recordFunction); throw new IOException("unexpected record type: "+recordFunction);
} }
if (wrt == HwmfRecordType.eof) break; if (wrt == HwmfRecordType.eof) {
break;
}
if (wrt.clazz == null) { if (wrt.clazz == null) {
throw new IOException("unsupported record type: "+recordFunction); throw new IOException("unsupported record type: "+recordFunction);
} }
@ -89,7 +95,7 @@ public class HwmfPicture {
} }
consumedSize += wr.init(leis, recordSize, recordFunction); consumedSize += wr.init(leis, recordSize, recordFunction);
int remainingSize = recordSize - consumedSize; int remainingSize = (int)(recordSize - consumedSize);
if (remainingSize < 0) { if (remainingSize < 0) {
throw new RecordFormatException("read too many bytes. record size: "+recordSize + "; comsumed size: "+consumedSize); throw new RecordFormatException("read too many bytes. record size: "+recordSize + "; comsumed size: "+consumedSize);
} else if(remainingSize > 0) { } else if(remainingSize > 0) {
@ -100,6 +106,7 @@ public class HwmfPicture {
} }
} }
} }
}
public List<HwmfRecord> getRecords() { public List<HwmfRecord> getRecords() {
return Collections.unmodifiableList(records); return Collections.unmodifiableList(records);

View File

@ -32,6 +32,7 @@ import java.io.FileInputStream;
import java.io.FileOutputStream; import java.io.FileOutputStream;
import java.io.FilterInputStream; import java.io.FilterInputStream;
import java.io.IOException; import java.io.IOException;
import java.io.InputStream;
import java.net.URL; import java.net.URL;
import java.nio.charset.Charset; import java.nio.charset.Charset;
import java.util.List; import java.util.List;
@ -57,32 +58,30 @@ import org.junit.Ignore;
import org.junit.Test; import org.junit.Test;
public class TestHwmfParsing { public class TestHwmfParsing {
private static final POIDataSamples samples = POIDataSamples.getSlideShowInstance();
@Test @Test
public void parse() throws IOException { public void parse() throws IOException {
File f = POIDataSamples.getSlideShowInstance().getFile("santa.wmf"); try (InputStream fis = samples.openResourceAsStream("santa.wmf")) {
FileInputStream fis = new FileInputStream(f);
HwmfPicture wmf = new HwmfPicture(fis); HwmfPicture wmf = new HwmfPicture(fis);
fis.close();
List<HwmfRecord> records = wmf.getRecords(); List<HwmfRecord> records = wmf.getRecords();
assertEquals(581, records.size()); assertEquals(581, records.size());
} }
}
@Test(expected = RecordFormatException.class) @Test(expected = RecordFormatException.class)
public void testInfiniteLoop() throws Exception { public void testInfiniteLoop() throws Exception {
File f = POIDataSamples.getSlideShowInstance().getFile("61338.wmf"); try (InputStream is = samples.openResourceAsStream("61338.wmf")) {
FileInputStream fis = null; new HwmfPicture(is);
try {
fis = new FileInputStream(f);
HwmfPicture wmf = new HwmfPicture(fis);
} finally {
fis.close();
} }
} }
@Test @Test
@Ignore("This is work-in-progress and not a real unit test ...") @Ignore("This is work-in-progress and not a real unit test ...")
public void paint() throws IOException { public void paint() throws IOException {
File f = POIDataSamples.getSlideShowInstance().getFile("santa.wmf"); File f = samples.getFile("santa.wmf");
// File f = new File("bla.wmf"); // File f = new File("bla.wmf");
FileInputStream fis = new FileInputStream(f); FileInputStream fis = new FileInputStream(f);
HwmfPicture wmf = new HwmfPicture(fis); HwmfPicture wmf = new HwmfPicture(fis);

View File

@ -29,6 +29,7 @@ import java.util.Map;
import org.apache.poi.poifs.eventfilesystem.POIFSReader; import org.apache.poi.poifs.eventfilesystem.POIFSReader;
import org.apache.poi.poifs.eventfilesystem.POIFSReaderEvent; import org.apache.poi.poifs.eventfilesystem.POIFSReaderEvent;
import org.apache.poi.poifs.eventfilesystem.POIFSReaderListener; import org.apache.poi.poifs.eventfilesystem.POIFSReaderListener;
import org.apache.poi.util.IOUtils;
/** /**
* Test (Proof of concept) program that employs the * Test (Proof of concept) program that employs the
@ -110,16 +111,15 @@ public class ReaderWriter
@Override @Override
public void processPOIFSReaderEvent(final POIFSReaderEvent event) public void processPOIFSReaderEvent(final POIFSReaderEvent event)
{ {
@SuppressWarnings("resource")
DocumentInputStream istream = event.getStream(); DocumentInputStream istream = event.getStream();
POIFSDocumentPath path = event.getPath(); POIFSDocumentPath path = event.getPath();
String name = event.getName(); String name = event.getName();
try try {
{ byte[] data = IOUtils.toByteArray(istream);
int size = istream.available(); int size = data.length;
byte[] data = new byte[ istream.available() ];
istream.read(data);
DocumentDescriptor descriptor = new DocumentDescriptor(path, DocumentDescriptor descriptor = new DocumentDescriptor(path,
name); name);

View File

@ -17,22 +17,28 @@
package org.apache.poi.poifs.filesystem; package org.apache.poi.poifs.filesystem;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
import java.io.ByteArrayInputStream; import java.io.ByteArrayInputStream;
import java.io.File; import java.io.File;
import java.io.FileInputStream; import java.io.FileInputStream;
import java.io.IOException; import java.io.IOException;
import java.io.InputStream;
import java.util.Arrays; import java.util.Arrays;
import junit.framework.TestCase;
import org.apache.poi.POIDataSamples; import org.apache.poi.POIDataSamples;
import org.apache.poi.poifs.property.DirectoryProperty; import org.apache.poi.poifs.property.DirectoryProperty;
import org.apache.poi.poifs.storage.RawDataBlock; import org.apache.poi.poifs.storage.RawDataBlock;
import org.apache.poi.util.SuppressForbidden;
import org.junit.Before;
import org.junit.Test;
/** /**
* Class to test DocumentInputStream functionality * Class to test DocumentInputStream functionality
*/ */
public final class TestDocumentInputStream extends TestCase { public final class TestDocumentInputStream {
private DocumentNode _workbook_n; private DocumentNode _workbook_n;
private DocumentNode _workbook_o; private DocumentNode _workbook_o;
private byte[] _workbook_data; private byte[] _workbook_data;
@ -42,8 +48,8 @@ public final class TestDocumentInputStream extends TestCase {
// any block size // any block size
private static final int _buffer_size = 6; private static final int _buffer_size = 6;
@Override @Before
protected void setUp() throws Exception { public void setUp() throws Exception {
int blocks = (_workbook_size + 511) / 512; int blocks = (_workbook_size + 511) / 512;
_workbook_data = new byte[ 512 * blocks ]; _workbook_data = new byte[ 512 * blocks ];
@ -92,6 +98,7 @@ public final class TestDocumentInputStream extends TestCase {
/** /**
* test constructor * test constructor
*/ */
@Test
public void testConstructor() throws IOException { public void testConstructor() throws IOException {
DocumentInputStream ostream = new ODocumentInputStream(_workbook_o); DocumentInputStream ostream = new ODocumentInputStream(_workbook_o);
DocumentInputStream nstream = new NDocumentInputStream(_workbook_n); DocumentInputStream nstream = new NDocumentInputStream(_workbook_n);
@ -99,8 +106,8 @@ public final class TestDocumentInputStream extends TestCase {
assertEquals(_workbook_size, _workbook_o.getSize()); assertEquals(_workbook_size, _workbook_o.getSize());
assertEquals(_workbook_size, _workbook_n.getSize()); assertEquals(_workbook_size, _workbook_n.getSize());
assertEquals(_workbook_size, ostream.available()); assertEquals(_workbook_size, available(ostream));
assertEquals(_workbook_size, nstream.available()); assertEquals(_workbook_size, available(nstream));
ostream.close(); ostream.close();
nstream.close(); nstream.close();
@ -109,23 +116,24 @@ public final class TestDocumentInputStream extends TestCase {
/** /**
* test available() behavior * test available() behavior
*/ */
@Test
public void testAvailable() throws IOException { public void testAvailable() throws IOException {
DocumentInputStream ostream = new DocumentInputStream(_workbook_o); DocumentInputStream ostream = new DocumentInputStream(_workbook_o);
DocumentInputStream nstream = new NDocumentInputStream(_workbook_n); DocumentInputStream nstream = new NDocumentInputStream(_workbook_n);
assertEquals(_workbook_size, ostream.available()); assertEquals(_workbook_size, available(ostream));
assertEquals(_workbook_size, nstream.available()); assertEquals(_workbook_size, available(nstream));
ostream.close(); ostream.close();
nstream.close(); nstream.close();
try { try {
ostream.available(); available(ostream);
fail("Should have caught IOException"); fail("Should have caught IOException");
} catch (IllegalStateException ignored) { } catch (IllegalStateException ignored) {
// as expected // as expected
} }
try { try {
nstream.available(); available(nstream);
fail("Should have caught IOException"); fail("Should have caught IOException");
} catch (IllegalStateException ignored) { } catch (IllegalStateException ignored) {
// as expected // as expected
@ -135,6 +143,7 @@ public final class TestDocumentInputStream extends TestCase {
/** /**
* test mark/reset/markSupported. * test mark/reset/markSupported.
*/ */
@Test
public void testMarkFunctions() throws IOException { public void testMarkFunctions() throws IOException {
byte[] buffer = new byte[ _workbook_size / 5 ]; byte[] buffer = new byte[ _workbook_size / 5 ];
byte[] small_buffer = new byte[212]; byte[] small_buffer = new byte[212];
@ -152,12 +161,12 @@ public final class TestDocumentInputStream extends TestCase {
_workbook_data[ j ], buffer[ j ] _workbook_data[ j ], buffer[ j ]
); );
} }
assertEquals(_workbook_size - buffer.length, stream.available()); assertEquals(_workbook_size - buffer.length, available(stream));
// Reset, and check the available goes back to being the // Reset, and check the available goes back to being the
// whole of the stream // whole of the stream
stream.reset(); stream.reset();
assertEquals(_workbook_size, stream.available()); assertEquals(_workbook_size, available(stream));
// Read part of a block // Read part of a block
@ -168,7 +177,7 @@ public final class TestDocumentInputStream extends TestCase {
_workbook_data[ j ], small_buffer[ j ] _workbook_data[ j ], small_buffer[ j ]
); );
} }
assertEquals(_workbook_size - small_buffer.length, stream.available()); assertEquals(_workbook_size - small_buffer.length, available(stream));
stream.mark(0); stream.mark(0);
// Read the next part // Read the next part
@ -179,11 +188,11 @@ public final class TestDocumentInputStream extends TestCase {
_workbook_data[ j+small_buffer.length ], small_buffer[ j ] _workbook_data[ j+small_buffer.length ], small_buffer[ j ]
); );
} }
assertEquals(_workbook_size - 2*small_buffer.length, stream.available()); assertEquals(_workbook_size - 2*small_buffer.length, available(stream));
// Reset, check it goes back to where it was // Reset, check it goes back to where it was
stream.reset(); stream.reset();
assertEquals(_workbook_size - small_buffer.length, stream.available()); assertEquals(_workbook_size - small_buffer.length, available(stream));
// Read // Read
stream.read(small_buffer); stream.read(small_buffer);
@ -193,7 +202,7 @@ public final class TestDocumentInputStream extends TestCase {
_workbook_data[ j+small_buffer.length ], small_buffer[ j ] _workbook_data[ j+small_buffer.length ], small_buffer[ j ]
); );
} }
assertEquals(_workbook_size - 2*small_buffer.length, stream.available()); assertEquals(_workbook_size - 2*small_buffer.length, available(stream));
// Now read at various points // Now read at various points
@ -236,11 +245,11 @@ public final class TestDocumentInputStream extends TestCase {
_workbook_data[ j ], buffer[ j ] _workbook_data[ j ], buffer[ j ]
); );
} }
assertEquals(_workbook_size - buffer.length, stream.available()); assertEquals(_workbook_size - buffer.length, available(stream));
// Read all of it again, check it began at the start again // Read all of it again, check it began at the start again
stream.reset(); stream.reset();
assertEquals(_workbook_size, stream.available()); assertEquals(_workbook_size, available(stream));
stream.read(buffer); stream.read(buffer);
for (int j = 0; j < buffer.length; j++) { for (int j = 0; j < buffer.length; j++) {
@ -254,7 +263,7 @@ public final class TestDocumentInputStream extends TestCase {
stream.mark(12); stream.mark(12);
stream.read(buffer); stream.read(buffer);
assertEquals(_workbook_size - (2 * buffer.length), assertEquals(_workbook_size - (2 * buffer.length),
stream.available()); available(stream));
for (int j = buffer.length; j < (2 * buffer.length); j++) for (int j = buffer.length; j < (2 * buffer.length); j++)
{ {
assertEquals("checking byte " + j, _workbook_data[ j ], assertEquals("checking byte " + j, _workbook_data[ j ],
@ -263,12 +272,12 @@ public final class TestDocumentInputStream extends TestCase {
// Reset, should go back to only one buffer full read // Reset, should go back to only one buffer full read
stream.reset(); stream.reset();
assertEquals(_workbook_size - buffer.length, stream.available()); assertEquals(_workbook_size - buffer.length, available(stream));
// Read the buffer again // Read the buffer again
stream.read(buffer); stream.read(buffer);
assertEquals(_workbook_size - (2 * buffer.length), assertEquals(_workbook_size - (2 * buffer.length),
stream.available()); available(stream));
for (int j = buffer.length; j < (2 * buffer.length); j++) for (int j = buffer.length; j < (2 * buffer.length); j++)
{ {
assertEquals("checking byte " + j, _workbook_data[ j ], assertEquals("checking byte " + j, _workbook_data[ j ],
@ -281,6 +290,7 @@ public final class TestDocumentInputStream extends TestCase {
/** /**
* test simple read method * test simple read method
*/ */
@Test
public void testReadSingleByte() throws IOException { public void testReadSingleByte() throws IOException {
DocumentInputStream[] streams = new DocumentInputStream[] { DocumentInputStream[] streams = new DocumentInputStream[] {
new DocumentInputStream(_workbook_o), new DocumentInputStream(_workbook_o),
@ -297,7 +307,7 @@ public final class TestDocumentInputStream extends TestCase {
( byte ) b); ( byte ) b);
remaining--; remaining--;
assertEquals("checking remaining after reading byte " + j, assertEquals("checking remaining after reading byte " + j,
remaining, stream.available()); remaining, available(stream));
} }
// Ensure we fell off the end // Ensure we fell off the end
@ -317,6 +327,7 @@ public final class TestDocumentInputStream extends TestCase {
/** /**
* Test buffered read * Test buffered read
*/ */
@Test
public void testBufferRead() throws IOException { public void testBufferRead() throws IOException {
DocumentInputStream[] streams = new DocumentInputStream[] { DocumentInputStream[] streams = new DocumentInputStream[] {
new DocumentInputStream(_workbook_o), new DocumentInputStream(_workbook_o),
@ -333,22 +344,22 @@ public final class TestDocumentInputStream extends TestCase {
// test reading zero length buffer // test reading zero length buffer
assertEquals(0, stream.read(new byte[ 0 ])); assertEquals(0, stream.read(new byte[ 0 ]));
assertEquals(_workbook_size, stream.available()); assertEquals(_workbook_size, available(stream));
byte[] buffer = new byte[ _buffer_size ]; byte[] buffer = new byte[ _buffer_size ];
int offset = 0; int offset = 0;
while (stream.available() >= buffer.length) while (available(stream) >= buffer.length)
{ {
assertEquals(_buffer_size, stream.read(buffer)); assertEquals(_buffer_size, stream.read(buffer));
for (byte data : buffer) { for (byte element : buffer) {
assertEquals("in main loop, byte " + offset, assertEquals("in main loop, byte " + offset,
_workbook_data[ offset ], data); _workbook_data[ offset ], element);
offset++; offset++;
} }
assertEquals("offset " + offset, _workbook_size - offset, assertEquals("offset " + offset, _workbook_size - offset,
stream.available()); available(stream));
} }
assertEquals(_workbook_size % _buffer_size, stream.available()); assertEquals(_workbook_size % _buffer_size, available(stream));
Arrays.fill(buffer, ( byte ) 0); Arrays.fill(buffer, ( byte ) 0);
int count = stream.read(buffer); int count = stream.read(buffer);
@ -378,6 +389,7 @@ public final class TestDocumentInputStream extends TestCase {
/** /**
* Test complex buffered read * Test complex buffered read
*/ */
@Test
public void testComplexBufferRead() throws IOException { public void testComplexBufferRead() throws IOException {
DocumentInputStream[] streams = new DocumentInputStream[] { DocumentInputStream[] streams = new DocumentInputStream[] {
new DocumentInputStream(_workbook_o), new DocumentInputStream(_workbook_o),
@ -413,11 +425,11 @@ public final class TestDocumentInputStream extends TestCase {
// test reading zero // test reading zero
assertEquals(0, stream.read(new byte[ 5 ], 0, 0)); assertEquals(0, stream.read(new byte[ 5 ], 0, 0));
assertEquals(_workbook_size, stream.available()); assertEquals(_workbook_size, available(stream));
byte[] buffer = new byte[ _workbook_size ]; byte[] buffer = new byte[ _workbook_size ];
int offset = 0; int offset = 0;
while (stream.available() >= _buffer_size) while (available(stream) >= _buffer_size)
{ {
Arrays.fill(buffer, ( byte ) 0); Arrays.fill(buffer, ( byte ) 0);
assertEquals(_buffer_size, assertEquals(_buffer_size,
@ -437,9 +449,9 @@ public final class TestDocumentInputStream extends TestCase {
} }
offset += _buffer_size; offset += _buffer_size;
assertEquals("offset " + offset, _workbook_size - offset, assertEquals("offset " + offset, _workbook_size - offset,
stream.available()); available(stream));
} }
assertEquals(_workbook_size % _buffer_size, stream.available()); assertEquals(_workbook_size % _buffer_size, available(stream));
Arrays.fill(buffer, ( byte ) 0); Arrays.fill(buffer, ( byte ) 0);
int count = stream.read(buffer, offset, int count = stream.read(buffer, offset,
_workbook_size % _buffer_size); _workbook_size % _buffer_size);
@ -474,38 +486,40 @@ public final class TestDocumentInputStream extends TestCase {
/** /**
* Tests that we can skip within the stream * Tests that we can skip within the stream
*/ */
@Test
public void testSkip() throws IOException { public void testSkip() throws IOException {
DocumentInputStream[] streams = new DocumentInputStream[] { DocumentInputStream[] streams = new DocumentInputStream[] {
new DocumentInputStream(_workbook_o), new DocumentInputStream(_workbook_o),
new NDocumentInputStream(_workbook_n) new NDocumentInputStream(_workbook_n)
}; };
for(DocumentInputStream stream : streams) { for(DocumentInputStream stream : streams) {
assertEquals(_workbook_size, stream.available()); assertEquals(_workbook_size, available(stream));
int count = stream.available(); int count = available(stream);
while (stream.available() >= _buffer_size) { while (available(stream) >= _buffer_size) {
assertEquals(_buffer_size, stream.skip(_buffer_size)); assertEquals(_buffer_size, stream.skip(_buffer_size));
count -= _buffer_size; count -= _buffer_size;
assertEquals(count, stream.available()); assertEquals(count, available(stream));
} }
assertEquals(_workbook_size % _buffer_size, assertEquals(_workbook_size % _buffer_size,
stream.skip(_buffer_size)); stream.skip(_buffer_size));
assertEquals(0, stream.available()); assertEquals(0, available(stream));
stream.reset(); stream.reset();
assertEquals(_workbook_size, stream.available()); assertEquals(_workbook_size, available(stream));
assertEquals(_workbook_size, stream.skip(_workbook_size * 2)); assertEquals(_workbook_size, stream.skip(_workbook_size * 2));
assertEquals(0, stream.available()); assertEquals(0, available(stream));
stream.reset(); stream.reset();
assertEquals(_workbook_size, stream.available()); assertEquals(_workbook_size, available(stream));
assertEquals(_workbook_size, assertEquals(_workbook_size,
stream.skip(2 + ( long ) Integer.MAX_VALUE)); stream.skip(2 + ( long ) Integer.MAX_VALUE));
assertEquals(0, stream.available()); assertEquals(0, available(stream));
} }
} }
/** /**
* Test that we can read files at multiple levels down the tree * Test that we can read files at multiple levels down the tree
*/ */
@Test
public void testReadMultipleTreeLevels() throws Exception { public void testReadMultipleTreeLevels() throws Exception {
final POIDataSamples _samples = POIDataSamples.getPublisherInstance(); final POIDataSamples _samples = POIDataSamples.getPublisherInstance();
File sample = _samples.getFile("Sample.pub"); File sample = _samples.getFile("Sample.pub");
@ -551,4 +565,9 @@ public final class TestDocumentInputStream extends TestCase {
npoifs.close(); npoifs.close();
} }
} }
@SuppressForbidden("just for testing")
private static int available(InputStream is) throws IOException {
return is.available();
}
} }

View File

@ -33,6 +33,7 @@ import org.apache.poi.poifs.storage.BATBlock;
import org.apache.poi.poifs.storage.BlockAllocationTableReader; import org.apache.poi.poifs.storage.BlockAllocationTableReader;
import org.apache.poi.poifs.storage.HeaderBlock; import org.apache.poi.poifs.storage.HeaderBlock;
import org.apache.poi.poifs.storage.RawDataBlockList; import org.apache.poi.poifs.storage.RawDataBlockList;
import org.apache.poi.util.IOUtils;
/** /**
* Tests for the older OPOIFS-based POIFSFileSystem * Tests for the older OPOIFS-based POIFSFileSystem
@ -285,13 +286,8 @@ public final class TestPOIFSFileSystem extends TestCase {
checkAllDirectoryContents((DirectoryEntry)entry); checkAllDirectoryContents((DirectoryEntry)entry);
} else { } else {
DocumentNode doc = (DocumentNode) entry; DocumentNode doc = (DocumentNode) entry;
DocumentInputStream dis = new DocumentInputStream(doc); try (DocumentInputStream dis = new DocumentInputStream(doc)) {
try { IOUtils.toByteArray(dis);
int numBytes = dis.available();
byte[] data = new byte [numBytes];
dis.read(data);
} finally {
dis.close();
} }
} }
} }

View File

@ -172,6 +172,29 @@ public final class TestIOUtils {
} }
} }
@Test(expected = RecordFormatException.class)
public void testMaxLengthTooLong() throws IOException {
try (InputStream is = new FileInputStream(TMP)) {
IOUtils.toByteArray(is, Integer.MAX_VALUE, 100);
}
}
@Test
public void testMaxLengthIgnored() throws IOException {
try (InputStream is = new FileInputStream(TMP)) {
IOUtils.toByteArray(is, 90, Integer.MAX_VALUE);
IOUtils.toByteArray(is, 90, 100);
IOUtils.toByteArray(is, Integer.MAX_VALUE, Integer.MAX_VALUE);
}
}
@Test(expected = RecordFormatException.class)
public void testMaxLengthInvalid() throws IOException {
try (InputStream is = new FileInputStream(TMP)) {
IOUtils.toByteArray(is, 90, 80);
}
}
@Test @Test
public void testWonkyInputStream() throws IOException { public void testWonkyInputStream() throws IOException {
long skipped = IOUtils.skipFully(new WonkyInputStream(), 10000); long skipped = IOUtils.skipFully(new WonkyInputStream(), 10000);

View File

@ -258,10 +258,14 @@ public final class TestLittleEndian {
InputStream stream = new ByteArrayInputStream(_good_array); InputStream stream = new ByteArrayInputStream(_good_array);
int count = 0; int count = 0;
while (stream.available() > 0) { while (true) {
try {
short value = LittleEndian.readShort(stream); short value = LittleEndian.readShort(stream);
assertEquals(value, expected_value); assertEquals(value, expected_value);
count++; count++;
} catch (BufferUnderrunException e) {
break;
}
} }
assertEquals(count, assertEquals(count,
_good_array.length / LittleEndianConsts.SHORT_SIZE); _good_array.length / LittleEndianConsts.SHORT_SIZE);
@ -283,10 +287,14 @@ public final class TestLittleEndian {
InputStream stream = new ByteArrayInputStream(_good_array); InputStream stream = new ByteArrayInputStream(_good_array);
int count = 0; int count = 0;
while (stream.available() > 0) { while (true) {
try {
int value = LittleEndian.readInt(stream); int value = LittleEndian.readInt(stream);
assertEquals(value, expected_value); assertEquals(value, expected_value);
count++; count++;
} catch (BufferUnderrunException e) {
break;
}
} }
assertEquals(count, _good_array.length / LittleEndianConsts.INT_SIZE); assertEquals(count, _good_array.length / LittleEndianConsts.INT_SIZE);
stream = new ByteArrayInputStream(_bad_array); stream = new ByteArrayInputStream(_bad_array);
@ -308,10 +316,14 @@ public final class TestLittleEndian {
InputStream stream = new ByteArrayInputStream(_good_array); InputStream stream = new ByteArrayInputStream(_good_array);
int count = 0; int count = 0;
while (stream.available() > 0) { while (true) {
try {
long value = LittleEndian.readLong(stream); long value = LittleEndian.readLong(stream);
assertEquals(value, expected_value); assertEquals(value, expected_value);
count++; count++;
} catch (BufferUnderrunException e) {
break;
}
} }
assertEquals(count, assertEquals(count,
_good_array.length / LittleEndianConsts.LONG_SIZE); _good_array.length / LittleEndianConsts.LONG_SIZE);
@ -324,21 +336,17 @@ public final class TestLittleEndian {
} }
} }
// public void testReadFromStream() throws IOException { @Test(expected = BufferUnderrunException.class)
// int actual; public void testReadFromStream() throws IOException {
// actual = LittleEndian.readUShort(new ByteArrayInputStream(new byte[] { 5, -128, })); int actual;
// assertEquals(32773, actual); actual = LittleEndian.readUShort(new ByteArrayInputStream(new byte[] { 5, -128, }));
// assertEquals(32773, actual);
// actual = LittleEndian.readUShort(new ByteArrayInputStream(new byte[] { 1, 2, 3, 4, }));
// assertEquals(513, actual); actual = LittleEndian.readUShort(new ByteArrayInputStream(new byte[] { 1, 2, 3, 4, }));
// assertEquals(513, actual);
// try {
// LittleEndian.readInt(new ByteArrayInputStream(new byte[] { 1, 2, 3, })); LittleEndian.readInt(new ByteArrayInputStream(new byte[] { 1, 2, 3, }));
// fail("Should have caught BufferUnderrunException"); }
// } catch (BufferUnderrunException ignored) {
// // as expected
// }
// }
@Test @Test
public void testUnsignedByteToInt() { public void testUnsignedByteToInt() {