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