From ca73f5eb7e02a920fdf8febd96b9264e763c4a6c Mon Sep 17 00:00:00 2001 From: Nick Burch Date: Mon, 9 Jul 2007 22:19:21 +0000 Subject: [PATCH] Initial HSMF (outlook) support from Travis git-svn-id: https://svn.apache.org/repos/asf/poi/trunk@554774 13f79535-47bb-0310-9956-ffa450edef68 --- build.xml | 2 + .../src/org/apache/poi/hsmf/MAPIMessage.java | 146 +++++++++++ .../org/apache/poi/hsmf/datatypes/Chunk.java | 68 +++++ .../org/apache/poi/hsmf/datatypes/Chunks.java | 40 +++ .../poi/hsmf/datatypes/StringChunk.java | 53 ++++ .../org/apache/poi/hsmf/datatypes/Types.java | 26 ++ .../exceptions/ChunkNotFoundException.java | 26 ++ .../DirectoryChunkNotFoundException.java | 30 +++ .../poi/hsmf/parsers/POIFSChunkParser.java | 232 ++++++++++++++++++ .../org/apache/poi/hsmf/AllTests.java | 40 +++ .../org/apache/poi/hsmf/data/blank.msg | Bin 0 -> 6656 bytes .../apache/poi/hsmf/data/simple_test_msg.msg | Bin 0 -> 16896 bytes .../poi/hsmf/model/TestBlankFileRead.java | 125 ++++++++++ .../apache/poi/hsmf/model/TestChunkData.java | 72 ++++++ .../poi/hsmf/model/TestSimpleFileRead.java | 129 ++++++++++ 15 files changed, 989 insertions(+) create mode 100644 src/scratchpad/src/org/apache/poi/hsmf/MAPIMessage.java create mode 100644 src/scratchpad/src/org/apache/poi/hsmf/datatypes/Chunk.java create mode 100644 src/scratchpad/src/org/apache/poi/hsmf/datatypes/Chunks.java create mode 100644 src/scratchpad/src/org/apache/poi/hsmf/datatypes/StringChunk.java create mode 100644 src/scratchpad/src/org/apache/poi/hsmf/datatypes/Types.java create mode 100644 src/scratchpad/src/org/apache/poi/hsmf/exceptions/ChunkNotFoundException.java create mode 100644 src/scratchpad/src/org/apache/poi/hsmf/exceptions/DirectoryChunkNotFoundException.java create mode 100644 src/scratchpad/src/org/apache/poi/hsmf/parsers/POIFSChunkParser.java create mode 100644 src/scratchpad/testcases/org/apache/poi/hsmf/AllTests.java create mode 100644 src/scratchpad/testcases/org/apache/poi/hsmf/data/blank.msg create mode 100644 src/scratchpad/testcases/org/apache/poi/hsmf/data/simple_test_msg.msg create mode 100644 src/scratchpad/testcases/org/apache/poi/hsmf/model/TestBlankFileRead.java create mode 100644 src/scratchpad/testcases/org/apache/poi/hsmf/model/TestChunkData.java create mode 100644 src/scratchpad/testcases/org/apache/poi/hsmf/model/TestSimpleFileRead.java diff --git a/build.xml b/build.xml index 5012001fb..ce1cf7ff4 100644 --- a/build.xml +++ b/build.xml @@ -503,6 +503,7 @@ under the License. + @@ -536,6 +537,7 @@ under the License. + diff --git a/src/scratchpad/src/org/apache/poi/hsmf/MAPIMessage.java b/src/scratchpad/src/org/apache/poi/hsmf/MAPIMessage.java new file mode 100644 index 000000000..ed215f638 --- /dev/null +++ b/src/scratchpad/src/org/apache/poi/hsmf/MAPIMessage.java @@ -0,0 +1,146 @@ +/* ==================================================================== + 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.File; +import java.io.FileInputStream; +import java.io.IOException; +import java.io.InputStream; + +import org.apache.poi.hsmf.datatypes.Chunk; +import org.apache.poi.hsmf.datatypes.Chunks; +import org.apache.poi.hsmf.datatypes.StringChunk; +import org.apache.poi.hsmf.exceptions.ChunkNotFoundException; +import org.apache.poi.hsmf.parsers.POIFSChunkParser; +import org.apache.poi.poifs.filesystem.POIFSFileSystem; + +/** + * Reads an Outlook MSG File in and provides hooks into its data structure. + * + * @author Travis Ferguson + */ +public class MAPIMessage { + private POIFSChunkParser chunkParser; + private POIFSFileSystem fs; + + /** + * Constructor for creating new files. + * + */ + public MAPIMessage() { + //TODO make writing possible + } + + + /** + * Constructor for reading MSG Files. + * @param filename + * @throws IOException + */ + public MAPIMessage(String filename) throws IOException { + InputStream in = new FileInputStream(new File(filename)); + this.fs = new POIFSFileSystem(in); + chunkParser = new POIFSChunkParser(this.fs); + } + + /** + * Gets a string value based on the passed chunk. + * @param chunk + * @return + * @throws ChunkNotFoundException + */ + public String getStringFromChunk(StringChunk chunk) throws ChunkNotFoundException { + Chunk out = this.chunkParser.getDocumentNode(chunk); + StringChunk strchunk = (StringChunk)out; + return strchunk.toString(); + } + + + /** + * Gets the plain text body of this Outlook Message + * @return The string representation of the 'text' version of the body, if available. + * @throws IOException + * @throws ChunkNotFoundException + */ + public String getTextBody() throws IOException, ChunkNotFoundException { + return getStringFromChunk(Chunks.getInstance().textBodyChunk); + } + + /** + * Gets the subject line of the Outlook Message + * @return + * @throws ChunkNotFoundException + */ + public String getSubject() throws ChunkNotFoundException { + return getStringFromChunk(Chunks.getInstance().subjectChunk); + } + + + /** + * Gets the display value of the "TO" line of the outlook message + * This is not the actual list of addresses/values that will be sent to if you click Reply in the email. + * @return + * @throws ChunkNotFoundException + */ + public String getDisplayTo() throws ChunkNotFoundException { + return getStringFromChunk(Chunks.getInstance().displayToChunk); + } + + /** + * Gets the display value of the "TO" line of the outlook message + * This is not the actual list of addresses/values that will be sent to if you click Reply in the email. + * @return + * @throws ChunkNotFoundException + */ + public String getDisplayCC() throws ChunkNotFoundException { + return getStringFromChunk(Chunks.getInstance().displayCCChunk); + } + + /** + * Gets the display value of the "TO" line of the outlook message + * This is not the actual list of addresses/values that will be sent to if you click Reply in the email. + * @return + * @throws ChunkNotFoundException + */ + public String getDisplayBCC() throws ChunkNotFoundException { + return getStringFromChunk(Chunks.getInstance().displayBCCChunk); + } + + + /** + * Gets the conversation topic of the parsed Outlook Message. + * This is the part of the subject line that is after the RE: and FWD: + * @return + * @throws ChunkNotFoundException + */ + public String getConversationTopic() throws ChunkNotFoundException { + return getStringFromChunk(Chunks.getInstance().conversationTopic); + } + + /** + * Gets the message class of the parsed Outlook Message. + * (Yes, you can use this to determine if a message is a calendar item, note, or actual outlook Message) + * For emails the class will be IPM.Note + * + * @return + * @throws ChunkNotFoundException + */ + public String getMessageClass() throws ChunkNotFoundException { + return getStringFromChunk(Chunks.getInstance().messageClass); + } +} diff --git a/src/scratchpad/src/org/apache/poi/hsmf/datatypes/Chunk.java b/src/scratchpad/src/org/apache/poi/hsmf/datatypes/Chunk.java new file mode 100644 index 000000000..785a336fa --- /dev/null +++ b/src/scratchpad/src/org/apache/poi/hsmf/datatypes/Chunk.java @@ -0,0 +1,68 @@ +/* ==================================================================== + 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.datatypes; + +import java.io.ByteArrayOutputStream; + +abstract public class Chunk { + protected int chunkId; + protected int type; + protected String namePrefix = "__substg1.0_"; + + /** + * Gets the id of this chunk + * @return + */ + public int getChunkId() { + return this.chunkId; + } + + /** + * Gets the numeric type of this chunk. + * @return + */ + public int getType() { + return this.type; + } + + /** + * Creates a string to use to identify this chunk in the POI file system object. + * @return + */ + public String getEntryName() { + String type = Integer.toHexString(this.type); + while(type.length() < 4) type = "0" + type; + + String chunkId = Integer.toHexString(this.chunkId); + while(chunkId.length() < 4) chunkId = "0" + chunkId; + + return this.namePrefix + chunkId.toUpperCase() + type.toUpperCase(); + } + + /** + * Gets a reference to a ByteArrayOutputStream that contains the value of this chunk. + * @return + */ + public abstract ByteArrayOutputStream getValueByteArray(); + + /** + * Sets the value of this chunk using a OutputStream + * @param value + */ + public abstract void setValue(ByteArrayOutputStream value); +} diff --git a/src/scratchpad/src/org/apache/poi/hsmf/datatypes/Chunks.java b/src/scratchpad/src/org/apache/poi/hsmf/datatypes/Chunks.java new file mode 100644 index 000000000..309efeac9 --- /dev/null +++ b/src/scratchpad/src/org/apache/poi/hsmf/datatypes/Chunks.java @@ -0,0 +1,40 @@ +/* ==================================================================== + 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.datatypes; + + +/** + * Collection of convenence chunks for standard parts of the MSG file. + * + * @author Travis Ferguson + */ +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 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). + + public static Chunks getInstance() { + return new Chunks(); + } +} diff --git a/src/scratchpad/src/org/apache/poi/hsmf/datatypes/StringChunk.java b/src/scratchpad/src/org/apache/poi/hsmf/datatypes/StringChunk.java new file mode 100644 index 000000000..2058b8ac5 --- /dev/null +++ b/src/scratchpad/src/org/apache/poi/hsmf/datatypes/StringChunk.java @@ -0,0 +1,53 @@ +/* ==================================================================== + 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.datatypes; + +import java.io.ByteArrayOutputStream; + +/** + * A Chunk made up of a single string. + * @author Travis Ferguson + */ +public class StringChunk extends Chunk { + + private String value; + + public StringChunk(int chunkId) { + this.chunkId = chunkId; + this.type = Types.STRING; + } + + /* (non-Javadoc) + * @see org.apache.poi.hsmf.Chunk.Chunk#getValueByteArray() + */ + public ByteArrayOutputStream getValueByteArray() { + // TODO Auto-generated method stub + return null; + } + + /* (non-Javadoc) + * @see org.apache.poi.hsmf.Chunk.Chunk#setValue(java.io.ByteArrayOutputStream) + */ + public void setValue(ByteArrayOutputStream value) { + this.value = value.toString().replaceAll("\0", ""); + } + + public String toString() { + return this.value; + } +} diff --git a/src/scratchpad/src/org/apache/poi/hsmf/datatypes/Types.java b/src/scratchpad/src/org/apache/poi/hsmf/datatypes/Types.java new file mode 100644 index 000000000..9297666af --- /dev/null +++ b/src/scratchpad/src/org/apache/poi/hsmf/datatypes/Types.java @@ -0,0 +1,26 @@ +/* ==================================================================== + 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.datatypes; + +public class Types { + public static int BINARY = 0x0102; + public static int STRING = 0x001E; + public static int LONG = 0x0003; + public static int TIME = 0x0040; + public static int BOOLEAN = 0x000B; +} diff --git a/src/scratchpad/src/org/apache/poi/hsmf/exceptions/ChunkNotFoundException.java b/src/scratchpad/src/org/apache/poi/hsmf/exceptions/ChunkNotFoundException.java new file mode 100644 index 000000000..5c04518cb --- /dev/null +++ b/src/scratchpad/src/org/apache/poi/hsmf/exceptions/ChunkNotFoundException.java @@ -0,0 +1,26 @@ +/* ==================================================================== + 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.exceptions; + +public class ChunkNotFoundException extends Exception { + private static final long serialVersionUID = 1L; + + public ChunkNotFoundException(String chunkName) { + super(chunkName + " was named, but not found in POIFS object"); + } +} diff --git a/src/scratchpad/src/org/apache/poi/hsmf/exceptions/DirectoryChunkNotFoundException.java b/src/scratchpad/src/org/apache/poi/hsmf/exceptions/DirectoryChunkNotFoundException.java new file mode 100644 index 000000000..308d0c670 --- /dev/null +++ b/src/scratchpad/src/org/apache/poi/hsmf/exceptions/DirectoryChunkNotFoundException.java @@ -0,0 +1,30 @@ +/* ==================================================================== + 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.exceptions; + +/** + * Exception for when a directory chunk is not found but is expected. + * @author Travis Ferguson + */ +public class DirectoryChunkNotFoundException extends Exception { + private static final long serialVersionUID = 1L; + + public DirectoryChunkNotFoundException(String directory) { + super("Directory Chunk " + directory + " was not found!"); + } +} diff --git a/src/scratchpad/src/org/apache/poi/hsmf/parsers/POIFSChunkParser.java b/src/scratchpad/src/org/apache/poi/hsmf/parsers/POIFSChunkParser.java new file mode 100644 index 000000000..bdfb29e2a --- /dev/null +++ b/src/scratchpad/src/org/apache/poi/hsmf/parsers/POIFSChunkParser.java @@ -0,0 +1,232 @@ +/* ==================================================================== + 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.parsers; + +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.Iterator; + +import org.apache.poi.hsmf.datatypes.Chunk; +import org.apache.poi.hsmf.exceptions.ChunkNotFoundException; +import org.apache.poi.hsmf.exceptions.DirectoryChunkNotFoundException; +import org.apache.poi.poifs.filesystem.DirectoryEntry; +import org.apache.poi.poifs.filesystem.DirectoryNode; +import org.apache.poi.poifs.filesystem.DocumentNode; +import org.apache.poi.poifs.filesystem.POIFSDocument; +import org.apache.poi.poifs.filesystem.POIFSFileSystem; +import org.apache.poi.poifs.property.DirectoryProperty; +import org.apache.poi.poifs.property.DocumentProperty; +import org.apache.poi.poifs.storage.BlockWritable; + +/** + * Provides a HashMap with the ability to parse a PIOFS object and provide + * an 'easy to access' hashmap structure for the document chunks inside it. + * + * @author Travis Ferguson + */ +public class POIFSChunkParser { + /** + * Constructor + * @param fs + * @throws IOException + */ + public POIFSChunkParser(POIFSFileSystem fs) throws IOException { + this.setFileSystem(fs); + } + + + /** + * Set the POIFileSystem object that this object is using. + * @param fs + * @throws IOException + */ + public void setFileSystem(POIFSFileSystem fs) throws IOException { + this.fs = fs; + this.reparseFileSystem(); + } + + /** + * Get a reference to the FileSystem object that this object is currently using. + * @return + */ + public POIFSFileSystem getFileSystem() { + return this.fs; + } + + /** + * Reparse the FileSystem object, resetting all the chunks stored in this object + * @throws IOException + * + */ + public void reparseFileSystem() throws IOException { + // first clear this object of all chunks + DirectoryEntry root = this.fs.getRoot(); + Iterator iter = root.getEntries(); + + this.directoryMap = this.processPOIIterator(iter); + } + + /** + * Pull the chunk data that's stored in this object's hashmap out and return it as a HashMap. + * @param entryName + * @return + */ + public Object getChunk(HashMap dirMap, String entryName) { + if(dirMap == null) return null; + else { + return dirMap.get(entryName); + } + } + + /** + * Pull a directory/hashmap out of this hashmap and return it + * @param directoryName + * @return HashMap containing the chunks stored in the named directoryChunk + * @throws DirectoryChunkNotFoundException This is thrown should the directoryMap HashMap on this object be null + * or for some reason the directory is not found, is equal to null, or is for some reason not a HashMap/aka Directory Node. + */ + public HashMap getDirectoryChunk(String directoryName) throws DirectoryChunkNotFoundException { + DirectoryChunkNotFoundException excep = new DirectoryChunkNotFoundException(directoryName); + Object obj = getChunk(this.directoryMap, directoryName); + if(obj == null || !(obj instanceof HashMap)) throw excep; + + return (HashMap)obj; + } + + /** + * Pulls a ByteArrayOutputStream from this objects HashMap, this can be used to read a byte array of the contents of the given chunk. + * @param directoryMap, chunk + * @return + * @throws ChunkNotFoundException + */ + public Chunk getDocumentNode(HashMap dirNode, Chunk chunk) throws ChunkNotFoundException { + String entryName = chunk.getEntryName(); + ChunkNotFoundException excep = new ChunkNotFoundException(entryName); + Object obj = getChunk(dirNode, entryName); + if(obj == null || !(obj instanceof ByteArrayOutputStream)) throw excep; + + chunk.setValue((ByteArrayOutputStream)obj); + + return chunk; + } + + /** + * Pulls a Chunk out of this objects root Node tree. + * @param chunk + * @return + * @throws ChunkNotFoundException + */ + public Chunk getDocumentNode(Chunk chunk) throws ChunkNotFoundException { + return getDocumentNode(this.directoryMap, chunk); + } + + + /** + * Processes an iterator returned by a POIFS call to getRoot().getEntries() + * @param iter + * @return + * @throws IOException + */ + private HashMap processPOIIterator(Iterator iter) throws IOException { + HashMap currentNode = new HashMap(); + + while(iter.hasNext()) { + Object obj = iter.next(); + if(obj instanceof DocumentNode) { + this.processDocumentNode((DocumentNode)obj, currentNode); + } else if(obj instanceof DirectoryNode) { + String blockName = ((DirectoryNode)obj).getName(); + Iterator viewIt = null; + if( ((DirectoryNode)obj).preferArray()) { + Object[] arr = ((DirectoryNode)obj).getViewableArray(); + ArrayList viewList = new ArrayList(arr.length); + + for(int i = 0; i < arr.length; i++) { + viewList.add(arr[i]); + } + viewIt = viewList.iterator(); + } else { + viewIt = ((DirectoryNode)obj).getViewableIterator(); + } + //store the next node on the hashmap + currentNode.put(blockName, processPOIIterator(viewIt)); + } else if(obj instanceof DirectoryProperty) { + //don't do anything with the directory property chunk... + } else { + System.err.println("Unknown node: " + obj.toString()); + } + } + return currentNode; + } + + /** + * Processes a document node and adds it to the current directory HashMap + * @param obj + * @throws java.io.IOException + */ + private void processDocumentNode(DocumentNode obj, HashMap currentObj) throws IOException { + String blockName = ((DocumentNode)obj).getName(); + + Iterator viewIt = null; + if( ((DocumentNode)obj).preferArray()) { + Object[] arr = ((DocumentNode)obj).getViewableArray(); + ArrayList viewList = new ArrayList(arr.length); + + for(int i = 0; i < arr.length; i++) { + viewList.add(arr[i]); + } + viewIt = viewList.iterator(); + } else { + viewIt = ((DocumentNode)obj).getViewableIterator(); + } + + while(viewIt.hasNext()) { + Object view = viewIt.next(); + + if(view instanceof DocumentProperty) { + //we don't care about the properties + } else if(view instanceof POIFSDocument) { + //check if our node has blocks or if it can just be read raw. + int blockCount = ((POIFSDocument)view).countBlocks(); + //System.out.println("Block Name: " + blockName); + if(blockCount <= 0) { + ByteArrayOutputStream out = new ByteArrayOutputStream(); + + BlockWritable[] bws = ((POIFSDocument)view).getSmallBlocks(); + for(int i = 0; i < bws.length; i++) { + bws[i].writeBlocks(out); + } + currentObj.put(blockName, out); + } else { + ByteArrayOutputStream out = new ByteArrayOutputStream(); + ((POIFSDocument)view).writeBlocks(out); + currentObj.put(blockName, out); + } + } else { + System.err.println("Unknown View Type: " + view.toString()); + } + } + } + + /* private instance variables */ + private static final long serialVersionUID = 1L; + private POIFSFileSystem fs; + private HashMap directoryMap; +} diff --git a/src/scratchpad/testcases/org/apache/poi/hsmf/AllTests.java b/src/scratchpad/testcases/org/apache/poi/hsmf/AllTests.java new file mode 100644 index 000000000..18a622c32 --- /dev/null +++ b/src/scratchpad/testcases/org/apache/poi/hsmf/AllTests.java @@ -0,0 +1,40 @@ +/* ==================================================================== + 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 junit.framework.*; + +public class AllTests + extends TestCase +{ + + public AllTests(String s) + { + super(s); + } + + public static Test suite() + { + 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.TestChunkData.class); + + return suite; + } +} diff --git a/src/scratchpad/testcases/org/apache/poi/hsmf/data/blank.msg b/src/scratchpad/testcases/org/apache/poi/hsmf/data/blank.msg new file mode 100644 index 0000000000000000000000000000000000000000..0bdb812401d7045019685c76947d7c9fd98575a8 GIT binary patch literal 6656 zcmeHL%}Z2K6hCj?e5m7RA7++=P*}N;nT*m!rIl7hQsOEIDU>M437wL5vkBS+F8Tv( z*G_C7NSkP-yNDoa(HFFgTBZGd_j=>K!Flh#$&ku3oOj-NbMO7#k8{qw_be?oy?r#_ z`c6vdn7FdMQY}f(cL(oeMU5gcyii|WSy|B^Mew>wf6)y5KwsmlYsj@ufK%XVa22=) zoCb4^$(!_V&A_na@heEX49EoJq};`7+fy?v#>_|qs-^1~6+{-)QwFur&)LgEjWbW5 zJ1*2BjSMu3--KL|n=&rfWenOKyiLj!v^@UFNta}G(KVm@#c>QiZFeo2{c=v>8tE-V z{3*FDSMe;!wN<=PVDfhmoKwXnR3ladK(Q8Fn>B~#)k$(^P$D#eZ3is@I>dZxS?kI|z_GY?Vgnet}- zjUH77$Dc!1@yBb$Hu&^^Q?497qpps<;qjOI3AYpW!?YVDSw1ZOYWzPF{~4=|#RreS z*J}Rh!91_ojz3|I-*1IaTtjF&l-OUp@ekVItG0MXWgkMpQGm8$gP&7-f2jQ_+$iHG zwjI8m{qc-m3$`7axz+3+KCr@`dqA>;Lcdg5pmOcLIy^ zZ)E$Yy@uJpj{b~4?7=|UW#ajp=h=F&t^G4r&<9B6#!uXCIbwxxeBTzIzREt6g7HsT z;n$&^tzcVxmJi40+B5!y&wteMbFJ*~dH)H;S9|%E2;=8H)efKMpHTeB_dlLJ?C|aO z-zK!TJ;L}|o_DkLqP>e<{U2U_8@NArI`Q#+_1LrGdbJ$h&g&PKK94@0IQih>rXy+LP81o@6BWL>{1o!=&q56MWp zp6|35S4O5Y;(Bzqn30%APa#(qQ};1rUoLLqJZ~92gWEWD#y_5e%g3Jq#9h1i6V?Ce ztENBY@U`ckP~OKlRE`k;WgGrv&cQbRF;<41|9h0@Tg4yc|IByT;{0f>zK~q$@abte;L}|>?9(&YBWX{xQQA{l zoOVS_m@(-+5clsjnWy9T3(ocdfI4-oe9 zq8HBV IU^ak$0yTmo4gdfE literal 0 HcmV?d00001 diff --git a/src/scratchpad/testcases/org/apache/poi/hsmf/data/simple_test_msg.msg b/src/scratchpad/testcases/org/apache/poi/hsmf/data/simple_test_msg.msg new file mode 100644 index 0000000000000000000000000000000000000000..731fecc23652d66c7bd05ede3c98935f251a9644 GIT binary patch literal 16896 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 b|IT*BO?%%>KKX9g)ir{5gP%X2)fV_4+JlyU literal 0 HcmV?d00001 diff --git a/src/scratchpad/testcases/org/apache/poi/hsmf/model/TestBlankFileRead.java b/src/scratchpad/testcases/org/apache/poi/hsmf/model/TestBlankFileRead.java new file mode 100644 index 000000000..05735870b --- /dev/null +++ b/src/scratchpad/testcases/org/apache/poi/hsmf/model/TestBlankFileRead.java @@ -0,0 +1,125 @@ +/* ==================================================================== + 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 the library can read blank msg files. + * + * @author Travis Ferguson + * + */ +public class TestBlankFileRead extends TestCase { + private MAPIMessage mapiMessage; + + /** + * Initialize this test, load up the blank.msg mapi message. + * @throws IOException + */ + public TestBlankFileRead() throws IOException { + String dirname = System.getProperty("HSMF.testdata.path"); + this.mapiMessage = new MAPIMessage(dirname + "/blank.msg"); + } + + /** + * Check if we can read the body of the blank message, we expect "". + * + * @throws Exception + */ + public void testReadBody() throws Exception { + try { + mapiMessage.getTextBody(); + } catch(ChunkNotFoundException exp) { + return; + } + + TestCase.fail("Should have thrown a ChunkNotFoundException but didn't"); + } + + + /** + * 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(); + String expected = ""; + + 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 subject line of the blank message, we expect "" + * + * @throws Exception + */ + public void testReadSubject() throws Exception { + String obtained = mapiMessage.getSubject(); + TestCase.assertEquals("", obtained); + } + + + /** + * Check if we can read the subject line of the blank message, we expect "" + * + * @throws Exception + */ + public void testReadConversationTopic() throws Exception { + try { + mapiMessage.getConversationTopic(); + } catch(ChunkNotFoundException exp) { + return; + } + TestCase.fail("We shouldn't have a ConversationTopic node on the blank.msg file."); + } + + +} diff --git a/src/scratchpad/testcases/org/apache/poi/hsmf/model/TestChunkData.java b/src/scratchpad/testcases/org/apache/poi/hsmf/model/TestChunkData.java new file mode 100644 index 000000000..dc4b53129 --- /dev/null +++ b/src/scratchpad/testcases/org/apache/poi/hsmf/model/TestChunkData.java @@ -0,0 +1,72 @@ +/* ==================================================================== + 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 org.apache.poi.hsmf.datatypes.Chunk; +import org.apache.poi.hsmf.datatypes.Chunks; +import org.apache.poi.hsmf.datatypes.StringChunk; + +import junit.framework.TestCase; + +/** + * Verifies that the Chunks class is actually setup properly and hasn't been changed in ways + * that will break the library. + * + * @author Travis Ferguson + * + */ +public class TestChunkData extends TestCase { + public void testChunkCreate() { + StringChunk chunk = new StringChunk(0x0200); + TestCase.assertEquals("__substg1.0_0200001E", chunk.getEntryName()); + + /* test the lower and upper limits of the chunk ids */ + chunk = new StringChunk(0x0000); + TestCase.assertEquals("__substg1.0_0000001E", chunk.getEntryName()); + + chunk = new StringChunk(0xFFFF); + TestCase.assertEquals("__substg1.0_FFFF001E", chunk.getEntryName()); + } + + public void testTextBodyChunk() { + StringChunk chunk = new StringChunk(0x1000); + TestCase.assertEquals(chunk.getEntryName(), Chunks.getInstance().textBodyChunk.getEntryName()); + } + + public void testDisplayToChunk() { + StringChunk chunk = new StringChunk(0x0E04); + TestCase.assertEquals(chunk.getEntryName(), Chunks.getInstance().displayToChunk.getEntryName()); + } + + + public void testDisplayCCChunk() { + StringChunk chunk = new StringChunk(0x0E03); + TestCase.assertEquals(chunk.getEntryName(), Chunks.getInstance().displayCCChunk.getEntryName()); + } + + public void testDisplayBCCChunk() { + StringChunk chunk = new StringChunk(0x0E02); + TestCase.assertEquals(chunk.getEntryName(), Chunks.getInstance().displayBCCChunk.getEntryName()); + } + + public void testSubjectChunk() { + Chunk chunk = new StringChunk(0x0037); + TestCase.assertEquals(chunk.getEntryName(), Chunks.getInstance().subjectChunk.getEntryName()); + } + +} diff --git a/src/scratchpad/testcases/org/apache/poi/hsmf/model/TestSimpleFileRead.java b/src/scratchpad/testcases/org/apache/poi/hsmf/model/TestSimpleFileRead.java new file mode 100644 index 000000000..0ede68965 --- /dev/null +++ b/src/scratchpad/testcases/org/apache/poi/hsmf/model/TestSimpleFileRead.java @@ -0,0 +1,129 @@ +/* ==================================================================== + 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 read a simple msg file, that is in plain/text format with no attachments + * or extra recipents. + * + * @author Travis Ferguson + */ +public class TestSimpleFileRead extends TestCase { +private MAPIMessage mapiMessage; + + /** + * Initialize this test, load up the blank.msg mapi message. + * @throws Exception + */ + public TestSimpleFileRead() throws IOException { + String dirname = System.getProperty("HSMF.testdata.path"); + this.mapiMessage = new MAPIMessage(dirname + "/simple_test_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(); + String expected = "travis@overwrittenstack.com"; + + 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(); + String expected = "This is a test message."; + + TestCase.assertEquals(obtained, expected); + } + + /** + * 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 = "test message"; + + 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("test message", 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); + } + + + +}