]> source.dussan.org Git - poi.git/commitdiff
Bug 66425: Avoid a ClassCastException found via oss-fuzz
authorDominik Stadler <centic@apache.org>
Sun, 6 Aug 2023 14:57:47 +0000 (14:57 +0000)
committerDominik Stadler <centic@apache.org>
Sun, 6 Aug 2023 14:57:47 +0000 (14:57 +0000)
We try to avoid throwing ClassCastException, but it was possible
to trigger one here with a specially crafted input-file

Also rework test a bit to use try-with-resources and proper formatting

Should fix https://bugs.chromium.org/p/oss-fuzz/issues/detail?id=61221

git-svn-id: https://svn.apache.org/repos/asf/poi/trunk@1911494 13f79535-47bb-0310-9956-ffa450edef68

poi-scratchpad/src/main/java/org/apache/poi/hsmf/datatypes/Chunks.java
poi-scratchpad/src/test/java/org/apache/poi/hsmf/parsers/TestPOIFSChunkParser.java
test-data/hsmf/clusterfuzz-testcase-minimized-POIHSMFFuzzer-4848576776503296.msg [new file with mode: 0644]
test-data/spreadsheet/stress.xls

index 041ab37556ad019ad8270da798a9e2ff734ec3ad..2ffcb3f3b2ca7b90aa44d6d8cab04cd9a7696101 100644 (file)
@@ -194,67 +194,65 @@ public final class Chunks implements ChunkGroupWithProperties {
     public void record(Chunk chunk) {
         // Work out what MAPIProperty this corresponds to
         MAPIProperty prop = MAPIProperty.get(chunk.getChunkId());
-        if (prop == MAPIProperty.UNKNOWN) {
-            long id = (chunk.getChunkId() << 16) + (long)chunk.getType().getId();
-            prop = unknownProperties.get(id);
-            if (prop == null) {
-                prop = MAPIProperty.createCustom(chunk.getChunkId(), chunk.getType(), chunk.getEntryName());
-                unknownProperties.put(id, prop);
+        try {
+            if (prop == MAPIProperty.UNKNOWN) {
+                long id = (chunk.getChunkId() << 16) + (long) chunk.getType().getId();
+                prop = unknownProperties.get(id);
+                if (prop == null) {
+                    prop = MAPIProperty.createCustom(chunk.getChunkId(), chunk.getType(), chunk.getEntryName());
+                    unknownProperties.put(id, prop);
+                }
             }
-        }
-
-        // Assign it for easy lookup, as best we can
-        if (prop == MAPIProperty.MESSAGE_CLASS) {
-            messageClass = (StringChunk) chunk;
-        } else if (prop == MAPIProperty.INTERNET_MESSAGE_ID) {
-            messageId = (StringChunk) chunk;
-        } else if (prop == MAPIProperty.MESSAGE_SUBMISSION_ID) {
-            // TODO - parse
-            submissionChunk = (MessageSubmissionChunk) chunk;
-        } else if (prop == MAPIProperty.RECEIVED_BY_ADDRTYPE) {
-            sentByServerType = (StringChunk) chunk;
-        } else if (prop == MAPIProperty.TRANSPORT_MESSAGE_HEADERS) {
-            messageHeaders = (StringChunk) chunk;
-        }
 
-        else if (prop == MAPIProperty.CONVERSATION_TOPIC) {
-            conversationTopic = (StringChunk) chunk;
-        } else if (prop == MAPIProperty.SUBJECT) {
-            subjectChunk = (StringChunk) chunk;
-        } /*else if (prop == MAPIProperty.ORIGINAL_SUBJECT) {
+            // Assign it for easy lookup, as best we can
+            if (prop == MAPIProperty.MESSAGE_CLASS) {
+                messageClass = (StringChunk) chunk;
+            } else if (prop == MAPIProperty.INTERNET_MESSAGE_ID) {
+                messageId = (StringChunk) chunk;
+            } else if (prop == MAPIProperty.MESSAGE_SUBMISSION_ID) {
+                // TODO - parse
+                submissionChunk = (MessageSubmissionChunk) chunk;
+            } else if (prop == MAPIProperty.RECEIVED_BY_ADDRTYPE) {
+                sentByServerType = (StringChunk) chunk;
+            } else if (prop == MAPIProperty.TRANSPORT_MESSAGE_HEADERS) {
+                messageHeaders = (StringChunk) chunk;
+            } else if (prop == MAPIProperty.CONVERSATION_TOPIC) {
+                conversationTopic = (StringChunk) chunk;
+            } else if (prop == MAPIProperty.SUBJECT) {
+                subjectChunk = (StringChunk) chunk;
+            } /*else if (prop == MAPIProperty.ORIGINAL_SUBJECT) {
             // TODO
-        }*/
-
-        else if (prop == MAPIProperty.DISPLAY_TO) {
-            displayToChunk = (StringChunk) chunk;
-        } else if (prop == MAPIProperty.DISPLAY_CC) {
-            displayCCChunk = (StringChunk) chunk;
-        } else if (prop == MAPIProperty.DISPLAY_BCC) {
-            displayBCCChunk = (StringChunk) chunk;
-        }
-
-        else if (prop == MAPIProperty.SENDER_EMAIL_ADDRESS) {
-            emailFromChunk = (StringChunk) chunk;
-        } else if (prop == MAPIProperty.SENDER_NAME) {
-            displayFromChunk = (StringChunk) chunk;
-        } else if (prop == MAPIProperty.BODY) {
-            textBodyChunk = (StringChunk) chunk;
-        } else if (prop == MAPIProperty.BODY_HTML) {
-            if (chunk instanceof StringChunk) {
-                htmlBodyChunkString = (StringChunk) chunk;
-            }
-            if (chunk instanceof ByteChunk) {
-                htmlBodyChunkBinary = (ByteChunk) chunk;
+        }*/ else if (prop == MAPIProperty.DISPLAY_TO) {
+                displayToChunk = (StringChunk) chunk;
+            } else if (prop == MAPIProperty.DISPLAY_CC) {
+                displayCCChunk = (StringChunk) chunk;
+            } else if (prop == MAPIProperty.DISPLAY_BCC) {
+                displayBCCChunk = (StringChunk) chunk;
+            } else if (prop == MAPIProperty.SENDER_EMAIL_ADDRESS) {
+                emailFromChunk = (StringChunk) chunk;
+            } else if (prop == MAPIProperty.SENDER_NAME) {
+                displayFromChunk = (StringChunk) chunk;
+            } else if (prop == MAPIProperty.BODY) {
+                textBodyChunk = (StringChunk) chunk;
+            } else if (prop == MAPIProperty.BODY_HTML) {
+                if (chunk instanceof StringChunk) {
+                    htmlBodyChunkString = (StringChunk) chunk;
+                }
+                if (chunk instanceof ByteChunk) {
+                    htmlBodyChunkBinary = (ByteChunk) chunk;
+                }
+            } else if (prop == MAPIProperty.RTF_COMPRESSED) {
+                rtfBodyChunk = (ByteChunk) chunk;
+            } else if (chunk instanceof MessagePropertiesChunk) {
+                messageProperties = (MessagePropertiesChunk) chunk;
             }
-        } else if (prop == MAPIProperty.RTF_COMPRESSED) {
-            rtfBodyChunk = (ByteChunk) chunk;
-        } else if (chunk instanceof MessagePropertiesChunk) {
-            messageProperties = (MessagePropertiesChunk) chunk;
-        }
 
-        // And add to the main list
-        allChunks.computeIfAbsent(prop, k -> new ArrayList<>());
-        allChunks.get(prop).add(chunk);
+            // And add to the main list
+            allChunks.computeIfAbsent(prop, k -> new ArrayList<>());
+            allChunks.get(prop).add(chunk);
+        } catch (ClassCastException e) {
+            throw new IllegalArgumentException("Property and type of chunk did not match, had property " + prop + " and type of chunk: " + chunk.getClass());
+        }
     }
 
     @Override
index 528fefd548b540ece8bfd7ec4b9ea8a929bc6e94..f93ff19f0f662d3e29d08bd48f752730fb58f59c 100644 (file)
@@ -21,6 +21,7 @@ import static org.junit.jupiter.api.Assertions.assertEquals;
 import static org.junit.jupiter.api.Assertions.assertFalse;
 import static org.junit.jupiter.api.Assertions.assertNotNull;
 import static org.junit.jupiter.api.Assertions.assertNull;
+import static org.junit.jupiter.api.Assertions.assertThrows;
 import static org.junit.jupiter.api.Assertions.assertTrue;
 
 import java.io.IOException;
@@ -48,294 +49,294 @@ import org.junit.jupiter.api.Test;
  * Tests to verify that the chunk parser works properly
  */
 public final class TestPOIFSChunkParser {
-   private final POIDataSamples samples = POIDataSamples.getHSMFInstance();
-
-   @Test
-   void testFindsCore() throws IOException, ChunkNotFoundException {
-      POIFSFileSystem simple = new POIFSFileSystem(samples.getFile("quick.msg"), true);
-
-      // Check a few core things are present
-      simple.getRoot().getEntry(
-            (new StringChunk(MAPIProperty.SUBJECT.id, Types.ASCII_STRING)).getEntryName()
-      );
-      simple.getRoot().getEntry(
-            (new StringChunk(MAPIProperty.SENDER_NAME.id, Types.ASCII_STRING)).getEntryName()
-      );
-
-      // Now load the file
-      MAPIMessage msg = new MAPIMessage(simple);
-      assertEquals("Kevin Roast", msg.getDisplayTo());
-      assertEquals("Kevin Roast", msg.getDisplayFrom());
-      assertEquals("Test the content transformer", msg.getSubject());
-
-      // Check date too
-      Calendar calExp = LocaleUtil.getLocaleCalendar(2007,5,14,9,42,55);
-      Calendar calAct = msg.getMessageDate();
-      assertEquals( calExp, calAct );
-
-      msg.close();
-      simple.close();
-   }
-
-   @Test
-   void testFindsRecips() throws IOException, ChunkNotFoundException {
-      POIFSFileSystem simple = new POIFSFileSystem(samples.getFile("quick.msg"), true);
-
-      simple.getRoot().getEntry("__recip_version1.0_#00000000");
-
-      ChunkGroup[] groups = POIFSChunkParser.parse(simple.getRoot());
-      assertEquals(3, groups.length);
-      assertTrue(groups[0] instanceof Chunks);
-      assertTrue(groups[1] instanceof RecipientChunks);
-      assertTrue(groups[2] instanceof NameIdChunks);
-
-      RecipientChunks recips = (RecipientChunks)groups[1];
-      assertEquals("kevin.roast@alfresco.org", recips.getRecipientSMTPChunk().getValue());
-      assertEquals("/O=HOSTEDSERVICE2/OU=FIRST ADMINISTRATIVE GROUP/CN=RECIPIENTS/CN=Kevin.roast@ben",
-            recips.getRecipientEmailChunk().getValue());
-
-      String search = new String(recips.getRecipientSearchChunk().getValue(), StandardCharsets.US_ASCII);
-      assertEquals("CN=KEVIN.ROAST@BEN\0", search.substring(search.length()-19));
-
-      // Now via MAPIMessage
-      MAPIMessage msg = new MAPIMessage(simple);
-      assertNotNull(msg.getRecipientDetailsChunks());
-      assertEquals(1, msg.getRecipientDetailsChunks().length);
-
-      assertEquals("kevin.roast@alfresco.org", msg.getRecipientDetailsChunks()[0].getRecipientSMTPChunk().getValue());
-      assertEquals("kevin.roast@alfresco.org", msg.getRecipientDetailsChunks()[0].getRecipientEmailAddress());
-      assertEquals("Kevin Roast", msg.getRecipientDetailsChunks()[0].getRecipientName());
-      assertEquals("kevin.roast@alfresco.org", msg.getRecipientEmailAddress());
-
-
-      // Try both SMTP and EX files for recipient
-      assertEquals("EX", msg.getRecipientDetailsChunks()[0].getDeliveryTypeChunk().getValue());
-      assertEquals("kevin.roast@alfresco.org", msg.getRecipientDetailsChunks()[0].getRecipientSMTPChunk().getValue());
-      assertEquals("/O=HOSTEDSERVICE2/OU=FIRST ADMINISTRATIVE GROUP/CN=RECIPIENTS/CN=Kevin.roast@ben",
-            msg.getRecipientDetailsChunks()[0].getRecipientEmailChunk().getValue());
-      msg.close();
-      simple.close();
-
-
-      // Now look at another message
-      simple = new POIFSFileSystem(samples.getFile("simple_test_msg.msg"), true);
-      msg = new MAPIMessage(simple);
-      assertNotNull(msg.getRecipientDetailsChunks());
-      assertEquals(1, msg.getRecipientDetailsChunks().length);
-
-      assertEquals("SMTP", msg.getRecipientDetailsChunks()[0].getDeliveryTypeChunk().getValue());
-      assertNull(msg.getRecipientDetailsChunks()[0].getRecipientSMTPChunk());
-      assertNull(msg.getRecipientDetailsChunks()[0].getRecipientNameChunk());
-      assertEquals("travis@overwrittenstack.com", msg.getRecipientDetailsChunks()[0].getRecipientEmailChunk().getValue());
-      assertEquals("travis@overwrittenstack.com", msg.getRecipientEmailAddress());
-
-      msg.close();
-      simple.close();
-   }
-
-   @Test
-   void testFindsMultipleRecipients() throws IOException, ChunkNotFoundException {
-      POIFSFileSystem multiple = new POIFSFileSystem(samples.getFile("example_received_unicode.msg"), true);
-
-      multiple.getRoot().getEntry("__recip_version1.0_#00000000");
-      multiple.getRoot().getEntry("__recip_version1.0_#00000001");
-      multiple.getRoot().getEntry("__recip_version1.0_#00000002");
-      multiple.getRoot().getEntry("__recip_version1.0_#00000003");
-      multiple.getRoot().getEntry("__recip_version1.0_#00000004");
-      multiple.getRoot().getEntry("__recip_version1.0_#00000005");
-
-      ChunkGroup[] groups = POIFSChunkParser.parse(multiple.getRoot());
-      assertEquals(9, groups.length);
-      assertTrue(groups[0] instanceof Chunks);
-      assertTrue(groups[1] instanceof RecipientChunks);
-      assertTrue(groups[2] instanceof RecipientChunks);
-      assertTrue(groups[3] instanceof RecipientChunks);
-      assertTrue(groups[4] instanceof RecipientChunks);
-      assertTrue(groups[5] instanceof AttachmentChunks);
-      assertTrue(groups[6] instanceof RecipientChunks);
-      assertTrue(groups[7] instanceof RecipientChunks);
-      assertTrue(groups[8] instanceof NameIdChunks);
-
-      // In FS order initially
-      RecipientChunks[] chunks = new RecipientChunks[] {
-            (RecipientChunks)groups[1],
-            (RecipientChunks)groups[2],
-            (RecipientChunks)groups[3],
-            (RecipientChunks)groups[4],
-            (RecipientChunks)groups[6],
-            (RecipientChunks)groups[7],
-      };
-      assertEquals(6, chunks.length);
-      assertEquals(0, chunks[0].getRecipientNumber());
-      assertEquals(2, chunks[1].getRecipientNumber());
-      assertEquals(4, chunks[2].getRecipientNumber());
-      assertEquals(5, chunks[3].getRecipientNumber());
-      assertEquals(3, chunks[4].getRecipientNumber());
-      assertEquals(1, chunks[5].getRecipientNumber());
-
-      // Check
-      assertEquals("'Ashutosh Dandavate'", chunks[0].getRecipientName());
-      assertEquals("ashutosh.dandavate@alfresco.com", chunks[0].getRecipientEmailAddress());
-      assertEquals("'Mike Farman'", chunks[1].getRecipientName());
-      assertEquals("mikef@alfresco.com", chunks[1].getRecipientEmailAddress());
-      assertEquals("nick.burch@alfresco.com", chunks[2].getRecipientName());
-      assertEquals("nick.burch@alfresco.com", chunks[2].getRecipientEmailAddress());
-      assertEquals("'Roy Wetherall'", chunks[3].getRecipientName());
-      assertEquals("roy.wetherall@alfresco.com", chunks[3].getRecipientEmailAddress());
-      assertEquals("nickb@alfresco.com", chunks[4].getRecipientName());
-      assertEquals("nickb@alfresco.com", chunks[4].getRecipientEmailAddress());
-      assertEquals("'Paul Holmes-Higgin'", chunks[5].getRecipientName());
-      assertEquals("paul.hh@alfresco.com", chunks[5].getRecipientEmailAddress());
-
-      // Now sort, and re-check
-      Arrays.sort(chunks, new RecipientChunksSorter());
-
-      assertEquals("'Ashutosh Dandavate'", chunks[0].getRecipientName());
-      assertEquals("ashutosh.dandavate@alfresco.com", chunks[0].getRecipientEmailAddress());
-      assertEquals("'Paul Holmes-Higgin'", chunks[1].getRecipientName());
-      assertEquals("paul.hh@alfresco.com", chunks[1].getRecipientEmailAddress());
-      assertEquals("'Mike Farman'", chunks[2].getRecipientName());
-      assertEquals("mikef@alfresco.com", chunks[2].getRecipientEmailAddress());
-      assertEquals("nickb@alfresco.com", chunks[3].getRecipientName());
-      assertEquals("nickb@alfresco.com", chunks[3].getRecipientEmailAddress());
-      assertEquals("nick.burch@alfresco.com", chunks[4].getRecipientName());
-      assertEquals("nick.burch@alfresco.com", chunks[4].getRecipientEmailAddress());
-      assertEquals("'Roy Wetherall'", chunks[5].getRecipientName());
-      assertEquals("roy.wetherall@alfresco.com", chunks[5].getRecipientEmailAddress());
-
-      // Finally check on message
-      MAPIMessage msg = new MAPIMessage(multiple);
-      assertEquals(6, msg.getRecipientEmailAddressList().length);
-      assertEquals(6, msg.getRecipientNamesList().length);
-
-      assertEquals("'Ashutosh Dandavate'", msg.getRecipientNamesList()[0]);
-      assertEquals("'Paul Holmes-Higgin'", msg.getRecipientNamesList()[1]);
-      assertEquals("'Mike Farman'",        msg.getRecipientNamesList()[2]);
-      assertEquals("nickb@alfresco.com",   msg.getRecipientNamesList()[3]);
-      assertEquals("nick.burch@alfresco.com", msg.getRecipientNamesList()[4]);
-      assertEquals("'Roy Wetherall'",      msg.getRecipientNamesList()[5]);
-
-      assertEquals("ashutosh.dandavate@alfresco.com", msg.getRecipientEmailAddressList()[0]);
-      assertEquals("paul.hh@alfresco.com",            msg.getRecipientEmailAddressList()[1]);
-      assertEquals("mikef@alfresco.com",              msg.getRecipientEmailAddressList()[2]);
-      assertEquals("nickb@alfresco.com",              msg.getRecipientEmailAddressList()[3]);
-      assertEquals("nick.burch@alfresco.com",         msg.getRecipientEmailAddressList()[4]);
-      assertEquals("roy.wetherall@alfresco.com",      msg.getRecipientEmailAddressList()[5]);
-
-      msg.close();
-      multiple.close();
-   }
-
-   @Test
-   void testFindsNameId() throws IOException {
-      POIFSFileSystem simple = new POIFSFileSystem(samples.getFile("quick.msg"), true);
-
-      simple.getRoot().getEntry("__nameid_version1.0");
-
-      ChunkGroup[] groups = POIFSChunkParser.parse(simple.getRoot());
-      assertEquals(3, groups.length);
-      assertTrue(groups[0] instanceof Chunks);
-      assertTrue(groups[1] instanceof RecipientChunks);
-      assertTrue(groups[2] instanceof NameIdChunks);
-
-      NameIdChunks nameId = (NameIdChunks)groups[2];
-      assertEquals(10, nameId.getAll().length);
-
-      // Now via MAPIMessage
-      MAPIMessage msg = new MAPIMessage(simple);
-      assertNotNull(msg.getNameIdChunks());
-      assertEquals(10, msg.getNameIdChunks().getAll().length);
-
-      msg.close();
-      simple.close();
-   }
-
-   @Test
-   void testFindsAttachments() throws IOException, ChunkNotFoundException {
-      POIFSFileSystem with = new POIFSFileSystem(samples.getFile("attachment_test_msg.msg"), true);
-      POIFSFileSystem without = new POIFSFileSystem(samples.getFile("quick.msg"), true);
-      AttachmentChunks attachment;
-
-
-      // Check raw details on the one with
-      with.getRoot().getEntry("__attach_version1.0_#00000000");
-      with.getRoot().getEntry("__attach_version1.0_#00000001");
-      POIFSChunkParser.parse(with.getRoot());
-
-      ChunkGroup[] groups = POIFSChunkParser.parse(with.getRoot());
-      assertEquals(5, groups.length);
-      assertTrue(groups[0] instanceof Chunks);
-      assertTrue(groups[1] instanceof RecipientChunks);
-      assertTrue(groups[2] instanceof AttachmentChunks);
-      assertTrue(groups[3] instanceof AttachmentChunks);
-      assertTrue(groups[4] instanceof NameIdChunks);
-
-      attachment = (AttachmentChunks)groups[2];
-      assertEquals("TEST-U~1.DOC", attachment.getAttachFileName().toString());
-      assertEquals("test-unicode.doc", attachment.getAttachLongFileName().toString());
-      assertEquals(24064, attachment.getAttachData().getValue().length);
-
-      attachment = (AttachmentChunks)groups[3];
-      assertEquals("pj1.txt", attachment.getAttachFileName().toString());
-      assertEquals("pj1.txt", attachment.getAttachLongFileName().toString());
-      assertEquals(89, attachment.getAttachData().getValue().length);
-
-
-      // Check raw details on one without
-      assertFalse(without.getRoot().hasEntry("__attach_version1.0_#00000000"));
-      assertFalse(without.getRoot().hasEntry("__attach_version1.0_#00000001"));
-
-       // One with, from the top
-      MAPIMessage msgWith = new MAPIMessage(with);
-      assertEquals(2, msgWith.getAttachmentFiles().length);
-
-      attachment = msgWith.getAttachmentFiles()[0];
-      assertEquals("TEST-U~1.DOC", attachment.getAttachFileName().toString());
-      assertEquals("test-unicode.doc", attachment.getAttachLongFileName().toString());
-      assertEquals(24064, attachment.getAttachData().getValue().length);
-
-      attachment = msgWith.getAttachmentFiles()[1];
-      assertEquals("pj1.txt", attachment.getAttachFileName().toString());
-      assertEquals("pj1.txt", attachment.getAttachLongFileName().toString());
-      assertEquals(89, attachment.getAttachData().getValue().length);
-
-      // Plus check core details are there
-      assertEquals("'nicolas1.23456@free.fr'", msgWith.getDisplayTo());
-      assertEquals("Nicolas1 23456", msgWith.getDisplayFrom());
-      assertEquals("test pi\u00e8ce jointe 1", msgWith.getSubject());
-
-       // One without, from the top
-      MAPIMessage msgWithout = new MAPIMessage(without);
-
-      // No attachments
-      assertEquals(0, msgWithout.getAttachmentFiles().length);
-
-      // But has core details
-      assertEquals("Kevin Roast", msgWithout.getDisplayTo());
-      assertEquals("Kevin Roast", msgWithout.getDisplayFrom());
-      assertEquals("Test the content transformer", msgWithout.getSubject());
-
-      msgWithout.close();
-      msgWith.close();
-      without.close();
-      with.close();
-   }
-
-   /**
-    * Bugzilla #51873 - Outlook 2002 files created with dragging and
-    *  dropping files to the disk include a non-standard named streams
-    *  such as "Olk10SideProps_0001"
-    */
-   @Test
-   void testOlk10SideProps() throws IOException, ChunkNotFoundException {
-      POIFSFileSystem poifs = new POIFSFileSystem(samples.getFile("51873.msg"), true);
-      MAPIMessage msg = new MAPIMessage(poifs);
-
-      // Check core details came through
-      assertEquals("bubba@bubbasmith.com", msg.getDisplayTo());
-      assertEquals("Test with Olk10SideProps_ Chunk", msg.getSubject());
-
-      msg.close();
-      poifs.close();
-   }
+
+    private final POIDataSamples samples = POIDataSamples.getHSMFInstance();
+
+    @Test
+    void testFindsCore() throws IOException, ChunkNotFoundException {
+        try (POIFSFileSystem simple = new POIFSFileSystem(samples.getFile("quick.msg"), true)) {
+
+            // Check a few core things are present
+            simple.getRoot().getEntry(
+                    (new StringChunk(MAPIProperty.SUBJECT.id, Types.ASCII_STRING)).getEntryName()
+            );
+            simple.getRoot().getEntry(
+                    (new StringChunk(MAPIProperty.SENDER_NAME.id, Types.ASCII_STRING)).getEntryName()
+            );
+
+            // Now load the file
+            try (MAPIMessage msg = new MAPIMessage(simple)) {
+                assertEquals("Kevin Roast", msg.getDisplayTo());
+                assertEquals("Kevin Roast", msg.getDisplayFrom());
+                assertEquals("Test the content transformer", msg.getSubject());
+
+                // Check date too
+                Calendar calExp = LocaleUtil.getLocaleCalendar(2007, 5, 14, 9, 42, 55);
+                Calendar calAct = msg.getMessageDate();
+                assertEquals(calExp, calAct);
+            }
+        }
+    }
+
+    @Test
+    void testFindsRecips() throws IOException, ChunkNotFoundException {
+        try (POIFSFileSystem simple = new POIFSFileSystem(samples.getFile("quick.msg"), true)) {
+
+            simple.getRoot().getEntry("__recip_version1.0_#00000000");
+
+            ChunkGroup[] groups = POIFSChunkParser.parse(simple.getRoot());
+            assertEquals(3, groups.length);
+            assertTrue(groups[0] instanceof Chunks);
+            assertTrue(groups[1] instanceof RecipientChunks);
+            assertTrue(groups[2] instanceof NameIdChunks);
+
+            RecipientChunks recips = (RecipientChunks) groups[1];
+            assertEquals("kevin.roast@alfresco.org", recips.getRecipientSMTPChunk().getValue());
+            assertEquals("/O=HOSTEDSERVICE2/OU=FIRST ADMINISTRATIVE GROUP/CN=RECIPIENTS/CN=Kevin.roast@ben",
+                    recips.getRecipientEmailChunk().getValue());
+
+            String search = new String(recips.getRecipientSearchChunk().getValue(), StandardCharsets.US_ASCII);
+            assertEquals("CN=KEVIN.ROAST@BEN\0", search.substring(search.length() - 19));
+
+            // Now via MAPIMessage
+            try (MAPIMessage msg = new MAPIMessage(simple)) {
+                assertNotNull(msg.getRecipientDetailsChunks());
+                assertEquals(1, msg.getRecipientDetailsChunks().length);
+
+                assertEquals("kevin.roast@alfresco.org", msg.getRecipientDetailsChunks()[0].getRecipientSMTPChunk().getValue());
+                assertEquals("kevin.roast@alfresco.org", msg.getRecipientDetailsChunks()[0].getRecipientEmailAddress());
+                assertEquals("Kevin Roast", msg.getRecipientDetailsChunks()[0].getRecipientName());
+                assertEquals("kevin.roast@alfresco.org", msg.getRecipientEmailAddress());
+
+                // Try both SMTP and EX files for recipient
+                assertEquals("EX", msg.getRecipientDetailsChunks()[0].getDeliveryTypeChunk().getValue());
+                assertEquals("kevin.roast@alfresco.org", msg.getRecipientDetailsChunks()[0].getRecipientSMTPChunk().getValue());
+                assertEquals("/O=HOSTEDSERVICE2/OU=FIRST ADMINISTRATIVE GROUP/CN=RECIPIENTS/CN=Kevin.roast@ben",
+                        msg.getRecipientDetailsChunks()[0].getRecipientEmailChunk().getValue());
+            }
+        }
+
+            // Now look at another message
+        try (POIFSFileSystem simple = new POIFSFileSystem(samples.getFile("simple_test_msg.msg"), true)) {
+            try (MAPIMessage msg = new MAPIMessage(simple)) {
+                assertNotNull(msg.getRecipientDetailsChunks());
+                assertEquals(1, msg.getRecipientDetailsChunks().length);
+
+                assertEquals("SMTP", msg.getRecipientDetailsChunks()[0].getDeliveryTypeChunk().getValue());
+                assertNull(msg.getRecipientDetailsChunks()[0].getRecipientSMTPChunk());
+                assertNull(msg.getRecipientDetailsChunks()[0].getRecipientNameChunk());
+                assertEquals("travis@overwrittenstack.com",
+                        msg.getRecipientDetailsChunks()[0].getRecipientEmailChunk().getValue());
+                assertEquals("travis@overwrittenstack.com", msg.getRecipientEmailAddress());
+            }
+        }
+    }
+
+    @Test
+    void testFindsMultipleRecipients() throws IOException, ChunkNotFoundException {
+        try (POIFSFileSystem multiple = new POIFSFileSystem(samples.getFile("example_received_unicode.msg"), true)) {
+
+            multiple.getRoot().getEntry("__recip_version1.0_#00000000");
+            multiple.getRoot().getEntry("__recip_version1.0_#00000001");
+            multiple.getRoot().getEntry("__recip_version1.0_#00000002");
+            multiple.getRoot().getEntry("__recip_version1.0_#00000003");
+            multiple.getRoot().getEntry("__recip_version1.0_#00000004");
+            multiple.getRoot().getEntry("__recip_version1.0_#00000005");
+
+            ChunkGroup[] groups = POIFSChunkParser.parse(multiple.getRoot());
+            assertEquals(9, groups.length);
+            assertTrue(groups[0] instanceof Chunks);
+            assertTrue(groups[1] instanceof RecipientChunks);
+            assertTrue(groups[2] instanceof RecipientChunks);
+            assertTrue(groups[3] instanceof RecipientChunks);
+            assertTrue(groups[4] instanceof RecipientChunks);
+            assertTrue(groups[5] instanceof AttachmentChunks);
+            assertTrue(groups[6] instanceof RecipientChunks);
+            assertTrue(groups[7] instanceof RecipientChunks);
+            assertTrue(groups[8] instanceof NameIdChunks);
+
+            // In FS order initially
+            RecipientChunks[] chunks = new RecipientChunks[] {
+                    (RecipientChunks) groups[1],
+                    (RecipientChunks) groups[2],
+                    (RecipientChunks) groups[3],
+                    (RecipientChunks) groups[4],
+                    (RecipientChunks) groups[6],
+                    (RecipientChunks) groups[7],
+            };
+            assertEquals(6, chunks.length);
+            assertEquals(0, chunks[0].getRecipientNumber());
+            assertEquals(2, chunks[1].getRecipientNumber());
+            assertEquals(4, chunks[2].getRecipientNumber());
+            assertEquals(5, chunks[3].getRecipientNumber());
+            assertEquals(3, chunks[4].getRecipientNumber());
+            assertEquals(1, chunks[5].getRecipientNumber());
+
+            // Check
+            assertEquals("'Ashutosh Dandavate'", chunks[0].getRecipientName());
+            assertEquals("ashutosh.dandavate@alfresco.com", chunks[0].getRecipientEmailAddress());
+            assertEquals("'Mike Farman'", chunks[1].getRecipientName());
+            assertEquals("mikef@alfresco.com", chunks[1].getRecipientEmailAddress());
+            assertEquals("nick.burch@alfresco.com", chunks[2].getRecipientName());
+            assertEquals("nick.burch@alfresco.com", chunks[2].getRecipientEmailAddress());
+            assertEquals("'Roy Wetherall'", chunks[3].getRecipientName());
+            assertEquals("roy.wetherall@alfresco.com", chunks[3].getRecipientEmailAddress());
+            assertEquals("nickb@alfresco.com", chunks[4].getRecipientName());
+            assertEquals("nickb@alfresco.com", chunks[4].getRecipientEmailAddress());
+            assertEquals("'Paul Holmes-Higgin'", chunks[5].getRecipientName());
+            assertEquals("paul.hh@alfresco.com", chunks[5].getRecipientEmailAddress());
+
+            // Now sort, and re-check
+            Arrays.sort(chunks, new RecipientChunksSorter());
+
+            assertEquals("'Ashutosh Dandavate'", chunks[0].getRecipientName());
+            assertEquals("ashutosh.dandavate@alfresco.com", chunks[0].getRecipientEmailAddress());
+            assertEquals("'Paul Holmes-Higgin'", chunks[1].getRecipientName());
+            assertEquals("paul.hh@alfresco.com", chunks[1].getRecipientEmailAddress());
+            assertEquals("'Mike Farman'", chunks[2].getRecipientName());
+            assertEquals("mikef@alfresco.com", chunks[2].getRecipientEmailAddress());
+            assertEquals("nickb@alfresco.com", chunks[3].getRecipientName());
+            assertEquals("nickb@alfresco.com", chunks[3].getRecipientEmailAddress());
+            assertEquals("nick.burch@alfresco.com", chunks[4].getRecipientName());
+            assertEquals("nick.burch@alfresco.com", chunks[4].getRecipientEmailAddress());
+            assertEquals("'Roy Wetherall'", chunks[5].getRecipientName());
+            assertEquals("roy.wetherall@alfresco.com", chunks[5].getRecipientEmailAddress());
+
+            // Finally check on message
+            try (MAPIMessage msg = new MAPIMessage(multiple)) {
+                assertEquals(6, msg.getRecipientEmailAddressList().length);
+                assertEquals(6, msg.getRecipientNamesList().length);
+
+                assertEquals("'Ashutosh Dandavate'", msg.getRecipientNamesList()[0]);
+                assertEquals("'Paul Holmes-Higgin'", msg.getRecipientNamesList()[1]);
+                assertEquals("'Mike Farman'", msg.getRecipientNamesList()[2]);
+                assertEquals("nickb@alfresco.com", msg.getRecipientNamesList()[3]);
+                assertEquals("nick.burch@alfresco.com", msg.getRecipientNamesList()[4]);
+                assertEquals("'Roy Wetherall'", msg.getRecipientNamesList()[5]);
+
+                assertEquals("ashutosh.dandavate@alfresco.com", msg.getRecipientEmailAddressList()[0]);
+                assertEquals("paul.hh@alfresco.com", msg.getRecipientEmailAddressList()[1]);
+                assertEquals("mikef@alfresco.com", msg.getRecipientEmailAddressList()[2]);
+                assertEquals("nickb@alfresco.com", msg.getRecipientEmailAddressList()[3]);
+                assertEquals("nick.burch@alfresco.com", msg.getRecipientEmailAddressList()[4]);
+                assertEquals("roy.wetherall@alfresco.com", msg.getRecipientEmailAddressList()[5]);
+            }
+        }
+    }
+
+    @Test
+    void testFindsNameId() throws IOException {
+        try (POIFSFileSystem simple = new POIFSFileSystem(samples.getFile("quick.msg"), true)) {
+
+            simple.getRoot().getEntry("__nameid_version1.0");
+
+            ChunkGroup[] groups = POIFSChunkParser.parse(simple.getRoot());
+            assertEquals(3, groups.length);
+            assertTrue(groups[0] instanceof Chunks);
+            assertTrue(groups[1] instanceof RecipientChunks);
+            assertTrue(groups[2] instanceof NameIdChunks);
+
+            NameIdChunks nameId = (NameIdChunks) groups[2];
+            assertEquals(10, nameId.getAll().length);
+
+            // Now via MAPIMessage
+            try (MAPIMessage msg = new MAPIMessage(simple)) {
+                assertNotNull(msg.getNameIdChunks());
+                assertEquals(10, msg.getNameIdChunks().getAll().length);
+            }
+        }
+    }
+
+    @Test
+    void testFindsAttachments() throws IOException, ChunkNotFoundException {
+        try (POIFSFileSystem with = new POIFSFileSystem(samples.getFile("attachment_test_msg.msg"), true);
+                POIFSFileSystem without = new POIFSFileSystem(samples.getFile("quick.msg"), true)) {
+            AttachmentChunks attachment;
+
+            // Check raw details on the one with
+            with.getRoot().getEntry("__attach_version1.0_#00000000");
+            with.getRoot().getEntry("__attach_version1.0_#00000001");
+            POIFSChunkParser.parse(with.getRoot());
+
+            ChunkGroup[] groups = POIFSChunkParser.parse(with.getRoot());
+            assertEquals(5, groups.length);
+            assertTrue(groups[0] instanceof Chunks);
+            assertTrue(groups[1] instanceof RecipientChunks);
+            assertTrue(groups[2] instanceof AttachmentChunks);
+            assertTrue(groups[3] instanceof AttachmentChunks);
+            assertTrue(groups[4] instanceof NameIdChunks);
+
+            attachment = (AttachmentChunks) groups[2];
+            assertEquals("TEST-U~1.DOC", attachment.getAttachFileName().toString());
+            assertEquals("test-unicode.doc", attachment.getAttachLongFileName().toString());
+            assertEquals(24064, attachment.getAttachData().getValue().length);
+
+            attachment = (AttachmentChunks) groups[3];
+            assertEquals("pj1.txt", attachment.getAttachFileName().toString());
+            assertEquals("pj1.txt", attachment.getAttachLongFileName().toString());
+            assertEquals(89, attachment.getAttachData().getValue().length);
+
+            // Check raw details on one without
+            assertFalse(without.getRoot().hasEntry("__attach_version1.0_#00000000"));
+            assertFalse(without.getRoot().hasEntry("__attach_version1.0_#00000001"));
+
+            // One with, from the top
+            try (MAPIMessage msgWith = new MAPIMessage(with)) {
+                assertEquals(2, msgWith.getAttachmentFiles().length);
+
+                attachment = msgWith.getAttachmentFiles()[0];
+                assertEquals("TEST-U~1.DOC", attachment.getAttachFileName().toString());
+                assertEquals("test-unicode.doc", attachment.getAttachLongFileName().toString());
+                assertEquals(24064, attachment.getAttachData().getValue().length);
+
+                attachment = msgWith.getAttachmentFiles()[1];
+                assertEquals("pj1.txt", attachment.getAttachFileName().toString());
+                assertEquals("pj1.txt", attachment.getAttachLongFileName().toString());
+                assertEquals(89, attachment.getAttachData().getValue().length);
+
+                // Plus check core details are there
+                assertEquals("'nicolas1.23456@free.fr'", msgWith.getDisplayTo());
+                assertEquals("Nicolas1 23456", msgWith.getDisplayFrom());
+                assertEquals("test pi\u00e8ce jointe 1", msgWith.getSubject());
+
+                // One without, from the top
+                try (MAPIMessage msgWithout = new MAPIMessage(without)) {
+
+                    // No attachments
+                    assertEquals(0, msgWithout.getAttachmentFiles().length);
+
+                    // But has core details
+                    assertEquals("Kevin Roast", msgWithout.getDisplayTo());
+                    assertEquals("Kevin Roast", msgWithout.getDisplayFrom());
+                    assertEquals("Test the content transformer", msgWithout.getSubject());
+                }
+            }
+
+        }
+    }
+
+    /**
+     * Bugzilla #51873 - Outlook 2002 files created with dragging and
+     * dropping files to the disk include a non-standard named streams
+     * such as "Olk10SideProps_0001"
+     */
+    @Test
+    void testOlk10SideProps() throws IOException, ChunkNotFoundException {
+        try (POIFSFileSystem poifs = new POIFSFileSystem(samples.getFile("51873.msg"), true)) {
+            try (MAPIMessage msg = new MAPIMessage(poifs)) {
+                // Check core details came through
+                assertEquals("bubba@bubbasmith.com", msg.getDisplayTo());
+                assertEquals("Test with Olk10SideProps_ Chunk", msg.getSubject());
+            }
+        }
+    }
+
+    @Test
+    void testInvalidChunk() throws IOException {
+        try (POIFSFileSystem poifs =
+                new POIFSFileSystem(samples.getFile("clusterfuzz-testcase-minimized-POIHSMFFuzzer-4848576776503296.msg"), true)) {
+            assertThrows(IllegalArgumentException.class,
+                    () -> new MAPIMessage(poifs));
+        }
+    }
 }
diff --git a/test-data/hsmf/clusterfuzz-testcase-minimized-POIHSMFFuzzer-4848576776503296.msg b/test-data/hsmf/clusterfuzz-testcase-minimized-POIHSMFFuzzer-4848576776503296.msg
new file mode 100644 (file)
index 0000000..fc86ac0
Binary files /dev/null and b/test-data/hsmf/clusterfuzz-testcase-minimized-POIHSMFFuzzer-4848576776503296.msg differ
index 1032a86a92d98429da0b6f81b25cdebd0dcd66c4..bd7aa3bcc7752f329c42d153b046a39b37201446 100644 (file)
Binary files a/test-data/spreadsheet/stress.xls and b/test-data/spreadsheet/stress.xls differ