You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@poi.apache.org by fa...@apache.org on 2022/10/18 19:10:32 UTC

svn commit: r1904685 - in /poi/trunk/poi-scratchpad/src: main/java/org/apache/poi/hsmf/datatypes/PropertiesChunk.java main/java/org/apache/poi/hsmf/datatypes/StoragePropertiesChunk.java test/java/org/apache/poi/hsmf/datatypes/TestChunkData.java

Author: fanningpj
Date: Tue Oct 18 19:10:32 2022
New Revision: 1904685

URL: http://svn.apache.org/viewvc?rev=1904685&view=rev
Log:
[bug-66301] Add a method to properly write the header necessary for a MSG attachment. Thanks to Lyn Evans.

Modified:
    poi/trunk/poi-scratchpad/src/main/java/org/apache/poi/hsmf/datatypes/PropertiesChunk.java
    poi/trunk/poi-scratchpad/src/main/java/org/apache/poi/hsmf/datatypes/StoragePropertiesChunk.java
    poi/trunk/poi-scratchpad/src/test/java/org/apache/poi/hsmf/datatypes/TestChunkData.java

Modified: poi/trunk/poi-scratchpad/src/main/java/org/apache/poi/hsmf/datatypes/PropertiesChunk.java
URL: http://svn.apache.org/viewvc/poi/trunk/poi-scratchpad/src/main/java/org/apache/poi/hsmf/datatypes/PropertiesChunk.java?rev=1904685&r1=1904684&r2=1904685&view=diff
==============================================================================
--- poi/trunk/poi-scratchpad/src/main/java/org/apache/poi/hsmf/datatypes/PropertiesChunk.java (original)
+++ poi/trunk/poi-scratchpad/src/main/java/org/apache/poi/hsmf/datatypes/PropertiesChunk.java Tue Oct 18 19:10:32 2022
@@ -218,7 +218,8 @@ public abstract class PropertiesChunk ex
                     prop = MAPIProperty.createCustom(id, type, "Unknown " + id);
                 }
                 if (type == null) {
-                    LOG.atWarn().log("Invalid type found, expected {} but got {} for property {}", prop.usualType, box(typeID),prop);
+                    LOG.atWarn().log("Invalid type found, expected {} but got {} for property {}",
+                            prop.usualType, box(typeID), prop);
                     going = false;
                     break;
                 }
@@ -391,6 +392,47 @@ public abstract class PropertiesChunk ex
         return variableLengthProperties;
     }
 
+    /**
+     * Writes the manually pre-calculated(have header and data written manually) properties.
+     *
+     * @param out
+     *          The {@code OutputStream}.
+     * @return The variable-length properties that need to be written in another
+     *         node.
+     * @throws IOException
+     *           If an I/O error occurs.
+     */
+    protected List<PropertyValue> writePreCalculatedProperties(OutputStream out) throws IOException {
+        List<PropertyValue> variableLengthProperties = new ArrayList<>();
+        for (Entry<MAPIProperty, PropertyValue> entry : properties.entrySet()) {
+            MAPIProperty property = entry.getKey();
+            PropertyValue value = entry.getValue();
+            if (value == null) {
+                continue;
+            }
+            if (property.id < 0) {
+                continue;
+            }
+            // generic header
+            // page 23, point 2.4.2
+            // tag is the property id and its type
+            long tag = Long.parseLong(getActualTypeTag(property, value.getActualType()), 16);
+            LittleEndian.putUInt(tag, out);
+            LittleEndian.putUInt(value.getFlags(), out); // readable + writable
+
+            MAPIType type = value.getActualType();
+            if (type.isFixedLength()) {
+                // page 11, point 2.1.2
+                writeFixedLengthValueHeader(out, property, type, value);
+            } else {
+                // page 12, point 2.1.3
+                writeVariableLengthPreCalculatedValue(out, value);
+                variableLengthProperties.add(value);
+            }
+        }
+        return variableLengthProperties;
+    }
+
     private void writeFixedLengthValueHeader(OutputStream out, MAPIProperty property, MAPIType type, PropertyValue value) throws IOException {
         // fixed type header
         // page 24, point 2.4.2.1.1
@@ -402,6 +444,19 @@ public abstract class PropertiesChunk ex
         out.write(new byte[8 - length]);
     }
 
+    /**
+     * Writes out pre-calculated raw values which assume any variable length property `data`
+     *  field to already have size, reserved and manually written header
+     * @param out
+     * @throws IOException
+     */
+    private void writeVariableLengthPreCalculatedValue(OutputStream out, PropertyValue value) throws IOException {
+        // variable length header
+        // page 24, point 2.4.2.2
+        byte[] bytes = value.getRawValue();
+        out.write(bytes);
+    }
+
     private void writeVariableLengthValueHeader(OutputStream out, MAPIProperty propertyEx, MAPIType type,
         PropertyValue value) throws IOException {
         // variable length header
@@ -419,6 +474,15 @@ public abstract class PropertiesChunk ex
         LittleEndian.putUInt(0, out);
     }
 
+    private String getActualTypeTag(MAPIProperty property, MAPIType actualType) {
+        StringBuilder buffer = new StringBuilder(Integer.toHexString(property.id).toUpperCase(Locale.ROOT));
+        while (buffer.length() < 4) {
+            buffer.insert(0, "0");
+        }
+        buffer.append(actualType.asFileEnding());
+        return buffer.toString();
+    }
+
     private String getFileName(MAPIProperty property, MAPIType actualType) {
         StringBuilder str = new StringBuilder(Integer.toHexString(property.id).toUpperCase(Locale.ROOT));
         int need0count = 4 - str.length();

Modified: poi/trunk/poi-scratchpad/src/main/java/org/apache/poi/hsmf/datatypes/StoragePropertiesChunk.java
URL: http://svn.apache.org/viewvc/poi/trunk/poi-scratchpad/src/main/java/org/apache/poi/hsmf/datatypes/StoragePropertiesChunk.java?rev=1904685&r1=1904684&r2=1904685&view=diff
==============================================================================
--- poi/trunk/poi-scratchpad/src/main/java/org/apache/poi/hsmf/datatypes/StoragePropertiesChunk.java (original)
+++ poi/trunk/poi-scratchpad/src/main/java/org/apache/poi/hsmf/datatypes/StoragePropertiesChunk.java Tue Oct 18 19:10:32 2022
@@ -25,7 +25,7 @@ import org.apache.poi.util.LittleEndian;
 
 /**
  * A {@link PropertiesChunk} for a Storage Properties, such as Attachments and
- * Recipients. This only has a 8 byte header
+ * Recipients. This only has an 8 byte header.
  */
 public class StoragePropertiesChunk extends PropertiesChunk {
     public StoragePropertiesChunk(ChunkGroup parentGroup) {
@@ -49,4 +49,18 @@ public class StoragePropertiesChunk exte
         // Now properties
         writeProperties(out);
     }
+
+    /**
+     * Writes out pre-calculated header values which assume any variable length property `data`
+     *  field to already have Size and Reserved
+     * @param out output stream (calling code must close this stream)
+     * @throws IOException
+     */
+    public void writePreCalculatedValue(OutputStream out) throws IOException {
+        // 8 bytes of reserved zeros
+        out.write(new byte[8]);
+
+        // Now properties
+        writePreCalculatedProperties(out);
+    }
 }
\ No newline at end of file

Modified: poi/trunk/poi-scratchpad/src/test/java/org/apache/poi/hsmf/datatypes/TestChunkData.java
URL: http://svn.apache.org/viewvc/poi/trunk/poi-scratchpad/src/test/java/org/apache/poi/hsmf/datatypes/TestChunkData.java?rev=1904685&r1=1904684&r2=1904685&view=diff
==============================================================================
--- poi/trunk/poi-scratchpad/src/test/java/org/apache/poi/hsmf/datatypes/TestChunkData.java (original)
+++ poi/trunk/poi-scratchpad/src/test/java/org/apache/poi/hsmf/datatypes/TestChunkData.java Tue Oct 18 19:10:32 2022
@@ -17,10 +17,14 @@
 
 package org.apache.poi.hsmf.datatypes;
 
-import static org.junit.jupiter.api.Assertions.assertEquals;
-
 import org.junit.jupiter.api.Test;
 
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.util.List;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+
 /**
  * Verifies that the Chunks class is actually setup properly and hasn't been changed in ways
  * that will break the library.
@@ -28,22 +32,22 @@ import org.junit.jupiter.api.Test;
 public final class TestChunkData {
     @Test
     void testChunkCreate() {
-       Chunk chunk;
+        Chunk chunk;
 
         chunk = new StringChunk(0x0200, Types.createCustom(0x001E));
         assertEquals("__substg1.0_0200001E", chunk.getEntryName());
         assertEquals(0x0200, chunk.getChunkId());
         assertEquals(0x001E, chunk.getType().getId());
 
-      chunk = new StringChunk("__substg1.0_", 0x0200, Types.createCustom(0x001E));
-      assertEquals("__substg1.0_0200001E", chunk.getEntryName());
-      assertEquals(0x0200, chunk.getChunkId());
-      assertEquals(0x001E, chunk.getType().getId());
-
-      chunk = new StringChunk("__substg1.0_", 0x0200, Types.getById(0x001E));
-      assertEquals("__substg1.0_0200001E", chunk.getEntryName());
-      assertEquals(0x0200, chunk.getChunkId());
-      assertEquals(0x001E, chunk.getType().getId());
+        chunk = new StringChunk("__substg1.0_", 0x0200, Types.createCustom(0x001E));
+        assertEquals("__substg1.0_0200001E", chunk.getEntryName());
+        assertEquals(0x0200, chunk.getChunkId());
+        assertEquals(0x001E, chunk.getType().getId());
+
+        chunk = new StringChunk("__substg1.0_", 0x0200, Types.getById(0x001E));
+        assertEquals("__substg1.0_0200001E", chunk.getEntryName());
+        assertEquals(0x0200, chunk.getChunkId());
+        assertEquals(0x001E, chunk.getType().getId());
 
         /* test the lower and upper limits of the chunk ids */
         chunk = new StringChunk(0x0000, Types.createCustom(0x001E));
@@ -65,26 +69,40 @@ public final class TestChunkData {
     @Test
     void testDisplayToChunk() {
         StringChunk chunk = new StringChunk(0x0E04, Types.UNICODE_STRING);
-      assertEquals(chunk.getChunkId(), MAPIProperty.DISPLAY_TO.id);
+        assertEquals(chunk.getChunkId(), MAPIProperty.DISPLAY_TO.id);
     }
 
 
     @Test
     void testDisplayCCChunk() {
         StringChunk chunk = new StringChunk(0x0E03, Types.UNICODE_STRING);
-      assertEquals(chunk.getChunkId(), MAPIProperty.DISPLAY_CC.id);
+        assertEquals(chunk.getChunkId(), MAPIProperty.DISPLAY_CC.id);
     }
 
     @Test
     void testDisplayBCCChunk() {
         StringChunk chunk = new StringChunk(0x0E02, Types.UNICODE_STRING);
-      assertEquals(chunk.getChunkId(), MAPIProperty.DISPLAY_BCC.id);
+        assertEquals(chunk.getChunkId(), MAPIProperty.DISPLAY_BCC.id);
     }
 
     @Test
     void testSubjectChunk() {
         Chunk chunk = new StringChunk(0x0037, Types.UNICODE_STRING);
-      assertEquals(chunk.getChunkId(), MAPIProperty.SUBJECT.id);
+        assertEquals(chunk.getChunkId(), MAPIProperty.SUBJECT.id);
     }
 
+    @Test
+    void testWritePreCalculatedProperties() throws IOException {
+        try (ByteArrayOutputStream stream = new ByteArrayOutputStream()) {
+            StoragePropertiesChunk storagePropertiesChunk = new StoragePropertiesChunk(null);
+            PropertyValue.LongPropertyValue attachSize =
+                    new PropertyValue.LongPropertyValue(MAPIProperty.ATTACH_SIZE, 6L,  new byte[0]);
+            PropertyValue currentValue = new PropertyValue(MAPIProperty.DISPLAY_BCC, 6L, new byte[0]);
+            attachSize.setValue(3934266);
+            storagePropertiesChunk.setProperty(attachSize);
+            storagePropertiesChunk.setProperty(currentValue);
+            List<PropertyValue> propertyValue= storagePropertiesChunk.writePreCalculatedProperties(stream);
+            assertEquals(propertyValue.size(),1);
+        }
+    }
 }



---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@poi.apache.org
For additional commands, e-mail: commits-help@poi.apache.org