]> source.dussan.org Git - poi.git/commitdiff
Add code for reading Ole10Native data
authorMaxim Valyanskiy <maxcom@apache.org>
Thu, 9 Sep 2010 13:18:48 +0000 (13:18 +0000)
committerMaxim Valyanskiy <maxcom@apache.org>
Thu, 9 Sep 2010 13:18:48 +0000 (13:18 +0000)
git-svn-id: https://svn.apache.org/repos/asf/poi/trunk@995415 13f79535-47bb-0310-9956-ffa450edef68

src/java/org/apache/poi/poifs/filesystem/Ole10Native.java [new file with mode: 0644]
src/java/org/apache/poi/poifs/filesystem/Ole10NativeException.java [new file with mode: 0644]
src/testcases/org/apache/poi/poifs/filesystem/AllPOIFSFileSystemTests.java
src/testcases/org/apache/poi/poifs/filesystem/TestOle10Native.java [new file with mode: 0644]
test-data/poifs/oleObject1.bin [new file with mode: 0644]

diff --git a/src/java/org/apache/poi/poifs/filesystem/Ole10Native.java b/src/java/org/apache/poi/poifs/filesystem/Ole10Native.java
new file mode 100644 (file)
index 0000000..3b2ff76
--- /dev/null
@@ -0,0 +1,259 @@
+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 &quot;{01}Ole10Native&quot; 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
diff --git a/src/java/org/apache/poi/poifs/filesystem/Ole10NativeException.java b/src/java/org/apache/poi/poifs/filesystem/Ole10NativeException.java
new file mode 100644 (file)
index 0000000..d3c36ea
--- /dev/null
@@ -0,0 +1,7 @@
+package org.apache.poi.poifs.filesystem;
+
+public class Ole10NativeException extends Exception {
+    public Ole10NativeException(String message) {
+        super(message);
+    }
+}
index 9c8f6acd748031293c0a231cfe4a4579d8bd92b8..295b5ad574bf4edc8e67dc5674194601b44ff238 100644 (file)
@@ -40,6 +40,7 @@ public final class AllPOIFSFileSystemTests {
         result.addTestSuite(TestPOIFSDocumentPath.class);
         result.addTestSuite(TestPOIFSFileSystem.class);
         result.addTestSuite(TestPropertySorter.class);
+        result.addTestSuite(TestOle10Native.class);
         return result;
     }
 }
diff --git a/src/testcases/org/apache/poi/poifs/filesystem/TestOle10Native.java b/src/testcases/org/apache/poi/poifs/filesystem/TestOle10Native.java
new file mode 100644 (file)
index 0000000..332cdab
--- /dev/null
@@ -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());
+    }
+}
diff --git a/test-data/poifs/oleObject1.bin b/test-data/poifs/oleObject1.bin
new file mode 100644 (file)
index 0000000..fc46302
Binary files /dev/null and b/test-data/poifs/oleObject1.bin differ