Refactor the TNEF Attributes to better match the MAPI ones, and move both into their own package

git-svn-id: https://svn.apache.org/repos/asf/poi/trunk@1076251 13f79535-47bb-0310-9956-ffa450edef68
This commit is contained in:
Nick Burch 2011-03-02 15:25:35 +00:00
parent 03b0eabcf8
commit 20f6e280a5
11 changed files with 355 additions and 316 deletions

View File

@ -20,15 +20,22 @@ package org.apache.poi.hmef;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
import org.apache.poi.hmef.attribute.TNEFAttribute;
import org.apache.poi.hmef.attribute.MAPIAttribute;
/** /**
* An attachment within a {@link HMEFMessage} * An attachment within a {@link HMEFMessage}. Provides both helper
* methods to get at common parts and attributes of the attachment,
* and list methods to get all of them.
*/ */
public final class Attachment { public final class Attachment {
private final List<Attribute> attributes = new ArrayList<Attribute>(); private final List<TNEFAttribute> attributes = new ArrayList<TNEFAttribute>();
private final List<MAPIAttribute> mapiAttributes = new ArrayList<MAPIAttribute>(); private final List<MAPIAttribute> mapiAttributes = new ArrayList<MAPIAttribute>();
protected void addAttribute(Attribute attr) {
protected void addAttribute(TNEFAttribute attr) {
attributes.add(attr); attributes.add(attr);
} }
@ -36,7 +43,7 @@ public final class Attachment {
mapiAttributes.add(attr); mapiAttributes.add(attr);
} }
public List<Attribute> getAttributes() { public List<TNEFAttribute> getAttributes() {
return attributes; return attributes;
} }

View File

@ -1,262 +0,0 @@
/* ====================================================================
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.hmef;
import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.apache.poi.util.IOUtils;
import org.apache.poi.util.LittleEndian;
/**
* An attribute which applies to a {@link HMEFMessage}
* or one of its {@link Attachment}s.
* Note - the types and IDs differ from standard Outlook/MAPI
* ones, so we can't just re-use the HSMF ones.
*/
public final class Attribute {
// Types taken from http://msdn.microsoft.com/en-us/library/microsoft.exchange.data.contenttypes.tnef.tnefattributetype%28v=EXCHG.140%29.aspx
public static final int TYPE_TRIPLES = 0x0000;
public static final int TYPE_STRING = 0x0001;
public static final int TYPE_TEXT = 0x0002;
public static final int TYPE_DATE = 0x0003;
public static final int TYPE_SHORT = 0x0004;
public static final int TYPE_LONG = 0x0005;
public static final int TYPE_BYTE = 0x0006;
public static final int TYPE_WORD = 0x0007;
public static final int TYPE_DWORD = 0x0008;
public static final int TYPE_MAX = 0x0009;
// Types taken from http://msdn.microsoft.com/en-us/library/microsoft.exchange.data.contenttypes.tnef.tnefpropertytype%28v=EXCHG.140%29.aspx
/** AppTime - application time value */
public static final int PTYPE_APPTIME = 0x0007;
/** Binary - counted byte array */
public static final int PTYPE_BINARY = 0x0102;
/** Boolean - 16-bit Boolean value. '0' is false. Non-zero is true */
public static final int PTYPE_BOOLEAN = 0x000B;
/** ClassId - OLE GUID */
public static final int PTYPE_CLASSID = 0x0048;
/** Currency - signed 64-bit integer that represents a base ten decimal with four digits to the right of the decimal point */
public static final int PTYPE_CURRENCY = 0x0006;
/** Double - floating point double */
public static final int PTYPE_DOUBLE = 0x0005;
/** Error - 32-bit error value */
public static final int PTYPE_ERROR = 0x000A;
/** I2 - signed 16-bit value */
public static final int PTYPE_I2 = 0x0002;
/** I8 - 8-byte signed integer */
public static final int PTYPE_I8 = 0x0014;
/** Long - signed 32-bit value */
public static final int PTYPE_LONG = 0x0003;
/** MultiValued - Value part contains multiple values */
public static final int PTYPE_MULTIVALUED = 0x1000;
/** Null - NULL property value */
public static final int PTYPE_NULL = 0x0001;
/** Object - embedded object in a property */
public static final int PTYPE_OBJECT = 0x000D;
/** R4 - 4-byte floating point value */
public static final int PTYPE_R4 = 0x0004;
/** String8 - null-terminated 8-bit character string */
public static final int PTYPE_STRING8 = 0x001E;
/** SysTime - FILETIME 64-bit integer specifying the number of 100ns periods since Jan 1, 1601 */
public static final int PTYPE_SYSTIME = 0x0040;
/** Unicode - null-terminated Unicode string */
public static final int PTYPE_UNICODE = 0x001F;
/** Unspecified */
public static final int PTYPE_UNSPECIFIED = 0x0000;
// Levels taken from http://msdn.microsoft.com/en-us/library/microsoft.exchange.data.contenttypes.tnef.tnefattributelevel%28v=EXCHG.140%29.aspx
public static final int LEVEL_MESSAGE = 0x01;
public static final int LEVEL_ATTACHMENT = 0x02;
public static final int LEVEL_END_OF_FILE = -0x01;
// ID information taken from http://msdn.microsoft.com/en-us/library/microsoft.exchange.data.contenttypes.tnef.tnefattributetag%28v=EXCHG.140%29.aspx
public static final AttributeID ID_AIDOWNER =
new AttributeID(0x0008, TYPE_LONG, "AidOwner", "PR_OWNER_APPT_ID");
public static final AttributeID ID_ATTACHCREATEDATE =
new AttributeID(0x8012, TYPE_DATE, "AttachCreateDate", "PR_CREATION_TIME");
public static final AttributeID ID_ATTACHDATA =
new AttributeID(0x800F, TYPE_BYTE, "AttachData", "PR_ATTACH_DATA_BIN");
public static final AttributeID ID_ATTACHMENT =
new AttributeID(0x9005, TYPE_BYTE, "Attachment", null);
public static final AttributeID ID_ATTACHMETAFILE =
new AttributeID(0x8011, TYPE_BYTE, "AttachMetaFile", "PR_ATTACH_RENDERING");
public static final AttributeID ID_ATTACHMODIFYDATE =
new AttributeID(0x8013, TYPE_DATE, "AttachModifyDate", "PR_LAST_MODIFICATION_TIME");
public static final AttributeID ID_ATTACHRENDERDATA =
new AttributeID(0x9002, TYPE_BYTE, "AttachRenderData", "attAttachRenddata");
public static final AttributeID ID_ATTACHTITLE =
new AttributeID(0x8010, TYPE_STRING, "AttachTitle", "PR_ATTACH_FILENAME");
public static final AttributeID ID_ATTACHTRANSPORTFILENAME =
new AttributeID(0x9001, TYPE_BYTE, "AttachTransportFilename", "PR_ATTACH_TRANSPORT_NAME");
public static final AttributeID ID_BODY =
new AttributeID(0x800C, TYPE_TEXT, "Body", "PR_BODY");
public static final AttributeID ID_CONVERSATIONID =
new AttributeID(0x800B, TYPE_STRING, "ConversationId", "PR_CONVERSATION_KEY");
public static final AttributeID ID_DATEEND =
new AttributeID(0x0007, TYPE_DATE, "DateEnd", "PR_END_DATE");
public static final AttributeID ID_DATEMODIFIED =
new AttributeID(0x8020, TYPE_DATE, "DateModified", "PR_LAST_MODIFICATION_TIME ");
public static final AttributeID ID_DATERECEIVED =
new AttributeID(0x8006, TYPE_DATE, "DateReceived", "PR_MESSAGE_DELIVERY_TIME ");
public static final AttributeID ID_DATESENT =
new AttributeID(0x8005, TYPE_DATE, "DateSent", "PR_CLIENT_SUBMIT_TIME ");
public static final AttributeID ID_DATESTART =
new AttributeID(0x0006, TYPE_DATE, "DateStart", "PR_START_DATE ");
public static final AttributeID ID_DELEGATE =
new AttributeID(0x0002, TYPE_BYTE, "Delegate", "PR_RCVD_REPRESENTING_xxx ");
public static final AttributeID ID_FROM =
new AttributeID(0x8000, TYPE_STRING, "From", "PR_SENDER_ENTRYID");
public static final AttributeID ID_MAPIPROPERTIES =
new AttributeID(0x9003, TYPE_BYTE, "MapiProperties", null);
public static final AttributeID ID_MESSAGECLASS =
new AttributeID(0x8008, TYPE_WORD, "MessageClass", "PR_MESSAGE_CLASS ");
public static final AttributeID ID_MESSAGEID =
new AttributeID(0x8009, TYPE_STRING, "MessageId", "PR_SEARCH_KEY");
public static final AttributeID ID_MESSAGESTATUS =
new AttributeID(0x8007, TYPE_BYTE, "MessageStatus", "PR_MESSAGE_FLAGS");
public static final AttributeID ID_NULL =
new AttributeID(0x0000, -1, "Null", null);
public static final AttributeID ID_OEMCODEPAGE =
new AttributeID(0x9007, TYPE_BYTE, "OemCodepage", "AttOemCodepage");
public static final AttributeID ID_ORIGINALMESSAGECLASS =
new AttributeID(0x0006, TYPE_WORD, "OriginalMessageClass", "PR_ORIG_MESSAGE_CLASS");
public static final AttributeID ID_OWNER =
new AttributeID(0x0000, TYPE_BYTE, "Owner", "PR_RCVD_REPRESENTING_xxx");
public static final AttributeID ID_PARENTID =
new AttributeID(0x800A, TYPE_STRING, "ParentId", "PR_PARENT_KEY");
public static final AttributeID ID_PRIORITY =
new AttributeID(0x800D, TYPE_SHORT, "Priority", "PR_IMPORTANCE");
public static final AttributeID ID_RECIPIENTTABLE =
new AttributeID(0x9004, TYPE_BYTE, "RecipientTable", "PR_MESSAGE_RECIPIENTS");
public static final AttributeID ID_REQUESTRESPONSE =
new AttributeID(0x009, TYPE_SHORT, "RequestResponse", "PR_RESPONSE_REQUESTED");
public static final AttributeID ID_SENTFOR =
new AttributeID(0x0001, TYPE_BYTE, "SentFor", "PR_SENT_REPRESENTING_xxx");
public static final AttributeID ID_SUBJECT =
new AttributeID(0x8004, TYPE_STRING, "Subject", "PR_SUBJECT");
public static final AttributeID ID_TNEFVERSION =
new AttributeID(0x9006, TYPE_DWORD, "TnefVersion", "attTnefVersion");
public static final AttributeID ID_UNKNOWN =
new AttributeID(-1, -1, "Unknown", null);
/**
* Holds information on one potential ID of an
* attribute, and provides handy lookups for it.
*/
public static class AttributeID {
private static Map<Integer, List<AttributeID>> attributes = new HashMap<Integer, List<AttributeID>>();
public final int id;
public final int usualType;
public final String name;
public final String mapiProperty;
private AttributeID(int id, int usualType, String name, String mapiProperty) {
this.id = id;
this.usualType = usualType;
this.name = name;
this.mapiProperty = mapiProperty;
// Store it for lookup
if(! attributes.containsKey(id)) {
attributes.put(id, new ArrayList<AttributeID>());
}
attributes.get(id).add(this);
}
public static AttributeID getBest(int id, int type) {
List<AttributeID> attrs = attributes.get(id);
if(attrs == null) {
return ID_UNKNOWN;
}
// If there's only one, it's easy
if(attrs.size() == 1) {
return attrs.get(0);
}
// Try by type
for(AttributeID attr : attrs) {
if(attr.usualType == type) return attr;
}
// Go for the first if we can't otherwise decide...
return attrs.get(0);
}
public String toString() {
StringBuffer str = new StringBuffer();
str.append(name);
str.append(" [");
str.append(id);
str.append("]");
if(mapiProperty != null) {
str.append(" (");
str.append(mapiProperty);
str.append(")");
}
return str.toString();
}
}
private final AttributeID id;
private final int type;
private final byte[] data;
private final int checksum;
/**
* Constructs a single new attribute from
* the contents of the stream
*/
public Attribute(InputStream inp) throws IOException {
int id = LittleEndian.readUShort(inp);
this.type = LittleEndian.readUShort(inp);
int length = LittleEndian.readInt(inp);
this.id = AttributeID.getBest(id, type);
data = new byte[length];
IOUtils.readFully(inp, data);
checksum = LittleEndian.readUShort(inp);
// TODO Handle the MapiProperties attribute in
// a different way, as we need to recurse into it
}
public AttributeID getId() {
return id;
}
public int getType() {
return type;
}
public byte[] getData() {
return data;
}
public String toString() {
return "Attachment " + getId().toString() + ", type=" + type +
", data length=" + data.length;
}
}

View File

@ -22,7 +22,9 @@ import java.io.InputStream;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
import org.apache.poi.hmef.Attribute.AttributeID; import org.apache.poi.hmef.attribute.MAPIAttribute;
import org.apache.poi.hmef.attribute.TNEFAttribute;
import org.apache.poi.hmef.attribute.TNEFProperty;
import org.apache.poi.hsmf.datatypes.MAPIProperty; import org.apache.poi.hsmf.datatypes.MAPIProperty;
import org.apache.poi.util.LittleEndian; import org.apache.poi.util.LittleEndian;
@ -38,7 +40,7 @@ public final class HMEFMessage {
public static final long HEADER_SIGNATURE = 0x223e9f78; public static final long HEADER_SIGNATURE = 0x223e9f78;
private int fileId; private int fileId;
private List<Attribute> messageAttributes = new ArrayList<Attribute>(); private List<TNEFAttribute> messageAttributes = new ArrayList<TNEFAttribute>();
private List<MAPIAttribute> mapiAttributes = new ArrayList<MAPIAttribute>(); private List<MAPIAttribute> mapiAttributes = new ArrayList<MAPIAttribute>();
private List<Attachment> attachments = new ArrayList<Attachment>(); private List<Attachment> attachments = new ArrayList<Attachment>();
@ -59,16 +61,16 @@ public final class HMEFMessage {
process(inp, 0); process(inp, 0);
// Finally expand out the MAPI Attributes // Finally expand out the MAPI Attributes
for(Attribute attr : messageAttributes) { for(TNEFAttribute attr : messageAttributes) {
if(attr.getId() == Attribute.ID_MAPIPROPERTIES) { if(attr.getProperty() == TNEFProperty.ID_MAPIPROPERTIES) {
mapiAttributes.addAll( mapiAttributes.addAll(
MAPIAttribute.create(attr) MAPIAttribute.create(attr)
); );
} }
} }
for(Attachment attachment : attachments) { for(Attachment attachment : attachments) {
for(Attribute attr : attachment.getAttributes()) { for(TNEFAttribute attr : attachment.getAttributes()) {
if(attr.getId() == Attribute.ID_MAPIPROPERTIES) { if(attr.getProperty()== TNEFProperty.ID_MAPIPROPERTIES) {
attachment.getMAPIAttributes().addAll( attachment.getMAPIAttributes().addAll(
MAPIAttribute.create(attr) MAPIAttribute.create(attr)
); );
@ -80,19 +82,19 @@ public final class HMEFMessage {
private void process(InputStream inp, int lastLevel) throws IOException { private void process(InputStream inp, int lastLevel) throws IOException {
// Fetch the level // Fetch the level
int level = inp.read(); int level = inp.read();
if(level == Attribute.LEVEL_END_OF_FILE) { if(level == TNEFProperty.LEVEL_END_OF_FILE) {
return; return;
} }
// Build the attribute // Build the attribute
Attribute attr = new Attribute(inp); TNEFAttribute attr = new TNEFAttribute(inp);
// Decide what to attach it to, based on the levels and IDs // Decide what to attach it to, based on the levels and IDs
if(level == Attribute.LEVEL_MESSAGE) { if(level == TNEFProperty.LEVEL_MESSAGE) {
messageAttributes.add(attr); messageAttributes.add(attr);
} else if(level == Attribute.LEVEL_ATTACHMENT) { } else if(level == TNEFProperty.LEVEL_ATTACHMENT) {
// Previous attachment or a new one? // Previous attachment or a new one?
if(attachments.size() == 0 || attr.getId() == Attribute.ID_ATTACHRENDERDATA) { if(attachments.size() == 0 || attr.getProperty() == TNEFProperty.ID_ATTACHRENDERDATA) {
attachments.add(new Attachment()); attachments.add(new Attachment());
} }
@ -111,7 +113,7 @@ public final class HMEFMessage {
* Note - In a typical message, most of the interesting properties * Note - In a typical message, most of the interesting properties
* are stored as {@link MAPIAttribute}s - see {@link #getMessageMAPIAttributes()} * are stored as {@link MAPIAttribute}s - see {@link #getMessageMAPIAttributes()}
*/ */
public List<Attribute> getMessageAttributes() { public List<TNEFAttribute> getMessageAttributes() {
return messageAttributes; return messageAttributes;
} }
@ -135,9 +137,9 @@ public final class HMEFMessage {
* Return the message attribute with the given ID, * Return the message attribute with the given ID,
* or null if there isn't one. * or null if there isn't one.
*/ */
public Attribute getMessageAttribute(AttributeID id) { public TNEFAttribute getMessageAttribute(TNEFProperty id) {
for(Attribute attr : messageAttributes) { for(TNEFAttribute attr : messageAttributes) {
if(attr.getId() == id) { if(attr.getProperty() == id) {
return attr; return attr;
} }
} }

View File

@ -15,7 +15,7 @@
limitations under the License. limitations under the License.
==================================================================== */ ==================================================================== */
package org.apache.poi.hmef; package org.apache.poi.hmef.attribute;
import java.io.ByteArrayInputStream; import java.io.ByteArrayInputStream;
import java.io.IOException; import java.io.IOException;
@ -23,6 +23,8 @@ import java.io.InputStream;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
import org.apache.poi.hmef.Attachment;
import org.apache.poi.hmef.HMEFMessage;
import org.apache.poi.hsmf.datatypes.MAPIProperty; import org.apache.poi.hsmf.datatypes.MAPIProperty;
import org.apache.poi.hsmf.datatypes.Types; import org.apache.poi.hsmf.datatypes.Types;
import org.apache.poi.util.HexDump; import org.apache.poi.util.HexDump;
@ -79,11 +81,11 @@ public class MAPIAttribute {
* Parses a MAPI Properties TNEF Attribute, and returns * Parses a MAPI Properties TNEF Attribute, and returns
* the list of MAPI Attributes contained within it * the list of MAPI Attributes contained within it
*/ */
public static List<MAPIAttribute> create(Attribute parent) throws IOException { public static List<MAPIAttribute> create(TNEFAttribute parent) throws IOException {
if(parent.getId() != Attribute.ID_MAPIPROPERTIES) { if(parent.getProperty() != TNEFProperty.ID_MAPIPROPERTIES) {
throw new IllegalArgumentException( throw new IllegalArgumentException(
"Can only create from a MAPIProperty attribute, " + "Can only create from a MAPIProperty attribute, " +
"instead received a " + parent.getId() + " one" "instead received a " + parent.getProperty() + " one"
); );
} }
ByteArrayInputStream inp = new ByteArrayInputStream(parent.getData()); ByteArrayInputStream inp = new ByteArrayInputStream(parent.getData());

View File

@ -15,11 +15,14 @@
limitations under the License. limitations under the License.
==================================================================== */ ==================================================================== */
package org.apache.poi.hmef; package org.apache.poi.hmef.attribute;
import java.io.ByteArrayInputStream; import java.io.ByteArrayInputStream;
import java.io.IOException; import java.io.IOException;
import org.apache.poi.hmef.Attachment;
import org.apache.poi.hmef.CompressedRTF;
import org.apache.poi.hmef.HMEFMessage;
import org.apache.poi.hsmf.datatypes.MAPIProperty; import org.apache.poi.hsmf.datatypes.MAPIProperty;
import org.apache.poi.util.StringUtil; import org.apache.poi.util.StringUtil;

View File

@ -15,24 +15,15 @@
limitations under the License. limitations under the License.
==================================================================== */ ==================================================================== */
package org.apache.poi.hmef; package org.apache.poi.hmef.attribute;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.UnsupportedEncodingException; import java.io.UnsupportedEncodingException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.apache.poi.util.HexDump; import org.apache.poi.hmef.Attachment;
import org.apache.poi.util.IOUtils; import org.apache.poi.hmef.HMEFMessage;
import org.apache.poi.util.LittleEndian;
import org.apache.poi.util.StringUtil;
import org.apache.poi.hmef.Attribute.AttributeID;
import org.apache.poi.hsmf.datatypes.MAPIProperty; import org.apache.poi.hsmf.datatypes.MAPIProperty;
import org.apache.poi.hsmf.datatypes.Types; import org.apache.poi.hsmf.datatypes.Types;
import org.apache.poi.util.StringUtil;
/** /**
* A pure-MAPI attribute holding a String, which applies * A pure-MAPI attribute holding a String, which applies

View File

@ -0,0 +1,76 @@
/* ====================================================================
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.hmef.attribute;
import java.io.IOException;
import java.io.InputStream;
import org.apache.poi.hmef.Attachment;
import org.apache.poi.hmef.HMEFMessage;
import org.apache.poi.util.IOUtils;
import org.apache.poi.util.LittleEndian;
/**
* An attribute which applies to a {@link HMEFMessage}
* or one of its {@link Attachment}s.
* Note - the types and IDs differ from standard Outlook/MAPI
* ones, so we can't just re-use the HSMF ones.
*/
public final class TNEFAttribute {
private final TNEFProperty property;
private final int type;
private final byte[] data;
private final int checksum;
/**
* Constructs a single new attribute from
* the contents of the stream
*/
public TNEFAttribute(InputStream inp) throws IOException {
int id = LittleEndian.readUShort(inp);
this.type = LittleEndian.readUShort(inp);
int length = LittleEndian.readInt(inp);
property = TNEFProperty.getBest(id, type);
data = new byte[length];
IOUtils.readFully(inp, data);
checksum = LittleEndian.readUShort(inp);
// TODO Handle the MapiProperties attribute in
// a different way, as we need to recurse into it
}
public TNEFProperty getProperty() {
return property;
}
public int getType() {
return type;
}
public byte[] getData() {
return data;
}
public String toString() {
return "Attachment " + property.toString() + ", type=" + type +
", data length=" + data.length;
}
}

View File

@ -0,0 +1,216 @@
/* ====================================================================
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.hmef.attribute;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
* Holds the list of TNEF Attributes, and allows lookup
* by friendly name, ID and MAPI Property Name.
*
* Note - the types and IDs differ from standard Outlook/MAPI
* ones, so we can't just re-use the HSMF ones.
*/
public final class TNEFProperty {
private static Map<Integer, List<TNEFProperty>> properties = new HashMap<Integer, List<TNEFProperty>>();
// Types taken from http://msdn.microsoft.com/en-us/library/microsoft.exchange.data.contenttypes.tnef.tnefattributetype%28v=EXCHG.140%29.aspx
public static final int TYPE_TRIPLES = 0x0000;
public static final int TYPE_STRING = 0x0001;
public static final int TYPE_TEXT = 0x0002;
public static final int TYPE_DATE = 0x0003;
public static final int TYPE_SHORT = 0x0004;
public static final int TYPE_LONG = 0x0005;
public static final int TYPE_BYTE = 0x0006;
public static final int TYPE_WORD = 0x0007;
public static final int TYPE_DWORD = 0x0008;
public static final int TYPE_MAX = 0x0009;
// Types taken from http://msdn.microsoft.com/en-us/library/microsoft.exchange.data.contenttypes.tnef.tnefpropertytype%28v=EXCHG.140%29.aspx
/** AppTime - application time value */
public static final int PTYPE_APPTIME = 0x0007;
/** Binary - counted byte array */
public static final int PTYPE_BINARY = 0x0102;
/** Boolean - 16-bit Boolean value. '0' is false. Non-zero is true */
public static final int PTYPE_BOOLEAN = 0x000B;
/** ClassId - OLE GUID */
public static final int PTYPE_CLASSID = 0x0048;
/** Currency - signed 64-bit integer that represents a base ten decimal with four digits to the right of the decimal point */
public static final int PTYPE_CURRENCY = 0x0006;
/** Double - floating point double */
public static final int PTYPE_DOUBLE = 0x0005;
/** Error - 32-bit error value */
public static final int PTYPE_ERROR = 0x000A;
/** I2 - signed 16-bit value */
public static final int PTYPE_I2 = 0x0002;
/** I8 - 8-byte signed integer */
public static final int PTYPE_I8 = 0x0014;
/** Long - signed 32-bit value */
public static final int PTYPE_LONG = 0x0003;
/** MultiValued - Value part contains multiple values */
public static final int PTYPE_MULTIVALUED = 0x1000;
/** Null - NULL property value */
public static final int PTYPE_NULL = 0x0001;
/** Object - embedded object in a property */
public static final int PTYPE_OBJECT = 0x000D;
/** R4 - 4-byte floating point value */
public static final int PTYPE_R4 = 0x0004;
/** String8 - null-terminated 8-bit character string */
public static final int PTYPE_STRING8 = 0x001E;
/** SysTime - FILETIME 64-bit integer specifying the number of 100ns periods since Jan 1, 1601 */
public static final int PTYPE_SYSTIME = 0x0040;
/** Unicode - null-terminated Unicode string */
public static final int PTYPE_UNICODE = 0x001F;
/** Unspecified */
public static final int PTYPE_UNSPECIFIED = 0x0000;
// Levels taken from http://msdn.microsoft.com/en-us/library/microsoft.exchange.data.contenttypes.tnef.tnefattributelevel%28v=EXCHG.140%29.aspx
public static final int LEVEL_MESSAGE = 0x01;
public static final int LEVEL_ATTACHMENT = 0x02;
public static final int LEVEL_END_OF_FILE = -0x01;
// ID information taken from http://msdn.microsoft.com/en-us/library/microsoft.exchange.data.contenttypes.tnef.tnefattributetag%28v=EXCHG.140%29.aspx
public static final TNEFProperty ID_AIDOWNER =
new TNEFProperty(0x0008, TYPE_LONG, "AidOwner", "PR_OWNER_APPT_ID");
public static final TNEFProperty ID_ATTACHCREATEDATE =
new TNEFProperty(0x8012, TYPE_DATE, "AttachCreateDate", "PR_CREATION_TIME");
public static final TNEFProperty ID_ATTACHDATA =
new TNEFProperty(0x800F, TYPE_BYTE, "AttachData", "PR_ATTACH_DATA_BIN");
public static final TNEFProperty ID_ATTACHMENT =
new TNEFProperty(0x9005, TYPE_BYTE, "Attachment", null);
public static final TNEFProperty ID_ATTACHMETAFILE =
new TNEFProperty(0x8011, TYPE_BYTE, "AttachMetaFile", "PR_ATTACH_RENDERING");
public static final TNEFProperty ID_ATTACHMODIFYDATE =
new TNEFProperty(0x8013, TYPE_DATE, "AttachModifyDate", "PR_LAST_MODIFICATION_TIME");
public static final TNEFProperty ID_ATTACHRENDERDATA =
new TNEFProperty(0x9002, TYPE_BYTE, "AttachRenderData", "attAttachRenddata");
public static final TNEFProperty ID_ATTACHTITLE =
new TNEFProperty(0x8010, TYPE_STRING, "AttachTitle", "PR_ATTACH_FILENAME");
public static final TNEFProperty ID_ATTACHTRANSPORTFILENAME =
new TNEFProperty(0x9001, TYPE_BYTE, "AttachTransportFilename", "PR_ATTACH_TRANSPORT_NAME");
public static final TNEFProperty ID_BODY =
new TNEFProperty(0x800C, TYPE_TEXT, "Body", "PR_BODY");
public static final TNEFProperty ID_CONVERSATIONID =
new TNEFProperty(0x800B, TYPE_STRING, "ConversationId", "PR_CONVERSATION_KEY");
public static final TNEFProperty ID_DATEEND =
new TNEFProperty(0x0007, TYPE_DATE, "DateEnd", "PR_END_DATE");
public static final TNEFProperty ID_DATEMODIFIED =
new TNEFProperty(0x8020, TYPE_DATE, "DateModified", "PR_LAST_MODIFICATION_TIME ");
public static final TNEFProperty ID_DATERECEIVED =
new TNEFProperty(0x8006, TYPE_DATE, "DateReceived", "PR_MESSAGE_DELIVERY_TIME ");
public static final TNEFProperty ID_DATESENT =
new TNEFProperty(0x8005, TYPE_DATE, "DateSent", "PR_CLIENT_SUBMIT_TIME ");
public static final TNEFProperty ID_DATESTART =
new TNEFProperty(0x0006, TYPE_DATE, "DateStart", "PR_START_DATE ");
public static final TNEFProperty ID_DELEGATE =
new TNEFProperty(0x0002, TYPE_BYTE, "Delegate", "PR_RCVD_REPRESENTING_xxx ");
public static final TNEFProperty ID_FROM =
new TNEFProperty(0x8000, TYPE_STRING, "From", "PR_SENDER_ENTRYID");
public static final TNEFProperty ID_MAPIPROPERTIES =
new TNEFProperty(0x9003, TYPE_BYTE, "MapiProperties", null);
public static final TNEFProperty ID_MESSAGECLASS =
new TNEFProperty(0x8008, TYPE_WORD, "MessageClass", "PR_MESSAGE_CLASS ");
public static final TNEFProperty ID_MESSAGEID =
new TNEFProperty(0x8009, TYPE_STRING, "MessageId", "PR_SEARCH_KEY");
public static final TNEFProperty ID_MESSAGESTATUS =
new TNEFProperty(0x8007, TYPE_BYTE, "MessageStatus", "PR_MESSAGE_FLAGS");
public static final TNEFProperty ID_NULL =
new TNEFProperty(0x0000, -1, "Null", null);
public static final TNEFProperty ID_OEMCODEPAGE =
new TNEFProperty(0x9007, TYPE_BYTE, "OemCodepage", "AttOemCodepage");
public static final TNEFProperty ID_ORIGINALMESSAGECLASS =
new TNEFProperty(0x0006, TYPE_WORD, "OriginalMessageClass", "PR_ORIG_MESSAGE_CLASS");
public static final TNEFProperty ID_OWNER =
new TNEFProperty(0x0000, TYPE_BYTE, "Owner", "PR_RCVD_REPRESENTING_xxx");
public static final TNEFProperty ID_PARENTID =
new TNEFProperty(0x800A, TYPE_STRING, "ParentId", "PR_PARENT_KEY");
public static final TNEFProperty ID_PRIORITY =
new TNEFProperty(0x800D, TYPE_SHORT, "Priority", "PR_IMPORTANCE");
public static final TNEFProperty ID_RECIPIENTTABLE =
new TNEFProperty(0x9004, TYPE_BYTE, "RecipientTable", "PR_MESSAGE_RECIPIENTS");
public static final TNEFProperty ID_REQUESTRESPONSE =
new TNEFProperty(0x009, TYPE_SHORT, "RequestResponse", "PR_RESPONSE_REQUESTED");
public static final TNEFProperty ID_SENTFOR =
new TNEFProperty(0x0001, TYPE_BYTE, "SentFor", "PR_SENT_REPRESENTING_xxx");
public static final TNEFProperty ID_SUBJECT =
new TNEFProperty(0x8004, TYPE_STRING, "Subject", "PR_SUBJECT");
public static final TNEFProperty ID_TNEFVERSION =
new TNEFProperty(0x9006, TYPE_DWORD, "TnefVersion", "attTnefVersion");
public static final TNEFProperty ID_UNKNOWN =
new TNEFProperty(-1, -1, "Unknown", null);
/** The TNEF Property ID */
public final int id;
/** Usual Type */
public final int usualType;
/** Property Name */
public final String name;
/** Equivalent MAPI Property */
public final String mapiProperty;
private TNEFProperty(int id, int usualType, String name, String mapiProperty) {
this.id = id;
this.usualType = usualType;
this.name = name;
this.mapiProperty = mapiProperty;
// Store it for lookup
if(! properties.containsKey(id)) {
properties.put(id, new ArrayList<TNEFProperty>());
}
properties.get(id).add(this);
}
public static TNEFProperty getBest(int id, int type) {
List<TNEFProperty> attrs = properties.get(id);
if(attrs == null) {
return ID_UNKNOWN;
}
// If there's only one, it's easy
if(attrs.size() == 1) {
return attrs.get(0);
}
// Try by type
for(TNEFProperty attr : attrs) {
if(attr.usualType == type) return attr;
}
// Go for the first if we can't otherwise decide...
return attrs.get(0);
}
public String toString() {
StringBuffer str = new StringBuffer();
str.append(name);
str.append(" [");
str.append(id);
str.append("]");
if(mapiProperty != null) {
str.append(" (");
str.append(mapiProperty);
str.append(")");
}
return str.toString();
}
}

View File

@ -22,9 +22,10 @@ import java.io.IOException;
import java.io.InputStream; import java.io.InputStream;
import java.util.List; import java.util.List;
import org.apache.poi.hmef.Attribute;
import org.apache.poi.hmef.HMEFMessage; import org.apache.poi.hmef.HMEFMessage;
import org.apache.poi.hmef.MAPIAttribute; import org.apache.poi.hmef.attribute.TNEFAttribute;
import org.apache.poi.hmef.attribute.MAPIAttribute;
import org.apache.poi.hmef.attribute.TNEFProperty;
import org.apache.poi.util.HexDump; import org.apache.poi.util.HexDump;
import org.apache.poi.util.LittleEndian; import org.apache.poi.util.LittleEndian;
@ -82,17 +83,17 @@ public final class HMEFDumper {
while(true) { while(true) {
// Fetch the level // Fetch the level
level = inp.read(); level = inp.read();
if(level == Attribute.LEVEL_END_OF_FILE) { if(level == TNEFProperty.LEVEL_END_OF_FILE) {
break; break;
} }
// Build the attribute // Build the attribute
Attribute attr = new Attribute(inp); TNEFAttribute attr = new TNEFAttribute(inp);
// Print the attribute into // Print the attribute into
System.out.println( System.out.println(
"Level " + level + " : Type " + attr.getType() + "Level " + level + " : Type " + attr.getType() +
" : ID " + attr.getId().toString() " : ID " + attr.getProperty().toString()
); );
// Print the contents // Print the contents
@ -124,7 +125,7 @@ public final class HMEFDumper {
} }
System.out.println(); System.out.println();
if(attr.getId() == Attribute.ID_MAPIPROPERTIES) { if(attr.getProperty() == TNEFProperty.ID_MAPIPROPERTIES) {
List<MAPIAttribute> attrs = MAPIAttribute.create(attr); List<MAPIAttribute> attrs = MAPIAttribute.create(attr);
for(MAPIAttribute ma : attrs) { for(MAPIAttribute ma : attrs) {
System.out.println(indent + indent + ma); System.out.println(indent + indent + ma);

View File

@ -22,7 +22,8 @@ import java.io.ByteArrayInputStream;
import junit.framework.TestCase; import junit.framework.TestCase;
import org.apache.poi.POIDataSamples; import org.apache.poi.POIDataSamples;
import org.apache.poi.hmef.Attribute.AttributeID; import org.apache.poi.hmef.attribute.MAPIAttribute;
import org.apache.poi.hmef.attribute.MAPIRtfAttribute;
import org.apache.poi.hsmf.datatypes.MAPIProperty; import org.apache.poi.hsmf.datatypes.MAPIProperty;
import org.apache.poi.util.LittleEndian; import org.apache.poi.util.LittleEndian;
import org.apache.poi.util.StringUtil; import org.apache.poi.util.StringUtil;

View File

@ -20,6 +20,8 @@ package org.apache.poi.hmef;
import junit.framework.TestCase; import junit.framework.TestCase;
import org.apache.poi.POIDataSamples; import org.apache.poi.POIDataSamples;
import org.apache.poi.hmef.attribute.TNEFAttribute;
import org.apache.poi.hmef.attribute.TNEFProperty;
public final class TestHMEFMessage extends TestCase { public final class TestHMEFMessage extends TestCase {
private static final POIDataSamples _samples = POIDataSamples.getHMEFInstance(); private static final POIDataSamples _samples = POIDataSamples.getHMEFInstance();
@ -70,20 +72,20 @@ public final class TestHMEFMessage extends TestCase {
// Should have version, codepage, class and MAPI // Should have version, codepage, class and MAPI
assertEquals(4, msg.getMessageAttributes().size()); assertEquals(4, msg.getMessageAttributes().size());
assertNotNull(msg.getMessageAttribute(Attribute.ID_TNEFVERSION)); assertNotNull(msg.getMessageAttribute(TNEFProperty.ID_TNEFVERSION));
assertNotNull(msg.getMessageAttribute(Attribute.ID_OEMCODEPAGE)); assertNotNull(msg.getMessageAttribute(TNEFProperty.ID_OEMCODEPAGE));
assertNotNull(msg.getMessageAttribute(Attribute.ID_MESSAGECLASS)); assertNotNull(msg.getMessageAttribute(TNEFProperty.ID_MESSAGECLASS));
assertNotNull(msg.getMessageAttribute(Attribute.ID_MAPIPROPERTIES)); assertNotNull(msg.getMessageAttribute(TNEFProperty.ID_MAPIPROPERTIES));
// Check the order // Check the order
assertEquals(Attribute.ID_TNEFVERSION, msg.getMessageAttributes().get(0).getId()); assertEquals(TNEFProperty.ID_TNEFVERSION, msg.getMessageAttributes().get(0).getProperty());
assertEquals(Attribute.ID_OEMCODEPAGE, msg.getMessageAttributes().get(1).getId()); assertEquals(TNEFProperty.ID_OEMCODEPAGE, msg.getMessageAttributes().get(1).getProperty());
assertEquals(Attribute.ID_MESSAGECLASS, msg.getMessageAttributes().get(2).getId()); assertEquals(TNEFProperty.ID_MESSAGECLASS, msg.getMessageAttributes().get(2).getProperty());
assertEquals(Attribute.ID_MAPIPROPERTIES, msg.getMessageAttributes().get(3).getId()); assertEquals(TNEFProperty.ID_MAPIPROPERTIES, msg.getMessageAttributes().get(3).getProperty());
// Check some that aren't there // Check some that aren't there
assertNull(msg.getMessageAttribute(Attribute.ID_AIDOWNER)); assertNull(msg.getMessageAttribute(TNEFProperty.ID_AIDOWNER));
assertNull(msg.getMessageAttribute(Attribute.ID_ATTACHDATA)); assertNull(msg.getMessageAttribute(TNEFProperty.ID_ATTACHDATA));
// Now check the details of one or two // Now check the details of one or two
// TODO // TODO