From 1c99cef041b558d749e943f4fbdbb512066163cd Mon Sep 17 00:00:00 2001 From: Nick Burch Date: Fri, 1 Feb 2013 11:45:31 +0000 Subject: [PATCH] Start on decoding fixed sized HSMF properties, and linking the variable sized ones with their matching chunks git-svn-id: https://svn.apache.org/repos/asf/poi/trunk@1441398 13f79535-47bb-0310-9956-ffa450edef68 --- .../src/org/apache/poi/hsmf/MAPIMessage.java | 8 +-- .../poi/hsmf/datatypes/AttachmentChunks.java | 12 +++- .../datatypes/ChunkBasedPropertyValue.java | 44 +++++++++++++ .../apache/poi/hsmf/datatypes/ChunkGroup.java | 5 ++ .../org/apache/poi/hsmf/datatypes/Chunks.java | 66 +++++++++++-------- .../datatypes/MessagePropertiesChunk.java | 4 +- .../poi/hsmf/datatypes/NameIdChunks.java | 9 +++ .../poi/hsmf/datatypes/PropertiesChunk.java | 15 ++++- .../poi/hsmf/datatypes/RecipientChunks.java | 13 +++- .../datatypes/StoragePropertiesChunk.java | 4 +- .../poi/hsmf/parsers/POIFSChunkParser.java | 10 ++- 11 files changed, 149 insertions(+), 41 deletions(-) create mode 100644 src/scratchpad/src/org/apache/poi/hsmf/datatypes/ChunkBasedPropertyValue.java diff --git a/src/scratchpad/src/org/apache/poi/hsmf/MAPIMessage.java b/src/scratchpad/src/org/apache/poi/hsmf/MAPIMessage.java index 73e6b89478..4c5176c8bf 100644 --- a/src/scratchpad/src/org/apache/poi/hsmf/MAPIMessage.java +++ b/src/scratchpad/src/org/apache/poi/hsmf/MAPIMessage.java @@ -418,14 +418,14 @@ public class MAPIMessage extends POIDocument { * @see #guess7BitEncoding() */ public void set7BitEncoding(String charset) { - for(Chunk c : mainChunks.getAll()) { + for(Chunk c : mainChunks.getChunks()) { if(c instanceof StringChunk) { ((StringChunk)c).set7BitEncoding(charset); } } if (nameIdChunks!=null) { - for(Chunk c : nameIdChunks.getAll()) { + for(Chunk c : nameIdChunks.getChunks()) { if(c instanceof StringChunk) { ((StringChunk)c).set7BitEncoding(charset); } @@ -446,7 +446,7 @@ public class MAPIMessage extends POIDocument { * are stored as 7 bit rather than unicode? */ public boolean has7BitEncodingStrings() { - for(Chunk c : mainChunks.getAll()) { + for(Chunk c : mainChunks.getChunks()) { if(c instanceof StringChunk) { if( ((StringChunk)c).getType() == Types.ASCII_STRING ) { return true; @@ -455,7 +455,7 @@ public class MAPIMessage extends POIDocument { } if (nameIdChunks!=null) { - for(Chunk c : nameIdChunks.getAll()) { + for(Chunk c : nameIdChunks.getChunks()) { if(c instanceof StringChunk) { if( ((StringChunk)c).getType() == Types.ASCII_STRING ) { return true; diff --git a/src/scratchpad/src/org/apache/poi/hsmf/datatypes/AttachmentChunks.java b/src/scratchpad/src/org/apache/poi/hsmf/datatypes/AttachmentChunks.java index aa17b7f7bb..eddf6787d2 100644 --- a/src/scratchpad/src/org/apache/poi/hsmf/datatypes/AttachmentChunks.java +++ b/src/scratchpad/src/org/apache/poi/hsmf/datatypes/AttachmentChunks.java @@ -171,7 +171,17 @@ public class AttachmentChunks implements ChunkGroup { // And add to the main list allChunks.add(chunk); } - + + /** + * Used to flag that all the chunks of the attachment + * have now been located. + */ + public void chunksComplete() { + // Currently, we don't need to do anything special once + // all the chunks have been located + } + + /** * Orders by the attachment number. */ diff --git a/src/scratchpad/src/org/apache/poi/hsmf/datatypes/ChunkBasedPropertyValue.java b/src/scratchpad/src/org/apache/poi/hsmf/datatypes/ChunkBasedPropertyValue.java new file mode 100644 index 0000000000..36c723bdf7 --- /dev/null +++ b/src/scratchpad/src/org/apache/poi/hsmf/datatypes/ChunkBasedPropertyValue.java @@ -0,0 +1,44 @@ +/* ==================================================================== + 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; + + +/** + * A variable length {@link PropertyValue} that is + * backed by a {@link Chunk} + * TODO Provide a way to link these up with the chunks + */ +public class ChunkBasedPropertyValue extends PropertyValue { + public ChunkBasedPropertyValue(MAPIProperty property, long flags, byte[] offsetData) { + super(property, flags, offsetData); + } + + @Override + public Chunk getValue() { + // TODO Decode the value into an offset + // TODO Look up the chunk based on that + return null; + } + + /** + * Stores the offset of the chunk as the property value + */ + public void setValue(Chunk chunk) { + // TODO + } +} diff --git a/src/scratchpad/src/org/apache/poi/hsmf/datatypes/ChunkGroup.java b/src/scratchpad/src/org/apache/poi/hsmf/datatypes/ChunkGroup.java index 8f03cdda6a..2bed67a7b2 100644 --- a/src/scratchpad/src/org/apache/poi/hsmf/datatypes/ChunkGroup.java +++ b/src/scratchpad/src/org/apache/poi/hsmf/datatypes/ChunkGroup.java @@ -33,4 +33,9 @@ public interface ChunkGroup { * Called by the parser whenever a chunk is found. */ public void record(Chunk chunk); + + /** + * Called by the parser when all chunks have been found. + */ + public void chunksComplete(); } 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 f7e211a6ec..b4897b4fcc 100644 --- a/src/scratchpad/src/org/apache/poi/hsmf/datatypes/Chunks.java +++ b/src/scratchpad/src/org/apache/poi/hsmf/datatypes/Chunks.java @@ -18,7 +18,9 @@ package org.apache.poi.hsmf.datatypes; import java.util.ArrayList; +import java.util.HashMap; import java.util.List; +import java.util.Map; /** @@ -30,8 +32,8 @@ import java.util.List; * http://msdn.microsoft.com/en-us/library/ms526356%28v=exchg.10%29.aspx */ public final class Chunks implements ChunkGroup { - /** Holds all the chunks that were found. */ - private List allChunks = new ArrayList(); + /** Holds all the chunks that were found, indexed by their MAPIProperty */ + private Map> allChunks = new HashMap>(); /** Type of message that the MSG represents (ie. IPM.Note) */ public StringChunk messageClass; @@ -70,65 +72,72 @@ public final class Chunks implements ChunkGroup { /** The message properties */ public MessagePropertiesChunk messageProperties; - - public Chunk[] getAll() { - return allChunks.toArray(new Chunk[allChunks.size()]); + public Map> getAll() { + return allChunks; } public Chunk[] getChunks() { - return getAll(); + ArrayList chunks = new ArrayList(allChunks.size()); + for (List c : allChunks.values()) { + chunks.addAll(c); + } + return chunks.toArray(new Chunk[chunks.size()]); } /** * Called by the parser whenever a chunk is found. */ public void record(Chunk chunk) { - if(chunk.getChunkId() == MAPIProperty.MESSAGE_CLASS.id) { + // Work out what MAPIProperty this corresponds to + MAPIProperty prop = MAPIProperty.get(chunk.getChunkId()); + + // Assign it for easy lookup, as best we can + if(prop == MAPIProperty.MESSAGE_CLASS) { messageClass = (StringChunk)chunk; } - else if(chunk.getChunkId() == MAPIProperty.INTERNET_MESSAGE_ID.id) { + else if(prop == MAPIProperty.INTERNET_MESSAGE_ID) { messageId = (StringChunk)chunk; } - else if(chunk.getChunkId() == MAPIProperty.MESSAGE_SUBMISSION_ID.id) { + else if(prop == MAPIProperty.MESSAGE_SUBMISSION_ID) { // TODO - parse submissionChunk = (MessageSubmissionChunk)chunk; } - else if(chunk.getChunkId() == MAPIProperty.RECEIVED_BY_ADDRTYPE.id) { + else if(prop == MAPIProperty.RECEIVED_BY_ADDRTYPE) { sentByServerType = (StringChunk)chunk; } - else if(chunk.getChunkId() == MAPIProperty.TRANSPORT_MESSAGE_HEADERS.id) { + else if(prop == MAPIProperty.TRANSPORT_MESSAGE_HEADERS) { messageHeaders = (StringChunk)chunk; } - else if(chunk.getChunkId() == MAPIProperty.CONVERSATION_TOPIC.id) { + else if(prop == MAPIProperty.CONVERSATION_TOPIC) { conversationTopic = (StringChunk)chunk; } - else if(chunk.getChunkId() == MAPIProperty.SUBJECT.id) { + else if(prop == MAPIProperty.SUBJECT) { subjectChunk = (StringChunk)chunk; } - else if(chunk.getChunkId() == MAPIProperty.ORIGINAL_SUBJECT.id) { + else if(prop == MAPIProperty.ORIGINAL_SUBJECT) { // TODO } - else if(chunk.getChunkId() == MAPIProperty.DISPLAY_TO.id) { + else if(prop == MAPIProperty.DISPLAY_TO) { displayToChunk = (StringChunk)chunk; } - else if(chunk.getChunkId() == MAPIProperty.DISPLAY_CC.id) { + else if(prop == MAPIProperty.DISPLAY_CC) { displayCCChunk = (StringChunk)chunk; } - else if(chunk.getChunkId() == MAPIProperty.DISPLAY_BCC.id) { + else if(prop == MAPIProperty.DISPLAY_BCC) { displayBCCChunk = (StringChunk)chunk; } - else if(chunk.getChunkId() == MAPIProperty.SENDER_EMAIL_ADDRESS.id) { + else if(prop == MAPIProperty.SENDER_EMAIL_ADDRESS) { emailFromChunk = (StringChunk)chunk; } - else if(chunk.getChunkId() == MAPIProperty.SENDER_NAME.id) { + else if(prop == MAPIProperty.SENDER_NAME) { displayFromChunk = (StringChunk)chunk; } - else if(chunk.getChunkId() == MAPIProperty.BODY.id) { + else if(prop == MAPIProperty.BODY) { textBodyChunk = (StringChunk)chunk; } - else if(chunk.getChunkId() == MAPIProperty.BODY_HTML.id) { + else if(prop == MAPIProperty.BODY_HTML) { if(chunk instanceof StringChunk) { htmlBodyChunkString = (StringChunk)chunk; } @@ -136,16 +145,21 @@ public final class Chunks implements ChunkGroup { htmlBodyChunkBinary = (ByteChunk)chunk; } } - else if(chunk.getChunkId() == MAPIProperty.RTF_COMPRESSED.id) { + else if(prop == MAPIProperty.RTF_COMPRESSED) { rtfBodyChunk = (ByteChunk)chunk; } - else if(chunk.getChunkId() == MAPIProperty.UNKNOWN.id && - chunk instanceof MessagePropertiesChunk) { - // TODO Should we maybe collect the contents of this? + else if(chunk instanceof MessagePropertiesChunk) { messageProperties = (MessagePropertiesChunk) chunk; } // And add to the main list - allChunks.add(chunk); + if (allChunks.get(prop) == null) { + allChunks.put(prop, new ArrayList()); + } + allChunks.get(prop).add(chunk); + } + + public void chunksComplete() { + // TODO Match variable sized properties to their chunks + index } } diff --git a/src/scratchpad/src/org/apache/poi/hsmf/datatypes/MessagePropertiesChunk.java b/src/scratchpad/src/org/apache/poi/hsmf/datatypes/MessagePropertiesChunk.java index ab9b1bfaf5..dbf77c51c6 100644 --- a/src/scratchpad/src/org/apache/poi/hsmf/datatypes/MessagePropertiesChunk.java +++ b/src/scratchpad/src/org/apache/poi/hsmf/datatypes/MessagePropertiesChunk.java @@ -33,8 +33,8 @@ public class MessagePropertiesChunk extends PropertiesChunk { private long recipientCount; private long attachmentCount; - public MessagePropertiesChunk() { - super(); + public MessagePropertiesChunk(ChunkGroup parentGroup) { + super(parentGroup); } public long getNextRecipientId() { diff --git a/src/scratchpad/src/org/apache/poi/hsmf/datatypes/NameIdChunks.java b/src/scratchpad/src/org/apache/poi/hsmf/datatypes/NameIdChunks.java index bb78ea308e..a976d9395f 100644 --- a/src/scratchpad/src/org/apache/poi/hsmf/datatypes/NameIdChunks.java +++ b/src/scratchpad/src/org/apache/poi/hsmf/datatypes/NameIdChunks.java @@ -44,4 +44,13 @@ public final class NameIdChunks implements ChunkGroup { public void record(Chunk chunk) { allChunks.add(chunk); } + + /** + * Used to flag that all the chunks of the NameID + * have now been located. + */ + public void chunksComplete() { + // Currently, we don't need to do anything special once + // all the chunks have been located + } } diff --git a/src/scratchpad/src/org/apache/poi/hsmf/datatypes/PropertiesChunk.java b/src/scratchpad/src/org/apache/poi/hsmf/datatypes/PropertiesChunk.java index 17ff31473c..5e57adb729 100644 --- a/src/scratchpad/src/org/apache/poi/hsmf/datatypes/PropertiesChunk.java +++ b/src/scratchpad/src/org/apache/poi/hsmf/datatypes/PropertiesChunk.java @@ -25,8 +25,9 @@ import java.util.HashMap; import java.util.List; import java.util.Map; +import org.apache.poi.hsmf.datatypes.PropertyValue.LongLongPropertyValue; +import org.apache.poi.hsmf.datatypes.PropertyValue.TimePropertyValue; import org.apache.poi.hsmf.datatypes.Types.MAPIType; -import org.apache.poi.hsmf.datatypes.PropertyValue.*; import org.apache.poi.util.IOUtils; import org.apache.poi.util.LittleEndian; import org.apache.poi.util.LittleEndian.BufferUnderrunException; @@ -52,11 +53,19 @@ public abstract class PropertiesChunk extends Chunk { private Map> properties = new HashMap>(); + /** + * The ChunkGroup that these properties apply to. Used when + * matching chunks to variable sized properties + * TODO Make use of this + */ + private ChunkGroup parentGroup; + /** * Creates a Properties Chunk. */ - protected PropertiesChunk() { + protected PropertiesChunk(ChunkGroup parentGroup) { super(NAME, -1, Types.UNKNOWN); + this.parentGroup = parentGroup; } @Override @@ -132,7 +141,7 @@ public abstract class PropertiesChunk extends Chunk { // Wrap and store PropertyValue propVal = null; if (isPointer) { - // TODO Pointer type which can do lookup + propVal = new ChunkBasedPropertyValue(prop, flags, data); } else if (type == Types.LONG_LONG) { propVal = new LongLongPropertyValue(prop, flags, data); 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 a3f57248ac..2ac4eee19a 100644 --- a/src/scratchpad/src/org/apache/poi/hsmf/datatypes/RecipientChunks.java +++ b/src/scratchpad/src/org/apache/poi/hsmf/datatypes/RecipientChunks.java @@ -77,7 +77,11 @@ public final class RecipientChunks implements ChunkGroup { * as in recipientNameChunk */ public StringChunk recipientDisplayNameChunk; - + /** + * Holds the fixed sized properties, and the + * pointers to the data of variable sized ones + */ + private PropertiesChunk recipientProperties; public RecipientChunks(String name) { recipientNumber = -1; @@ -191,11 +195,18 @@ public final class RecipientChunks implements ChunkGroup { else if(chunk.getChunkId() == DELIVERY_TYPE.id) { deliveryTypeChunk = (StringChunk)chunk; } + else if(chunk instanceof PropertiesChunk) { + recipientProperties = (PropertiesChunk) chunk; + } // And add to the main list allChunks.add(chunk); } + public void chunksComplete() { + // TODO Match variable sized properties to their chunks + index + } + /** * Orders by the recipient number. */ diff --git a/src/scratchpad/src/org/apache/poi/hsmf/datatypes/StoragePropertiesChunk.java b/src/scratchpad/src/org/apache/poi/hsmf/datatypes/StoragePropertiesChunk.java index 166d38ba9c..5cb0d4bc7c 100644 --- a/src/scratchpad/src/org/apache/poi/hsmf/datatypes/StoragePropertiesChunk.java +++ b/src/scratchpad/src/org/apache/poi/hsmf/datatypes/StoragePropertiesChunk.java @@ -29,8 +29,8 @@ import org.apache.poi.util.LittleEndian; * This only has a 8 byte header */ public class StoragePropertiesChunk extends PropertiesChunk { - public StoragePropertiesChunk() { - super(); + public StoragePropertiesChunk(ChunkGroup parentGroup) { + super(parentGroup); } @Override 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 434d0cbd6b..5709643cec 100644 --- a/src/scratchpad/src/org/apache/poi/hsmf/parsers/POIFSChunkParser.java +++ b/src/scratchpad/src/org/apache/poi/hsmf/parsers/POIFSChunkParser.java @@ -92,6 +92,12 @@ public final class POIFSChunkParser { // Now do the top level chunks processChunks(node, mainChunks); + // All chunks are now processed, have the ChunkGroup + // match up variable-length properties and their chunks + for (ChunkGroup group : groups) { + // TODO + } + // Finish return groups.toArray(new ChunkGroup[groups.size()]); } @@ -123,10 +129,10 @@ public final class POIFSChunkParser { if (entryName.equals(PropertiesChunk.NAME)) { if (grouping instanceof Chunks) { // These should be the properties for the message itself - chunk = new MessagePropertiesChunk(); + chunk = new MessagePropertiesChunk(grouping); } else { // Will be properties on an attachment or recipient - chunk = new StoragePropertiesChunk(); + chunk = new StoragePropertiesChunk(grouping); } } else { // Check it's a regular chunk -- 2.39.5