]> source.dussan.org Git - poi.git/commitdiff
Add initial low level code for HMEF, an implementation of TNEF (aka winmail.dat)...
authorNick Burch <nick@apache.org>
Wed, 22 Dec 2010 05:04:19 +0000 (05:04 +0000)
committerNick Burch <nick@apache.org>
Wed, 22 Dec 2010 05:04:19 +0000 (05:04 +0000)
git-svn-id: https://svn.apache.org/repos/asf/poi/trunk@1051756 13f79535-47bb-0310-9956-ffa450edef68

src/documentation/content/xdocs/book.xml
src/documentation/content/xdocs/hdgf/book.xml
src/documentation/content/xdocs/hmef/book.xml [new file with mode: 0644]
src/documentation/content/xdocs/hmef/index.xml [new file with mode: 0644]
src/documentation/content/xdocs/index.xml
src/documentation/content/xdocs/overview.xml
src/scratchpad/src/org/apache/poi/hmef/Attachment.java [new file with mode: 0644]
src/scratchpad/src/org/apache/poi/hmef/Attribute.java [new file with mode: 0644]
src/scratchpad/src/org/apache/poi/hmef/CompressedRTF.java [new file with mode: 0644]
src/scratchpad/src/org/apache/poi/hmef/HMEFMessage.java [new file with mode: 0644]
src/scratchpad/src/org/apache/poi/hmef/dev/HMEFDumper.java [new file with mode: 0644]

index 604bb4c0f1a7c58e9a88c5f916d54296659e5aea..e581d00ea2d10eb7c066398b13dd13d58ba3af28 100644 (file)
@@ -57,6 +57,7 @@
         <menu-item label="OLE2 Document Props (HPSF)" href="hpsf/index.html"/>
         <menu-item label="Outlook (HSMF)" href="hsmf/index.html"/>
         <menu-item label="Visio (HDGF)" href="hdgf/index.html"/>
+        <menu-item label="TNEF (HMEF)" href="hmef/index.html"/>
         <menu-item label="Publisher (HPBF)" href="hpbf/index.html"/>
     </menu>
 
index fb37a33a7529170241223fe00a11d0a19bfbd17b..dcee53a814d40d41a1f5bc4c2e8e196ca31860e6 100644 (file)
@@ -20,7 +20,7 @@
 <!DOCTYPE book PUBLIC "-//APACHE//DTD Cocoon Documentation Book V1.0//EN" "../dtd/book-cocoon-v10.dtd">
 
 <book software="POI Project"
-    title="HGDF"
+    title="HDGF"
     copyright="@year@ POI Project">
 
     <menu label="Apache POI">
diff --git a/src/documentation/content/xdocs/hmef/book.xml b/src/documentation/content/xdocs/hmef/book.xml
new file mode 100644 (file)
index 0000000..6e42baf
--- /dev/null
@@ -0,0 +1,34 @@
+<?xml version="1.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.
+   ====================================================================
+-->
+<!DOCTYPE book PUBLIC "-//APACHE//DTD Cocoon Documentation Book V1.0//EN" "../dtd/book-cocoon-v10.dtd">
+
+<book software="POI Project"
+    title="HMEF"
+    copyright="@year@ POI Project">
+
+    <menu label="Apache POI">
+        <menu-item label="Top" href="../index.html"/>
+    </menu>
+
+    <menu label="HMEF">
+        <menu-item label="Overview" href="index.html"/>
+       </menu>
+       
+</book>
diff --git a/src/documentation/content/xdocs/hmef/index.xml b/src/documentation/content/xdocs/hmef/index.xml
new file mode 100644 (file)
index 0000000..95ad884
--- /dev/null
@@ -0,0 +1,59 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+   ====================================================================
+   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.
+   ====================================================================
+-->
+<!DOCTYPE document PUBLIC "-//APACHE//DTD Documentation V1.1//EN" "../dtd/document-v11.dtd">
+
+<document>
+    <header>
+        <title>POI-HMEF - Java API To Access Microsoft Transport Neutral Encoding Files (TNEF)</title>
+        <subtitle>Overview</subtitle>
+        <authors>
+            <person name="Nick Burch" email="nick at apache dot org"/>
+        </authors>
+    </header>
+
+    <body>
+       <section>
+         <title>Overview</title>
+
+         <p>HMEF is the POI Project's pure Java implementation of the 
+            TNEF (Transport Neurtral Encoding Format), aka winmail.dat,
+            which is used by Outlook and Exchange in some situations.</p>
+          <p>Currently, HMEF provides a low-level, read-only api for 
+            accessing core TNEF attributes. Embedded MAPI attributes
+            are not yet processed, and attachments are not properly
+            handled either.</p>
+          <p>HMEF is currently very much a work-in-progress, and we hope
+            to add a text extractor and attachment extractor in the not
+            too distant future.</p>
+                       <p>To get a feel for the contents of a file, and to track down
+                        where data of interest is stored, HMEF comes with
+                        <link href="http://svn.apache.org/repos/asf/poi/trunk/src/scratchpad/src/org/apache/poi/hmef/dev/">HMEFDumper</link>
+                        to print out the contents of the file.</p>
+        <note> 
+          This code currently lives the 
+          <link href="http://svn.apache.org/viewcvs.cgi/poi/trunk/src/scratchpad/">scratchpad area</link> 
+          of the POI SVN repository. 
+          Ensure that you have the scratchpad jar or the scratchpad 
+          build area in your classpath before experimenting with this code.
+        </note>
+
+       </section>
+    </body>
+</document>
index 2db376ab2d09457ebd201e65285b140139941c44..ae328c45f04ff496462de1c79594b30381ed63a0 100644 (file)
        to this format in October 2007. We would welcome contributions.
       </p>
       <p>
-       There are also projects for <link href="hdgf/index.html">Visio (HDGF)</link> and <link href="hpbf/index.html">Publisher (HPBF)</link>.
+        There are also projects for 
+        <link href="hdgf/index.html">Visio (HDGF)</link>,
+        <link href="hmef/index.html">TNEF (HMEF)</link>,
+        and <link href="hpbf/index.html">Publisher (HPBF)</link>.
       </p>
       <p>
          As a general policy we collaborate as much as possible with other projects to
index 4a21cf7d91afe7fe9ca9278b161ce67db57b73ab..2dd51325d758c996008854f5227b56dad9259228 100644 (file)
            information</link>.
        </p>
       </section>
+      <section><title>HMEF for TNEF (winmail.dat) Outlook Attachments</title>
+       <p>
+     HMEF is our port of the Microsoft TNEF (Transport Neutral Encoding
+     Format) file format to pure Java. TNEF is sometimes used by Outlook
+     for encoding the message, and will typically come through as
+     winmail.dat. HMEF currently only supports reading at a low level, but
+     we hope to add text and attachment extraction shortly. Please see <link
+           href="./hmef/index.html">the HMEF project page for more
+           information</link>.
+       </p>
+      </section>
       <section><title>HSMF for Outlook Messages</title>
        <p>
          HSMF is our port of the Microsoft Outlook message file format to pure
diff --git a/src/scratchpad/src/org/apache/poi/hmef/Attachment.java b/src/scratchpad/src/org/apache/poi/hmef/Attachment.java
new file mode 100644 (file)
index 0000000..4da794c
--- /dev/null
@@ -0,0 +1,37 @@
+/* ====================================================================
+   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.util.ArrayList;
+import java.util.List;
+
+
+/**
+ * An attachment within a {@link HMEFMessage}
+ */
+public final class Attachment {
+   private final List<Attribute> attributes = new ArrayList<Attribute>();
+   
+   protected void addAttribute(Attribute attr) {
+      attributes.add(attr);
+   }
+   
+   public List<Attribute> 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
new file mode 100644 (file)
index 0000000..a03b36c
--- /dev/null
@@ -0,0 +1,755 @@
+/* ====================================================================
+   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);
+   
+   // MAPI IDs taken from http://msdn.microsoft.com/en-us/library/microsoft.exchange.data.contenttypes.tnef.tnefpropertyid%28v=EXCHG.140%29.aspx
+   // TODO Merge this with the HSMF lists if appropriate
+/*
+    AbDefaultDir = 0x3d06,
+    AbDefaultPab = 0x3d07,
+    AbProviderId = 0x3615,
+    AbProviders = 0x3d01,
+    AbSearchPath = 0x3d05,
+    AbSearchPathUpdate = 0x3d11,
+    Access = 0xff4,
+    AccessLevel = 0xff7,
+    Account = 0x3a00,
+    AcknowledgementMode = 1,
+    Addrtype = 0x3002,
+    AlternateRecipient = 0x3a01,
+    AlternateRecipientAllowed = 2,
+    Anr = 0x360c,
+    Assistant = 0x3a30,
+    AssistantTelephoneNumber = 0x3a2e,
+    AssocContentCount = 0x3617,
+    AttachAdditionalInfo = 0x370f,
+    AttachContentBase = 0x3711,
+    AttachContentId = 0x3712,
+    AttachContentLocation = 0x3713,
+    AttachData = 0x3701,
+    AttachDisposition = 0x3716,
+    AttachEncoding = 0x3702,
+    AttachExtension = 0x3703,
+    AttachFilename = 0x3704,
+    AttachFlags = 0x3714,
+    AttachLongFilename = 0x3707,
+    AttachLongPathname = 0x370d,
+    AttachmentX400Parameters = 0x3700,
+    AttachMethod = 0x3705,
+    AttachMimeSequence = 0x3710,
+    AttachMimeTag = 0x370e,
+    AttachNetscapeMacInfo = 0x3715,
+    AttachNum = 0xe21,
+    AttachPathname = 0x3708,
+    AttachRendering = 0x3709,
+    AttachSize = 0xe20,
+    AttachTag = 0x370a,
+    AttachTransportName = 0x370c,
+    AuthorizingUsers = 3,
+    AutoForwardComment = 4,
+    AutoForwarded = 5,
+    AutoResponseSuppress = 0x3fdf,
+    BeeperTelephoneNumber = 0x3a21,
+    Birthday = 0x3a42,
+    Body = 0x1000,
+    BodyContentId = 0x1015,
+    BodyContentLocation = 0x1014,
+    BodyCrc = 0xe1c,
+    BodyHtml = 0x1013,
+    Business2TelephoneNumber = 0x3a1b,
+    BusinessAddressCity = 0x3a27,
+    BusinessAddressCountry = 0x3a26,
+    BusinessAddressPostalCode = 0x3a2a,
+    BusinessAddressStreet = 0x3a29,
+    BusinessFaxNumber = 0x3a24,
+    BusinessHomePage = 0x3a51,
+    CallbackTelephoneNumber = 0x3a02,
+    CarTelephoneNumber = 0x3a1e,
+    ChildrensNames = 0x3a58,
+    ClientSubmitTime = 0x39,
+    Comment = 0x3004,
+    CommonViewsEntryId = 0x35e6,
+    CompanyMainPhoneNumber = 0x3a57,
+    CompanyName = 0x3a16,
+    ComputerNetworkName = 0x3a49,
+    ContactAddrtypes = 0x3a54,
+    ContactDefaultAddressIndex = 0x3a55,
+    ContactEmailAddresses = 0x3a56,
+    ContactEntryIds = 0x3a53,
+    ContactVersion = 0x3a52,
+    ContainerClass = 0x3613,
+    ContainerContents = 0x360f,
+    ContainerFlags = 0x3600,
+    ContainerHierarchy = 0x360e,
+    ContainerModifyVersion = 0x3614,
+    ContentConfidentialityAlgorithmId = 6,
+    ContentCorrelator = 7,
+    ContentCount = 0x3602,
+    ContentIdentifier = 8,
+    ContentIntegrityCheck = 0xc00,
+    ContentLength = 9,
+    ContentReturnRequested = 10,
+    ContentsSortOrder = 0x360d,
+    ContentUnread = 0x3603,
+    ControlFlags = 0x3f00,
+    ControlId = 0x3f07,
+    ControlStructure = 0x3f01,
+    ControlType = 0x3f02,
+    ConversationIndex = 0x71,
+    ConversationKey = 11,
+    ConversationTopic = 0x70,
+    ConversionEits = 12,
+    ConversionProhibited = 0x3a03,
+    ConversionWithLossProhibited = 13,
+    ConvertedEits = 14,
+    Correlate = 0xe0c,
+    CorrelateMtsid = 0xe0d,
+    Country = 0x3a26,
+    CreateTemplates = 0x3604,
+    CreationTime = 0x3007,
+    CreationVersion = 0xe19,
+    CurrentVersion = 0xe00,
+    CustomerId = 0x3a4a,
+    DefaultProfile = 0x3d04,
+    DefaultStore = 0x3400,
+    DefaultViewEntryId = 0x3616,
+    DefCreateDl = 0x3611,
+    DefCreateMailuser = 0x3612,
+    DeferredDeliveryTime = 15,
+    Delegation = 0x7e,
+    DeleteAfterSubmit = 0xe01,
+    DeliverTime = 0x10,
+    DeliveryPoint = 0xc07,
+    Deltax = 0x3f03,
+    Deltay = 0x3f04,
+    DepartmentName = 0x3a18,
+    Depth = 0x3005,
+    DetailsTable = 0x3605,
+    DiscardReason = 0x11,
+    DiscloseRecipients = 0x3a04,
+    DisclosureOfRecipients = 0x12,
+    DiscreteValues = 0xe0e,
+    DiscVal = 0x4a,
+    DisplayBcc = 0xe02,
+    DisplayCc = 0xe03,
+    DisplayName = 0x3001,
+    DisplayNamePrefix = 0x3a45,
+    DisplayTo = 0xe04,
+    DisplayType = 0x3900,
+    DlExpansionHistory = 0x13,
+    DlExpansionProhibited = 20,
+    EmailAddress = 0x3003,
+    EndDate = 0x61,
+    EntryId = 0xfff,
+    ExpandBeginTime = 0x3618,
+    ExpandedBeginTime = 0x361a,
+    ExpandedEndTime = 0x361b,
+    ExpandEndTime = 0x3619,
+    ExpiryTime = 0x15,
+    ExplicitConversion = 0xc01,
+    FilteringHooks = 0x3d08,
+    FinderEntryId = 0x35e7,
+    FolderAssociatedContents = 0x3610,
+    FolderType = 0x3601,
+    FormCategory = 0x3304,
+    FormCategorySub = 0x3305,
+    FormClsid = 0x3302,
+    FormContactName = 0x3303,
+    FormDesignerGuid = 0x3309,
+    FormDesignerName = 0x3308,
+    FormHidden = 0x3307,
+    FormHostMap = 0x3306,
+    FormMessageBehavior = 0x330a,
+    FormVersion = 0x3301,
+    FtpSite = 0x3a4c,
+    Gender = 0x3a4d,
+    Generation = 0x3a05,
+    GivenName = 0x3a06,
+    GovernmentIdNumber = 0x3a07,
+    Hasattach = 0xe1b,
+    HeaderFolderEntryId = 0x3e0a,
+    Hobbies = 0x3a43,
+    Home2TelephoneNumber = 0x3a2f,
+    HomeAddressCity = 0x3a59,
+    HomeAddressCountry = 0x3a5a,
+    HomeAddressPostalCode = 0x3a5b,
+    HomeAddressPostOfficeBox = 0x3a5e,
+    HomeAddressStateOrProvince = 0x3a5c,
+    HomeAddressStreet = 0x3a5d,
+    HomeFaxNumber = 0x3a25,
+    HomeTelephoneNumber = 0x3a09,
+    Icon = 0xffd,
+    IdentityDisplay = 0x3e00,
+    IdentityEntryId = 0x3e01,
+    IdentitySearchKey = 0x3e05,
+    ImplicitConversionProhibited = 0x16,
+    Importance = 0x17,
+    IncompleteCopy = 0x35,
+    INetMailOverrideCharset = 0x5903,
+    INetMailOverrideFormat = 0x5902,
+    InitialDetailsPane = 0x3f08,
+    Initials = 0x3a0a,
+    InReplyToId = 0x1042,
+    InstanceKey = 0xff6,
+    InternetApproved = 0x1030,
+    InternetArticleNumber = 0xe23,
+    InternetControl = 0x1031,
+    InternetCPID = 0x3fde,
+    InternetDistribution = 0x1032,
+    InternetFollowupTo = 0x1033,
+    InternetLines = 0x1034,
+    InternetMessageId = 0x1035,
+    InternetNewsgroups = 0x1036,
+    InternetNntpPath = 0x1038,
+    InternetOrganization = 0x1037,
+    InternetPrecedence = 0x1041,
+    InternetReferences = 0x1039,
+    IpmId = 0x18,
+    IpmOutboxEntryId = 0x35e2,
+    IpmOutboxSearchKey = 0x3411,
+    IpmReturnRequested = 0xc02,
+    IpmSentmailEntryId = 0x35e4,
+    IpmSentmailSearchKey = 0x3413,
+    IpmSubtreeEntryId = 0x35e0,
+    IpmSubtreeSearchKey = 0x3410,
+    IpmWastebasketEntryId = 0x35e3,
+    IpmWastebasketSearchKey = 0x3412,
+    IsdnNumber = 0x3a2d,
+    Keyword = 0x3a0b,
+    Language = 0x3a0c,
+    Languages = 0x2f,
+    LastModificationTime = 0x3008,
+    LatestDeliveryTime = 0x19,
+    ListHelp = 0x1043,
+    ListSubscribe = 0x1044,
+    ListUnsubscribe = 0x1045,
+    Locality = 0x3a27,
+    LocallyDelivered = 0x6745,
+    Location = 0x3a0d,
+    LockBranchId = 0x3800,
+    LockDepth = 0x3808,
+    LockEnlistmentContext = 0x3804,
+    LockExpiryTime = 0x380a,
+    LockPersistent = 0x3807,
+    LockResourceDid = 0x3802,
+    LockResourceFid = 0x3801,
+    LockResourceMid = 0x3803,
+    LockScope = 0x3806,
+    LockTimeout = 0x3809,
+    LockType = 0x3805,
+    MailPermission = 0x3a0e,
+    ManagerName = 0x3a4e,
+    MappingSignature = 0xff8,
+    MdbProvider = 0x3414,
+    MessageAttachments = 0xe13,
+    MessageCcMe = 0x58,
+    MessageClass = 0x1a,
+    MessageCodepage = 0x3ffd,
+    MessageDeliveryId = 0x1b,
+    MessageDeliveryTime = 0xe06,
+    MessageDownloadTime = 0xe18,
+    MessageFlags = 0xe07,
+    MessageRecipients = 0xe12,
+    MessageRecipMe = 0x59,
+    MessageSecurityLabel = 30,
+    MessageSize = 0xe08,
+    MessageSubmissionId = 0x47,
+    MessageToken = 0xc03,
+    MessageToMe = 0x57,
+    MhsCommonName = 0x3a0f,
+    MiddleName = 0x3a44,
+    MiniIcon = 0xffc,
+    MobileTelephoneNumber = 0x3a1c,
+    ModifyVersion = 0xe1a,
+    MsgStatus = 0xe17,
+    NdrDiagCode = 0xc05,
+    NdrReasonCode = 0xc04,
+    NdrStatusCode = 0xc20,
+    NewsgroupName = 0xe24,
+    Nickname = 0x3a4f,
+    NntpXref = 0x1040,
+    NonReceiptNotificationRequested = 0xc06,
+    NonReceiptReason = 0x3e,
+    NormalizedSubject = 0xe1d,
+    NtSecurityDescriptor = 0xe27,
+    Null = 1,
+    ObjectType = 0xffe,
+    ObsoletedIpms = 0x1f,
+    Office2TelephoneNumber = 0x3a1b,
+    OfficeLocation = 0x3a19,
+    OfficeTelephoneNumber = 0x3a08,
+    OofReplyType = 0x4080,
+    OrganizationalIdNumber = 0x3a10,
+    OrigEntryId = 0x300f,
+    OriginalAuthorAddrtype = 0x79,
+    OriginalAuthorEmailAddress = 0x7a,
+    OriginalAuthorEntryId = 0x4c,
+    OriginalAuthorName = 0x4d,
+    OriginalAuthorSearchKey = 0x56,
+    OriginalDeliveryTime = 0x55,
+    OriginalDisplayBcc = 0x72,
+    OriginalDisplayCc = 0x73,
+    OriginalDisplayName = 0x3a13,
+    OriginalDisplayTo = 0x74,
+    OriginalEits = 0x21,
+    OriginalEntryId = 0x3a12,
+    OriginallyIntendedRecipAddrtype = 0x7b,
+    OriginallyIntendedRecipEmailAddress = 0x7c,
+    OriginallyIntendedRecipEntryId = 0x1012,
+    OriginallyIntendedRecipientName = 0x20,
+    OriginalSearchKey = 0x3a14,
+    OriginalSenderAddrtype = 0x66,
+    OriginalSenderEmailAddress = 0x67,
+    OriginalSenderEntryId = 0x5b,
+    OriginalSenderName = 90,
+    OriginalSenderSearchKey = 0x5c,
+    OriginalSensitivity = 0x2e,
+    OriginalSentRepresentingAddrtype = 0x68,
+    OriginalSentRepresentingEmailAddress = 0x69,
+    OriginalSentRepresentingEntryId = 0x5e,
+    OriginalSentRepresentingName = 0x5d,
+    OriginalSentRepresentingSearchKey = 0x5f,
+    OriginalSubject = 0x49,
+    OriginalSubmitTime = 0x4e,
+    OriginatingMtaCertificate = 0xe25,
+    OriginatorAndDlExpansionHistory = 0x1002,
+    OriginatorCertificate = 0x22,
+    OriginatorDeliveryReportRequested = 0x23,
+    OriginatorNonDeliveryReportRequested = 0xc08,
+    OriginatorRequestedAlternateRecipient = 0xc09,
+    OriginatorReturnAddress = 0x24,
+    OriginCheck = 0x27,
+    OrigMessageClass = 0x4b,
+    OtherAddressCity = 0x3a5f,
+    OtherAddressCountry = 0x3a60,
+    OtherAddressPostalCode = 0x3a61,
+    OtherAddressPostOfficeBox = 0x3a64,
+    OtherAddressStateOrProvince = 0x3a62,
+    OtherAddressStreet = 0x3a63,
+    OtherTelephoneNumber = 0x3a1f,
+    OwnerApptId = 0x62,
+    OwnStoreEntryId = 0x3e06,
+    PagerTelephoneNumber = 0x3a21,
+    ParentDisplay = 0xe05,
+    ParentEntryId = 0xe09,
+    ParentKey = 0x25,
+    PersonalHomePage = 0x3a50,
+    PhysicalDeliveryBureauFaxDelivery = 0xc0a,
+    PhysicalDeliveryMode = 0xc0b,
+    PhysicalDeliveryReportRequest = 0xc0c,
+    PhysicalForwardingAddress = 0xc0d,
+    PhysicalForwardingAddressRequested = 0xc0e,
+    PhysicalForwardingProhibited = 0xc0f,
+    PhysicalRenditionAttributes = 0xc10,
+    PostalAddress = 0x3a15,
+    PostalCode = 0x3a2a,
+    PostFolderEntries = 0x103b,
+    PostFolderNames = 0x103c,
+    PostOfficeBox = 0x3a2b,
+    PostReplyDenied = 0x103f,
+    PostReplyFolderEntries = 0x103d,
+    PostReplyFolderNames = 0x103e,
+    PreferredByName = 0x3a47,
+    Preprocess = 0xe22,
+    PrimaryCapability = 0x3904,
+    PrimaryFaxNumber = 0x3a23,
+    PrimaryTelephoneNumber = 0x3a1a,
+    Priority = 0x26,
+    Profession = 0x3a46,
+    ProfileName = 0x3d12,
+    ProofOfDelivery = 0xc11,
+    ProofOfDeliveryRequested = 0xc12,
+    ProofOfSubmission = 0xe26,
+    ProofOfSubmissionRequested = 40,
+    PropIdSecureMax = 0x67ff,
+    PropIdSecureMin = 0x67f0,
+    ProviderDisplay = 0x3006,
+    ProviderDllName = 0x300a,
+    ProviderOrdinal = 0x300d,
+    ProviderSubmitTime = 0x48,
+    ProviderUid = 0x300c,
+    Puid = 0x300e,
+    RadioTelephoneNumber = 0x3a1d,
+    RcvdRepresentingAddrtype = 0x77,
+    RcvdRepresentingEmailAddress = 120,
+    RcvdRepresentingEntryId = 0x43,
+    RcvdRepresentingName = 0x44,
+    RcvdRepresentingSearchKey = 0x52,
+    ReadReceiptEntryId = 70,
+    ReadReceiptRequested = 0x29,
+    ReadReceiptSearchKey = 0x53,
+    ReceiptTime = 0x2a,
+    ReceivedByAddrtype = 0x75,
+    ReceivedByEmailAddress = 0x76,
+    ReceivedByEntryId = 0x3f,
+    ReceivedByName = 0x40,
+    ReceivedBySearchKey = 0x51,
+    ReceiveFolderSettings = 0x3415,
+    RecipientCertificate = 0xc13,
+    RecipientNumberForAdvice = 0xc14,
+    RecipientReassignmentProhibited = 0x2b,
+    RecipientStatus = 0xe15,
+    RecipientType = 0xc15,
+    RecordKey = 0xff9,
+    RedirectionHistory = 0x2c,
+    ReferredByName = 0x3a47,
+    RegisteredMailType = 0xc16,
+    RelatedIpms = 0x2d,
+    RemoteProgress = 0x3e0b,
+    RemoteProgressText = 0x3e0c,
+    RemoteValidateOk = 0x3e0d,
+    RenderingPosition = 0x370b,
+    ReplyRecipientEntries = 0x4f,
+    ReplyRecipientNames = 80,
+    ReplyRequested = 0xc17,
+    ReplyTime = 0x30,
+    ReportEntryId = 0x45,
+    ReportingDlName = 0x1003,
+    ReportingMtaCertificate = 0x1004,
+    ReportName = 0x3a,
+    ReportSearchKey = 0x54,
+    ReportTag = 0x31,
+    ReportText = 0x1001,
+    ReportTime = 50,
+    RequestedDeliveryMethod = 0xc18,
+    ResourceFlags = 0x3009,
+    ResourceMethods = 0x3e02,
+    ResourcePath = 0x3e07,
+    ResourceType = 0x3e03,
+    ResponseRequested = 0x63,
+    Responsibility = 0xe0f,
+    ReturnedIpm = 0x33,
+    Rowid = 0x3000,
+    RowType = 0xff5,
+    RtfCompressed = 0x1009,
+    RtfInSync = 0xe1f,
+    RtfSyncBodyCount = 0x1007,
+    RtfSyncBodyCrc = 0x1006,
+    RtfSyncBodyTag = 0x1008,
+    RtfSyncPrefixCount = 0x1010,
+    RtfSyncTrailingCount = 0x1011,
+    Search = 0x3607,
+    SearchKey = 0x300b,
+    Security = 0x34,
+    Selectable = 0x3609,
+    SenderAddrtype = 0xc1e,
+    SenderEmailAddress = 0xc1f,
+    SenderEntryId = 0xc19,
+    SenderName = 0xc1a,
+    SenderSearchKey = 0xc1d,
+    SendInternetEncoding = 0x3a71,
+    SendRecallReport = 0x6803,
+    SendRichInfo = 0x3a40,
+    Sensitivity = 0x36,
+    SentmailEntryId = 0xe0a,
+    SentRepresentingAddrtype = 100,
+    SentRepresentingEmailAddress = 0x65,
+    SentRepresentingEntryId = 0x41,
+    SentRepresentingName = 0x42,
+    SentRepresentingSearchKey = 0x3b,
+    ServiceDeleteFiles = 0x3d10,
+    ServiceDllName = 0x3d0a,
+    ServiceEntryName = 0x3d0b,
+    ServiceExtraUids = 0x3d0d,
+    ServiceName = 0x3d09,
+    Services = 0x3d0e,
+    ServiceSupportFiles = 0x3d0f,
+    ServiceUid = 0x3d0c,
+    SevenBitDisplayName = 0x39ff,
+    SmtpAddress = 0x39fe,
+    SpoolerStatus = 0xe10,
+    SpouseName = 0x3a48,
+    StartDate = 0x60,
+    StateOrProvince = 0x3a28,
+    Status = 0x360b,
+    StatusCode = 0x3e04,
+    StatusString = 0x3e08,
+    StoreEntryId = 0xffb,
+    StoreProviders = 0x3d00,
+    StoreRecordKey = 0xffa,
+    StoreState = 0x340e,
+    StoreSupportMask = 0x340d,
+    StreetAddress = 0x3a29,
+    Subfolders = 0x360a,
+    Subject = 0x37,
+    SubjectIpm = 0x38,
+    SubjectPrefix = 0x3d,
+    SubmitFlags = 0xe14,
+    Supersedes = 0x103a,
+    SupplementaryInfo = 0xc1b,
+    Surname = 0x3a11,
+    TelexNumber = 0x3a2c,
+    Templateid = 0x3902,
+    Title = 0x3a17,
+    TnefCorrelationKey = 0x7f,
+    TransmitableDisplayName = 0x3a20,
+    TransportKey = 0xe16,
+    TransportMessageHeaders = 0x7d,
+    TransportProviders = 0x3d02,
+    TransportStatus = 0xe11,
+    TtytddPhoneNumber = 0x3a4b,
+    TypeOfMtsUser = 0xc1c,
+    UserCertificate = 0x3a22,
+    UserX509Certificate = 0x3a70,
+    ValidFolderMask = 0x35df,
+    ViewsEntryId = 0x35e5,
+    WeddingAnniversary = 0x3a41,
+    X400ContentType = 60,
+    X400DeferredDeliveryCancel = 0x3e09,
+    Xpos = 0x3f05,
+    Ypos = 0x3f06
+ */
+   
+   /**
+    * 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;
+   }
+}
diff --git a/src/scratchpad/src/org/apache/poi/hmef/CompressedRTF.java b/src/scratchpad/src/org/apache/poi/hmef/CompressedRTF.java
new file mode 100644 (file)
index 0000000..c2bb38e
--- /dev/null
@@ -0,0 +1,95 @@
+/* ====================================================================
+   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.io.OutputStream;
+import java.io.UnsupportedEncodingException;
+
+import org.apache.poi.util.IOUtils;
+import org.apache.poi.util.LZWDecompresser;
+import org.apache.poi.util.LittleEndian;
+
+
+/**
+ * Within a {@link HMEFMessage}, the content is often
+ *  stored in as RTF, but LZW compressed. This class
+ *  handles decompressing it for you.
+ */
+public final class CompressedRTF extends LZWDecompresser {
+   public static final byte[] COMPRESSED_SIGNATURE =
+      new byte[] { (byte)'L', (byte)'Z', (byte)'F', (byte)'U' };
+   public static final byte[] UNCOMPRESSED_SIGNATURE =
+      new byte[] { (byte)'M', (byte)'E', (byte)'L', (byte)'A' };
+   public static final int COMPRESSED_SIGNATURE_INT =
+      LittleEndian.getInt(COMPRESSED_SIGNATURE);
+   public static final int UNCOMPRESSED_SIGNATURE_INT =
+      LittleEndian.getInt(UNCOMPRESSED_SIGNATURE);
+   
+   // The 4096 byte LZW dictionary is pre-loaded with some common
+   //  RTF fragments. These come from RTFLIB32.LIB, which ships
+   //  with older versions of Visual Studio or the EDK
+   public static final String LZW_RTF_PRELOAD = 
+      "{\\rtf1\\ansi\\mac\\deff0\\deftab720{\\fonttbl;}{\\f0\\fnil \\froman \\fswiss " +
+      "\\fmodern \\fscript \\fdecor MS Sans SerifSymbolArialTimes New RomanCourier" +
+      "{\\colortbl\\red0\\green0\\blue0\n\r\\par \\pard\\plain\\f0\\fs20\\b\\i\\u\\tab\\tx";
+   
+   public CompressedRTF() {
+      super(true);
+   }
+
+   public void decompress(InputStream src, OutputStream res) throws IOException {
+      // Validate the header on the front of the RTF
+      int compressedSize = LittleEndian.readInt(src);
+      int uncompressedSize = LittleEndian.readInt(src);
+      int compressionType = LittleEndian.readInt(src);
+      int dataCRC = LittleEndian.readInt(src);
+      
+      // TODO - Handle CRC checking on the output side
+      
+      // Do we need to do anything?
+      if(compressionType == UNCOMPRESSED_SIGNATURE_INT) {
+         // Nope, nothing fancy to do
+         IOUtils.copy(src, res);
+      } else if(compressionType == COMPRESSED_SIGNATURE_INT) {
+         // We need to decompress it below
+      } else {
+         throw new IllegalArgumentException("Invalid compression signature " + compressionType);
+      }
+
+      // Have it processed
+      super.decompress(src, res);
+   }
+
+   @Override
+   protected int adjustDictionaryOffset(int offset) {
+      // TODO Do we need to change anything?
+      return 0;
+   }
+
+   @Override
+   protected void populateDictionary(byte[] dict) {
+      try {
+         byte[] preload = LZW_RTF_PRELOAD.getBytes("US-ASCII");
+         System.arraycopy(preload, 0, dict, 0, preload.length);
+      } catch(UnsupportedEncodingException e) {
+         throw new RuntimeException("Your JVM is broken as it doesn't support US ASCII");
+      }
+   }
+}
diff --git a/src/scratchpad/src/org/apache/poi/hmef/HMEFMessage.java b/src/scratchpad/src/org/apache/poi/hmef/HMEFMessage.java
new file mode 100644 (file)
index 0000000..d9eaac3
--- /dev/null
@@ -0,0 +1,87 @@
+/* ====================================================================
+   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.List;
+
+import org.apache.poi.util.LittleEndian;
+
+/**
+ * HMEF - Implementation of the Microsoft TNEF message
+ *  encoding format (aka winmail.dat)
+ * See:
+ *   http://support.microsoft.com/kb/241538
+ *   http://en.wikipedia.org/wiki/Transport_Neutral_Encapsulation_Format
+ *   http://search.cpan.org/dist/Convert-TNEF/
+ */
+public final class HMEFMessage {
+   public static final long HEADER_SIGNATURE = 0x223e9f78;
+   
+   private int fileId; 
+   private List<Attribute> messageAttributes = new ArrayList<Attribute>();
+   private List<Attachment> attachments = new ArrayList<Attachment>();
+   
+   public HMEFMessage(InputStream inp) throws IOException {
+      // Check the signature matches
+      long sig = LittleEndian.readInt(inp);
+      if(sig != HEADER_SIGNATURE) {
+         throw new IllegalArgumentException(
+               "TNEF signature not detected in file, " +
+               "expected " + HEADER_SIGNATURE + " but got " + sig
+         );
+      }
+      
+      // Read the File ID
+      fileId = LittleEndian.readUShort(inp);
+      
+      // Now begin processing the contents
+      process(inp, 0);
+   }
+   
+   private void process(InputStream inp, int lastLevel) throws IOException {
+      // Fetch the level
+      int level = inp.read();
+      if(level == Attribute.LEVEL_END_OF_FILE) {
+         return;
+      }
+    
+      // Build the attribute
+      Attribute attr = new Attribute(inp);
+      
+      // Decide what to attach it to, based on the levels and IDs
+      if(level == Attribute.LEVEL_MESSAGE) {
+         messageAttributes.add(attr);
+      } else if(level == Attribute.LEVEL_ATTACHMENT) {
+         // Previous attachment or a new one?
+         if(attachments.size() == 0 || attr.getId() == Attribute.ID_ATTACHRENDERDATA) {
+            attachments.add(new Attachment());
+         }
+         
+         // Save the attribute for it
+         attachments.get(attachments.size()-1).addAttribute(attr);
+      } else {
+         throw new IllegalStateException("Unhandled level " + level);
+      }
+      
+      // Handle the next one down
+      process(inp, level);
+   }
+}
diff --git a/src/scratchpad/src/org/apache/poi/hmef/dev/HMEFDumper.java b/src/scratchpad/src/org/apache/poi/hmef/dev/HMEFDumper.java
new file mode 100644 (file)
index 0000000..9a30fdd
--- /dev/null
@@ -0,0 +1,109 @@
+/* ====================================================================
+   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.dev;
+
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+
+import org.apache.poi.hmef.Attribute;
+import org.apache.poi.hmef.HMEFMessage;
+import org.apache.poi.util.HexDump;
+import org.apache.poi.util.LittleEndian;
+
+/**
+ * Developer focused raw dumper
+ */
+public final class HMEFDumper {
+   public static void main(String[] args) throws Exception {
+      if(args.length < 1) {
+         throw new IllegalArgumentException("Filename must be given");
+      }
+      
+      for(String filename : args) {
+         HMEFDumper dumper = new HMEFDumper(
+               new FileInputStream(filename)
+         );
+         dumper.dump();
+      }
+   }
+   
+   private InputStream inp;
+   public HMEFDumper(InputStream inp) throws IOException {
+      this.inp = inp;
+      
+      // Check the signature matches
+      long sig = LittleEndian.readInt(inp);
+      if(sig != HMEFMessage.HEADER_SIGNATURE) {
+         throw new IllegalArgumentException(
+               "TNEF signature not detected in file, " +
+               "expected " + HMEFMessage.HEADER_SIGNATURE + 
+               " but got " + sig
+         );
+      }
+      
+      // Skip over the File ID
+      LittleEndian.readUShort(inp);
+   }
+   
+   private void dump() throws IOException {
+      int level;
+      
+      while(true) {
+         // Fetch the level
+         level = inp.read();
+         if(level == Attribute.LEVEL_END_OF_FILE) {
+            break;
+         }
+       
+         // Build the attribute
+         Attribute attr = new Attribute(inp);
+         
+         // Print the attribute into
+         System.out.println(
+               "Level " + level + " : Type " + attr.getType() +
+               " : ID " + attr.getId().toString()
+         );
+         
+         // Print the contents
+         String indent = "  ";
+         System.out.println(indent + "Data of length " + attr.getData().length);
+         if(attr.getData().length > 0) {
+            int len = Math.min( attr.getData().length, 48 );
+            int loops = len/16;
+            if(loops == 0) loops = 1;
+            
+            for(int i=0; i<loops; i++) {
+               int thisLen = 16;
+               int offset = i*16;
+               if(i == loops-1) {
+                  thisLen = len - offset;
+               }
+
+               byte data[] = new byte[thisLen];
+               System.arraycopy(attr.getData(), offset, data, 0, thisLen);
+               
+               System.out.print(
+                     indent + HexDump.dump(data, 0, 0)
+               );
+            }
+         }
+         System.out.println();
+      }
+   }
+}