import org.apache.poi.util.StringUtil;
@Internal
-class UnicodeString
-{
-
- private final static POILogger logger = POILogFactory
- .getLogger( UnicodeString.class );
+class UnicodeString {
+ private final static POILogger logger =
+ POILogFactory.getLogger( UnicodeString.class );
private byte[] _value;
- UnicodeString( byte[] data, int offset )
- {
+ UnicodeString(byte[] data, int offset) {
int length = LittleEndian.getInt( data, offset );
+ int dataOffset = offset + LittleEndian.INT_SIZE;
+
+ if (! validLength(length, data, dataOffset)) {
+ // If the length looks wrong, this might be because the offset is sometimes expected
+ // to be on a 4 byte boundary. Try checking with that if so, rather than blowing up with
+ // and ArrayIndexOutOfBoundsException below
+ boolean valid = false;
+ int past4byte = offset % 4;
+ if (past4byte != 0) {
+ offset = offset + past4byte;
+ length = LittleEndian.getInt( data, offset );
+ dataOffset = offset + LittleEndian.INT_SIZE;
+
+ valid = validLength(length, data, dataOffset);
+ }
+
+ if (!valid) {
+ throw new IllegalPropertySetDataException(
+ "UnicodeString started at offset #" + offset +
+ " is not NULL-terminated" );
+ }
+ }
if ( length == 0 )
{
return;
}
- _value = LittleEndian.getByteArray( data, offset
- + LittleEndian.INT_SIZE, length * 2 );
+ _value = LittleEndian.getByteArray( data, dataOffset, length * 2 );
+ }
+
+ /**
+ * Checks to see if the specified length seems valid,
+ * given the amount of data available still to read,
+ * and the requirement that the string be NULL-terminated
+ */
+ boolean validLength(int length, byte[] data, int offset) {
+ if (length == 0) {
+ return true;
+ }
+
+ int endOffset = offset + (length * 2);
+ if (endOffset <= data.length) {
+ // Data Length is OK, ensure it's null terminated too
+ if (data[endOffset-1] == 0 && data[endOffset-2] == 0) {
+ // Length looks plausible
+ return true;
+ }
+ }
- if ( _value[length * 2 - 1] != 0 || _value[length * 2 - 2] != 0 )
- throw new IllegalPropertySetDataException(
- "UnicodeString started at offset #" + offset
- + " is not NULL-terminated" );
+ // Something's up/invalid with that length for the given data+offset
+ return false;
}
int getSize()
import junit.framework.TestCase;
+import org.apache.poi.POIDataSamples;
+import org.apache.poi.hpsf.DocumentSummaryInformation;
+import org.apache.poi.hpsf.PropertySetFactory;
+import org.apache.poi.hpsf.SummaryInformation;
import org.apache.poi.hssf.usermodel.HSSFWorkbook;
+import org.apache.poi.poifs.filesystem.DocumentInputStream;
+import org.apache.poi.poifs.filesystem.POIFSFileSystem;
/**
* Tests various bugs have been fixed
*/
public final class TestHPSFBugs extends TestCase {
+ private static final POIDataSamples _samples = POIDataSamples.getHPSFInstance();
+
/**
* Ensure that we can create a new HSSF Workbook,
* then add some properties to it, save +
assertEquals(12345, wb.getSummaryInformation().getCreateDateTime().getTime());
assertEquals("Apache", wb.getDocumentSummaryInformation().getCompany());
}
+
+ /**
+ * Some files seem to want the length and data to be on a 4-byte boundary,
+ * and without that you'll hit an ArrayIndexOutOfBoundsException after
+ * reading junk
+ */
+ public void test54233() throws Exception {
+ DocumentInputStream dis;
+ POIFSFileSystem fs =
+ new POIFSFileSystem(_samples.openResourceAsStream("TestNon4ByteBoundary.doc"));
+
+ dis = fs.createDocumentInputStream(SummaryInformation.DEFAULT_STREAM_NAME);
+ SummaryInformation si = (SummaryInformation)PropertySetFactory.create(dis);
+
+ dis = fs.createDocumentInputStream(DocumentSummaryInformation.DEFAULT_STREAM_NAME);
+ DocumentSummaryInformation dsi = (DocumentSummaryInformation)PropertySetFactory.create(dis);
+
+ // Test
+ assertEquals("Microsoft Word 10.0", si.getApplicationName());
+ assertEquals("", si.getTitle());
+ assertEquals("", si.getAuthor());
+ assertEquals("Cour de Justice", dsi.getCompany());
+
+
+ // Write out and read back, should still be valid
+ // TODO
+ }
}