From 2ae2bf346fcd02d2477a481d1c66b0bb17992f7b Mon Sep 17 00:00:00 2001 From: Rainer Klute Date: Fri, 8 Feb 2008 11:55:43 +0000 Subject: [PATCH] - Fixed bug 44375 - HPSF now copes with a broken dictionary in Document Summary Information stream. RuntimeExceptions that occured when trying to read bogus data are now caught. Dictionary entries up to but not including the bogus one are preserved, the rest is ignored. git-svn-id: https://svn.apache.org/repos/asf/poi/trunk@619848 13f79535-47bb-0310-9956-ffa450edef68 --- src/java/org/apache/poi/hpsf/Property.java | 132 ++++++++++-------- src/java/org/apache/poi/hpsf/Section.java | 2 +- .../org/apache/poi/hpsf/VariantSupport.java | 50 +++++-- .../org/apache/poi/hpsf/basic/TestBasic.java | 41 ------ .../org/apache/poi/hpsf/basic/TestBugs.java | 38 ----- .../poi/hpsf/basic/TestReadAllFiles.java | 110 +++++++++++++++ .../data/{Bug44375.xls => TestBug44375.xls} | Bin 16896 -> 16896 bytes 7 files changed, 222 insertions(+), 151 deletions(-) delete mode 100644 src/testcases/org/apache/poi/hpsf/basic/TestBugs.java create mode 100644 src/testcases/org/apache/poi/hpsf/basic/TestReadAllFiles.java rename src/testcases/org/apache/poi/hpsf/data/{Bug44375.xls => TestBug44375.xls} (99%) diff --git a/src/java/org/apache/poi/hpsf/Property.java b/src/java/org/apache/poi/hpsf/Property.java index 3cfb58e6d1..16b4f7e41b 100644 --- a/src/java/org/apache/poi/hpsf/Property.java +++ b/src/java/org/apache/poi/hpsf/Property.java @@ -23,6 +23,8 @@ import java.util.Map; import org.apache.poi.util.HexDump; import org.apache.poi.util.LittleEndian; +import org.apache.poi.util.POILogFactory; +import org.apache.poi.util.POILogger; /** *

A property in a {@link Section} of a {@link PropertySet}.

@@ -113,7 +115,8 @@ public class Property * * @param id the property's ID. * @param type the property's type, see {@link Variant}. - * @param value the property's value. Only certain types are allowed, see {@link Variant}. + * @param value the property's value. Only certain types are allowed, see + * {@link Variant}. */ public Property(final long id, final long type, final Object value) { @@ -210,68 +213,80 @@ public class Property o += LittleEndian.INT_SIZE; final Map m = new HashMap((int) nrEntries, (float) 1.0); - for (int i = 0; i < nrEntries; i++) + + try { - /* The key. */ - final Long id = new Long(LittleEndian.getUInt(src, o)); - o += LittleEndian.INT_SIZE; - - /* The value (a string). The length is the either the - * number of (two-byte) characters if the character set is Unicode - * or the number of bytes if the character set is not Unicode. - * The length includes terminating 0x00 bytes which we have to strip - * off to create a Java string. */ - long sLength = LittleEndian.getUInt(src, o); - o += LittleEndian.INT_SIZE; - - /* Read the string. */ - final StringBuffer b = new StringBuffer(); - switch (codepage) + for (int i = 0; i < nrEntries; i++) { - case -1: - { - /* Without a codepage the length is equal to the number of - * bytes. */ - b.append(new String(src, o, (int) sLength)); - break; - } - case Constants.CP_UNICODE: + /* The key. */ + final Long id = new Long(LittleEndian.getUInt(src, o)); + o += LittleEndian.INT_SIZE; + + /* The value (a string). The length is the either the + * number of (two-byte) characters if the character set is Unicode + * or the number of bytes if the character set is not Unicode. + * The length includes terminating 0x00 bytes which we have to strip + * off to create a Java string. */ + long sLength = LittleEndian.getUInt(src, o); + o += LittleEndian.INT_SIZE; + + /* Read the string. */ + final StringBuffer b = new StringBuffer(); + switch (codepage) { - /* The length is the number of characters, i.e. the number - * of bytes is twice the number of the characters. */ - final int nrBytes = (int) (sLength * 2); - final byte[] h = new byte[nrBytes]; - for (int i2 = 0; i2 < nrBytes; i2 += 2) + case -1: + { + /* Without a codepage the length is equal to the number of + * bytes. */ + b.append(new String(src, o, (int) sLength)); + break; + } + case Constants.CP_UNICODE: + { + /* The length is the number of characters, i.e. the number + * of bytes is twice the number of the characters. */ + final int nrBytes = (int) (sLength * 2); + final byte[] h = new byte[nrBytes]; + for (int i2 = 0; i2 < nrBytes; i2 += 2) + { + h[i2] = src[o + i2 + 1]; + h[i2 + 1] = src[o + i2]; + } + b.append(new String(h, 0, nrBytes, + VariantSupport.codepageToEncoding(codepage))); + break; + } + default: { - h[i2] = src[o + i2 + 1]; - h[i2 + 1] = src[o + i2]; + /* For encodings other than Unicode the length is the number + * of bytes. */ + b.append(new String(src, o, (int) sLength, + VariantSupport.codepageToEncoding(codepage))); + break; } - b.append(new String(h, 0, nrBytes, - VariantSupport.codepageToEncoding(codepage))); - break; } - default: + + /* Strip 0x00 characters from the end of the string: */ + while (b.length() > 0 && b.charAt(b.length() - 1) == 0x00) + b.setLength(b.length() - 1); + if (codepage == Constants.CP_UNICODE) { - /* For encodings other than Unicode the length is the number - * of bytes. */ - b.append(new String(src, o, (int) sLength, - VariantSupport.codepageToEncoding(codepage))); - break; + if (sLength % 2 == 1) + sLength++; + o += (sLength + sLength); } + else + o += sLength; + m.put(id, b.toString()); } - - /* Strip 0x00 characters from the end of the string: */ - while (b.length() > 0 && b.charAt(b.length() - 1) == 0x00) - b.setLength(b.length() - 1); - if (codepage == Constants.CP_UNICODE) - { - if (sLength % 2 == 1) - sLength++; - o += (sLength + sLength); - } - else - o += sLength; - m.put(id, b.toString()); + } + catch (RuntimeException ex) + { + final POILogger l = POILogFactory.getLogger(getClass()); + l.log(POILogger.WARN, + "The property set's dictionary contains bogus data. " + + "All dictionary entries starting with the one with ID " + + id + " will be ignored.", ex); } return m; } @@ -320,11 +335,10 @@ public class Property /** - *

Compares two properties.

- * - *

Please beware that a property with ID == 0 is a special case: It does not have a type, and its value is the section's - * dictionary. Another special case are strings: Two properties may have - * the different types Variant.VT_LPSTR and Variant.VT_LPWSTR;

+ *

Compares two properties.

Please beware that a property with + * ID == 0 is a special case: It does not have a type, and its value is the + * section's dictionary. Another special case are strings: Two properties + * may have the different types Variant.VT_LPSTR and Variant.VT_LPWSTR;

* * @see Object#equals(java.lang.Object) */ diff --git a/src/java/org/apache/poi/hpsf/Section.java b/src/java/org/apache/poi/hpsf/Section.java index 3b041ed9c2..76824e7b7e 100644 --- a/src/java/org/apache/poi/hpsf/Section.java +++ b/src/java/org/apache/poi/hpsf/Section.java @@ -210,7 +210,7 @@ public class Section /* Pass 1: Read the property list. */ int pass1Offset = o1; - List propertyList = new ArrayList(propertyCount); + final List propertyList = new ArrayList(propertyCount); PropertyListEntry ple; for (int i = 0; i < properties.length; i++) { diff --git a/src/java/org/apache/poi/hpsf/VariantSupport.java b/src/java/org/apache/poi/hpsf/VariantSupport.java index 1986cac30c..703e8abe8d 100644 --- a/src/java/org/apache/poi/hpsf/VariantSupport.java +++ b/src/java/org/apache/poi/hpsf/VariantSupport.java @@ -109,25 +109,51 @@ public class VariantSupport extends Variant } + /** + *

HPSF is able to read these {@link Variant} types.

+ */ + final static public int[] SUPPORTED_TYPES = { Variant.VT_EMPTY, + Variant.VT_I2, Variant.VT_I4, Variant.VT_I8, Variant.VT_R8, + Variant.VT_FILETIME, Variant.VT_LPSTR, Variant.VT_LPWSTR, + Variant.VT_CF, Variant.VT_BOOL }; + + + + /** + *

Checks whether HPSF supports the specified variant type. Unsupported + * types should be implemented included in the {@link #SUPPORTED_TYPES} + * array.

+ * + * @see Variant + * @param variantType the variant type to check + * @return true if HPFS supports this type, else + * false + */ + public boolean isSupportedType(final int variantType) + { + for (int i = 0; i < SUPPORTED_TYPES.length; i++) + if (variantType == SUPPORTED_TYPES[i]) + return true; + return false; + } + + /** *

Reads a variant type from a byte array.

- * + * * @param src The byte array - * @param offset The offset in the byte array where the variant - * starts - * @param length The length of the variant including the variant - * type field + * @param offset The offset in the byte array where the variant starts + * @param length The length of the variant including the variant type field * @param type The variant type to read - * @param codepage The codepage to use to write non-wide strings - * @return A Java object that corresponds best to the variant - * field. For example, a VT_I4 is returned as a {@link Long}, a - * VT_LPSTR as a {@link String}. + * @param codepage The codepage to use for non-wide strings + * @return A Java object that corresponds best to the variant field. For + * example, a VT_I4 is returned as a {@link Long}, a VT_LPSTR as a + * {@link String}. * @exception ReadingNotSupportedException if a property is to be written - * who's variant type HPSF does not yet support + * who's variant type HPSF does not yet support * @exception UnsupportedEncodingException if the specified codepage is not - * supported. - * + * supported. * @see Variant */ public static Object read(final byte[] src, final int offset, diff --git a/src/testcases/org/apache/poi/hpsf/basic/TestBasic.java b/src/testcases/org/apache/poi/hpsf/basic/TestBasic.java index 521ce819e8..1932f0f11b 100644 --- a/src/testcases/org/apache/poi/hpsf/basic/TestBasic.java +++ b/src/testcases/org/apache/poi/hpsf/basic/TestBasic.java @@ -20,7 +20,6 @@ package org.apache.poi.hpsf.basic; import java.io.ByteArrayInputStream; import java.io.File; -import java.io.FileFilter; import java.io.FileNotFoundException; import java.io.IOException; import java.io.InputStream; @@ -227,46 +226,6 @@ public class TestBasic extends TestCase - /** - *

This test methods reads all property set streams from all POI - * filesystems in the "data" directory.

- */ - public void testReadAllFiles() - { - final File dataDir = - new File(System.getProperty("HPSF.testdata.path")); - final File[] fileList = dataDir.listFiles(new FileFilter() - { - public boolean accept(final File f) - { - return f.isFile(); - } - }); - try - { - for (int i = 0; i < fileList.length; i++) - { - File f = fileList[i]; - /* Read the POI filesystem's property set streams: */ - final POIFile[] psf1 = Util.readPropertySets(f); - - for (int j = 0; j < psf1.length; j++) - { - final InputStream in = - new ByteArrayInputStream(psf1[j].getBytes()); - PropertySetFactory.create(in); - } - } - } - catch (Throwable t) - { - final String s = org.apache.poi.hpsf.Util.toString(t); - fail(s); - } - } - - - /** *

Runs the test cases stand-alone.

* diff --git a/src/testcases/org/apache/poi/hpsf/basic/TestBugs.java b/src/testcases/org/apache/poi/hpsf/basic/TestBugs.java deleted file mode 100644 index 0d2b55d5f8..0000000000 --- a/src/testcases/org/apache/poi/hpsf/basic/TestBugs.java +++ /dev/null @@ -1,38 +0,0 @@ -package org.apache.poi.hpsf.basic; - -import java.io.File; -import java.io.FileInputStream; - -import org.apache.poi.hpsf.DocumentSummaryInformation; -import org.apache.poi.hpsf.PropertySet; -import org.apache.poi.hpsf.PropertySetFactory; -import org.apache.poi.hpsf.SummaryInformation; -import org.apache.poi.poifs.filesystem.DocumentInputStream; -import org.apache.poi.poifs.filesystem.POIFSFileSystem; - -import junit.framework.TestCase; - -public class TestBugs extends TestCase { - private String dirname; - - protected void setUp() throws Exception { - dirname = System.getProperty("HPSF.testdata.path"); - } - - public void BROKENtestBug44375() throws Exception { - POIFSFileSystem fs = new POIFSFileSystem( - new FileInputStream(new File(dirname,"Bug44375.xls")) - ); - - DocumentInputStream dis; - PropertySet set; - - dis = fs.createDocumentInputStream(DocumentSummaryInformation.DEFAULT_STREAM_NAME); - set = PropertySetFactory.create(dis); - - dis = fs.createDocumentInputStream(SummaryInformation.DEFAULT_STREAM_NAME); - // This currently fails - set = PropertySetFactory.create(dis); - } - -} diff --git a/src/testcases/org/apache/poi/hpsf/basic/TestReadAllFiles.java b/src/testcases/org/apache/poi/hpsf/basic/TestReadAllFiles.java new file mode 100644 index 0000000000..f68b4c3cc9 --- /dev/null +++ b/src/testcases/org/apache/poi/hpsf/basic/TestReadAllFiles.java @@ -0,0 +1,110 @@ +/* ==================================================================== + 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.hpsf.basic; + +import java.io.ByteArrayInputStream; +import java.io.File; +import java.io.FileFilter; +import java.io.InputStream; + +import junit.framework.TestCase; + +import org.apache.poi.hpsf.PropertySetFactory; + + + +/** + *

Tests some HPSF functionality by reading all property sets from all files + * in the "data" directory. If you want to ensure HPSF can deal with a certain + * OLE2 file, just add it to the "data" directory and run this test case.

+ * + * @author Rainer Klute (klute@rainer-klute.de) + * @since 2008-02-08 + * @version $Id: TestBasic.java 489730 2006-12-22 19:18:16Z bayard $ + */ +public class TestReadAllFiles extends TestCase +{ + + /** + *

Test case constructor.

+ * + * @param name The test case's name. + */ + public TestReadAllFiles(final String name) + { + super(name); + } + + + + /** + *

This test methods reads all property set streams from all POI + * filesystems in the "data" directory.

+ */ + public void testReadAllFiles() + { + final File dataDir = + new File(System.getProperty("HPSF.testdata.path")); + final File[] fileList = dataDir.listFiles(new FileFilter() + { + public boolean accept(final File f) + { + return f.isFile(); + } + }); + try + { + for (int i = 0; i < fileList.length; i++) + { + final File f = fileList[i]; + /* Read the POI filesystem's property set streams: */ + final POIFile[] psf1 = Util.readPropertySets(f); + + for (int j = 0; j < psf1.length; j++) + { + final InputStream in = + new ByteArrayInputStream(psf1[j].getBytes()); + PropertySetFactory.create(in); + } + } + } + catch (Throwable t) + { + final String s = org.apache.poi.hpsf.Util.toString(t); + fail(s); + } + } + + + + /** + *

Runs the test cases stand-alone.

+ * + * @param args Command-line arguments (ignored) + * + * @exception Throwable if any sort of exception or error occurs + */ + public static void main(final String[] args) throws Throwable + { + System.setProperty("HPSF.testdata.path", + "./src/testcases/org/apache/poi/hpsf/data"); + junit.textui.TestRunner.run(TestReadAllFiles.class); + } + +} diff --git a/src/testcases/org/apache/poi/hpsf/data/Bug44375.xls b/src/testcases/org/apache/poi/hpsf/data/TestBug44375.xls similarity index 99% rename from src/testcases/org/apache/poi/hpsf/data/Bug44375.xls rename to src/testcases/org/apache/poi/hpsf/data/TestBug44375.xls index 1e253d175cab5737a8b9cb8379d874b359e5a474..0ebd762934c6e7fcf75bc714de6fc468685b695c 100644 GIT binary patch delta 52 zcmZo@VQgq&+@Qh4&%?kFl$e>9TBP8eQ(BTb*^w!HasU(8<{3;^Y&j-$3pv|lonYKt I;?Tng0I0_h+W-In delta 43 zcmZo@VQgq&+@Qh4&B4HsQ;=Vpl#`e@*^w!HasU(G<{3;^Y$xC4;n-Z_(8CA-DDe(U -- 2.39.5