From: Nick Burch Date: Wed, 22 Dec 2010 05:04:19 +0000 (+0000) Subject: Add initial low level code for HMEF, an implementation of TNEF (aka winmail.dat)... X-Git-Tag: REL_3_8_BETA1~81 X-Git-Url: https://source.dussan.org/?a=commitdiff_plain;h=b3f1888f6bcaff1166c778f5d85c505474fe8a77;p=poi.git Add initial low level code for HMEF, an implementation of TNEF (aka winmail.dat). Currently processes core attributes, but not embedded MAPI ones, and still needs to do more for extraction git-svn-id: https://svn.apache.org/repos/asf/poi/trunk@1051756 13f79535-47bb-0310-9956-ffa450edef68 --- diff --git a/src/documentation/content/xdocs/book.xml b/src/documentation/content/xdocs/book.xml index 604bb4c0f1..e581d00ea2 100644 --- a/src/documentation/content/xdocs/book.xml +++ b/src/documentation/content/xdocs/book.xml @@ -57,6 +57,7 @@ + diff --git a/src/documentation/content/xdocs/hdgf/book.xml b/src/documentation/content/xdocs/hdgf/book.xml index fb37a33a75..dcee53a814 100644 --- a/src/documentation/content/xdocs/hdgf/book.xml +++ b/src/documentation/content/xdocs/hdgf/book.xml @@ -20,7 +20,7 @@ diff --git a/src/documentation/content/xdocs/hmef/book.xml b/src/documentation/content/xdocs/hmef/book.xml new file mode 100644 index 0000000000..6e42baf15f --- /dev/null +++ b/src/documentation/content/xdocs/hmef/book.xml @@ -0,0 +1,34 @@ + + + + + + + + + + + + + + + diff --git a/src/documentation/content/xdocs/hmef/index.xml b/src/documentation/content/xdocs/hmef/index.xml new file mode 100644 index 0000000000..95ad884609 --- /dev/null +++ b/src/documentation/content/xdocs/hmef/index.xml @@ -0,0 +1,59 @@ + + + + + +
+ POI-HMEF - Java API To Access Microsoft Transport Neutral Encoding Files (TNEF) + Overview + + + +
+ + +
+ Overview + +

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.

+

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.

+

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.

+

To get a feel for the contents of a file, and to track down + where data of interest is stored, HMEF comes with + HMEFDumper + to print out the contents of the file.

+ + This code currently lives the + scratchpad area + 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. + + +
+ +
diff --git a/src/documentation/content/xdocs/index.xml b/src/documentation/content/xdocs/index.xml index 2db376ab2d..ae328c45f0 100644 --- a/src/documentation/content/xdocs/index.xml +++ b/src/documentation/content/xdocs/index.xml @@ -70,7 +70,10 @@ to this format in October 2007. We would welcome contributions.

- There are also projects for Visio (HDGF) and Publisher (HPBF). + There are also projects for + Visio (HDGF), + TNEF (HMEF), + and Publisher (HPBF).

As a general policy we collaborate as much as possible with other projects to diff --git a/src/documentation/content/xdocs/overview.xml b/src/documentation/content/xdocs/overview.xml index 4a21cf7d91..2dd51325d7 100644 --- a/src/documentation/content/xdocs/overview.xml +++ b/src/documentation/content/xdocs/overview.xml @@ -107,6 +107,17 @@ information.

+
HMEF for TNEF (winmail.dat) Outlook Attachments +

+ 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 the HMEF project page for more + information. +

+
HSMF for Outlook Messages

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 index 0000000000..4da794c633 --- /dev/null +++ b/src/scratchpad/src/org/apache/poi/hmef/Attachment.java @@ -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 attributes = new ArrayList(); + + protected void addAttribute(Attribute attr) { + attributes.add(attr); + } + + public List 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 index 0000000000..a03b36cfc7 --- /dev/null +++ b/src/scratchpad/src/org/apache/poi/hmef/Attribute.java @@ -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> attributes = new HashMap>(); + + 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()); + } + attributes.get(id).add(this); + } + public static AttributeID getBest(int id, int type) { + List 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 index 0000000000..c2bb38eefd --- /dev/null +++ b/src/scratchpad/src/org/apache/poi/hmef/CompressedRTF.java @@ -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 index 0000000000..d9eaac3937 --- /dev/null +++ b/src/scratchpad/src/org/apache/poi/hmef/HMEFMessage.java @@ -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 messageAttributes = new ArrayList(); + private List attachments = new ArrayList(); + + 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 index 0000000000..9a30fdddf9 --- /dev/null +++ b/src/scratchpad/src/org/apache/poi/hmef/dev/HMEFDumper.java @@ -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