diff --git a/src/scratchpad/src/org/apache/poi/hsmf/MAPIMessage.java b/src/scratchpad/src/org/apache/poi/hsmf/MAPIMessage.java index d69ce0026..7c19a8e4b 100644 --- a/src/scratchpad/src/org/apache/poi/hsmf/MAPIMessage.java +++ b/src/scratchpad/src/org/apache/poi/hsmf/MAPIMessage.java @@ -172,6 +172,23 @@ public class MAPIMessage { public String getDisplayBCC() throws ChunkNotFoundException { return getStringFromChunk(mainChunks.displayBCCChunk); } + + + /** + * Returns the recipient's email address, checking all the + * likely chunks in search of it. + */ + public String getRecipientEmailAddress() throws ChunkNotFoundException { + if(recipientChunks == null) { + throw new ChunkNotFoundException("No recipients section present"); + } + String email = recipientChunks.getRecipientEmailAddress(); + if(email != null) { + return email; + } else { + throw new ChunkNotFoundException(); + } + } /** diff --git a/src/scratchpad/src/org/apache/poi/hsmf/datatypes/ByteChunk.java b/src/scratchpad/src/org/apache/poi/hsmf/datatypes/ByteChunk.java index 06ff28ccc..2f430ed54 100644 --- a/src/scratchpad/src/org/apache/poi/hsmf/datatypes/ByteChunk.java +++ b/src/scratchpad/src/org/apache/poi/hsmf/datatypes/ByteChunk.java @@ -61,4 +61,16 @@ public class ByteChunk extends Chunk { public void setValue(byte[] value) { this.value = value; } + + /** + * Returns the data, formatted as a string assuming it + * was a non-unicode string. + * If your data isn't in fact stored as basically + * ASCII, don't expect this to return much of any + * sense.... + * @return + */ + public String getAs7bitString() { + return StringChunk.parseAs7BitData(value); + } } diff --git a/src/scratchpad/src/org/apache/poi/hsmf/datatypes/RecipientChunks.java b/src/scratchpad/src/org/apache/poi/hsmf/datatypes/RecipientChunks.java index d03a96242..15c35c069 100644 --- a/src/scratchpad/src/org/apache/poi/hsmf/datatypes/RecipientChunks.java +++ b/src/scratchpad/src/org/apache/poi/hsmf/datatypes/RecipientChunks.java @@ -28,15 +28,62 @@ import java.util.List; public final class RecipientChunks implements ChunkGroup { public static final String PREFIX = "__recip_version1.0_#"; + public static final int RECIPIENT_NAME = 0x3001; + public static final int DELIVERY_TYPE = 0x3002; public static final int RECIPIENT_SEARCH = 0x300B; public static final int RECIPIENT_EMAIL = 0x39FE; /** TODO */ - public ByteChunk recipientSearchChunk; - /** TODO */ + public ByteChunk recipientSearchChunk; + /** + * The "name", which could be their name if an + * internal person, or their email address + * if an external person + */ + public StringChunk recipientNameChunk; + /** + * The email address of the recipient, but + * isn't always present... + */ public StringChunk recipientEmailChunk; + /** + * Normally EX or SMTP. Will generally affect + * where the email address ends up. + */ + public StringChunk deliveryTypeChunk; + /** + * Tries to find their email address, in + * whichever chunk holds it given the + * delivery type. + */ + public String getRecipientEmailAddress() { + if(recipientEmailChunk != null) { + return recipientEmailChunk.getValue(); + } + // Probably in the name field + if(recipientNameChunk != null) { + String name = recipientNameChunk.getValue(); + if(name.indexOf('@') > -1) { + // Strip leading and trailing quotes if needed + if(name.startsWith("'") && name.endsWith("'")) { + return name.substring(1, name.length()-1); + } + return name; + } + } + // Check the search chunk + if(recipientSearchChunk != null) { + String search = recipientSearchChunk.getAs7bitString(); + if(search.indexOf("SMTP:") != -1) { + return search.substring(search.indexOf("SMTP:") + 5); + } + } + // Can't find it + return null; + } + /** Holds all the chunks that were found. */ private List allChunks = new ArrayList(); @@ -56,9 +103,15 @@ public final class RecipientChunks implements ChunkGroup { // TODO - parse recipientSearchChunk = (ByteChunk)chunk; break; + case RECIPIENT_NAME: + recipientNameChunk = (StringChunk)chunk; + break; case RECIPIENT_EMAIL: recipientEmailChunk = (StringChunk)chunk; break; + case DELIVERY_TYPE: + deliveryTypeChunk = (StringChunk)chunk; + break; } // And add to the main list diff --git a/src/scratchpad/src/org/apache/poi/hsmf/datatypes/StringChunk.java b/src/scratchpad/src/org/apache/poi/hsmf/datatypes/StringChunk.java index 5dfe11629..b735058dc 100644 --- a/src/scratchpad/src/org/apache/poi/hsmf/datatypes/StringChunk.java +++ b/src/scratchpad/src/org/apache/poi/hsmf/datatypes/StringChunk.java @@ -54,11 +54,7 @@ public class StringChunk extends Chunk { switch(type) { case Types.ASCII_STRING: - try { - tmpValue = new String(data, "CP1252"); - } catch (UnsupportedEncodingException e) { - throw new RuntimeException("Core encoding not found, JVM broken?", e); - } + tmpValue = parseAs7BitData(data); break; case Types.UNICODE_STRING: tmpValue = StringUtil.getFromUnicodeLE(data); @@ -92,11 +88,23 @@ public class StringChunk extends Chunk { out.write(data); } - + public String getValue() { return this.value; } public String toString() { return this.value; } + + /** + * Parses as non-unicode, supposedly 7 bit CP1252 data + * and returns the string that that yields. + */ + protected static String parseAs7BitData(byte[] data) { + try { + return new String(data, "CP1252"); + } catch (UnsupportedEncodingException e) { + throw new RuntimeException("Core encoding not found, JVM broken?", e); + } + } } diff --git a/src/scratchpad/testcases/org/apache/poi/hsmf/AllHSMFTests.java b/src/scratchpad/testcases/org/apache/poi/hsmf/AllHSMFTests.java index 260b658fc..80660aa05 100644 --- a/src/scratchpad/testcases/org/apache/poi/hsmf/AllHSMFTests.java +++ b/src/scratchpad/testcases/org/apache/poi/hsmf/AllHSMFTests.java @@ -24,19 +24,19 @@ import org.apache.poi.hsmf.datatypes.*; import org.apache.poi.hsmf.parsers.*; public final class AllHSMFTests { + public static Test suite() { + TestSuite suite = new TestSuite(AllHSMFTests.class.getName()); + suite.addTestSuite(TestBasics.class); + suite.addTestSuite(TestBlankFileRead.class); + suite.addTestSuite(TestSimpleFileRead.class); + suite.addTestSuite(TestOutlook30FileRead.class); + suite.addTestSuite(TestFileWithAttachmentsRead.class); - public static Test suite() { - TestSuite suite = new TestSuite(AllHSMFTests.class.getName()); - suite.addTestSuite(TestBlankFileRead.class); - suite.addTestSuite(TestSimpleFileRead.class); - suite.addTestSuite(TestOutlook30FileRead.class); - suite.addTestSuite(TestFileWithAttachmentsRead.class); - suite.addTestSuite(TestChunkData.class); suite.addTestSuite(TestTypes.class); suite.addTestSuite(TestPOIFSChunkParser.class); - - return suite; - } + + return suite; + } } diff --git a/src/scratchpad/testcases/org/apache/poi/hsmf/TestBasics.java b/src/scratchpad/testcases/org/apache/poi/hsmf/TestBasics.java new file mode 100644 index 000000000..793d9ade0 --- /dev/null +++ b/src/scratchpad/testcases/org/apache/poi/hsmf/TestBasics.java @@ -0,0 +1,77 @@ +/* ==================================================================== + Licensed to the Apache Software Foundation (ASF) under one or more + contributor license agreements. See the NOTICE file distributed with + this work for additional information regarding copyright ownership. + The ASF licenses this file to You under the Apache License, Version 2.0 + (the "License"); you may not use this file except in compliance with + the License. You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +==================================================================== */ + +package org.apache.poi.hsmf; + +import java.io.IOException; + +import junit.framework.TestCase; + +import org.apache.poi.POIDataSamples; + +/** + * Tests to verify that we can perform basic opperations on + * a range of files + */ +public final class TestBasics extends TestCase { + private MAPIMessage simple; + private MAPIMessage quick; + private MAPIMessage outlook30; + private MAPIMessage attachments; + + /** + * Initialize this test, load up the blank.msg mapi message. + * @throws Exception + */ + public TestBasics() throws IOException { + POIDataSamples samples = POIDataSamples.getHSMFInstance(); + simple = new MAPIMessage(samples.openResourceAsStream("simple_test_msg.msg")); + quick = new MAPIMessage(samples.openResourceAsStream("quick.msg")); + outlook30 = new MAPIMessage(samples.openResourceAsStream("outlook_30_msg.msg")); + attachments = new MAPIMessage(samples.openResourceAsStream("attachment_test_msg.msg")); + } + + /** + * Can we always get the recipient's email? + */ + public void testRecipientEmail() throws Exception { + assertEquals("travis@overwrittenstack.com", simple.getRecipientEmailAddress()); + assertEquals("kevin.roast@alfresco.org", quick.getRecipientEmailAddress()); + assertEquals("randall.scarberry@pnl.gov", outlook30.getRecipientEmailAddress()); + assertEquals("nicolas1.23456@free.fr", attachments.getRecipientEmailAddress()); + } + + /** + * Test subject + */ + public void testSubject() throws Exception { + assertEquals("test message", simple.getSubject()); + assertEquals("Test the content transformer", quick.getSubject()); + assertEquals("IN-SPIRE servers going down for a bit, back up around 8am", outlook30.getSubject()); + assertEquals("test pi\u00e8ce jointe 1", attachments.getSubject()); + } + + /** + * Test attachments + */ + public void testAttachments() throws Exception { + assertEquals(0, simple.getAttachmentFiles().length); + assertEquals(0, quick.getAttachmentFiles().length); + assertEquals(0, outlook30.getAttachmentFiles().length); + assertEquals(2, attachments.getAttachmentFiles().length); + } +} diff --git a/src/scratchpad/testcases/org/apache/poi/hsmf/parsers/TestPOIFSChunkParser.java b/src/scratchpad/testcases/org/apache/poi/hsmf/parsers/TestPOIFSChunkParser.java index c86b586be..5df734f1f 100644 --- a/src/scratchpad/testcases/org/apache/poi/hsmf/parsers/TestPOIFSChunkParser.java +++ b/src/scratchpad/testcases/org/apache/poi/hsmf/parsers/TestPOIFSChunkParser.java @@ -105,6 +105,18 @@ public final class TestPOIFSChunkParser extends TestCase { assertNotNull(msg.getRecipientDetailsChunks()); assertEquals("kevin.roast@alfresco.org", msg.getRecipientDetailsChunks().recipientEmailChunk.getValue()); + + + // Try both SMTP and EX files for recipient + assertEquals("EX", msg.getRecipientDetailsChunks().deliveryTypeChunk.getValue()); + assertEquals("kevin.roast@alfresco.org", msg.getRecipientDetailsChunks().recipientEmailChunk.getValue()); + + msg = new MAPIMessage(new POIFSFileSystem( + new FileInputStream(samples.getFile("simple_test_msg.msg")) + )); + assertEquals("SMTP", msg.getRecipientDetailsChunks().deliveryTypeChunk.getValue()); + assertEquals(null, msg.getRecipientDetailsChunks().recipientEmailChunk); + assertEquals("travis@overwrittenstack.com", msg.getRecipientDetailsChunks().recipientNameChunk.getValue()); } public void testFindsNameId() throws IOException {