git-svn-id: https://svn.apache.org/repos/asf/poi/trunk@1076251 13f79535-47bb-0310-9956-ffa450edef68tags/REL_3_8_BETA2
@@ -20,15 +20,22 @@ package org.apache.poi.hmef; | |||
import java.util.ArrayList; | |||
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 { | |||
private final List<Attribute> attributes = new ArrayList<Attribute>(); | |||
private final List<TNEFAttribute> attributes = new ArrayList<TNEFAttribute>(); | |||
private final List<MAPIAttribute> mapiAttributes = new ArrayList<MAPIAttribute>(); | |||
protected void addAttribute(Attribute attr) { | |||
protected void addAttribute(TNEFAttribute attr) { | |||
attributes.add(attr); | |||
} | |||
@@ -36,7 +43,7 @@ public final class Attachment { | |||
mapiAttributes.add(attr); | |||
} | |||
public List<Attribute> getAttributes() { | |||
public List<TNEFAttribute> getAttributes() { | |||
return attributes; | |||
} | |||
@@ -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; | |||
} | |||
} |
@@ -22,7 +22,9 @@ import java.io.InputStream; | |||
import java.util.ArrayList; | |||
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.util.LittleEndian; | |||
@@ -38,7 +40,7 @@ public final class HMEFMessage { | |||
public static final long HEADER_SIGNATURE = 0x223e9f78; | |||
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<Attachment> attachments = new ArrayList<Attachment>(); | |||
@@ -59,16 +61,16 @@ public final class HMEFMessage { | |||
process(inp, 0); | |||
// Finally expand out the MAPI Attributes | |||
for(Attribute attr : messageAttributes) { | |||
if(attr.getId() == Attribute.ID_MAPIPROPERTIES) { | |||
for(TNEFAttribute attr : messageAttributes) { | |||
if(attr.getProperty() == TNEFProperty.ID_MAPIPROPERTIES) { | |||
mapiAttributes.addAll( | |||
MAPIAttribute.create(attr) | |||
); | |||
} | |||
} | |||
for(Attachment attachment : attachments) { | |||
for(Attribute attr : attachment.getAttributes()) { | |||
if(attr.getId() == Attribute.ID_MAPIPROPERTIES) { | |||
for(TNEFAttribute attr : attachment.getAttributes()) { | |||
if(attr.getProperty()== TNEFProperty.ID_MAPIPROPERTIES) { | |||
attachment.getMAPIAttributes().addAll( | |||
MAPIAttribute.create(attr) | |||
); | |||
@@ -80,19 +82,19 @@ public final class HMEFMessage { | |||
private void process(InputStream inp, int lastLevel) throws IOException { | |||
// Fetch the level | |||
int level = inp.read(); | |||
if(level == Attribute.LEVEL_END_OF_FILE) { | |||
if(level == TNEFProperty.LEVEL_END_OF_FILE) { | |||
return; | |||
} | |||
// 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 | |||
if(level == Attribute.LEVEL_MESSAGE) { | |||
if(level == TNEFProperty.LEVEL_MESSAGE) { | |||
messageAttributes.add(attr); | |||
} else if(level == Attribute.LEVEL_ATTACHMENT) { | |||
} else if(level == TNEFProperty.LEVEL_ATTACHMENT) { | |||
// 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()); | |||
} | |||
@@ -111,7 +113,7 @@ public final class HMEFMessage { | |||
* Note - In a typical message, most of the interesting properties | |||
* are stored as {@link MAPIAttribute}s - see {@link #getMessageMAPIAttributes()} | |||
*/ | |||
public List<Attribute> getMessageAttributes() { | |||
public List<TNEFAttribute> getMessageAttributes() { | |||
return messageAttributes; | |||
} | |||
@@ -135,9 +137,9 @@ public final class HMEFMessage { | |||
* Return the message attribute with the given ID, | |||
* or null if there isn't one. | |||
*/ | |||
public Attribute getMessageAttribute(AttributeID id) { | |||
for(Attribute attr : messageAttributes) { | |||
if(attr.getId() == id) { | |||
public TNEFAttribute getMessageAttribute(TNEFProperty id) { | |||
for(TNEFAttribute attr : messageAttributes) { | |||
if(attr.getProperty() == id) { | |||
return attr; | |||
} | |||
} |
@@ -15,7 +15,7 @@ | |||
limitations under the License. | |||
==================================================================== */ | |||
package org.apache.poi.hmef; | |||
package org.apache.poi.hmef.attribute; | |||
import java.io.ByteArrayInputStream; | |||
import java.io.IOException; | |||
@@ -23,6 +23,8 @@ import java.io.InputStream; | |||
import java.util.ArrayList; | |||
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.Types; | |||
import org.apache.poi.util.HexDump; | |||
@@ -79,11 +81,11 @@ public class MAPIAttribute { | |||
* Parses a MAPI Properties TNEF Attribute, and returns | |||
* the list of MAPI Attributes contained within it | |||
*/ | |||
public static List<MAPIAttribute> create(Attribute parent) throws IOException { | |||
if(parent.getId() != Attribute.ID_MAPIPROPERTIES) { | |||
public static List<MAPIAttribute> create(TNEFAttribute parent) throws IOException { | |||
if(parent.getProperty() != TNEFProperty.ID_MAPIPROPERTIES) { | |||
throw new IllegalArgumentException( | |||
"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()); |
@@ -15,11 +15,14 @@ | |||
limitations under the License. | |||
==================================================================== */ | |||
package org.apache.poi.hmef; | |||
package org.apache.poi.hmef.attribute; | |||
import java.io.ByteArrayInputStream; | |||
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.util.StringUtil; | |||
@@ -15,24 +15,15 @@ | |||
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.util.ArrayList; | |||
import java.util.HashMap; | |||
import java.util.List; | |||
import java.util.Map; | |||
import org.apache.poi.util.HexDump; | |||
import org.apache.poi.util.IOUtils; | |||
import org.apache.poi.util.LittleEndian; | |||
import org.apache.poi.util.StringUtil; | |||
import org.apache.poi.hmef.Attribute.AttributeID; | |||
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.Types; | |||
import org.apache.poi.util.StringUtil; | |||
/** | |||
* A pure-MAPI attribute holding a String, which applies |
@@ -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; | |||
} | |||
} |
@@ -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(); | |||
} | |||
} |
@@ -22,9 +22,10 @@ import java.io.IOException; | |||
import java.io.InputStream; | |||
import java.util.List; | |||
import org.apache.poi.hmef.Attribute; | |||
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.LittleEndian; | |||
@@ -82,17 +83,17 @@ public final class HMEFDumper { | |||
while(true) { | |||
// Fetch the level | |||
level = inp.read(); | |||
if(level == Attribute.LEVEL_END_OF_FILE) { | |||
if(level == TNEFProperty.LEVEL_END_OF_FILE) { | |||
break; | |||
} | |||
// Build the attribute | |||
Attribute attr = new Attribute(inp); | |||
TNEFAttribute attr = new TNEFAttribute(inp); | |||
// Print the attribute into | |||
System.out.println( | |||
"Level " + level + " : Type " + attr.getType() + | |||
" : ID " + attr.getId().toString() | |||
" : ID " + attr.getProperty().toString() | |||
); | |||
// Print the contents | |||
@@ -124,7 +125,7 @@ public final class HMEFDumper { | |||
} | |||
System.out.println(); | |||
if(attr.getId() == Attribute.ID_MAPIPROPERTIES) { | |||
if(attr.getProperty() == TNEFProperty.ID_MAPIPROPERTIES) { | |||
List<MAPIAttribute> attrs = MAPIAttribute.create(attr); | |||
for(MAPIAttribute ma : attrs) { | |||
System.out.println(indent + indent + ma); |
@@ -22,7 +22,8 @@ import java.io.ByteArrayInputStream; | |||
import junit.framework.TestCase; | |||
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.util.LittleEndian; | |||
import org.apache.poi.util.StringUtil; |
@@ -20,6 +20,8 @@ package org.apache.poi.hmef; | |||
import junit.framework.TestCase; | |||
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 { | |||
private static final POIDataSamples _samples = POIDataSamples.getHMEFInstance(); | |||
@@ -70,20 +72,20 @@ public final class TestHMEFMessage extends TestCase { | |||
// Should have version, codepage, class and MAPI | |||
assertEquals(4, msg.getMessageAttributes().size()); | |||
assertNotNull(msg.getMessageAttribute(Attribute.ID_TNEFVERSION)); | |||
assertNotNull(msg.getMessageAttribute(Attribute.ID_OEMCODEPAGE)); | |||
assertNotNull(msg.getMessageAttribute(Attribute.ID_MESSAGECLASS)); | |||
assertNotNull(msg.getMessageAttribute(Attribute.ID_MAPIPROPERTIES)); | |||
assertNotNull(msg.getMessageAttribute(TNEFProperty.ID_TNEFVERSION)); | |||
assertNotNull(msg.getMessageAttribute(TNEFProperty.ID_OEMCODEPAGE)); | |||
assertNotNull(msg.getMessageAttribute(TNEFProperty.ID_MESSAGECLASS)); | |||
assertNotNull(msg.getMessageAttribute(TNEFProperty.ID_MAPIPROPERTIES)); | |||
// Check the order | |||
assertEquals(Attribute.ID_TNEFVERSION, msg.getMessageAttributes().get(0).getId()); | |||
assertEquals(Attribute.ID_OEMCODEPAGE, msg.getMessageAttributes().get(1).getId()); | |||
assertEquals(Attribute.ID_MESSAGECLASS, msg.getMessageAttributes().get(2).getId()); | |||
assertEquals(Attribute.ID_MAPIPROPERTIES, msg.getMessageAttributes().get(3).getId()); | |||
assertEquals(TNEFProperty.ID_TNEFVERSION, msg.getMessageAttributes().get(0).getProperty()); | |||
assertEquals(TNEFProperty.ID_OEMCODEPAGE, msg.getMessageAttributes().get(1).getProperty()); | |||
assertEquals(TNEFProperty.ID_MESSAGECLASS, msg.getMessageAttributes().get(2).getProperty()); | |||
assertEquals(TNEFProperty.ID_MAPIPROPERTIES, msg.getMessageAttributes().get(3).getProperty()); | |||
// Check some that aren't there | |||
assertNull(msg.getMessageAttribute(Attribute.ID_AIDOWNER)); | |||
assertNull(msg.getMessageAttribute(Attribute.ID_ATTACHDATA)); | |||
assertNull(msg.getMessageAttribute(TNEFProperty.ID_AIDOWNER)); | |||
assertNull(msg.getMessageAttribute(TNEFProperty.ID_ATTACHDATA)); | |||
// Now check the details of one or two | |||
// TODO |