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()
*/
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;

View File

@ -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.
*/

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.
*/
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;
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<Chunk> allChunks = new ArrayList<Chunk>();
/** Holds all the chunks that were found, indexed by their MAPIProperty */
private Map<MAPIProperty,List<Chunk>> allChunks = new HashMap<MAPIProperty,List<Chunk>>();
/** 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<MAPIProperty,List<Chunk>> getAll() {
return allChunks;
}
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.
*/
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<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 attachmentCount;
public MessagePropertiesChunk() {
super();
public MessagePropertiesChunk(ChunkGroup parentGroup) {
super(parentGroup);
}
public long getNextRecipientId() {

View File

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

View File

@ -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<MAPIProperty, List<PropertyValue>> properties =
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.
*/
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);

View File

@ -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.
*/

View File

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

View File

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