]> source.dussan.org Git - poi.git/commitdiff
Merged revisions 695649 via svnmerge from
authorNick Burch <nick@apache.org>
Mon, 15 Sep 2008 21:57:21 +0000 (21:57 +0000)
committerNick Burch <nick@apache.org>
Mon, 15 Sep 2008 21:57:21 +0000 (21:57 +0000)
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
src/documentation/content/xdocs/status.xml
src/scratchpad/src/org/apache/poi/hsmf/MAPIMessage.java
src/scratchpad/src/org/apache/poi/hsmf/datatypes/Chunks.java
src/scratchpad/src/org/apache/poi/hsmf/datatypes/StringChunk.java
src/scratchpad/src/org/apache/poi/hsmf/datatypes/Types.java
src/scratchpad/src/org/apache/poi/hsmf/parsers/POIFSChunkParser.java
src/scratchpad/testcases/org/apache/poi/hsmf/AllTests.java
src/scratchpad/testcases/org/apache/poi/hsmf/data/outlook_30_msg.msg [new file with mode: 0644]
src/scratchpad/testcases/org/apache/poi/hsmf/model/TestChunkData.java
src/scratchpad/testcases/org/apache/poi/hsmf/model/TestOutlook30FileRead.java [new file with mode: 0644]

index aa53ad8ad3f4bfe9ed3edbdd9063c3c7afb8980d..f26223742ff45eaaffe09f2154926432d83b6ac5 100644 (file)
@@ -66,7 +66,8 @@
            <action dev="POI-DEVELOPERS" type="add">Created a common interface for handling PowerPoint files, irrespective of if they are .ppt or .pptx</action>
            <action dev="POI-DEVELOPERS" type="add">Created a common interface for handling Excel files, irrespective of if they are .xls or .xlsx</action>
         </release>
-        <release version="3.1.1-alpha1" date="2008-??-??">
+        <release version="3.2-alpha1" date="2008-??-??">
+           <action dev="POI-DEVELOPERS" type="fix">45804 - Update HSMF to handle Outlook 3.0 msg files, which have a different string chunk type</action>
            <action dev="POI-DEVELOPERS" type="add">Expose the name of Named Cell Styles via HSSFCellStyle (normally held on the parent style though)</action>
            <action dev="POI-DEVELOPERS" type="fix">45978 - Fixed IOOBE in Ref3DPtg.toFormulaString() due eager initialisation of SheetReferences</action>
            <action dev="POI-DEVELOPERS" type="add">Made HSSFFormulaEvaluator no longer require initialisation with sheet or row</action>
index c60af7d9476cc9b30fc73598fd3b5f24b22e1aa8..8bf188faa1782a34bf8ee3730610058de0d0cbf1 100644 (file)
@@ -63,7 +63,8 @@
            <action dev="POI-DEVELOPERS" type="add">Created a common interface for handling PowerPoint files, irrespective of if they are .ppt or .pptx</action>
            <action dev="POI-DEVELOPERS" type="add">Created a common interface for handling Excel files, irrespective of if they are .xls or .xlsx</action>
         </release>
-        <release version="3.1.1-alpha1" date="2008-??-??">
+        <release version="3.2-alpha1" date="2008-??-??">
+           <action dev="POI-DEVELOPERS" type="fix">45804 - Update HSMF to handle Outlook 3.0 msg files, which have a different string chunk type</action>
            <action dev="POI-DEVELOPERS" type="add">Expose the name of Named Cell Styles via HSSFCellStyle (normally held on the parent style though)</action>
            <action dev="POI-DEVELOPERS" type="fix">45978 - Fixed IOOBE in Ref3DPtg.toFormulaString() due eager initialisation of SheetReferences</action>
            <action dev="POI-DEVELOPERS" type="add">Made HSSFFormulaEvaluator no longer require initialisation with sheet or row</action>
index eb915160b35d7b546eecb67b14b7a2ebdcfc9823..3669a5333ebd222d34f6914696222285ad049d1c 100644 (file)
@@ -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);
        }       
 }
index 6a3936d964c8e6662c6509d38bf15987b255442c..af77badb3b486b60862a272890eca503ddff5cbf 100644 (file)
@@ -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;
+       
+       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() {
-               return new Chunks();
+       public static Chunks getInstance(boolean newStringType) {
+               return new Chunks(newStringType);
        }
 }
index 2058b8ac58f1d18e6fb8cd79d82e6f97bbf8c0e1..fe90bd68847e960d971c0e3bb71512034d4679af 100644 (file)
@@ -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)
index 9297666af79e8199bc80e304a7186dcda55037e1..f4d675a05ac1cb37919931d0a3370a8a77ba8ac9 100644 (file)
@@ -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;
+       }
 }
index bdfb29e2a27ba3dbb17bd2c7be6fbdbfea3ea02c..c1e174e08e1c9cca32edc2f63efb411db4d466b6 100644 (file)
@@ -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
index 18a622c32b95070f6b096972c3bf64758580c7a4..e117ab89ee9b686735cfa6ec4d92b078a6dc16da 100644 (file)
@@ -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 (file)
index 0000000..0a58569
Binary files /dev/null and b/src/scratchpad/testcases/org/apache/poi/hsmf/data/outlook_30_msg.msg differ
index dc4b531294ec609bd899659c22a2565286765823..8f5dd47773c55a60d1fd9ef22e53068f363849c3 100644 (file)
@@ -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 (file)
index 0000000..7e94405
--- /dev/null
@@ -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);
+       }
+       
+       
+       
+}