import java.util.ArrayList;
import java.util.List;
-import org.apache.poi.hmef.attribute.TNEFAttribute;
import org.apache.poi.hmef.attribute.MAPIAttribute;
+import org.apache.poi.hmef.attribute.TNEFAttribute;
+import org.apache.poi.hmef.attribute.TNEFMAPIAttribute;
+import org.apache.poi.hmef.attribute.TNEFProperty;
+import org.apache.poi.hsmf.datatypes.MAPIProperty;
/**
private final List<TNEFAttribute> attributes = new ArrayList<TNEFAttribute>();
private final List<MAPIAttribute> mapiAttributes = new ArrayList<MAPIAttribute>();
-
-
protected void addAttribute(TNEFAttribute attr) {
attributes.add(attr);
+
+ if(attr instanceof TNEFMAPIAttribute) {
+ TNEFMAPIAttribute tnefMAPI = (TNEFMAPIAttribute)attr;
+ mapiAttributes.addAll( tnefMAPI.getMAPIAttributes() );
+ }
}
- protected void addAttribute(MAPIAttribute attr) {
- mapiAttributes.add(attr);
+ /**
+ * Return the attachment attribute with the given ID,
+ * or null if there isn't one.
+ */
+ public TNEFAttribute getMessageAttribute(TNEFProperty id) {
+ for(TNEFAttribute attr : attributes) {
+ if(attr.getProperty() == id) {
+ return attr;
+ }
+ }
+ return null;
}
+ /**
+ * Return the attachment MAPI Attribute with the given ID,
+ * or null if there isn't one.
+ */
+ public MAPIAttribute getMessageMAPIAttribute(MAPIProperty id) {
+ for(MAPIAttribute attr : mapiAttributes) {
+ if(attr.getProperty() == id) {
+ return attr;
+ }
+ }
+ return null;
+ }
+
+ /**
+ * Returns all HMEF/TNEF attributes of the attachment,
+ * such as filename, icon and contents
+ */
public List<TNEFAttribute> getAttributes() {
return attributes;
}
+ /**
+ * Returns all MAPI attributes of the attachment,
+ * such as extension, encoding, size and position
+ */
public List<MAPIAttribute> getMAPIAttributes() {
return mapiAttributes;
}
+
+ public String getFilename() {
+ TNEFAttribute attr = null;
+ return null;
+ }
}
import java.util.List;
import org.apache.poi.hmef.attribute.MAPIAttribute;
+import org.apache.poi.hmef.attribute.MAPIRtfAttribute;
+import org.apache.poi.hmef.attribute.MAPIStringAttribute;
import org.apache.poi.hmef.attribute.TNEFAttribute;
+import org.apache.poi.hmef.attribute.TNEFMAPIAttribute;
import org.apache.poi.hmef.attribute.TNEFProperty;
import org.apache.poi.hsmf.datatypes.MAPIProperty;
import org.apache.poi.util.LittleEndian;
// Now begin processing the contents
process(inp, 0);
-
- // Finally expand out the MAPI Attributes
- for(TNEFAttribute attr : messageAttributes) {
- if(attr.getProperty() == TNEFProperty.ID_MAPIPROPERTIES) {
- mapiAttributes.addAll(
- MAPIAttribute.create(attr)
- );
- }
- }
- for(Attachment attachment : attachments) {
- for(TNEFAttribute attr : attachment.getAttributes()) {
- if(attr.getProperty()== TNEFProperty.ID_MAPIPROPERTIES) {
- attachment.getMAPIAttributes().addAll(
- MAPIAttribute.create(attr)
- );
- }
- }
- }
}
private void process(InputStream inp, int lastLevel) throws IOException {
}
// Build the attribute
- TNEFAttribute attr = new TNEFAttribute(inp);
+ TNEFAttribute attr = TNEFAttribute.create(inp);
// Decide what to attach it to, based on the levels and IDs
if(level == TNEFProperty.LEVEL_MESSAGE) {
messageAttributes.add(attr);
+
+ if(attr instanceof TNEFMAPIAttribute) {
+ TNEFMAPIAttribute tnefMAPI = (TNEFMAPIAttribute)attr;
+ mapiAttributes.addAll( tnefMAPI.getMAPIAttributes() );
+ }
} else if(level == TNEFProperty.LEVEL_ATTACHMENT) {
// Previous attachment or a new one?
if(attachments.size() == 0 || attr.getProperty() == TNEFProperty.ID_ATTACHRENDERDATA) {
}
// Save the attribute for it
- attachments.get(attachments.size()-1).addAttribute(attr);
+ Attachment attach = attachments.get(attachments.size()-1);
+ attach.addAttribute(attr);
} else {
throw new IllegalStateException("Unhandled level " + level);
}
}
return null;
}
+
+ /**
+ * Return the string value of the mapi property, or null
+ * if it isn't set
+ */
+ private String getString(MAPIProperty id) {
+ MAPIAttribute attr = getMessageMAPIAttribute(id);
+ if(id == null) {
+ return null;
+ }
+ if(attr instanceof MAPIStringAttribute) {
+ return ((MAPIStringAttribute)attr).getDataString();
+ }
+ if(attr instanceof MAPIRtfAttribute) {
+ return ((MAPIRtfAttribute)attr).getDataString();
+ }
+
+ System.err.println("Warning, no string property found: " + attr.toString());
+ return null;
+ }
+
+ /**
+ * Returns the Message Subject, or null if the mapi property
+ * for this isn't set
+ */
+ public String getSubject() {
+ return getString(MAPIProperty.CONVERSATION_TOPIC);
+ }
+
+ /**
+ * Returns the Message Body, as RTF, or null if the mapi property
+ * for this isn't set
+ */
+ public String getBody() {
+ return getString(MAPIProperty.RTF_COMPRESSED);
+ }
}
* the list of MAPI Attributes contained within it
*/
public static List<MAPIAttribute> create(TNEFAttribute parent) throws IOException {
- if(parent.getProperty() != TNEFProperty.ID_MAPIPROPERTIES) {
+ if(parent.getProperty() == TNEFProperty.ID_MAPIPROPERTIES) {
+ // Regular MAPI Properties, normally on the message
+ }
+ else if(parent.getProperty() == TNEFProperty.ID_ATTACHMENT) {
+ // MAPI Properties for an attachment
+ }
+ else {
+ // Something else, oh dear...
throw new IllegalArgumentException(
"Can only create from a MAPIProperty attribute, " +
"instead received a " + parent.getProperty() + " one"
* Note - the types and IDs differ from standard Outlook/MAPI
* ones, so we can't just re-use the HSMF ones.
*/
-public final class TNEFAttribute {
+public class TNEFAttribute {
private final TNEFProperty property;
private final int type;
private final byte[] data;
private final int checksum;
/**
- * Constructs a single new attribute from
- * the contents of the stream
+ * Constructs a single new attribute from the id, type,
+ * and the contents of the stream
*/
- public TNEFAttribute(InputStream inp) throws IOException {
- int id = LittleEndian.readUShort(inp);
- this.type = LittleEndian.readUShort(inp);
+ protected TNEFAttribute(int id, int type, InputStream inp) throws IOException {
+ this.type = type;
int length = LittleEndian.readInt(inp);
property = TNEFProperty.getBest(id, type);
IOUtils.readFully(inp, data);
checksum = LittleEndian.readUShort(inp);
+ }
+
+ /**
+ * Creates a new TNEF Attribute by reading data from
+ * the stream within a {@link HMEFMessage}
+ */
+ public static TNEFAttribute create(InputStream inp) throws IOException {
+ int id = LittleEndian.readUShort(inp);
+ int type = LittleEndian.readUShort(inp);
- // TODO Handle the MapiProperties attribute in
- // a different way, as we need to recurse into it
+ // Create as appropriate
+ if(id == TNEFProperty.ID_MAPIPROPERTIES.id ||
+ id == TNEFProperty.ID_ATTACHMENT.id) {
+ return new TNEFMAPIAttribute(id, type, inp);
+ }
+ if(type == TNEFProperty.TYPE_STRING ||
+ type == TNEFProperty.TYPE_TEXT) {
+ return new TNEFStringAttribute(id, type, inp);
+ }
+ return new TNEFAttribute(id, type, inp);
}
public TNEFProperty getProperty() {
}
public String toString() {
- return "Attachment " + property.toString() + ", type=" + type +
+ return "Attribute " + property.toString() + ", type=" + type +
", data length=" + data.length;
}
}
--- /dev/null
+/* ====================================================================
+ Licensed to the Apache Software Foundation (ASF) under one or more
+ contributor license agreements. See the NOTICE file distributed with
+ this work for additional information regarding copyright ownership.
+ The ASF licenses this file to You under the Apache License, Version 2.0
+ (the "License"); you may not use this file except in compliance with
+ the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+==================================================================== */
+
+package org.apache.poi.hmef.attribute;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.List;
+
+import org.apache.poi.hmef.Attachment;
+import org.apache.poi.hmef.HMEFMessage;
+
+/**
+ * A TNEF Attribute holding MAPI Attributes, which applies to a
+ * {@link HMEFMessage} or one of its {@link Attachment}s.
+ */
+public final class TNEFMAPIAttribute extends TNEFAttribute {
+ private final List<MAPIAttribute> attributes;
+
+ /**
+ * Constructs a single new mapi containing attribute from the
+ * id, type, and the contents of the stream
+ */
+ protected TNEFMAPIAttribute(int id, int type, InputStream inp) throws IOException {
+ super(id, type, inp);
+
+ attributes = MAPIAttribute.create(this);
+ }
+
+ public List<MAPIAttribute> getMAPIAttributes() {
+ return attributes;
+ }
+
+ public String toString() {
+ return "Attribute " + getProperty().toString() + ", type=" + getType() +
+ ", " + attributes.size() + " MAPI Attributes";
+ }
+}
--- /dev/null
+/* ====================================================================
+ Licensed to the Apache Software Foundation (ASF) under one or more
+ contributor license agreements. See the NOTICE file distributed with
+ this work for additional information regarding copyright ownership.
+ The ASF licenses this file to You under the Apache License, Version 2.0
+ (the "License"); you may not use this file except in compliance with
+ the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+==================================================================== */
+
+package org.apache.poi.hmef.attribute;
+
+import java.io.IOException;
+import java.io.InputStream;
+
+import org.apache.poi.hmef.Attachment;
+import org.apache.poi.hmef.HMEFMessage;
+import org.apache.poi.util.StringUtil;
+
+/**
+ * An String attribute which applies to a {@link HMEFMessage}
+ * or one of its {@link Attachment}s.
+ */
+public final class TNEFStringAttribute extends TNEFAttribute {
+ /**
+ * Constructs a single new string attribute from the id, type,
+ * and the contents of the stream
+ */
+ protected TNEFStringAttribute(int id, int type, InputStream inp) throws IOException {
+ super(id, type, inp);
+ }
+
+ public String getString() {
+ byte[] data = getData();
+ // TODO Verify if these are the right way around
+ if(getType() == TNEFProperty.TYPE_TEXT) {
+ return StringUtil.getFromUnicodeLE(data);
+ }
+ return StringUtil.getFromCompressedUnicode(
+ data, 0, data.length
+ );
+ }
+
+ public String toString() {
+ return "Attribute " + getProperty().toString() + ", type=" + getType() +
+ ", data=" + getString();
+ }
+}
private void dump() throws IOException {
int level;
+ int attachments = 0;
while(true) {
// Fetch the level
}
// Build the attribute
- TNEFAttribute attr = new TNEFAttribute(inp);
+ TNEFAttribute attr = TNEFAttribute.create(inp);
+
+ // Is it a new attachment?
+ if(level == TNEFProperty.LEVEL_ATTACHMENT &&
+ attr.getProperty() == TNEFProperty.ID_ATTACHRENDERDATA) {
+ attachments++;
+ System.out.println();
+ System.out.println("Attachment # " + attachments);
+ System.out.println();
+ }
// Print the attribute into
System.out.println(
}
System.out.println();
- if(attr.getProperty() == TNEFProperty.ID_MAPIPROPERTIES) {
+ if(attr.getProperty() == TNEFProperty.ID_MAPIPROPERTIES ||
+ attr.getProperty() == TNEFProperty.ID_ATTACHMENT) {
List<MAPIAttribute> attrs = MAPIAttribute.create(attr);
for(MAPIAttribute ma : attrs) {
System.out.println(indent + indent + ma);
--- /dev/null
+/* ====================================================================
+ Licensed to the Apache Software Foundation (ASF) under one or more
+ contributor license agreements. See the NOTICE file distributed with
+ this work for additional information regarding copyright ownership.
+ The ASF licenses this file to You under the Apache License, Version 2.0
+ (the "License"); you may not use this file except in compliance with
+ the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+==================================================================== */
+
+package org.apache.poi.hmef;
+
+import junit.framework.TestCase;
+
+import org.apache.poi.POIDataSamples;
+
+public final class TestAttachments extends TestCase {
+ private static final POIDataSamples _samples = POIDataSamples.getHMEFInstance();
+
+ /**
+ * Check the file is as we expect
+ */
+ public void testCounts() throws Exception {
+ HMEFMessage msg = new HMEFMessage(
+ _samples.openResourceAsStream("quick-winmail.dat")
+ );
+
+ // Should have 5 attachments
+ assertEquals(5, msg.getAttachments().size());
+ }
+
+ /**
+ * Check some basic bits about the attachments
+ */
+ public void testBasicAttachments() throws Exception {
+ // TODO
+ }
+
+ /**
+ * Query the attachments in detail, and check we see
+ * the right values for key things
+ */
+ public void testAttachmentDetails() throws Exception {
+ // TODO
+ }
+
+ /**
+ * Ensure the attachment contents come back as they should do
+ */
+ public void testAttachmentContents() throws Exception {
+ // TODO
+ }
+}
import junit.framework.TestCase;
import org.apache.poi.POIDataSamples;
-import org.apache.poi.hmef.attribute.TNEFAttribute;
import org.apache.poi.hmef.attribute.TNEFProperty;
+import org.apache.poi.util.LittleEndian;
public final class TestHMEFMessage extends TestCase {
private static final POIDataSamples _samples = POIDataSamples.getHMEFInstance();
int mapiAttrCount = attach.getMAPIAttributes().size();
assertEquals(6, attrCount);
- // TODO
-// assertTrue("Should be 3-4 attributes, found " + mapiAttrCount, mapiAttrCount >= 20);
-// assertTrue("Should be 3-4 attributes, found " + mapiAttrCount, mapiAttrCount <= 25);
+ assertTrue("Should be 20-25 mapi attributes, found " + mapiAttrCount, mapiAttrCount >= 20);
+ assertTrue("Should be 20-25 mapi attributes, found " + mapiAttrCount, mapiAttrCount <= 25);
}
-
-
- // TODO
}
public void testBasicMessageAttributes() throws Exception {
assertNull(msg.getMessageAttribute(TNEFProperty.ID_ATTACHDATA));
// Now check the details of one or two
- // TODO
+ assertEquals(
+ 0x010000,
+ LittleEndian.getInt( msg.getMessageAttribute(TNEFProperty.ID_TNEFVERSION).getData() )
+ );
+ assertEquals(
+ "IPM.Microsoft Mail.Note\0",
+ new String(msg.getMessageAttribute(TNEFProperty.ID_MESSAGECLASS).getData(), "ASCII")
+ );
}
public void testBasicMessageMAPIAttributes() throws Exception {
- // TODO
- }
-
- public void testBasicAttachments() throws Exception {
- // TODO
- }
-
- public void testMessageAttributeDetails() throws Exception {
- // TODO
+ HMEFMessage msg = new HMEFMessage(
+ _samples.openResourceAsStream("quick-winmail.dat")
+ );
+
+ assertEquals("This is a test message", msg.getSubject());
+ assertEquals("{\\rtf1", msg.getBody().substring(0, 6));
}
}
--- /dev/null
+/* ====================================================================
+ Licensed to the Apache Software Foundation (ASF) under one or more
+ contributor license agreements. See the NOTICE file distributed with
+ this work for additional information regarding copyright ownership.
+ The ASF licenses this file to You under the Apache License, Version 2.0
+ (the "License"); you may not use this file except in compliance with
+ the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+==================================================================== */
+
+package org.apache.poi.hmef.attribute;
+
+import junit.framework.TestCase;
+
+import org.apache.poi.POIDataSamples;
+import org.apache.poi.hmef.HMEFMessage;
+
+public final class TestMAPIAttributes extends TestCase {
+ private static final POIDataSamples _samples = POIDataSamples.getHMEFInstance();
+
+ public void testOpen() throws Exception {
+ HMEFMessage msg = new HMEFMessage(
+ _samples.openResourceAsStream("quick-winmail.dat")
+ );
+
+ assertNotNull(msg);
+ }
+
+ // Test basics
+ // Test counts
+
+ // Check untyped
+ // Check typed
+ // Check common
+}
--- /dev/null
+/* ====================================================================
+ Licensed to the Apache Software Foundation (ASF) under one or more
+ contributor license agreements. See the NOTICE file distributed with
+ this work for additional information regarding copyright ownership.
+ The ASF licenses this file to You under the Apache License, Version 2.0
+ (the "License"); you may not use this file except in compliance with
+ the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+==================================================================== */
+
+package org.apache.poi.hmef.attribute;
+
+import junit.framework.TestCase;
+
+import org.apache.poi.POIDataSamples;
+import org.apache.poi.hmef.HMEFMessage;
+
+public final class TestTNEFAttributes extends TestCase {
+ private static final POIDataSamples _samples = POIDataSamples.getHMEFInstance();
+
+ public void testOpen() throws Exception {
+ HMEFMessage msg = new HMEFMessage(
+ _samples.openResourceAsStream("quick-winmail.dat")
+ );
+
+ assertNotNull(msg);
+ }
+
+ // Test counts
+
+ // Test basics
+
+ // Test string
+
+ // Test a bit of mapi
+}