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
This commit is contained in:
Nick Burch 2013-02-01 11:45:31 +00:00
parent 90d7b88fa3
commit 8edde11bbc
11 changed files with 149 additions and 41 deletions

View File

@ -418,14 +418,14 @@ public class MAPIMessage extends POIDocument {
* @see #guess7BitEncoding() * @see #guess7BitEncoding()
*/ */
public void set7BitEncoding(String charset) { public void set7BitEncoding(String charset) {
for(Chunk c : mainChunks.getAll()) { for(Chunk c : mainChunks.getChunks()) {
if(c instanceof StringChunk) { if(c instanceof StringChunk) {
((StringChunk)c).set7BitEncoding(charset); ((StringChunk)c).set7BitEncoding(charset);
} }
} }
if (nameIdChunks!=null) { if (nameIdChunks!=null) {
for(Chunk c : nameIdChunks.getAll()) { for(Chunk c : nameIdChunks.getChunks()) {
if(c instanceof StringChunk) { if(c instanceof StringChunk) {
((StringChunk)c).set7BitEncoding(charset); ((StringChunk)c).set7BitEncoding(charset);
} }
@ -446,7 +446,7 @@ public class MAPIMessage extends POIDocument {
* are stored as 7 bit rather than unicode? * are stored as 7 bit rather than unicode?
*/ */
public boolean has7BitEncodingStrings() { public boolean has7BitEncodingStrings() {
for(Chunk c : mainChunks.getAll()) { for(Chunk c : mainChunks.getChunks()) {
if(c instanceof StringChunk) { if(c instanceof StringChunk) {
if( ((StringChunk)c).getType() == Types.ASCII_STRING ) { if( ((StringChunk)c).getType() == Types.ASCII_STRING ) {
return true; return true;
@ -455,7 +455,7 @@ public class MAPIMessage extends POIDocument {
} }
if (nameIdChunks!=null) { if (nameIdChunks!=null) {
for(Chunk c : nameIdChunks.getAll()) { for(Chunk c : nameIdChunks.getChunks()) {
if(c instanceof StringChunk) { if(c instanceof StringChunk) {
if( ((StringChunk)c).getType() == Types.ASCII_STRING ) { if( ((StringChunk)c).getType() == Types.ASCII_STRING ) {
return true; return true;

View File

@ -172,6 +172,16 @@ public class AttachmentChunks implements ChunkGroup {
allChunks.add(chunk); 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. * Orders by the attachment number.
*/ */

View File

@ -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
}
}

View File

@ -33,4 +33,9 @@ public interface ChunkGroup {
* Called by the parser whenever a chunk is found. * Called by the parser whenever a chunk is found.
*/ */
public void record(Chunk chunk); public void record(Chunk chunk);
/**
* Called by the parser when all chunks have been found.
*/
public void chunksComplete();
} }

View File

@ -18,7 +18,9 @@
package org.apache.poi.hsmf.datatypes; package org.apache.poi.hsmf.datatypes;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.HashMap;
import java.util.List; 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 * http://msdn.microsoft.com/en-us/library/ms526356%28v=exchg.10%29.aspx
*/ */
public final class Chunks implements ChunkGroup { public final class Chunks implements ChunkGroup {
/** Holds all the chunks that were found. */ /** Holds all the chunks that were found, indexed by their MAPIProperty */
private List<Chunk> allChunks = new ArrayList<Chunk>(); private Map<MAPIProperty,List<Chunk>> allChunks = new HashMap<MAPIProperty,List<Chunk>>();
/** Type of message that the MSG represents (ie. IPM.Note) */ /** Type of message that the MSG represents (ie. IPM.Note) */
public StringChunk messageClass; public StringChunk messageClass;
@ -70,65 +72,72 @@ public final class Chunks implements ChunkGroup {
/** The message properties */ /** The message properties */
public MessagePropertiesChunk messageProperties; public MessagePropertiesChunk messageProperties;
public Map<MAPIProperty,List<Chunk>> getAll() {
public Chunk[] getAll() { return allChunks;
return allChunks.toArray(new Chunk[allChunks.size()]);
} }
public Chunk[] getChunks() { public Chunk[] getChunks() {
return getAll(); ArrayList<Chunk> chunks = new ArrayList<Chunk>(allChunks.size());
for (List<Chunk> c : allChunks.values()) {
chunks.addAll(c);
}
return chunks.toArray(new Chunk[chunks.size()]);
} }
/** /**
* Called by the parser whenever a chunk is found. * Called by the parser whenever a chunk is found.
*/ */
public void record(Chunk chunk) { 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; messageClass = (StringChunk)chunk;
} }
else if(chunk.getChunkId() == MAPIProperty.INTERNET_MESSAGE_ID.id) { else if(prop == MAPIProperty.INTERNET_MESSAGE_ID) {
messageId = (StringChunk)chunk; messageId = (StringChunk)chunk;
} }
else if(chunk.getChunkId() == MAPIProperty.MESSAGE_SUBMISSION_ID.id) { else if(prop == MAPIProperty.MESSAGE_SUBMISSION_ID) {
// TODO - parse // TODO - parse
submissionChunk = (MessageSubmissionChunk)chunk; submissionChunk = (MessageSubmissionChunk)chunk;
} }
else if(chunk.getChunkId() == MAPIProperty.RECEIVED_BY_ADDRTYPE.id) { else if(prop == MAPIProperty.RECEIVED_BY_ADDRTYPE) {
sentByServerType = (StringChunk)chunk; sentByServerType = (StringChunk)chunk;
} }
else if(chunk.getChunkId() == MAPIProperty.TRANSPORT_MESSAGE_HEADERS.id) { else if(prop == MAPIProperty.TRANSPORT_MESSAGE_HEADERS) {
messageHeaders = (StringChunk)chunk; messageHeaders = (StringChunk)chunk;
} }
else if(chunk.getChunkId() == MAPIProperty.CONVERSATION_TOPIC.id) { else if(prop == MAPIProperty.CONVERSATION_TOPIC) {
conversationTopic = (StringChunk)chunk; conversationTopic = (StringChunk)chunk;
} }
else if(chunk.getChunkId() == MAPIProperty.SUBJECT.id) { else if(prop == MAPIProperty.SUBJECT) {
subjectChunk = (StringChunk)chunk; subjectChunk = (StringChunk)chunk;
} }
else if(chunk.getChunkId() == MAPIProperty.ORIGINAL_SUBJECT.id) { else if(prop == MAPIProperty.ORIGINAL_SUBJECT) {
// TODO // TODO
} }
else if(chunk.getChunkId() == MAPIProperty.DISPLAY_TO.id) { else if(prop == MAPIProperty.DISPLAY_TO) {
displayToChunk = (StringChunk)chunk; displayToChunk = (StringChunk)chunk;
} }
else if(chunk.getChunkId() == MAPIProperty.DISPLAY_CC.id) { else if(prop == MAPIProperty.DISPLAY_CC) {
displayCCChunk = (StringChunk)chunk; displayCCChunk = (StringChunk)chunk;
} }
else if(chunk.getChunkId() == MAPIProperty.DISPLAY_BCC.id) { else if(prop == MAPIProperty.DISPLAY_BCC) {
displayBCCChunk = (StringChunk)chunk; displayBCCChunk = (StringChunk)chunk;
} }
else if(chunk.getChunkId() == MAPIProperty.SENDER_EMAIL_ADDRESS.id) { else if(prop == MAPIProperty.SENDER_EMAIL_ADDRESS) {
emailFromChunk = (StringChunk)chunk; emailFromChunk = (StringChunk)chunk;
} }
else if(chunk.getChunkId() == MAPIProperty.SENDER_NAME.id) { else if(prop == MAPIProperty.SENDER_NAME) {
displayFromChunk = (StringChunk)chunk; displayFromChunk = (StringChunk)chunk;
} }
else if(chunk.getChunkId() == MAPIProperty.BODY.id) { else if(prop == MAPIProperty.BODY) {
textBodyChunk = (StringChunk)chunk; textBodyChunk = (StringChunk)chunk;
} }
else if(chunk.getChunkId() == MAPIProperty.BODY_HTML.id) { else if(prop == MAPIProperty.BODY_HTML) {
if(chunk instanceof StringChunk) { if(chunk instanceof StringChunk) {
htmlBodyChunkString = (StringChunk)chunk; htmlBodyChunkString = (StringChunk)chunk;
} }
@ -136,16 +145,21 @@ public final class Chunks implements ChunkGroup {
htmlBodyChunkBinary = (ByteChunk)chunk; htmlBodyChunkBinary = (ByteChunk)chunk;
} }
} }
else if(chunk.getChunkId() == MAPIProperty.RTF_COMPRESSED.id) { else if(prop == MAPIProperty.RTF_COMPRESSED) {
rtfBodyChunk = (ByteChunk)chunk; rtfBodyChunk = (ByteChunk)chunk;
} }
else if(chunk.getChunkId() == MAPIProperty.UNKNOWN.id && else if(chunk instanceof MessagePropertiesChunk) {
chunk instanceof MessagePropertiesChunk) {
// TODO Should we maybe collect the contents of this?
messageProperties = (MessagePropertiesChunk) chunk; messageProperties = (MessagePropertiesChunk) chunk;
} }
// And add to the main list // And add to the main list
allChunks.add(chunk); if (allChunks.get(prop) == null) {
allChunks.put(prop, new ArrayList<Chunk>());
}
allChunks.get(prop).add(chunk);
}
public void chunksComplete() {
// TODO Match variable sized properties to their chunks + index
} }
} }

View File

@ -33,8 +33,8 @@ public class MessagePropertiesChunk extends PropertiesChunk {
private long recipientCount; private long recipientCount;
private long attachmentCount; private long attachmentCount;
public MessagePropertiesChunk() { public MessagePropertiesChunk(ChunkGroup parentGroup) {
super(); super(parentGroup);
} }
public long getNextRecipientId() { public long getNextRecipientId() {

View File

@ -44,4 +44,13 @@ public final class NameIdChunks implements ChunkGroup {
public void record(Chunk chunk) { public void record(Chunk chunk) {
allChunks.add(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
}
} }

View File

@ -25,8 +25,9 @@ import java.util.HashMap;
import java.util.List; import java.util.List;
import java.util.Map; 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.Types.MAPIType;
import org.apache.poi.hsmf.datatypes.PropertyValue.*;
import org.apache.poi.util.IOUtils; import org.apache.poi.util.IOUtils;
import org.apache.poi.util.LittleEndian; import org.apache.poi.util.LittleEndian;
import org.apache.poi.util.LittleEndian.BufferUnderrunException; import org.apache.poi.util.LittleEndian.BufferUnderrunException;
@ -52,11 +53,19 @@ public abstract class PropertiesChunk extends Chunk {
private Map<MAPIProperty, List<PropertyValue>> properties = private Map<MAPIProperty, List<PropertyValue>> properties =
new HashMap<MAPIProperty, List<PropertyValue>>(); new HashMap<MAPIProperty, List<PropertyValue>>();
/**
* 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. * Creates a Properties Chunk.
*/ */
protected PropertiesChunk() { protected PropertiesChunk(ChunkGroup parentGroup) {
super(NAME, -1, Types.UNKNOWN); super(NAME, -1, Types.UNKNOWN);
this.parentGroup = parentGroup;
} }
@Override @Override
@ -132,7 +141,7 @@ public abstract class PropertiesChunk extends Chunk {
// Wrap and store // Wrap and store
PropertyValue propVal = null; PropertyValue propVal = null;
if (isPointer) { if (isPointer) {
// TODO Pointer type which can do lookup propVal = new ChunkBasedPropertyValue(prop, flags, data);
} }
else if (type == Types.LONG_LONG) { else if (type == Types.LONG_LONG) {
propVal = new LongLongPropertyValue(prop, flags, data); propVal = new LongLongPropertyValue(prop, flags, data);

View File

@ -77,7 +77,11 @@ public final class RecipientChunks implements ChunkGroup {
* as in recipientNameChunk * as in recipientNameChunk
*/ */
public StringChunk recipientDisplayNameChunk; 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) { public RecipientChunks(String name) {
recipientNumber = -1; recipientNumber = -1;
@ -191,11 +195,18 @@ public final class RecipientChunks implements ChunkGroup {
else if(chunk.getChunkId() == DELIVERY_TYPE.id) { else if(chunk.getChunkId() == DELIVERY_TYPE.id) {
deliveryTypeChunk = (StringChunk)chunk; deliveryTypeChunk = (StringChunk)chunk;
} }
else if(chunk instanceof PropertiesChunk) {
recipientProperties = (PropertiesChunk) chunk;
}
// And add to the main list // And add to the main list
allChunks.add(chunk); allChunks.add(chunk);
} }
public void chunksComplete() {
// TODO Match variable sized properties to their chunks + index
}
/** /**
* Orders by the recipient number. * Orders by the recipient number.
*/ */

View File

@ -29,8 +29,8 @@ import org.apache.poi.util.LittleEndian;
* This only has a 8 byte header * This only has a 8 byte header
*/ */
public class StoragePropertiesChunk extends PropertiesChunk { public class StoragePropertiesChunk extends PropertiesChunk {
public StoragePropertiesChunk() { public StoragePropertiesChunk(ChunkGroup parentGroup) {
super(); super(parentGroup);
} }
@Override @Override

View File

@ -92,6 +92,12 @@ public final class POIFSChunkParser {
// Now do the top level chunks // Now do the top level chunks
processChunks(node, mainChunks); 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 // Finish
return groups.toArray(new ChunkGroup[groups.size()]); return groups.toArray(new ChunkGroup[groups.size()]);
} }
@ -123,10 +129,10 @@ public final class POIFSChunkParser {
if (entryName.equals(PropertiesChunk.NAME)) { if (entryName.equals(PropertiesChunk.NAME)) {
if (grouping instanceof Chunks) { if (grouping instanceof Chunks) {
// These should be the properties for the message itself // These should be the properties for the message itself
chunk = new MessagePropertiesChunk(); chunk = new MessagePropertiesChunk(grouping);
} else { } else {
// Will be properties on an attachment or recipient // Will be properties on an attachment or recipient
chunk = new StoragePropertiesChunk(); chunk = new StoragePropertiesChunk(grouping);
} }
} else { } else {
// Check it's a regular chunk // Check it's a regular chunk