diff --git a/src/scratchpad/src/org/apache/poi/hmef/HMEFMessage.java b/src/scratchpad/src/org/apache/poi/hmef/HMEFMessage.java index b53543680..cf8853436 100644 --- a/src/scratchpad/src/org/apache/poi/hmef/HMEFMessage.java +++ b/src/scratchpad/src/org/apache/poi/hmef/HMEFMessage.java @@ -39,7 +39,7 @@ import org.apache.poi.util.LittleEndian; * http://search.cpan.org/dist/Convert-TNEF/ */ public final class HMEFMessage { - public static final long HEADER_SIGNATURE = 0x223e9f78; + public static final int HEADER_SIGNATURE = 0x223e9f78; private int fileId; private List messageAttributes = new ArrayList(); @@ -48,7 +48,7 @@ public final class HMEFMessage { public HMEFMessage(InputStream inp) throws IOException { // Check the signature matches - long sig = LittleEndian.readInt(inp); + int sig = LittleEndian.readInt(inp); if(sig != HEADER_SIGNATURE) { throw new IllegalArgumentException( "TNEF signature not detected in file, " + @@ -60,42 +60,59 @@ public final class HMEFMessage { fileId = LittleEndian.readUShort(inp); // Now begin processing the contents - process(inp, 0); + process(inp); } - private void process(InputStream inp, int lastLevel) throws IOException { - // Fetch the level - int level = inp.read(); - if(level == TNEFProperty.LEVEL_END_OF_FILE) { - return; - } - + private void process(InputStream inp) throws IOException { + int level; + do { + // Fetch the level + level = inp.read(); + + // Decide what to attach it to, based on the levels and IDs + switch (level) { + case TNEFProperty.LEVEL_MESSAGE: + processMessage(inp); + break; + case TNEFProperty.LEVEL_ATTACHMENT: + processAttachment(inp); + break; + // ignore trailing newline + case '\r': + case '\n': + case TNEFProperty.LEVEL_END_OF_FILE: + break; + default: + throw new IllegalStateException("Unhandled level " + level); + } + } while (level != TNEFProperty.LEVEL_END_OF_FILE); + } + + void processMessage(InputStream inp) throws IOException { // Build the attribute 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) { - attachments.add(new Attachment()); - } - - // Save the attribute for it - Attachment attach = attachments.get(attachments.size()-1); - attach.addAttribute(attr); - } else { - throw new IllegalStateException("Unhandled level " + level); + + messageAttributes.add(attr); + + if (attr instanceof TNEFMAPIAttribute) { + TNEFMAPIAttribute tnefMAPI = (TNEFMAPIAttribute) attr; + mapiAttributes.addAll(tnefMAPI.getMAPIAttributes()); } - - // Handle the next one down - process(inp, level); + } + + void processAttachment(InputStream inp) throws IOException { + // Build the attribute + TNEFAttribute attr = TNEFAttribute.create(inp); + + // Previous attachment or a new one? + if (attachments.isEmpty() + || attr.getProperty() == TNEFProperty.ID_ATTACHRENDERDATA) { + attachments.add(new Attachment()); + } + + // Save the attribute for it + Attachment attach = attachments.get(attachments.size() - 1); + attach.addAttribute(attr); } /** diff --git a/src/scratchpad/src/org/apache/poi/hmef/dev/HMEFDumper.java b/src/scratchpad/src/org/apache/poi/hmef/dev/HMEFDumper.java index 7addb6b84..3d7bcfefe 100644 --- a/src/scratchpad/src/org/apache/poi/hmef/dev/HMEFDumper.java +++ b/src/scratchpad/src/org/apache/poi/hmef/dev/HMEFDumper.java @@ -23,8 +23,8 @@ import java.io.InputStream; import java.util.List; import org.apache.poi.hmef.HMEFMessage; -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.TNEFDateAttribute; import org.apache.poi.hmef.attribute.TNEFProperty; import org.apache.poi.hmef.attribute.TNEFStringAttribute; @@ -62,7 +62,7 @@ public final class HMEFDumper { this.inp = inp; // Check the signature matches - long sig = LittleEndian.readInt(inp); + int sig = LittleEndian.readInt(inp); if(sig != HMEFMessage.HEADER_SIGNATURE) { throw new IllegalArgumentException( "TNEF signature not detected in file, " + diff --git a/src/scratchpad/testcases/org/apache/poi/hmef/TestBugs.java b/src/scratchpad/testcases/org/apache/poi/hmef/TestBugs.java new file mode 100644 index 000000000..79529a736 --- /dev/null +++ b/src/scratchpad/testcases/org/apache/poi/hmef/TestBugs.java @@ -0,0 +1,48 @@ +package org.apache.poi.hmef; + +import junit.framework.TestCase; + +import org.apache.poi.POIDataSamples; +import org.apache.poi.hmef.attribute.MAPIAttribute; +import org.apache.poi.hmef.attribute.TNEFAttribute; +import org.apache.poi.hmef.attribute.TNEFProperty; +import org.apache.poi.hsmf.datatypes.MAPIProperty; +import org.apache.poi.util.LittleEndian; + +public class TestBugs extends TestCase { + public void test52400ReadSimpleTNEF() throws Exception { + POIDataSamples samples = POIDataSamples.getHMEFInstance(); + String testFile = "bug52400-winmail-simple.dat"; + HMEFMessage tnefDat = new HMEFMessage(samples.openResourceAsStream(testFile)); + MAPIAttribute bodyHtml = tnefDat.getMessageMAPIAttribute(MAPIProperty.BODY_HTML); + String bodyStr = new String(bodyHtml.getData(), getEncoding(tnefDat)); + assertTrue(bodyStr.contains("This is the message body.")); + } + + public void test52400ReadAttachedTNEF() throws Exception { + POIDataSamples samples = POIDataSamples.getHMEFInstance(); + String testFile = "bug52400-winmail-with-attachments.dat"; + HMEFMessage tnefDat = new HMEFMessage(samples.openResourceAsStream(testFile)); + MAPIAttribute bodyHtml = tnefDat.getMessageMAPIAttribute(MAPIProperty.BODY_HTML); + String bodyStr = new String(bodyHtml.getData(), getEncoding(tnefDat)); + assertTrue(bodyStr.contains("There are also two attachments.")); + assertEquals(2, tnefDat.getAttachments().size()); + } + + private String getEncoding(HMEFMessage tnefDat) { + TNEFAttribute oemCP = tnefDat.getMessageAttribute(TNEFProperty.ID_OEMCODEPAGE); + MAPIAttribute cpId = tnefDat.getMessageMAPIAttribute(MAPIProperty.INTERNET_CPID); + int codePage = 1252; + if (oemCP != null) { + codePage = LittleEndian.getInt(oemCP.getData()); + } else if (cpId != null) { + codePage = LittleEndian.getInt(cpId.getData()); + } + switch (codePage) { + // see http://en.wikipedia.org/wiki/Code_page for more + case 1252: return "Windows-1252"; + case 20127: return "US-ASCII"; + default: return "cp"+codePage; + } + } +} diff --git a/src/scratchpad/testcases/org/apache/poi/hmef/TestHMEFMessage.java b/src/scratchpad/testcases/org/apache/poi/hmef/TestHMEFMessage.java index ee14efc1d..d9344b1bd 100644 --- a/src/scratchpad/testcases/org/apache/poi/hmef/TestHMEFMessage.java +++ b/src/scratchpad/testcases/org/apache/poi/hmef/TestHMEFMessage.java @@ -17,6 +17,10 @@ package org.apache.poi.hmef; +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.InputStream; + import org.apache.poi.hmef.attribute.MAPIRtfAttribute; import org.apache.poi.hmef.attribute.TNEFProperty; import org.apache.poi.hsmf.datatypes.MAPIProperty; @@ -119,5 +123,80 @@ public final class TestHMEFMessage extends HMEFTest { // It's all low bytes byte[] contentsBytes = contents.getBytes("ASCII"); assertContents("message.rtf", contentsBytes); + + // try to get a message id that does not exist + assertNull(msg.getMessageMAPIAttribute(MAPIProperty.AB_DEFAULT_DIR)); + } + + public void testMessageSample1() throws Exception { + HMEFMessage msg = new HMEFMessage( + _samples.openResourceAsStream("winmail-sample1.dat")); + + // Firstly by byte + MAPIRtfAttribute rtf = (MAPIRtfAttribute) msg + .getMessageMAPIAttribute(MAPIProperty.RTF_COMPRESSED); + // assertContents("message.rtf", rtf.getData()); + assertNotNull(rtf); + + // Then by String + String contents = msg.getBody(); + //System.out.println(contents); + // It's all low bytes + byte[] contentsBytes = contents.getBytes("ASCII"); + // assertContents("message.rtf", contentsBytes); + assertNotNull(contentsBytes); + + assertNotNull(msg.getSubject()); + assertNotNull(msg.getBody()); + } + + public void testInvalidMessage() throws Exception { + InputStream str = new ByteArrayInputStream(new byte[] {0, 0, 0, 0}); + try { + assertNotNull(new HMEFMessage(str)); + fail("Should catch an exception here"); + } catch (IllegalArgumentException e) { + assertTrue(e.getMessage().contains("TNEF signature not detected in file, expected 574529400 but got 0")); + } + } + + + public void testNoData() throws Exception { + ByteArrayOutputStream out = new ByteArrayOutputStream(); + + // Header + LittleEndian.putInt(HMEFMessage.HEADER_SIGNATURE, out); + + // field + LittleEndian.putUShort(0, out); + + byte[] bytes = out.toByteArray(); + InputStream str = new ByteArrayInputStream(bytes); + HMEFMessage msg = new HMEFMessage(str); + assertNull(msg.getSubject()); + assertNull(msg.getBody()); + } + + public void testInvalidLevel() throws Exception { + ByteArrayOutputStream out = new ByteArrayOutputStream(); + + // Header + LittleEndian.putInt(HMEFMessage.HEADER_SIGNATURE, out); + + // field + LittleEndian.putUShort(0, out); + + // invalid level + LittleEndian.putUShort(90, out); + + byte[] bytes = out.toByteArray(); + InputStream str = new ByteArrayInputStream(bytes); + try { + assertNotNull(new HMEFMessage(str)); + fail("Should catch an exception here"); + } catch (IllegalStateException e) { + assertTrue(e.getMessage().contains("Unhandled level 90")); + } } } + diff --git a/test-data/hmef/bug52400-winmail-simple.dat b/test-data/hmef/bug52400-winmail-simple.dat new file mode 100644 index 000000000..cb9f5f5c9 Binary files /dev/null and b/test-data/hmef/bug52400-winmail-simple.dat differ diff --git a/test-data/hmef/bug52400-winmail-with-attachments.dat b/test-data/hmef/bug52400-winmail-with-attachments.dat new file mode 100644 index 000000000..9f18a8a90 Binary files /dev/null and b/test-data/hmef/bug52400-winmail-with-attachments.dat differ diff --git a/test-data/hmef/winmail-sample1.dat b/test-data/hmef/winmail-sample1.dat new file mode 100644 index 000000000..b9f629502 Binary files /dev/null and b/test-data/hmef/winmail-sample1.dat differ