diff options
Diffstat (limited to 'src')
8 files changed, 333 insertions, 5 deletions
diff --git a/src/java/org/apache/poi/util/LZWDecompresser.java b/src/java/org/apache/poi/util/LZWDecompresser.java index f172a01db4..51926b6c25 100644 --- a/src/java/org/apache/poi/util/LZWDecompresser.java +++ b/src/java/org/apache/poi/util/LZWDecompresser.java @@ -35,10 +35,16 @@ public abstract class LZWDecompresser { /** * Does the mask bit mean it's compressed or uncompressed? */ - private boolean maskMeansCompressed; + private final boolean maskMeansCompressed; + /** + * How much to append to the code length in the stream + * to get the real code length? Normally 2 or 3 + */ + private final int codeLengthIncrease; - protected LZWDecompresser(boolean maskMeansCompressed) { + protected LZWDecompresser(boolean maskMeansCompressed, int codeLengthIncrease) { this.maskMeansCompressed = maskMeansCompressed; + this.codeLengthIncrease = codeLengthIncrease; } /** @@ -135,7 +141,7 @@ public abstract class LZWDecompresser { // what position of the code to start at // (The position is the first 12 bits, the // length is the last 4 bits) - len = (dataIPt2 & 15) + 3; + len = (dataIPt2 & 15) + codeLengthIncrease; pntr = (dataIPt2 & 240)*16 + dataIPt1; // Adjust the pointer as needed diff --git a/src/scratchpad/src/org/apache/poi/hdgf/HDGFLZW.java b/src/scratchpad/src/org/apache/poi/hdgf/HDGFLZW.java index 290c14799b..f122c40f17 100644 --- a/src/scratchpad/src/org/apache/poi/hdgf/HDGFLZW.java +++ b/src/scratchpad/src/org/apache/poi/hdgf/HDGFLZW.java @@ -38,7 +38,7 @@ import org.apache.poi.util.LZWDecompresser; public class HDGFLZW extends LZWDecompresser { public HDGFLZW() { // We're the wrong way round! - super(false); + super(false, 3); } /** diff --git a/src/scratchpad/src/org/apache/poi/hmef/Attribute.java b/src/scratchpad/src/org/apache/poi/hmef/Attribute.java index d5e5aebdd8..8f7372ef1e 100644 --- a/src/scratchpad/src/org/apache/poi/hmef/Attribute.java +++ b/src/scratchpad/src/org/apache/poi/hmef/Attribute.java @@ -254,4 +254,9 @@ public final class Attribute { public byte[] getData() { return data; } + + public String toString() { + return "Attachment " + getId().toString() + ", type=" + type + + ", data length=" + data.length; + } } diff --git a/src/scratchpad/src/org/apache/poi/hmef/CompressedRTF.java b/src/scratchpad/src/org/apache/poi/hmef/CompressedRTF.java index aa3b41c650..81218bc9ba 100644 --- a/src/scratchpad/src/org/apache/poi/hmef/CompressedRTF.java +++ b/src/scratchpad/src/org/apache/poi/hmef/CompressedRTF.java @@ -54,7 +54,7 @@ public final class CompressedRTF extends LZWDecompresser { "{\\colortbl\\red0\\green0\\blue0\n\r\\par \\pard\\plain\\f0\\fs20\\b\\i\\u\\tab\\tx"; public CompressedRTF() { - super(true); + super(true, 2); } public void decompress(InputStream src, OutputStream res) throws IOException { diff --git a/src/scratchpad/src/org/apache/poi/hmef/HMEFMessage.java b/src/scratchpad/src/org/apache/poi/hmef/HMEFMessage.java index d86e6afad6..d74bcbefae 100644 --- a/src/scratchpad/src/org/apache/poi/hmef/HMEFMessage.java +++ b/src/scratchpad/src/org/apache/poi/hmef/HMEFMessage.java @@ -22,6 +22,8 @@ import java.io.InputStream; import java.util.ArrayList; import java.util.List; +import org.apache.poi.hmef.Attribute.AttributeID; +import org.apache.poi.hsmf.datatypes.MAPIProperty; import org.apache.poi.util.LittleEndian; /** @@ -103,4 +105,55 @@ public final class HMEFMessage { // Handle the next one down process(inp, level); } + + /** + * Returns all HMEF/TNEF attributes of the message. + * Note - In a typical message, most of the interesting properties + * are stored as {@link MAPIAttribute}s - see {@link #getMessageMAPIAttributes()} + */ + public List<Attribute> getMessageAttributes() { + return messageAttributes; + } + + /** + * Returns all MAPI attributes of the message. + * Note - A small number of HMEF/TNEF specific attributes normally + * apply to most messages, see {@link #getMessageAttributes()} + */ + public List<MAPIAttribute> getMessageMAPIAttributes() { + return mapiAttributes; + } + + /** + * Returns all the Attachments of the message. + */ + public List<Attachment> getAttachments() { + return attachments; + } + + /** + * Return the message attribute with the given ID, + * or null if there isn't one. + */ + public Attribute getMessageAttribute(AttributeID id) { + for(Attribute attr : messageAttributes) { + if(attr.getId() == id) { + return attr; + } + } + return null; + } + + /** + * Return the message 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; + } } diff --git a/src/scratchpad/testcases/org/apache/poi/hmef/TestCompressedRTF.java b/src/scratchpad/testcases/org/apache/poi/hmef/TestCompressedRTF.java new file mode 100644 index 0000000000..f9cc09c37a --- /dev/null +++ b/src/scratchpad/testcases/org/apache/poi/hmef/TestCompressedRTF.java @@ -0,0 +1,155 @@ +/* ==================================================================== + 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.ByteArrayInputStream; + +import junit.framework.TestCase; + +import org.apache.poi.POIDataSamples; +import org.apache.poi.hmef.Attribute.AttributeID; +import org.apache.poi.hsmf.datatypes.MAPIProperty; +import org.apache.poi.util.LittleEndian; +import org.apache.poi.util.StringUtil; + +public final class TestCompressedRTF extends TestCase { + private static final POIDataSamples _samples = POIDataSamples.getHMEFInstance(); + + private static final String block1 = "{\\rtf1\\adeflang102"; + private static final String block2 = block1 + "5\\ansi\\ansicpg1252"; + + /** + * Check that things are as we expected. If this fails, + * then decoding has no hope... + */ + public void testQuickBasics() throws Exception { + HMEFMessage msg = new HMEFMessage( + _samples.openResourceAsStream("quick-winmail.dat") + ); + + MAPIAttribute rtfAttr = msg.getMessageMAPIAttribute(MAPIProperty.RTF_COMPRESSED); + assertNotNull(rtfAttr); + assertTrue(rtfAttr instanceof MAPIRtfAttribute); + + // Check the start of the compressed version + assertEquals(5907, rtfAttr.getData().length); + + // First 16 bytes is header stuff + // Check it has the length + compressed marker + assertEquals(5907-4, LittleEndian.getShort(rtfAttr.getData())); + assertEquals( + "LZFu", + StringUtil.getFromCompressedUnicode(rtfAttr.getData(), 8, 4) + ); + + + // Now Look at the code + assertEquals((byte)0x07, rtfAttr.getData()[16+0]); // Flag: cccUUUUU + assertEquals((byte)0x00, rtfAttr.getData()[16+1]); // c1a: offset 0 / 0x000 + assertEquals((byte)0x06, rtfAttr.getData()[16+2]); // c1b: length 6+2 -> {\rtf1\a + assertEquals((byte)0x01, rtfAttr.getData()[16+3]); // c2a: offset 16 / 0x010 + assertEquals((byte)0x01, rtfAttr.getData()[16+4]); // c2b: length 1+2 -> def + assertEquals((byte)0x0b, rtfAttr.getData()[16+5]); // c3a: offset 182 / 0xb6 + assertEquals((byte)0x60, rtfAttr.getData()[16+6]); // c3b: length 0+2 -> la + assertEquals((byte)0x6e, rtfAttr.getData()[16+7]); // n + assertEquals((byte)0x67, rtfAttr.getData()[16+8]); // g + assertEquals((byte)0x31, rtfAttr.getData()[16+9]); // 1 + assertEquals((byte)0x30, rtfAttr.getData()[16+10]); // 0 + assertEquals((byte)0x32, rtfAttr.getData()[16+11]); // 2 + + assertEquals((byte)0x66, rtfAttr.getData()[16+12]); // Flag: UccUUccU + assertEquals((byte)0x35, rtfAttr.getData()[16+13]); // 5 + assertEquals((byte)0x00, rtfAttr.getData()[16+14]); // c2a: offset 6 / 0x006 + assertEquals((byte)0x64, rtfAttr.getData()[16+15]); // c2b: length 4+2 -> \ansi\a + assertEquals((byte)0x00, rtfAttr.getData()[16+16]); // c3a: offset 7 / 0x007 + assertEquals((byte)0x72, rtfAttr.getData()[16+17]); // c3b: length 2+2 -> nsi + assertEquals((byte)0x63, rtfAttr.getData()[16+18]); // c + assertEquals((byte)0x70, rtfAttr.getData()[16+19]); // p + assertEquals((byte)0x0d, rtfAttr.getData()[16+20]); // c6a: offset 221 / 0x0dd + assertEquals((byte)0xd0, rtfAttr.getData()[16+21]); // c6b: length 0+2 -> g1 + assertEquals((byte)0x0e, rtfAttr.getData()[16+22]); // c7a: offset 224 / 0x0e0 + assertEquals((byte)0x00, rtfAttr.getData()[16+23]); // c7b: length 0+2 -> 25 + assertEquals((byte)0x32, rtfAttr.getData()[16+24]); // 2 + } + + /** + * Check that we can decode the first 8 codes + * (1 flag byte + 8 codes) + */ + public void DISABLEDtestFirstBlock() throws Exception { + HMEFMessage msg = new HMEFMessage( + _samples.openResourceAsStream("quick-winmail.dat") + ); + + MAPIAttribute rtfAttr = msg.getMessageMAPIAttribute(MAPIProperty.RTF_COMPRESSED); + assertNotNull(rtfAttr); + + // Truncate to header + flag + data for flag + byte[] data = new byte[16+12]; + System.arraycopy(rtfAttr.getData(), 0, data, 0, data.length); + + // Decompress it + CompressedRTF comp = new CompressedRTF(); + byte[] decomp = comp.decompress(new ByteArrayInputStream(data)); + String decompStr = new String(decomp, "ASCII"); + + // Test +System.err.println(decompStr); + assertEquals(block1.length(), decomp.length); + assertEquals(block1, decompStr); + } + + /** + * Check that we can decode the first 16 codes + * (flag + 8 codes, flag + 8 codes) + */ + public void DISABLEDtestFirstTwoBlocks() throws Exception { + HMEFMessage msg = new HMEFMessage( + _samples.openResourceAsStream("quick-winmail.dat") + ); + + MAPIAttribute rtfAttr = msg.getMessageMAPIAttribute(MAPIProperty.RTF_COMPRESSED); + assertNotNull(rtfAttr); + + // Truncate to header + flag + data for flag + flag + data + byte[] data = new byte[16+12+13]; + System.arraycopy(rtfAttr.getData(), 0, data, 0, data.length); + + // Decompress it + CompressedRTF comp = new CompressedRTF(); + byte[] decomp = comp.decompress(new ByteArrayInputStream(data)); + String decompStr = new String(decomp, "ASCII"); + + // Test +System.err.println(decompStr); + assertEquals(block2.length(), decomp.length); + assertEquals(block2, decompStr); + } + + /** + * Check that we can correctly decode the whole file + * @throws Exception + */ + public void testFull() throws Exception { + HMEFMessage msg = new HMEFMessage( + _samples.openResourceAsStream("quick-winmail.dat") + ); + + // TODO + } +} diff --git a/src/scratchpad/testcases/org/apache/poi/hmef/TestHMEFMessage.java b/src/scratchpad/testcases/org/apache/poi/hmef/TestHMEFMessage.java new file mode 100644 index 0000000000..33e1edbbe3 --- /dev/null +++ b/src/scratchpad/testcases/org/apache/poi/hmef/TestHMEFMessage.java @@ -0,0 +1,103 @@ +/* ==================================================================== + 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 TestHMEFMessage 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); + } + + public void testCounts() throws Exception { + HMEFMessage msg = new HMEFMessage( + _samples.openResourceAsStream("quick-winmail.dat") + ); + + // Should have 4 attributes on the message + assertEquals(4, msg.getMessageAttributes().size()); + + // And should have 54 MAPI attributes on it + assertEquals(54, msg.getMessageMAPIAttributes().size()); + + + // Should have 5 attachments + assertEquals(5, msg.getAttachments().size()); + + // Each attachment should have 6 normal attributes, and + // 20 or so MAPI ones + for(Attachment attach : msg.getAttachments()) { + int attrCount = attach.getAttributes().size(); + 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); + } + + + // TODO + } + + public void testBasicMessageAttributes() throws Exception { + HMEFMessage msg = new HMEFMessage( + _samples.openResourceAsStream("quick-winmail.dat") + ); + + // Should have version, codepage, class and MAPI + assertEquals(4, msg.getMessageAttributes().size()); + assertNotNull(msg.getMessageAttribute(Attribute.ID_TNEFVERSION)); + assertNotNull(msg.getMessageAttribute(Attribute.ID_OEMCODEPAGE)); + assertNotNull(msg.getMessageAttribute(Attribute.ID_MESSAGECLASS)); + assertNotNull(msg.getMessageAttribute(Attribute.ID_MAPIPROPERTIES)); + + // Check the order + assertEquals(Attribute.ID_TNEFVERSION, msg.getMessageAttributes().get(0).getId()); + assertEquals(Attribute.ID_OEMCODEPAGE, msg.getMessageAttributes().get(1).getId()); + assertEquals(Attribute.ID_MESSAGECLASS, msg.getMessageAttributes().get(2).getId()); + assertEquals(Attribute.ID_MAPIPROPERTIES, msg.getMessageAttributes().get(3).getId()); + + // Check some that aren't there + assertNull(msg.getMessageAttribute(Attribute.ID_AIDOWNER)); + assertNull(msg.getMessageAttribute(Attribute.ID_ATTACHDATA)); + + // Now check the details of one or two + // TODO + } + + public void testBasicMessageMAPIAttributes() throws Exception { + // TODO + } + + public void testBasicAttachments() throws Exception { + // TODO + } + + public void testMessageAttributeDetails() throws Exception { + // TODO + } +} diff --git a/src/testcases/org/apache/poi/POIDataSamples.java b/src/testcases/org/apache/poi/POIDataSamples.java index d4ec6d6b7d..6f60a4e184 100644 --- a/src/testcases/org/apache/poi/POIDataSamples.java +++ b/src/testcases/org/apache/poi/POIDataSamples.java @@ -35,6 +35,7 @@ public final class POIDataSamples { private static POIDataSamples _instOpenxml4j;
private static POIDataSamples _instPOIFS;
private static POIDataSamples _instDDF;
+ private static POIDataSamples _instHMEF;
private static POIDataSamples _instHPSF;
private static POIDataSamples _instHPBF;
private static POIDataSamples _instHSMF;
@@ -99,6 +100,11 @@ public final class POIDataSamples { return _instHPBF;
}
+ public static POIDataSamples getHMEFInstance(){
+ if(_instHMEF == null) _instHMEF = new POIDataSamples("hmef");
+ return _instHMEF;
+ }
+
public static POIDataSamples getHSMFInstance(){
if(_instHSMF == null) _instHSMF = new POIDataSamples("hsmf");
return _instHSMF;
|