From 77e9256182d6a8bb3afc6e88ce567d66ecd3e5d6 Mon Sep 17 00:00:00 2001 From: Nick Burch Date: Mon, 15 Sep 2008 21:57:21 +0000 Subject: [PATCH] Merged revisions 695649 via svnmerge from https://svn.apache.org/repos/asf/poi/trunk ........ r695649 | nick | 2008-09-15 22:51:14 +0100 (Mon, 15 Sep 2008) | 1 line Fix inspired by bug #45804 - Update HSMF to handle Outlook 3.0 msg files, which have a different string chunk type ........ git-svn-id: https://svn.apache.org/repos/asf/poi/branches/ooxml@695651 13f79535-47bb-0310-9956-ffa450edef68 --- src/documentation/content/xdocs/changes.xml | 3 +- src/documentation/content/xdocs/status.xml | 3 +- .../src/org/apache/poi/hsmf/MAPIMessage.java | 21 +-- .../org/apache/poi/hsmf/datatypes/Chunks.java | 44 ++++-- .../poi/hsmf/datatypes/StringChunk.java | 21 ++- .../org/apache/poi/hsmf/datatypes/Types.java | 15 +- .../poi/hsmf/parsers/POIFSChunkParser.java | 33 ++++- .../org/apache/poi/hsmf/AllTests.java | 1 + .../apache/poi/hsmf/data/outlook_30_msg.msg | Bin 0 -> 97280 bytes .../apache/poi/hsmf/model/TestChunkData.java | 31 ++-- .../poi/hsmf/model/TestOutlook30FileRead.java | 135 ++++++++++++++++++ 11 files changed, 269 insertions(+), 38 deletions(-) create mode 100644 src/scratchpad/testcases/org/apache/poi/hsmf/data/outlook_30_msg.msg create mode 100644 src/scratchpad/testcases/org/apache/poi/hsmf/model/TestOutlook30FileRead.java diff --git a/src/documentation/content/xdocs/changes.xml b/src/documentation/content/xdocs/changes.xml index aa53ad8ad..f26223742 100644 --- a/src/documentation/content/xdocs/changes.xml +++ b/src/documentation/content/xdocs/changes.xml @@ -66,7 +66,8 @@ Created a common interface for handling PowerPoint files, irrespective of if they are .ppt or .pptx Created a common interface for handling Excel files, irrespective of if they are .xls or .xlsx - + + 45804 - Update HSMF to handle Outlook 3.0 msg files, which have a different string chunk type Expose the name of Named Cell Styles via HSSFCellStyle (normally held on the parent style though) 45978 - Fixed IOOBE in Ref3DPtg.toFormulaString() due eager initialisation of SheetReferences Made HSSFFormulaEvaluator no longer require initialisation with sheet or row diff --git a/src/documentation/content/xdocs/status.xml b/src/documentation/content/xdocs/status.xml index c60af7d94..8bf188faa 100644 --- a/src/documentation/content/xdocs/status.xml +++ b/src/documentation/content/xdocs/status.xml @@ -63,7 +63,8 @@ Created a common interface for handling PowerPoint files, irrespective of if they are .ppt or .pptx Created a common interface for handling Excel files, irrespective of if they are .xls or .xlsx - + + 45804 - Update HSMF to handle Outlook 3.0 msg files, which have a different string chunk type Expose the name of Named Cell Styles via HSSFCellStyle (normally held on the parent style though) 45978 - Fixed IOOBE in Ref3DPtg.toFormulaString() due eager initialisation of SheetReferences Made HSSFFormulaEvaluator no longer require initialisation with sheet or row diff --git a/src/scratchpad/src/org/apache/poi/hsmf/MAPIMessage.java b/src/scratchpad/src/org/apache/poi/hsmf/MAPIMessage.java index eb915160b..3669a5333 100644 --- a/src/scratchpad/src/org/apache/poi/hsmf/MAPIMessage.java +++ b/src/scratchpad/src/org/apache/poi/hsmf/MAPIMessage.java @@ -37,6 +37,7 @@ import org.apache.poi.poifs.filesystem.POIFSFileSystem; public class MAPIMessage { private POIFSChunkParser chunkParser; private POIFSFileSystem fs; + private Chunks chunks; /** * Constructor for creating new files. @@ -64,6 +65,10 @@ public class MAPIMessage { public MAPIMessage(InputStream in) throws IOException { this.fs = new POIFSFileSystem(in); chunkParser = new POIFSChunkParser(this.fs); + + // Figure out the right string type, based on + // the chunks present + chunks = chunkParser.identifyChunks(); } @@ -87,7 +92,7 @@ public class MAPIMessage { * @throws ChunkNotFoundException */ public String getTextBody() throws IOException, ChunkNotFoundException { - return getStringFromChunk(Chunks.getInstance().textBodyChunk); + return getStringFromChunk(chunks.textBodyChunk); } /** @@ -96,7 +101,7 @@ public class MAPIMessage { * @throws ChunkNotFoundException */ public String getSubject() throws ChunkNotFoundException { - return getStringFromChunk(Chunks.getInstance().subjectChunk); + return getStringFromChunk(chunks.subjectChunk); } @@ -107,7 +112,7 @@ public class MAPIMessage { * @throws ChunkNotFoundException */ public String getDisplayTo() throws ChunkNotFoundException { - return getStringFromChunk(Chunks.getInstance().displayToChunk); + return getStringFromChunk(chunks.displayToChunk); } /** @@ -117,7 +122,7 @@ public class MAPIMessage { * @throws ChunkNotFoundException */ public String getDisplayFrom() throws ChunkNotFoundException { - return getStringFromChunk(Chunks.getInstance().displayFromChunk); + return getStringFromChunk(chunks.displayFromChunk); } /** @@ -127,7 +132,7 @@ public class MAPIMessage { * @throws ChunkNotFoundException */ public String getDisplayCC() throws ChunkNotFoundException { - return getStringFromChunk(Chunks.getInstance().displayCCChunk); + return getStringFromChunk(chunks.displayCCChunk); } /** @@ -137,7 +142,7 @@ public class MAPIMessage { * @throws ChunkNotFoundException */ public String getDisplayBCC() throws ChunkNotFoundException { - return getStringFromChunk(Chunks.getInstance().displayBCCChunk); + return getStringFromChunk(chunks.displayBCCChunk); } @@ -148,7 +153,7 @@ public class MAPIMessage { * @throws ChunkNotFoundException */ public String getConversationTopic() throws ChunkNotFoundException { - return getStringFromChunk(Chunks.getInstance().conversationTopic); + return getStringFromChunk(chunks.conversationTopic); } /** @@ -160,6 +165,6 @@ public class MAPIMessage { * @throws ChunkNotFoundException */ public String getMessageClass() throws ChunkNotFoundException { - return getStringFromChunk(Chunks.getInstance().messageClass); + return getStringFromChunk(chunks.messageClass); } } diff --git a/src/scratchpad/src/org/apache/poi/hsmf/datatypes/Chunks.java b/src/scratchpad/src/org/apache/poi/hsmf/datatypes/Chunks.java index 6a3936d96..af77badb3 100644 --- a/src/scratchpad/src/org/apache/poi/hsmf/datatypes/Chunks.java +++ b/src/scratchpad/src/org/apache/poi/hsmf/datatypes/Chunks.java @@ -25,17 +25,39 @@ package org.apache.poi.hsmf.datatypes; */ public class Chunks { /* String parts of Outlook Messages that are currently known */ - public StringChunk messageClass = new StringChunk(0x001A); //Type of message that the MSG represents (ie. IPM.Note) - public StringChunk textBodyChunk = new StringChunk(0x1000); //BODY Chunk, for plain/text messages - public StringChunk subjectChunk = new StringChunk(0x0037); //Subject link chunk, in plain/text - public StringChunk displayToChunk = new StringChunk(0x0E04); //Value that is in the TO field (not actually the addresses as they are stored in recip directory nodes - public StringChunk displayFromChunk = new StringChunk(0x0C1A); //Value that is in the FROM field - public StringChunk displayCCChunk = new StringChunk(0x0E03); //value that shows in the CC field - public StringChunk displayBCCChunk = new StringChunk(0x0E02); //Value that shows in the BCC field - public StringChunk conversationTopic = new StringChunk(0x0070); //Sort of like the subject line, but without the RE: and FWD: parts. - public StringChunk sentByServerType = new StringChunk(0x0075); //Type of server that the message originated from (SMTP, etc). + + /** Type of message that the MSG represents (ie. IPM.Note) */ + public StringChunk messageClass; + /** BODY Chunk, for plain/text messages */ + public StringChunk textBodyChunk; + /** Subject link chunk, in plain/text */ + public StringChunk subjectChunk; + /** Value that is in the TO field (not actually the addresses as they are stored in recip directory nodes */ + public StringChunk displayToChunk; + /** Value that is in the FROM field */ + public StringChunk displayFromChunk; + /** value that shows in the CC field */ + public StringChunk displayCCChunk; + /** Value that shows in the BCC field */ + public StringChunk displayBCCChunk; + /** Sort of like the subject line, but without the RE: and FWD: parts. */ + public StringChunk conversationTopic; + /** Type of server that the message originated from (SMTP, etc). */ + public StringChunk sentByServerType; - public static Chunks getInstance() { - return new Chunks(); + private Chunks(boolean newStringType) { + messageClass = new StringChunk(0x001A, newStringType); + textBodyChunk = new StringChunk(0x1000, newStringType); + subjectChunk = new StringChunk(0x0037, newStringType); + displayToChunk = new StringChunk(0x0E04, newStringType); + displayFromChunk = new StringChunk(0x0C1A, newStringType); + displayCCChunk = new StringChunk(0x0E03, newStringType); + displayBCCChunk = new StringChunk(0x0E02, newStringType); + conversationTopic = new StringChunk(0x0070, newStringType); + sentByServerType = new StringChunk(0x0075, newStringType); + } + + public static Chunks getInstance(boolean newStringType) { + return new Chunks(newStringType); } } 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 2058b8ac5..fe90bd688 100644 --- a/src/scratchpad/src/org/apache/poi/hsmf/datatypes/StringChunk.java +++ b/src/scratchpad/src/org/apache/poi/hsmf/datatypes/StringChunk.java @@ -27,9 +27,26 @@ public class StringChunk extends Chunk { private String value; - public StringChunk(int chunkId) { + /** + * Creates a String Chunk, for either the old + * or new style of string chunk types. + */ + public StringChunk(int chunkId, boolean newStyleString) { + this(chunkId, getStringType(newStyleString)); + } + private static int getStringType(boolean newStyleString) { + if(newStyleString) + return Types.NEW_STRING; + return Types.OLD_STRING; + } + + /** + * Create a String Chunk, with the specified + * type. + */ + public StringChunk(int chunkId, int type) { this.chunkId = chunkId; - this.type = Types.STRING; + this.type = type; } /* (non-Javadoc) diff --git a/src/scratchpad/src/org/apache/poi/hsmf/datatypes/Types.java b/src/scratchpad/src/org/apache/poi/hsmf/datatypes/Types.java index 9297666af..f4d675a05 100644 --- a/src/scratchpad/src/org/apache/poi/hsmf/datatypes/Types.java +++ b/src/scratchpad/src/org/apache/poi/hsmf/datatypes/Types.java @@ -19,8 +19,21 @@ package org.apache.poi.hsmf.datatypes; public class Types { public static int BINARY = 0x0102; - public static int STRING = 0x001E; + + /** A string, until Outlook 3.0 */ + public static int OLD_STRING = 0x001E; + /** A string, from Outlook 3.0 onwards */ + public static int NEW_STRING = 0x001F; + public static int LONG = 0x0003; public static int TIME = 0x0040; public static int BOOLEAN = 0x000B; + + public static String asFileEnding(int type) { + String str = Integer.toHexString(type).toUpperCase(); + while(str.length() < 4) { + str = "0" + str; + } + return str; + } } diff --git a/src/scratchpad/src/org/apache/poi/hsmf/parsers/POIFSChunkParser.java b/src/scratchpad/src/org/apache/poi/hsmf/parsers/POIFSChunkParser.java index bdfb29e2a..c1e174e08 100644 --- a/src/scratchpad/src/org/apache/poi/hsmf/parsers/POIFSChunkParser.java +++ b/src/scratchpad/src/org/apache/poi/hsmf/parsers/POIFSChunkParser.java @@ -24,6 +24,8 @@ import java.util.HashMap; import java.util.Iterator; import org.apache.poi.hsmf.datatypes.Chunk; +import org.apache.poi.hsmf.datatypes.Chunks; +import org.apache.poi.hsmf.datatypes.Types; import org.apache.poi.hsmf.exceptions.ChunkNotFoundException; import org.apache.poi.hsmf.exceptions.DirectoryChunkNotFoundException; import org.apache.poi.poifs.filesystem.DirectoryEntry; @@ -82,7 +84,36 @@ public class POIFSChunkParser { this.directoryMap = this.processPOIIterator(iter); } - + + /** + * Returns a list of the standard chunk types, as + * appropriate for the chunks we find in the file. + */ + public Chunks identifyChunks() { + // Are they of the old or new type of strings? + boolean hasOldStrings = false; + boolean hasNewStrings = false; + String oldStringEnd = Types.asFileEnding(Types.OLD_STRING); + String newStringEnd = Types.asFileEnding(Types.NEW_STRING); + + for(Iterator i = directoryMap.keySet().iterator(); i.hasNext();) { + String entry = (String)i.next(); + if(entry.endsWith( oldStringEnd )) { + hasOldStrings = true; + } + if(entry.endsWith( newStringEnd )) { + hasNewStrings = true; + } + } + + if(hasOldStrings && hasNewStrings) { + throw new IllegalStateException("Your file contains string chunks of both the old and new types. Giving up"); + } else if(hasNewStrings) { + return Chunks.getInstance(true); + } + return Chunks.getInstance(false); + } + /** * Pull the chunk data that's stored in this object's hashmap out and return it as a HashMap. * @param entryName diff --git a/src/scratchpad/testcases/org/apache/poi/hsmf/AllTests.java b/src/scratchpad/testcases/org/apache/poi/hsmf/AllTests.java index 18a622c32..e117ab89e 100644 --- a/src/scratchpad/testcases/org/apache/poi/hsmf/AllTests.java +++ b/src/scratchpad/testcases/org/apache/poi/hsmf/AllTests.java @@ -33,6 +33,7 @@ public class AllTests TestSuite suite = new TestSuite(); suite.addTestSuite(org.apache.poi.hsmf.model.TestBlankFileRead.class); suite.addTestSuite(org.apache.poi.hsmf.model.TestSimpleFileRead.class); + suite.addTestSuite(org.apache.poi.hsmf.model.TestOutlook30FileRead.class); suite.addTestSuite(org.apache.poi.hsmf.model.TestChunkData.class); return suite; diff --git a/src/scratchpad/testcases/org/apache/poi/hsmf/data/outlook_30_msg.msg b/src/scratchpad/testcases/org/apache/poi/hsmf/data/outlook_30_msg.msg new file mode 100644 index 0000000000000000000000000000000000000000..0a585699a260035cb3e6baace8c1c64d81f8197d GIT binary patch literal 97280 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 literal 0 HcmV?d00001 diff --git a/src/scratchpad/testcases/org/apache/poi/hsmf/model/TestChunkData.java b/src/scratchpad/testcases/org/apache/poi/hsmf/model/TestChunkData.java index dc4b53129..8f5dd4777 100644 --- a/src/scratchpad/testcases/org/apache/poi/hsmf/model/TestChunkData.java +++ b/src/scratchpad/testcases/org/apache/poi/hsmf/model/TestChunkData.java @@ -31,42 +31,47 @@ import junit.framework.TestCase; * */ public class TestChunkData extends TestCase { + private Chunks chunks = Chunks.getInstance(false); + public void testChunkCreate() { - StringChunk chunk = new StringChunk(0x0200); + StringChunk chunk = new StringChunk(0x0200, false); TestCase.assertEquals("__substg1.0_0200001E", chunk.getEntryName()); /* test the lower and upper limits of the chunk ids */ - chunk = new StringChunk(0x0000); + chunk = new StringChunk(0x0000, false); TestCase.assertEquals("__substg1.0_0000001E", chunk.getEntryName()); - chunk = new StringChunk(0xFFFF); + chunk = new StringChunk(0xFFFF, false); TestCase.assertEquals("__substg1.0_FFFF001E", chunk.getEntryName()); + + chunk = new StringChunk(0xFFFF, true); + TestCase.assertEquals("__substg1.0_FFFF001F", chunk.getEntryName()); } public void testTextBodyChunk() { - StringChunk chunk = new StringChunk(0x1000); - TestCase.assertEquals(chunk.getEntryName(), Chunks.getInstance().textBodyChunk.getEntryName()); + StringChunk chunk = new StringChunk(0x1000, false); + TestCase.assertEquals(chunk.getEntryName(), chunks.textBodyChunk.getEntryName()); } public void testDisplayToChunk() { - StringChunk chunk = new StringChunk(0x0E04); - TestCase.assertEquals(chunk.getEntryName(), Chunks.getInstance().displayToChunk.getEntryName()); + StringChunk chunk = new StringChunk(0x0E04, false); + TestCase.assertEquals(chunk.getEntryName(), chunks.displayToChunk.getEntryName()); } public void testDisplayCCChunk() { - StringChunk chunk = new StringChunk(0x0E03); - TestCase.assertEquals(chunk.getEntryName(), Chunks.getInstance().displayCCChunk.getEntryName()); + StringChunk chunk = new StringChunk(0x0E03, false); + TestCase.assertEquals(chunk.getEntryName(), chunks.displayCCChunk.getEntryName()); } public void testDisplayBCCChunk() { - StringChunk chunk = new StringChunk(0x0E02); - TestCase.assertEquals(chunk.getEntryName(), Chunks.getInstance().displayBCCChunk.getEntryName()); + StringChunk chunk = new StringChunk(0x0E02, false); + TestCase.assertEquals(chunk.getEntryName(), chunks.displayBCCChunk.getEntryName()); } public void testSubjectChunk() { - Chunk chunk = new StringChunk(0x0037); - TestCase.assertEquals(chunk.getEntryName(), Chunks.getInstance().subjectChunk.getEntryName()); + Chunk chunk = new StringChunk(0x0037, false); + TestCase.assertEquals(chunk.getEntryName(), chunks.subjectChunk.getEntryName()); } } diff --git a/src/scratchpad/testcases/org/apache/poi/hsmf/model/TestOutlook30FileRead.java b/src/scratchpad/testcases/org/apache/poi/hsmf/model/TestOutlook30FileRead.java new file mode 100644 index 000000000..7e94405e0 --- /dev/null +++ b/src/scratchpad/testcases/org/apache/poi/hsmf/model/TestOutlook30FileRead.java @@ -0,0 +1,135 @@ +/* ==================================================================== + 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.model; + +import java.io.IOException; + +import org.apache.poi.hsmf.MAPIMessage; +import org.apache.poi.hsmf.exceptions.ChunkNotFoundException; + +import junit.framework.TestCase; + +/** + * Tests to verify that we can still work on the newer Outlook 3.0 files. + */ +public class TestOutlook30FileRead extends TestCase { +private MAPIMessage mapiMessage; + + /** + * Initialize this test, load up the blank.msg mapi message. + * @throws Exception + */ + public TestOutlook30FileRead() throws IOException { + String dirname = System.getProperty("HSMF.testdata.path"); + this.mapiMessage = new MAPIMessage(dirname + "/outlook_30_msg.msg"); + } + + /** + * Test to see if we can read the CC Chunk. + * @throws ChunkNotFoundException + * + */ + public void testReadDisplayCC() throws ChunkNotFoundException { + String obtained = mapiMessage.getDisplayCC(); + String expected = ""; + + TestCase.assertEquals(obtained, expected); + } + + /** + * Test to see if we can read the CC Chunk. + * @throws ChunkNotFoundException + * + */ + public void testReadDisplayTo() throws ChunkNotFoundException { + String obtained = mapiMessage.getDisplayTo(); + + assertTrue(obtained.startsWith("Bohn, Shawn")); + } + + /** + * Test to see if we can read the From Chunk. + * @throws ChunkNotFoundException + * + */ + public void testReadDisplayFrom() throws ChunkNotFoundException { + String obtained = mapiMessage.getDisplayFrom(); + String expected = "Cramer, Nick"; + + TestCase.assertEquals(obtained, expected); + } + + /** + * Test to see if we can read the CC Chunk. + * @throws ChunkNotFoundException + * + */ + public void testReadDisplayBCC() throws ChunkNotFoundException { + String obtained = mapiMessage.getDisplayBCC(); + String expected = ""; + + TestCase.assertEquals(obtained, expected); + } + + + /** + * Check if we can read the body of the blank message, we expect "". + * + * @throws Exception + */ + public void testReadBody() throws Exception { + String obtained = mapiMessage.getTextBody(); + assertTrue(obtained.startsWith("I am shutting down")); + } + + /** + * Check if we can read the subject line of the blank message, we expect "" + * + * @throws Exception + */ + public void testReadSubject() throws Exception { + String obtained = mapiMessage.getSubject(); + String expected = "IN-SPIRE servers going down for a bit, back up around 8am"; + + TestCase.assertEquals(expected, obtained); + } + + /** + * Check if we can read the subject line of the blank message, we expect "" + * + * @throws Exception + */ + public void testReadConversationTopic() throws Exception { + String obtained = mapiMessage.getConversationTopic(); + TestCase.assertEquals("IN-SPIRE servers going down for a bit, back up around 8am", obtained); + } + + + /** + * Check if we can read the subject line of the blank message, we expect "" + * + * @throws Exception + */ + public void testReadMessageClass() throws Exception { + String obtained = mapiMessage.getMessageClass(); + TestCase.assertEquals("IPM.Note", obtained); + } + + + +}