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);
}
mapiAttributes.add(attr);
}
- public List<Attribute> getAttributes() {
+ public List<TNEFAttribute> getAttributes() {
return attributes;
}
+++ /dev/null
-/* ====================================================================
- 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;
- }
-}
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;
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>();
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)
);
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());
}
* 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;
}
* 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;
}
}
+++ /dev/null
-/* ====================================================================
- 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.ByteArrayInputStream;
-import java.io.IOException;
-import java.io.InputStream;
-import java.util.ArrayList;
-import java.util.List;
-
-import org.apache.poi.hsmf.datatypes.MAPIProperty;
-import org.apache.poi.hsmf.datatypes.Types;
-import org.apache.poi.util.HexDump;
-import org.apache.poi.util.IOUtils;
-import org.apache.poi.util.LittleEndian;
-import org.apache.poi.util.StringUtil;
-
-/**
- * A pure-MAPI attribute which applies to a {@link HMEFMessage}
- * or one of its {@link Attachment}s.
- */
-public class MAPIAttribute {
- private final MAPIProperty property;
- private final int type;
- private final byte[] data;
-
- /**
- * Constructs a single new attribute from
- * the contents of the stream
- */
- public MAPIAttribute(MAPIProperty property, int type, byte[] data) {
- this.property = property;
- this.type = type;
- this.data = data;
- }
-
- public MAPIProperty getProperty() {
- return property;
- }
-
- public int getType() {
- return type;
- }
-
- public byte[] getData() {
- return data;
- }
-
- public String toString() {
- String hex;
- if(data.length <= 16) {
- hex = HexDump.toHex(data);
- } else {
- byte[] d = new byte[16];
- System.arraycopy(data, 0, d, 0, 16);
- hex = HexDump.toHex(d);
- hex = hex.substring(0, hex.length()-1) + ", ....]";
- }
-
- return property.toString() + " " + hex;
- }
-
- /**
- * 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) {
- throw new IllegalArgumentException(
- "Can only create from a MAPIProperty attribute, " +
- "instead received a " + parent.getId() + " one"
- );
- }
- ByteArrayInputStream inp = new ByteArrayInputStream(parent.getData());
-
- // First up, get the number of attributes
- int count = LittleEndian.readInt(inp);
- List<MAPIAttribute> attrs = new ArrayList<MAPIAttribute>();
-
- // Now, read each one in in turn
- for(int i=0; i<count; i++) {
- int typeAndMV = LittleEndian.readUShort(inp);
- int id = LittleEndian.readUShort(inp);
-
- // Is it either Multi-Valued or Variable-Length?
- boolean isMV = false;
- boolean isVL = false;
- int type = typeAndMV;
- if( (typeAndMV & Types.MULTIVALUED_FLAG) > 0 ) {
- isMV = true;
- type -= Types.MULTIVALUED_FLAG;
- }
- if(type == Types.ASCII_STRING || type == Types.UNICODE_STRING ||
- type == Types.BINARY || type == Types.DIRECTORY) {
- isVL = true;
- }
-
- // If it's a named property, rather than a standard
- // MAPI property, grab the details of it
- MAPIProperty prop = MAPIProperty.get(id);
- if(id >= 0x8000 && id <= 0xFFFF) {
- byte[] guid = new byte[16];
- IOUtils.readFully(inp, guid);
- int mptype = LittleEndian.readInt(inp);
-
- // Get the name of it
- String name;
- if(mptype == 0) {
- // It's based on a normal one
- int mpid = LittleEndian.readInt(inp);
- MAPIProperty base = MAPIProperty.get(mpid);
- name = base.name;
- } else {
- // Custom name was stored
- int mplen = LittleEndian.readInt(inp);
- byte[] mpdata = new byte[mplen];
- IOUtils.readFully(inp, mpdata);
- name = StringUtil.getFromUnicodeLE(mpdata, 0, (mplen/2)-1);
- skipToBoundary(mplen, inp);
- }
-
- // Now create
- prop = MAPIProperty.createCustom(id, type, name);
- }
- if(prop == MAPIProperty.UNKNOWN) {
- prop = MAPIProperty.createCustom(id, type, "(unknown " + Integer.toHexString(id) + ")");
- }
-
- // Now read in the value(s)
- int values = 1;
- if(isMV || isVL) {
- values = LittleEndian.readInt(inp);
- }
- for(int j=0; j<values; j++) {
- int len = getLength(type, inp);
- byte[] data = new byte[len];
- IOUtils.readFully(inp, data);
- skipToBoundary(len, inp);
-
- // Create
- MAPIAttribute attr;
- if(type == Types.UNICODE_STRING || type == Types.ASCII_STRING) {
- attr = new MAPIStringAttribute(prop, type, data);
- } else if(id == MAPIProperty.RTF_COMPRESSED.id) {
- attr = new MAPIRtfAttribute(prop, type, data);
- } else {
- attr = new MAPIAttribute(prop, type, data);
- }
- attrs.add(attr);
- }
- }
-
- // All done
- return attrs;
- }
- private static int getLength(int type, InputStream inp) throws IOException {
- switch(type) {
- case Types.NULL:
- return 0;
- case Types.BOOLEAN:
- case Types.SHORT:
- return 2;
- case Types.LONG:
- case Types.FLOAT:
- case Types.ERROR:
- return 4;
- case Types.LONG_LONG:
- case Types.DOUBLE:
- case Types.APP_TIME:
- case Types.TIME:
- case Types.CURRENCY:
- return 8;
- case Types.CLS_ID:
- return 16;
- case Types.ASCII_STRING:
- case Types.UNICODE_STRING:
- case Types.DIRECTORY:
- case Types.BINARY:
- // Need to read the length, as it varies
- return LittleEndian.readInt(inp);
- default:
- throw new IllegalArgumentException("Unknown type " + type);
- }
- }
- private static void skipToBoundary(int length, InputStream inp) throws IOException {
- // Data is always padded out to a 4 byte boundary
- if(length % 4 != 0) {
- int skip = 4 - (length % 4);
- byte[] padding = new byte[skip];
- IOUtils.readFully(inp, padding);
- }
- }
-}
+++ /dev/null
-/* ====================================================================
- 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.ByteArrayInputStream;
-import java.io.IOException;
-
-import org.apache.poi.hsmf.datatypes.MAPIProperty;
-import org.apache.poi.util.StringUtil;
-
-/**
- * A pure-MAPI attribute holding RTF (compressed or not), which applies
- * to a {@link HMEFMessage} or one of its {@link Attachment}s.
- */
-public final class MAPIRtfAttribute extends MAPIAttribute {
- private final String data;
-
- public MAPIRtfAttribute(MAPIProperty property, int type, byte[] data) throws IOException {
- super(property, type, data);
-
- CompressedRTF rtf = new CompressedRTF();
- byte[] decomp = rtf.decompress(new ByteArrayInputStream(data));
-
- this.data = StringUtil.getFromCompressedUnicode(decomp, 0, decomp.length);
- }
-
- public String getDataString() {
- return data;
- }
-
- public String toString() {
- return getProperty().toString() + " " + data;
- }
-}
+++ /dev/null
-/* ====================================================================
- 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.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.hsmf.datatypes.MAPIProperty;
-import org.apache.poi.hsmf.datatypes.Types;
-
-/**
- * A pure-MAPI attribute holding a String, which applies
- * to a {@link HMEFMessage} or one of its {@link Attachment}s.
- */
-public final class MAPIStringAttribute extends MAPIAttribute {
- private static final String CODEPAGE = "CP1252";
- private final String data;
-
- public MAPIStringAttribute(MAPIProperty property, int type, byte[] data) {
- super(property, type, data);
-
- String tmpData = null;
- if(type == Types.ASCII_STRING) {
- try {
- tmpData = new String(data, CODEPAGE);
- } catch(UnsupportedEncodingException e) {
- throw new RuntimeException("JVM Broken - core encoding " + CODEPAGE + " missing");
- }
- } else if(type == Types.UNICODE_STRING) {
- tmpData = StringUtil.getFromUnicodeLE(data);
- } else {
- throw new IllegalArgumentException("Not a string type " + type);
- }
-
- // Strip off the null terminator if present
- if(tmpData.endsWith("\0")) {
- tmpData = tmpData.substring(0, tmpData.length()-1);
- }
- this.data = tmpData;
- }
-
- public String getDataString() {
- return data;
- }
-
- public String toString() {
- return getProperty().toString() + " " + data;
- }
-}
--- /dev/null
+/* ====================================================================
+ 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.ByteArrayInputStream;
+import java.io.IOException;
+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;
+import org.apache.poi.util.IOUtils;
+import org.apache.poi.util.LittleEndian;
+import org.apache.poi.util.StringUtil;
+
+/**
+ * A pure-MAPI attribute which applies to a {@link HMEFMessage}
+ * or one of its {@link Attachment}s.
+ */
+public class MAPIAttribute {
+ private final MAPIProperty property;
+ private final int type;
+ private final byte[] data;
+
+ /**
+ * Constructs a single new attribute from
+ * the contents of the stream
+ */
+ public MAPIAttribute(MAPIProperty property, int type, byte[] data) {
+ this.property = property;
+ this.type = type;
+ this.data = data;
+ }
+
+ public MAPIProperty getProperty() {
+ return property;
+ }
+
+ public int getType() {
+ return type;
+ }
+
+ public byte[] getData() {
+ return data;
+ }
+
+ public String toString() {
+ String hex;
+ if(data.length <= 16) {
+ hex = HexDump.toHex(data);
+ } else {
+ byte[] d = new byte[16];
+ System.arraycopy(data, 0, d, 0, 16);
+ hex = HexDump.toHex(d);
+ hex = hex.substring(0, hex.length()-1) + ", ....]";
+ }
+
+ return property.toString() + " " + hex;
+ }
+
+ /**
+ * Parses a MAPI Properties TNEF Attribute, and returns
+ * the list of MAPI Attributes contained within it
+ */
+ 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.getProperty() + " one"
+ );
+ }
+ ByteArrayInputStream inp = new ByteArrayInputStream(parent.getData());
+
+ // First up, get the number of attributes
+ int count = LittleEndian.readInt(inp);
+ List<MAPIAttribute> attrs = new ArrayList<MAPIAttribute>();
+
+ // Now, read each one in in turn
+ for(int i=0; i<count; i++) {
+ int typeAndMV = LittleEndian.readUShort(inp);
+ int id = LittleEndian.readUShort(inp);
+
+ // Is it either Multi-Valued or Variable-Length?
+ boolean isMV = false;
+ boolean isVL = false;
+ int type = typeAndMV;
+ if( (typeAndMV & Types.MULTIVALUED_FLAG) > 0 ) {
+ isMV = true;
+ type -= Types.MULTIVALUED_FLAG;
+ }
+ if(type == Types.ASCII_STRING || type == Types.UNICODE_STRING ||
+ type == Types.BINARY || type == Types.DIRECTORY) {
+ isVL = true;
+ }
+
+ // If it's a named property, rather than a standard
+ // MAPI property, grab the details of it
+ MAPIProperty prop = MAPIProperty.get(id);
+ if(id >= 0x8000 && id <= 0xFFFF) {
+ byte[] guid = new byte[16];
+ IOUtils.readFully(inp, guid);
+ int mptype = LittleEndian.readInt(inp);
+
+ // Get the name of it
+ String name;
+ if(mptype == 0) {
+ // It's based on a normal one
+ int mpid = LittleEndian.readInt(inp);
+ MAPIProperty base = MAPIProperty.get(mpid);
+ name = base.name;
+ } else {
+ // Custom name was stored
+ int mplen = LittleEndian.readInt(inp);
+ byte[] mpdata = new byte[mplen];
+ IOUtils.readFully(inp, mpdata);
+ name = StringUtil.getFromUnicodeLE(mpdata, 0, (mplen/2)-1);
+ skipToBoundary(mplen, inp);
+ }
+
+ // Now create
+ prop = MAPIProperty.createCustom(id, type, name);
+ }
+ if(prop == MAPIProperty.UNKNOWN) {
+ prop = MAPIProperty.createCustom(id, type, "(unknown " + Integer.toHexString(id) + ")");
+ }
+
+ // Now read in the value(s)
+ int values = 1;
+ if(isMV || isVL) {
+ values = LittleEndian.readInt(inp);
+ }
+ for(int j=0; j<values; j++) {
+ int len = getLength(type, inp);
+ byte[] data = new byte[len];
+ IOUtils.readFully(inp, data);
+ skipToBoundary(len, inp);
+
+ // Create
+ MAPIAttribute attr;
+ if(type == Types.UNICODE_STRING || type == Types.ASCII_STRING) {
+ attr = new MAPIStringAttribute(prop, type, data);
+ } else if(id == MAPIProperty.RTF_COMPRESSED.id) {
+ attr = new MAPIRtfAttribute(prop, type, data);
+ } else {
+ attr = new MAPIAttribute(prop, type, data);
+ }
+ attrs.add(attr);
+ }
+ }
+
+ // All done
+ return attrs;
+ }
+ private static int getLength(int type, InputStream inp) throws IOException {
+ switch(type) {
+ case Types.NULL:
+ return 0;
+ case Types.BOOLEAN:
+ case Types.SHORT:
+ return 2;
+ case Types.LONG:
+ case Types.FLOAT:
+ case Types.ERROR:
+ return 4;
+ case Types.LONG_LONG:
+ case Types.DOUBLE:
+ case Types.APP_TIME:
+ case Types.TIME:
+ case Types.CURRENCY:
+ return 8;
+ case Types.CLS_ID:
+ return 16;
+ case Types.ASCII_STRING:
+ case Types.UNICODE_STRING:
+ case Types.DIRECTORY:
+ case Types.BINARY:
+ // Need to read the length, as it varies
+ return LittleEndian.readInt(inp);
+ default:
+ throw new IllegalArgumentException("Unknown type " + type);
+ }
+ }
+ private static void skipToBoundary(int length, InputStream inp) throws IOException {
+ // Data is always padded out to a 4 byte boundary
+ if(length % 4 != 0) {
+ int skip = 4 - (length % 4);
+ byte[] padding = new byte[skip];
+ IOUtils.readFully(inp, padding);
+ }
+ }
+}
--- /dev/null
+/* ====================================================================
+ 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.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;
+
+/**
+ * A pure-MAPI attribute holding RTF (compressed or not), which applies
+ * to a {@link HMEFMessage} or one of its {@link Attachment}s.
+ */
+public final class MAPIRtfAttribute extends MAPIAttribute {
+ private final String data;
+
+ public MAPIRtfAttribute(MAPIProperty property, int type, byte[] data) throws IOException {
+ super(property, type, data);
+
+ CompressedRTF rtf = new CompressedRTF();
+ byte[] decomp = rtf.decompress(new ByteArrayInputStream(data));
+
+ this.data = StringUtil.getFromCompressedUnicode(decomp, 0, decomp.length);
+ }
+
+ public String getDataString() {
+ return data;
+ }
+
+ public String toString() {
+ return getProperty().toString() + " " + data;
+ }
+}
--- /dev/null
+/* ====================================================================
+ 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.UnsupportedEncodingException;
+
+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
+ * to a {@link HMEFMessage} or one of its {@link Attachment}s.
+ */
+public final class MAPIStringAttribute extends MAPIAttribute {
+ private static final String CODEPAGE = "CP1252";
+ private final String data;
+
+ public MAPIStringAttribute(MAPIProperty property, int type, byte[] data) {
+ super(property, type, data);
+
+ String tmpData = null;
+ if(type == Types.ASCII_STRING) {
+ try {
+ tmpData = new String(data, CODEPAGE);
+ } catch(UnsupportedEncodingException e) {
+ throw new RuntimeException("JVM Broken - core encoding " + CODEPAGE + " missing");
+ }
+ } else if(type == Types.UNICODE_STRING) {
+ tmpData = StringUtil.getFromUnicodeLE(data);
+ } else {
+ throw new IllegalArgumentException("Not a string type " + type);
+ }
+
+ // Strip off the null terminator if present
+ if(tmpData.endsWith("\0")) {
+ tmpData = tmpData.substring(0, tmpData.length()-1);
+ }
+ this.data = tmpData;
+ }
+
+ public String getDataString() {
+ return data;
+ }
+
+ public String toString() {
+ return getProperty().toString() + " " + data;
+ }
+}
--- /dev/null
+/* ====================================================================
+ 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;
+ }
+}
--- /dev/null
+/* ====================================================================
+ 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();
+ }
+}
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;
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
}
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);
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;
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();
// 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