mirror of https://github.com/apache/poi.git
Add code for reading Ole10Native data
git-svn-id: https://svn.apache.org/repos/asf/poi/trunk@995415 13f79535-47bb-0310-9956-ffa450edef68
This commit is contained in:
parent
4ef42e3bbc
commit
017bde52e1
|
@ -0,0 +1,259 @@
|
||||||
|
package org.apache.poi.poifs.filesystem;
|
||||||
|
|
||||||
|
import org.apache.poi.util.*;
|
||||||
|
|
||||||
|
import java.io.ByteArrayOutputStream;
|
||||||
|
import java.io.FileNotFoundException;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.util.Arrays;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Represents an Ole10Native record which is wrapped around certain binary
|
||||||
|
* files being embedded in OLE2 documents.
|
||||||
|
*
|
||||||
|
* @author Rainer Schwarze
|
||||||
|
*/
|
||||||
|
public class Ole10Native {
|
||||||
|
// (the fields as they appear in the raw record:)
|
||||||
|
private final int totalSize; // 4 bytes, total size of record not including this field
|
||||||
|
private short flags1; // 2 bytes, unknown, mostly [02 00]
|
||||||
|
private final String label; // ASCIIZ, stored in this field without the terminating zero
|
||||||
|
private final String fileName; // ASCIIZ, stored in this field without the terminating zero
|
||||||
|
private short flags2; // 2 bytes, unknown, mostly [00 00]
|
||||||
|
// private byte unknown1Length; // 1 byte, specifying the length of the following byte array (unknown1)
|
||||||
|
private byte[] unknown1; // see below
|
||||||
|
private byte[] unknown2; // 3 bytes, unknown, mostly [00 00 00]
|
||||||
|
private final String command; // ASCIIZ, stored in this field without the terminating zero
|
||||||
|
private final int dataSize; // 4 bytes (if space), size of following buffer
|
||||||
|
private final byte[] dataBuffer; // varying size, the actual native data
|
||||||
|
private short flags3; // some final flags? or zero terminators?, sometimes not there
|
||||||
|
public static final String OLE10_NATIVE = "\u0001Ole10Native";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates an instance of this class from an embedded OLE Object. The OLE Object is expected
|
||||||
|
* to include a stream "{01}Ole10Native" which contains the actual
|
||||||
|
* data relevant for this class.
|
||||||
|
*
|
||||||
|
* @param poifs POI Filesystem object
|
||||||
|
* @return Returns an instance of this class
|
||||||
|
* @throws IOException on IO error
|
||||||
|
* @throws Ole10NativeException on invalid or unexcepted data format
|
||||||
|
*/
|
||||||
|
public static Ole10Native createFromEmbeddedOleObject(POIFSFileSystem poifs) throws IOException, Ole10NativeException {
|
||||||
|
boolean plain = false;
|
||||||
|
|
||||||
|
try {
|
||||||
|
poifs.getRoot().getEntry("\u0001Ole10ItemName");
|
||||||
|
plain = true;
|
||||||
|
} catch (FileNotFoundException ex) {
|
||||||
|
plain = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
DocumentInputStream dis = poifs.createDocumentInputStream(OLE10_NATIVE);
|
||||||
|
ByteArrayOutputStream bos = new ByteArrayOutputStream();
|
||||||
|
IOUtils.copy(dis, bos);
|
||||||
|
byte[] data = bos.toByteArray();
|
||||||
|
|
||||||
|
return new Ole10Native(data, 0, plain);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates an instance and fills the fields based on the data in the given buffer.
|
||||||
|
*
|
||||||
|
* @param data The buffer containing the Ole10Native record
|
||||||
|
* @param offset The start offset of the record in the buffer
|
||||||
|
* @throws Ole10NativeException on invalid or unexcepted data format
|
||||||
|
*/
|
||||||
|
public Ole10Native(byte[] data, int offset) throws Ole10NativeException {
|
||||||
|
this(data, offset, false);
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* Creates an instance and fills the fields based on the data in the given buffer.
|
||||||
|
*
|
||||||
|
* @param data The buffer containing the Ole10Native record
|
||||||
|
* @param offset The start offset of the record in the buffer
|
||||||
|
* @param plain Specified 'plain' format without filename
|
||||||
|
* @throws Ole10NativeException on invalid or unexcepted data format
|
||||||
|
*/
|
||||||
|
public Ole10Native(byte[] data, int offset, boolean plain) throws Ole10NativeException {
|
||||||
|
int ofs = offset; // current offset, initialized to start
|
||||||
|
|
||||||
|
if (data.length<offset+2) {
|
||||||
|
throw new Ole10NativeException("data is too small");
|
||||||
|
}
|
||||||
|
|
||||||
|
totalSize = LittleEndian.getInt(data, ofs);
|
||||||
|
ofs += LittleEndianConsts.INT_SIZE;
|
||||||
|
|
||||||
|
if (plain) {
|
||||||
|
dataBuffer = new byte[totalSize-4];
|
||||||
|
System.arraycopy(data, 4, dataBuffer, 0, dataBuffer.length);
|
||||||
|
dataSize = totalSize - 4;
|
||||||
|
label = "ole-"+ HexDump.toHex(Arrays.copyOf(dataBuffer, 8));
|
||||||
|
fileName = label;
|
||||||
|
command = label;
|
||||||
|
} else {
|
||||||
|
flags1 = LittleEndian.getShort(data, ofs);
|
||||||
|
ofs += LittleEndianConsts.SHORT_SIZE;
|
||||||
|
int len = getStringLength(data, ofs);
|
||||||
|
label = StringUtil.getFromCompressedUnicode(data, ofs, len - 1);
|
||||||
|
ofs += len;
|
||||||
|
len = getStringLength(data, ofs);
|
||||||
|
fileName = StringUtil.getFromCompressedUnicode(data, ofs, len - 1);
|
||||||
|
ofs += len;
|
||||||
|
flags2 = LittleEndian.getShort(data, ofs);
|
||||||
|
ofs += LittleEndianConsts.SHORT_SIZE;
|
||||||
|
len = LittleEndian.getUnsignedByte(data, ofs);
|
||||||
|
unknown1 = new byte[len];
|
||||||
|
ofs += len;
|
||||||
|
len = 3;
|
||||||
|
unknown2 = new byte[len];
|
||||||
|
ofs += len;
|
||||||
|
len = getStringLength(data, ofs);
|
||||||
|
command = StringUtil.getFromCompressedUnicode(data, ofs, len - 1);
|
||||||
|
ofs += len;
|
||||||
|
|
||||||
|
if (totalSize + LittleEndianConsts.INT_SIZE - ofs > LittleEndianConsts.INT_SIZE) {
|
||||||
|
dataSize = LittleEndian.getInt(data, ofs);
|
||||||
|
ofs += LittleEndianConsts.INT_SIZE;
|
||||||
|
|
||||||
|
if (dataSize > totalSize || dataSize<0) {
|
||||||
|
throw new Ole10NativeException("Invalid Ole10Native");
|
||||||
|
}
|
||||||
|
|
||||||
|
dataBuffer = new byte[dataSize];
|
||||||
|
System.arraycopy(data, ofs, dataBuffer, 0, dataSize);
|
||||||
|
ofs += dataSize;
|
||||||
|
|
||||||
|
if (unknown1.length > 0) {
|
||||||
|
flags3 = LittleEndian.getShort(data, ofs);
|
||||||
|
ofs += LittleEndianConsts.SHORT_SIZE;
|
||||||
|
} else {
|
||||||
|
flags3 = 0;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
throw new Ole10NativeException("Invalid Ole10Native");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Helper - determine length of zero terminated string (ASCIIZ).
|
||||||
|
*/
|
||||||
|
private static int getStringLength(byte[] data, int ofs) {
|
||||||
|
int len = 0;
|
||||||
|
while (len+ofs<data.length && data[ofs + len] != 0) {
|
||||||
|
len++;
|
||||||
|
}
|
||||||
|
len++;
|
||||||
|
return len;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the value of the totalSize field - the total length of the structure
|
||||||
|
* is totalSize + 4 (value of this field + size of this field).
|
||||||
|
*
|
||||||
|
* @return the totalSize
|
||||||
|
*/
|
||||||
|
public int getTotalSize() {
|
||||||
|
return totalSize;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns flags1 - currently unknown - usually 0x0002.
|
||||||
|
*
|
||||||
|
* @return the flags1
|
||||||
|
*/
|
||||||
|
public short getFlags1() {
|
||||||
|
return flags1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the label field - usually the name of the file (without directory) but
|
||||||
|
* probably may be any name specified during packaging/embedding the data.
|
||||||
|
*
|
||||||
|
* @return the label
|
||||||
|
*/
|
||||||
|
public String getLabel() {
|
||||||
|
return label;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the fileName field - usually the name of the file being embedded
|
||||||
|
* including the full path.
|
||||||
|
*
|
||||||
|
* @return the fileName
|
||||||
|
*/
|
||||||
|
public String getFileName() {
|
||||||
|
return fileName;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns flags2 - currently unknown - mostly 0x0000.
|
||||||
|
*
|
||||||
|
* @return the flags2
|
||||||
|
*/
|
||||||
|
public short getFlags2() {
|
||||||
|
return flags2;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns unknown1 field - currently unknown.
|
||||||
|
*
|
||||||
|
* @return the unknown1
|
||||||
|
*/
|
||||||
|
public byte[] getUnknown1() {
|
||||||
|
return unknown1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the unknown2 field - currently being a byte[3] - mostly {0, 0, 0}.
|
||||||
|
*
|
||||||
|
* @return the unknown2
|
||||||
|
*/
|
||||||
|
public byte[] getUnknown2() {
|
||||||
|
return unknown2;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the command field - usually the name of the file being embedded
|
||||||
|
* including the full path, may be a command specified during embedding the file.
|
||||||
|
*
|
||||||
|
* @return the command
|
||||||
|
*/
|
||||||
|
public String getCommand() {
|
||||||
|
return command;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the size of the embedded file. If the size is 0 (zero), no data has been
|
||||||
|
* embedded. To be sure, that no data has been embedded, check whether
|
||||||
|
* {@link #getDataBuffer()} returns <code>null</code>.
|
||||||
|
*
|
||||||
|
* @return the dataSize
|
||||||
|
*/
|
||||||
|
public int getDataSize() {
|
||||||
|
return dataSize;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the buffer containing the embedded file's data, or <code>null</code>
|
||||||
|
* if no data was embedded. Note that an embedding may provide information about
|
||||||
|
* the data, but the actual data is not included. (So label, filename etc. are
|
||||||
|
* available, but this method returns <code>null</code>.)
|
||||||
|
*
|
||||||
|
* @return the dataBuffer
|
||||||
|
*/
|
||||||
|
public byte[] getDataBuffer() {
|
||||||
|
return dataBuffer;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the flags3 - currently unknown.
|
||||||
|
*
|
||||||
|
* @return the flags3
|
||||||
|
*/
|
||||||
|
public short getFlags3() {
|
||||||
|
return flags3;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,7 @@
|
||||||
|
package org.apache.poi.poifs.filesystem;
|
||||||
|
|
||||||
|
public class Ole10NativeException extends Exception {
|
||||||
|
public Ole10NativeException(String message) {
|
||||||
|
super(message);
|
||||||
|
}
|
||||||
|
}
|
|
@ -40,6 +40,7 @@ public final class AllPOIFSFileSystemTests {
|
||||||
result.addTestSuite(TestPOIFSDocumentPath.class);
|
result.addTestSuite(TestPOIFSDocumentPath.class);
|
||||||
result.addTestSuite(TestPOIFSFileSystem.class);
|
result.addTestSuite(TestPOIFSFileSystem.class);
|
||||||
result.addTestSuite(TestPropertySorter.class);
|
result.addTestSuite(TestPropertySorter.class);
|
||||||
|
result.addTestSuite(TestOle10Native.class);
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,19 @@
|
||||||
|
package org.apache.poi.poifs.filesystem;
|
||||||
|
|
||||||
|
import junit.framework.TestCase;
|
||||||
|
import org.apache.poi.POIDataSamples;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
|
||||||
|
public class TestOle10Native extends TestCase {
|
||||||
|
private static final POIDataSamples dataSamples = POIDataSamples.getPOIFSInstance();
|
||||||
|
|
||||||
|
public void testOleNative() throws IOException, Ole10NativeException {
|
||||||
|
POIFSFileSystem fs = new POIFSFileSystem(dataSamples.openResourceAsStream("oleObject1.bin"));
|
||||||
|
|
||||||
|
Ole10Native ole = Ole10Native.createFromEmbeddedOleObject(fs);
|
||||||
|
|
||||||
|
assertEquals("File1.svg", ole.getLabel());
|
||||||
|
assertEquals("D:\\Documents and Settings\\rsc\\My Documents\\file1.svg", ole.getCommand());
|
||||||
|
}
|
||||||
|
}
|
Binary file not shown.
Loading…
Reference in New Issue