--- /dev/null
+package org.apache.poi.poifs.filesystem;\r
+\r
+import org.apache.poi.util.*;\r
+\r
+import java.io.ByteArrayOutputStream;\r
+import java.io.FileNotFoundException;\r
+import java.io.IOException;\r
+import java.util.Arrays;\r
+\r
+/**\r
+ * Represents an Ole10Native record which is wrapped around certain binary\r
+ * files being embedded in OLE2 documents.\r
+ *\r
+ * @author Rainer Schwarze\r
+ */\r
+public class Ole10Native {\r
+ // (the fields as they appear in the raw record:)\r
+ private final int totalSize; // 4 bytes, total size of record not including this field\r
+ private short flags1; // 2 bytes, unknown, mostly [02 00]\r
+ private final String label; // ASCIIZ, stored in this field without the terminating zero\r
+ private final String fileName; // ASCIIZ, stored in this field without the terminating zero\r
+ private short flags2; // 2 bytes, unknown, mostly [00 00]\r
+ // private byte unknown1Length; // 1 byte, specifying the length of the following byte array (unknown1)\r
+ private byte[] unknown1; // see below\r
+ private byte[] unknown2; // 3 bytes, unknown, mostly [00 00 00]\r
+ private final String command; // ASCIIZ, stored in this field without the terminating zero\r
+ private final int dataSize; // 4 bytes (if space), size of following buffer\r
+ private final byte[] dataBuffer; // varying size, the actual native data\r
+ private short flags3; // some final flags? or zero terminators?, sometimes not there\r
+ public static final String OLE10_NATIVE = "\u0001Ole10Native";\r
+\r
+ /**\r
+ * Creates an instance of this class from an embedded OLE Object. The OLE Object is expected\r
+ * to include a stream "{01}Ole10Native" which contains the actual\r
+ * data relevant for this class.\r
+ *\r
+ * @param poifs POI Filesystem object\r
+ * @return Returns an instance of this class\r
+ * @throws IOException on IO error\r
+ * @throws Ole10NativeException on invalid or unexcepted data format\r
+ */\r
+ public static Ole10Native createFromEmbeddedOleObject(POIFSFileSystem poifs) throws IOException, Ole10NativeException {\r
+ boolean plain = false;\r
+\r
+ try {\r
+ poifs.getRoot().getEntry("\u0001Ole10ItemName");\r
+ plain = true;\r
+ } catch (FileNotFoundException ex) {\r
+ plain = false;\r
+ }\r
+\r
+ DocumentInputStream dis = poifs.createDocumentInputStream(OLE10_NATIVE);\r
+ ByteArrayOutputStream bos = new ByteArrayOutputStream();\r
+ IOUtils.copy(dis, bos);\r
+ byte[] data = bos.toByteArray();\r
+\r
+ return new Ole10Native(data, 0, plain);\r
+ }\r
+\r
+ /**\r
+ * Creates an instance and fills the fields based on the data in the given buffer.\r
+ *\r
+ * @param data The buffer containing the Ole10Native record\r
+ * @param offset The start offset of the record in the buffer\r
+ * @throws Ole10NativeException on invalid or unexcepted data format\r
+ */\r
+ public Ole10Native(byte[] data, int offset) throws Ole10NativeException {\r
+ this(data, offset, false);\r
+ }\r
+ /**\r
+ * Creates an instance and fills the fields based on the data in the given buffer.\r
+ *\r
+ * @param data The buffer containing the Ole10Native record\r
+ * @param offset The start offset of the record in the buffer\r
+ * @param plain Specified 'plain' format without filename\r
+ * @throws Ole10NativeException on invalid or unexcepted data format\r
+ */\r
+ public Ole10Native(byte[] data, int offset, boolean plain) throws Ole10NativeException {\r
+ int ofs = offset; // current offset, initialized to start\r
+\r
+ if (data.length<offset+2) {\r
+ throw new Ole10NativeException("data is too small");\r
+ }\r
+\r
+ totalSize = LittleEndian.getInt(data, ofs);\r
+ ofs += LittleEndianConsts.INT_SIZE;\r
+\r
+ if (plain) {\r
+ dataBuffer = new byte[totalSize-4];\r
+ System.arraycopy(data, 4, dataBuffer, 0, dataBuffer.length);\r
+ dataSize = totalSize - 4;\r
+ label = "ole-"+ HexDump.toHex(Arrays.copyOf(dataBuffer, 8));\r
+ fileName = label;\r
+ command = label;\r
+ } else {\r
+ flags1 = LittleEndian.getShort(data, ofs);\r
+ ofs += LittleEndianConsts.SHORT_SIZE;\r
+ int len = getStringLength(data, ofs);\r
+ label = StringUtil.getFromCompressedUnicode(data, ofs, len - 1);\r
+ ofs += len;\r
+ len = getStringLength(data, ofs);\r
+ fileName = StringUtil.getFromCompressedUnicode(data, ofs, len - 1);\r
+ ofs += len;\r
+ flags2 = LittleEndian.getShort(data, ofs);\r
+ ofs += LittleEndianConsts.SHORT_SIZE;\r
+ len = LittleEndian.getUnsignedByte(data, ofs);\r
+ unknown1 = new byte[len];\r
+ ofs += len;\r
+ len = 3;\r
+ unknown2 = new byte[len];\r
+ ofs += len;\r
+ len = getStringLength(data, ofs);\r
+ command = StringUtil.getFromCompressedUnicode(data, ofs, len - 1);\r
+ ofs += len;\r
+\r
+ if (totalSize + LittleEndianConsts.INT_SIZE - ofs > LittleEndianConsts.INT_SIZE) {\r
+ dataSize = LittleEndian.getInt(data, ofs);\r
+ ofs += LittleEndianConsts.INT_SIZE;\r
+\r
+ if (dataSize > totalSize || dataSize<0) {\r
+ throw new Ole10NativeException("Invalid Ole10Native");\r
+ }\r
+\r
+ dataBuffer = new byte[dataSize];\r
+ System.arraycopy(data, ofs, dataBuffer, 0, dataSize);\r
+ ofs += dataSize;\r
+\r
+ if (unknown1.length > 0) {\r
+ flags3 = LittleEndian.getShort(data, ofs);\r
+ ofs += LittleEndianConsts.SHORT_SIZE;\r
+ } else {\r
+ flags3 = 0;\r
+ }\r
+ } else {\r
+ throw new Ole10NativeException("Invalid Ole10Native");\r
+ }\r
+ }\r
+ }\r
+\r
+ /*\r
+ * Helper - determine length of zero terminated string (ASCIIZ).\r
+ */\r
+ private static int getStringLength(byte[] data, int ofs) {\r
+ int len = 0;\r
+ while (len+ofs<data.length && data[ofs + len] != 0) {\r
+ len++;\r
+ }\r
+ len++;\r
+ return len;\r
+ }\r
+\r
+ /**\r
+ * Returns the value of the totalSize field - the total length of the structure\r
+ * is totalSize + 4 (value of this field + size of this field).\r
+ *\r
+ * @return the totalSize\r
+ */\r
+ public int getTotalSize() {\r
+ return totalSize;\r
+ }\r
+\r
+ /**\r
+ * Returns flags1 - currently unknown - usually 0x0002.\r
+ *\r
+ * @return the flags1\r
+ */\r
+ public short getFlags1() {\r
+ return flags1;\r
+ }\r
+\r
+ /**\r
+ * Returns the label field - usually the name of the file (without directory) but\r
+ * probably may be any name specified during packaging/embedding the data.\r
+ *\r
+ * @return the label\r
+ */\r
+ public String getLabel() {\r
+ return label;\r
+ }\r
+\r
+ /**\r
+ * Returns the fileName field - usually the name of the file being embedded\r
+ * including the full path.\r
+ *\r
+ * @return the fileName\r
+ */\r
+ public String getFileName() {\r
+ return fileName;\r
+ }\r
+\r
+ /**\r
+ * Returns flags2 - currently unknown - mostly 0x0000.\r
+ *\r
+ * @return the flags2\r
+ */\r
+ public short getFlags2() {\r
+ return flags2;\r
+ }\r
+\r
+ /**\r
+ * Returns unknown1 field - currently unknown.\r
+ *\r
+ * @return the unknown1\r
+ */\r
+ public byte[] getUnknown1() {\r
+ return unknown1;\r
+ }\r
+\r
+ /**\r
+ * Returns the unknown2 field - currently being a byte[3] - mostly {0, 0, 0}.\r
+ *\r
+ * @return the unknown2\r
+ */\r
+ public byte[] getUnknown2() {\r
+ return unknown2;\r
+ }\r
+\r
+ /**\r
+ * Returns the command field - usually the name of the file being embedded\r
+ * including the full path, may be a command specified during embedding the file.\r
+ *\r
+ * @return the command\r
+ */\r
+ public String getCommand() {\r
+ return command;\r
+ }\r
+\r
+ /**\r
+ * Returns the size of the embedded file. If the size is 0 (zero), no data has been\r
+ * embedded. To be sure, that no data has been embedded, check whether\r
+ * {@link #getDataBuffer()} returns <code>null</code>.\r
+ *\r
+ * @return the dataSize\r
+ */\r
+ public int getDataSize() {\r
+ return dataSize;\r
+ }\r
+\r
+ /**\r
+ * Returns the buffer containing the embedded file's data, or <code>null</code>\r
+ * if no data was embedded. Note that an embedding may provide information about\r
+ * the data, but the actual data is not included. (So label, filename etc. are\r
+ * available, but this method returns <code>null</code>.)\r
+ *\r
+ * @return the dataBuffer\r
+ */\r
+ public byte[] getDataBuffer() {\r
+ return dataBuffer;\r
+ }\r
+\r
+ /**\r
+ * Returns the flags3 - currently unknown.\r
+ *\r
+ * @return the flags3\r
+ */\r
+ public short getFlags3() {\r
+ return flags3;\r
+ }\r
+}\r