diff options
author | Dominik Stadler <centic@apache.org> | 2023-08-06 14:57:47 +0000 |
---|---|---|
committer | Dominik Stadler <centic@apache.org> | 2023-08-06 14:57:47 +0000 |
commit | 1e8e95c3a659e88c3303f6247298c3b865229122 (patch) | |
tree | 9781b598b3f9c90c35189a52680b575f3fc7ec08 | |
parent | e5ff0e4eb6abd50e3cbd4a44a5477be94450d702 (diff) | |
download | poi-1e8e95c3a659e88c3303f6247298c3b865229122.tar.gz poi-1e8e95c3a659e88c3303f6247298c3b865229122.zip |
Bug 66425: Avoid a ClassCastException found via oss-fuzz
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
-rw-r--r-- | poi-scratchpad/src/main/java/org/apache/poi/hsmf/datatypes/Chunks.java | 110 | ||||
-rw-r--r-- | poi-scratchpad/src/test/java/org/apache/poi/hsmf/parsers/TestPOIFSChunkParser.java | 581 | ||||
-rw-r--r-- | test-data/hsmf/clusterfuzz-testcase-minimized-POIHSMFFuzzer-4848576776503296.msg | bin | 0 -> 21504 bytes | |||
-rw-r--r-- | test-data/spreadsheet/stress.xls | bin | 39936 -> 39936 bytes |
4 files changed, 345 insertions, 346 deletions
diff --git a/poi-scratchpad/src/main/java/org/apache/poi/hsmf/datatypes/Chunks.java b/poi-scratchpad/src/main/java/org/apache/poi/hsmf/datatypes/Chunks.java index 041ab37556..2ffcb3f3b2 100644 --- a/poi-scratchpad/src/main/java/org/apache/poi/hsmf/datatypes/Chunks.java +++ b/poi-scratchpad/src/main/java/org/apache/poi/hsmf/datatypes/Chunks.java @@ -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 diff --git a/poi-scratchpad/src/test/java/org/apache/poi/hsmf/parsers/TestPOIFSChunkParser.java b/poi-scratchpad/src/test/java/org/apache/poi/hsmf/parsers/TestPOIFSChunkParser.java index 528fefd548..f93ff19f0f 100644 --- a/poi-scratchpad/src/test/java/org/apache/poi/hsmf/parsers/TestPOIFSChunkParser.java +++ b/poi-scratchpad/src/test/java/org/apache/poi/hsmf/parsers/TestPOIFSChunkParser.java @@ -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 Binary files differnew file mode 100644 index 0000000000..fc86ac083a --- /dev/null +++ b/test-data/hsmf/clusterfuzz-testcase-minimized-POIHSMFFuzzer-4848576776503296.msg diff --git a/test-data/spreadsheet/stress.xls b/test-data/spreadsheet/stress.xls Binary files differindex 1032a86a92..bd7aa3bcc7 100644 --- a/test-data/spreadsheet/stress.xls +++ b/test-data/spreadsheet/stress.xls |