]> source.dussan.org Git - poi.git/commitdiff
Refactor the TNEF Attributes to better match the MAPI ones, and move both into their...
authorNick Burch <nick@apache.org>
Wed, 2 Mar 2011 15:25:35 +0000 (15:25 +0000)
committerNick Burch <nick@apache.org>
Wed, 2 Mar 2011 15:25:35 +0000 (15:25 +0000)
git-svn-id: https://svn.apache.org/repos/asf/poi/trunk@1076251 13f79535-47bb-0310-9956-ffa450edef68

14 files changed:
src/scratchpad/src/org/apache/poi/hmef/Attachment.java
src/scratchpad/src/org/apache/poi/hmef/Attribute.java [deleted file]
src/scratchpad/src/org/apache/poi/hmef/HMEFMessage.java
src/scratchpad/src/org/apache/poi/hmef/MAPIAttribute.java [deleted file]
src/scratchpad/src/org/apache/poi/hmef/MAPIRtfAttribute.java [deleted file]
src/scratchpad/src/org/apache/poi/hmef/MAPIStringAttribute.java [deleted file]
src/scratchpad/src/org/apache/poi/hmef/attribute/MAPIAttribute.java [new file with mode: 0644]
src/scratchpad/src/org/apache/poi/hmef/attribute/MAPIRtfAttribute.java [new file with mode: 0644]
src/scratchpad/src/org/apache/poi/hmef/attribute/MAPIStringAttribute.java [new file with mode: 0644]
src/scratchpad/src/org/apache/poi/hmef/attribute/TNEFAttribute.java [new file with mode: 0644]
src/scratchpad/src/org/apache/poi/hmef/attribute/TNEFProperty.java [new file with mode: 0644]
src/scratchpad/src/org/apache/poi/hmef/dev/HMEFDumper.java
src/scratchpad/testcases/org/apache/poi/hmef/TestCompressedRTF.java
src/scratchpad/testcases/org/apache/poi/hmef/TestHMEFMessage.java

index a7c4d71f168d2172852a8a9fee551084b1d7ff67..1592941c5d2d6ae91804ddac360327d6a5062d3c 100644 (file)
@@ -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;
    }
    
diff --git a/src/scratchpad/src/org/apache/poi/hmef/Attribute.java b/src/scratchpad/src/org/apache/poi/hmef/Attribute.java
deleted file mode 100644 (file)
index 8f7372e..0000000
+++ /dev/null
@@ -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; 
-   }
-}
index d74bcbefae004bc6e6977ba8d30cb30cd49c5edb..7b108b9df98affd5b2c56f87e9cfcbf902cf9c9a 100644 (file)
@@ -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;
          }
       }
diff --git a/src/scratchpad/src/org/apache/poi/hmef/MAPIAttribute.java b/src/scratchpad/src/org/apache/poi/hmef/MAPIAttribute.java
deleted file mode 100644 (file)
index d892221..0000000
+++ /dev/null
@@ -1,208 +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.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);
-      }
-   }
-}
diff --git a/src/scratchpad/src/org/apache/poi/hmef/MAPIRtfAttribute.java b/src/scratchpad/src/org/apache/poi/hmef/MAPIRtfAttribute.java
deleted file mode 100644 (file)
index 567c2b6..0000000
+++ /dev/null
@@ -1,49 +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.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;
-   }
-}
diff --git a/src/scratchpad/src/org/apache/poi/hmef/MAPIStringAttribute.java b/src/scratchpad/src/org/apache/poi/hmef/MAPIStringAttribute.java
deleted file mode 100644 (file)
index 6597cb9..0000000
+++ /dev/null
@@ -1,75 +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.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;
-   }
-}
diff --git a/src/scratchpad/src/org/apache/poi/hmef/attribute/MAPIAttribute.java b/src/scratchpad/src/org/apache/poi/hmef/attribute/MAPIAttribute.java
new file mode 100644 (file)
index 0000000..4b1e3f7
--- /dev/null
@@ -0,0 +1,210 @@
+/* ====================================================================
+   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);
+      }
+   }
+}
diff --git a/src/scratchpad/src/org/apache/poi/hmef/attribute/MAPIRtfAttribute.java b/src/scratchpad/src/org/apache/poi/hmef/attribute/MAPIRtfAttribute.java
new file mode 100644 (file)
index 0000000..2d96c2a
--- /dev/null
@@ -0,0 +1,52 @@
+/* ====================================================================
+   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;
+   }
+}
diff --git a/src/scratchpad/src/org/apache/poi/hmef/attribute/MAPIStringAttribute.java b/src/scratchpad/src/org/apache/poi/hmef/attribute/MAPIStringAttribute.java
new file mode 100644 (file)
index 0000000..e7550de
--- /dev/null
@@ -0,0 +1,66 @@
+/* ====================================================================
+   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;
+   }
+}
diff --git a/src/scratchpad/src/org/apache/poi/hmef/attribute/TNEFAttribute.java b/src/scratchpad/src/org/apache/poi/hmef/attribute/TNEFAttribute.java
new file mode 100644 (file)
index 0000000..2ba783d
--- /dev/null
@@ -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; 
+   }
+}
diff --git a/src/scratchpad/src/org/apache/poi/hmef/attribute/TNEFProperty.java b/src/scratchpad/src/org/apache/poi/hmef/attribute/TNEFProperty.java
new file mode 100644 (file)
index 0000000..de4d8a5
--- /dev/null
@@ -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();
+   }
+}
index a64a558a17bad6bdd0615ff5aa6a50544da2d3e3..26ecbf52395120dc4438cca95c20e0b350836852 100644 (file)
@@ -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);
index f9cc09c37a8dc662aee0372db50ef882ab33ffd3..e6a8d6ca3792e6450230b9aa00dd1e6ff1ee5d3c 100644 (file)
@@ -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;
index 33e1edbbe3dded0580285d4cd0021dd101dde821..b4542d4316667bff1c43bc9c66b7f214f052bb19 100644 (file)
@@ -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