import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import org.apache.poi.poifs.filesystem.DocumentInputStream;
import org.apache.poi.poifs.filesystem.POIFSFileSystem;
import org.apache.poi.util.IOUtils;
import org.apache.poi.util.LittleEndian;
import org.apache.poi.util.StringUtil;
/**
* Represents an Ole10Native record which is wrapped around certain binary
* files being embedded in OLE2 documents.
*
* Issues:
*
* - no toByteArray() or write(...) implemented yet
* - Check embedding of zero-length files - may conflict with dataSize=0 case
*
*
* @author Rainer Schwarze
*/
public class Ole10NativeStructure {
// (the fields as they appear in the raw record:)
private int totalSize; // 4 bytes, total size of record not including this field
private short flags1; // 2 bytes, unknown, mostly [02 00]
private String label; // ASCIIZ, stored in this field without the terminating zero
private 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 String command; // ASCIIZ, stored in this field without the terminating zero
private int dataSize; // 4 bytes (if space), size of following buffer
private byte [] dataBuffer; // varying size, the actual native data
private short flags3; // some final flags? or zero terminators?, sometimes not there
/**
* 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 is
* @return Returns an instance of this class - not null.
* @throws IOException
*/
public static Ole10NativeStructure createFromEmbeddedOleObject(InputStream is) throws IOException {
POIFSFileSystem poifs = new POIFSFileSystem(is);
DocumentInputStream dis = poifs.createDocumentInputStream("\u0001Ole10Native");
ByteArrayOutputStream bos = new ByteArrayOutputStream();
IOUtils.copy(dis, bos);
byte [] data = bos.toByteArray();
Ole10NativeStructure s = new Ole10NativeStructure(data, 0);
return s;
}
/**
* 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
*/
public Ole10NativeStructure(byte[] data, int offset) {
super();
int ofs = offset; // current offset, initialized to start
totalSize = LittleEndian.getInt(data, ofs);
ofs += LittleEndian.INT_SIZE;
flags1 = LittleEndian.getShort(data, ofs);
ofs += LittleEndian.SHORT_SIZE;
int len = 0;
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 += LittleEndian.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+LittleEndian.INT_SIZE - ofs>LittleEndian.INT_SIZE) {
dataSize = LittleEndian.getInt(data, ofs);
ofs += LittleEndian.INT_SIZE;
dataBuffer = new byte[dataSize];
ofs += dataSize;
} else {
dataSize = 0;
dataBuffer = null; // new byte[0];
}
if (unknown1.length>0) {
flags3 = LittleEndian.getShort(data, ofs);
ofs += LittleEndian.SHORT_SIZE;
} else {
flags3 = 0;
}
}
/*
* Helper - determine length of zero terminated string (ASCIIZ).
*/
private int getStringLength(byte[] data, int ofs) {
int len = 0;
while (data[ofs+len]!=0) {
len++;
}
len++;
return len;
}
// public void dump() {
// System.out.println("===============================================");
// System.out.println("totalSize = " + totalSize + " (0x" + Integer.toHexString(totalSize) + ")");
// System.out.println("flags1 = " + Integer.toHexString(flags1));
// System.out.println("label = [" + label + "]");
// System.out.println("fileName = [" + fileName + "]");
// System.out.println("flags2 = " + Integer.toHexString(flags2));
// System.out.println("unknown1 = " + HexDump.toHex(unknown1));
// System.out.println("unknown2 = " + HexDump.toHex(unknown2));
// System.out.println("command = [" + command + "]");
// System.out.println("dataSize = " + dataSize + " (0x" + Integer.toHexString(dataSize) + ")");
// System.out.println("data = ");
// System.out.println("flags3 = " + Integer.toHexString(flags3));
// System.out.println("===============================================");
// }
/**
* 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 null
.
*
* @return the dataSize
*/
public int getDataSize() {
return dataSize;
}
/**
* Returns the buffer containing the embedded file's data, or null
* 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 null
.)
*
* @return the dataBuffer
*/
public byte[] getDataBuffer() {
return dataBuffer;
}
/**
* Returns the flags3 - currently unknown.
*
* @return the flags3
*/
public short getFlags3() {
return flags3;
}
}