Fix bug #47990 - Support for .msg attachments within a MAPIMessage .msg

git-svn-id: https://svn.apache.org/repos/asf/poi/trunk@982331 13f79535-47bb-0310-9956-ffa450edef68
This commit is contained in:
Nick Burch 2010-08-04 17:08:39 +00:00
parent ddbddfafa4
commit 818bc29696
8 changed files with 159 additions and 20 deletions

View File

@ -34,6 +34,7 @@
<changes>
<release version="3.7-beta2" date="2010-??-??">
<action dev="POI-DEVELOPERS" type="add">47990 - Support for .msg attachments within a MAPIMessage .msg</action>
<action dev="POI-DEVELOPERS" type="fix">Improve handling and warnings when closing OPCPackage objects</action>
<action dev="POI-DEVELOPERS" type="fix">49702 - Correct XSSFWorkbook.getNumCellStyles to check the right styles list</action>
<action dev="POI-DEVELOPERS" type="add">49690 - Add WorkbookUtil, which provies a way of generating valid sheet names</action>

View File

@ -110,6 +110,15 @@ public class DirectoryNode
return _path;
}
/**
* @return the filesystem that this belongs to
*/
public POIFSFileSystem getFileSystem()
{
return _filesystem;
}
/**
* open a document in the directory's entry's list of entries
*

View File

@ -42,6 +42,8 @@ public class AttachmentChunks implements ChunkGroup {
public StringChunk attachFileName;
public StringChunk attachLongFileName;
public StringChunk attachMimeTag;
public DirectoryChunk attachmentDirectory;
/**
* This is in WMF Format. You'll probably want to pass it
* to Apache Batik to turn it into a SVG that you can
@ -79,7 +81,13 @@ public class AttachmentChunks implements ChunkGroup {
public void record(Chunk chunk) {
switch(chunk.getChunkId()) {
case ATTACH_DATA:
attachData = (ByteChunk)chunk;
if(chunk instanceof ByteChunk) {
attachData = (ByteChunk)chunk;
} else if(chunk instanceof DirectoryChunk) {
attachmentDirectory = (DirectoryChunk)chunk;
} else {
System.err.println("Unexpected data chunk of type " + chunk);
}
break;
case ATTACH_EXTENSION:
attachExtension = (StringChunk)chunk;

View File

@ -0,0 +1,68 @@
/* ====================================================================
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.hsmf.datatypes;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import org.apache.poi.hsmf.MAPIMessage;
import org.apache.poi.poifs.filesystem.DirectoryNode;
/**
* A Chunk that is just a placeholder in the
* MAPIMessage directory structure, which
* contains children.
* This is most commonly used with nested
* MAPIMessages
*/
public class DirectoryChunk extends Chunk {
private DirectoryNode dir;
public DirectoryChunk(DirectoryNode dir, String namePrefix, int chunkId, int type) {
super(namePrefix, chunkId, type);
this.dir = dir;
}
/**
* Returns the directory entry for this chunk.
* You can then use standard POIFS methods to
* enumerate the entries in it.
*/
public DirectoryNode getDirectory() {
return dir;
}
/**
* Treats the directory as an embeded MAPIMessage
* (it normally is one), and returns a MAPIMessage
* object to process it with.
*/
public MAPIMessage getAsEmbededMessage() throws IOException {
return new MAPIMessage(dir, dir.getFileSystem());
}
@Override
public void readValue(InputStream value) {
// DirectoryChunks have 0 byte contents
}
@Override
public void writeValue(OutputStream out) {
// DirectoryChunks have 0 byte contents
}
}

View File

@ -32,6 +32,7 @@ public final class Types {
public static final int LONG = 0x0003;
public static final int TIME = 0x0040;
public static final int BOOLEAN = 0x000B;
public static final int DIRECTORY = 0x000D;
public static String asFileEnding(int type) {
String str = Integer.toHexString(type).toUpperCase();

View File

@ -25,6 +25,7 @@ import org.apache.poi.hsmf.datatypes.ByteChunk;
import org.apache.poi.hsmf.datatypes.Chunk;
import org.apache.poi.hsmf.datatypes.ChunkGroup;
import org.apache.poi.hsmf.datatypes.Chunks;
import org.apache.poi.hsmf.datatypes.DirectoryChunk;
import org.apache.poi.hsmf.datatypes.MessageSubmissionChunk;
import org.apache.poi.hsmf.datatypes.NameIdChunks;
import org.apache.poi.hsmf.datatypes.RecipientChunks;
@ -93,7 +94,11 @@ public final class POIFSChunkParser {
protected static void processChunks(DirectoryNode node, ChunkGroup grouping) {
for(Entry entry : node) {
if(entry instanceof DocumentNode) {
process((DocumentNode)entry, grouping);
process(entry, grouping);
} else if(entry instanceof DirectoryNode) {
if(entry.getName().endsWith(Types.asFileEnding(Types.DIRECTORY))) {
process(entry, grouping);
}
}
}
}
@ -101,7 +106,7 @@ public final class POIFSChunkParser {
/**
* Creates a chunk, and gives it to its parent group
*/
protected static void process(DocumentNode entry, ChunkGroup grouping) {
protected static void process(Entry entry, ChunkGroup grouping) {
String entryName = entry.getName();
if(entryName.length() < 9) {
@ -140,6 +145,11 @@ public final class POIFSChunkParser {
case Types.BINARY:
chunk = new ByteChunk(namePrefix, chunkId, type);
break;
case Types.DIRECTORY:
if(entry instanceof DirectoryNode) {
chunk = new DirectoryChunk((DirectoryNode)entry, namePrefix, chunkId, type);
}
break;
case Types.ASCII_STRING:
case Types.UNICODE_STRING:
chunk = new StringChunk(namePrefix, chunkId, type);
@ -148,13 +158,17 @@ public final class POIFSChunkParser {
}
if(chunk != null) {
try {
DocumentInputStream inp = new DocumentInputStream(entry);
chunk.readValue(inp);
grouping.record(chunk);
} catch(IOException e) {
System.err.println("Error reading from part " + entry.getName() + " - " + e.toString());
}
if(entry instanceof DocumentNode) {
try {
DocumentInputStream inp = new DocumentInputStream((DocumentNode)entry);
chunk.readValue(inp);
grouping.record(chunk);
} catch(IOException e) {
System.err.println("Error reading from part " + entry.getName() + " - " + e.toString());
}
} else {
grouping.record(chunk);
}
}
} catch(NumberFormatException e) {
// Name in the wrong format

View File

@ -31,7 +31,8 @@ import org.apache.poi.hsmf.exceptions.ChunkNotFoundException;
* @author Nicolas Bureau
*/
public class TestFileWithAttachmentsRead extends TestCase {
private MAPIMessage mapiMessage;
private MAPIMessage twoSimpleAttachments;
private MAPIMessage pdfMsgAttachments;
/**
* Initialize this test, load up the attachment_test_msg.msg mapi message.
@ -40,7 +41,8 @@ public class TestFileWithAttachmentsRead extends TestCase {
*/
public TestFileWithAttachmentsRead() throws IOException {
POIDataSamples samples = POIDataSamples.getHSMFInstance();
this.mapiMessage = new MAPIMessage(samples.openResourceAsStream("attachment_test_msg.msg"));
this.twoSimpleAttachments = new MAPIMessage(samples.openResourceAsStream("attachment_test_msg.msg"));
this.pdfMsgAttachments = new MAPIMessage(samples.openResourceAsStream("attachment_msg_pdf.msg"));
}
/**
@ -50,18 +52,20 @@ public class TestFileWithAttachmentsRead extends TestCase {
*
*/
public void testRetrieveAttachments() {
AttachmentChunks[] attachments = mapiMessage.getAttachmentFiles();
int obtained = attachments.length;
int expected = 2;
TestCase.assertEquals(obtained, expected);
// Simple file
AttachmentChunks[] attachments = twoSimpleAttachments.getAttachmentFiles();
assertEquals(2, attachments.length);
// Other file
attachments = pdfMsgAttachments.getAttachmentFiles();
assertEquals(2, attachments.length);
}
/**
* Test to see if attachments are not empty.
*/
public void testReadAttachments() throws IOException {
AttachmentChunks[] attachments = mapiMessage.getAttachmentFiles();
AttachmentChunks[] attachments = twoSimpleAttachments.getAttachmentFiles();
// Basic checks
for (AttachmentChunks attachment : attachments) {
@ -76,18 +80,52 @@ public class TestFileWithAttachmentsRead extends TestCase {
AttachmentChunks attachment;
// Now check in detail
attachment = mapiMessage.getAttachmentFiles()[0];
attachment = twoSimpleAttachments.getAttachmentFiles()[0];
assertEquals("TEST-U~1.DOC", attachment.attachFileName.toString());
assertEquals("test-unicode.doc", attachment.attachLongFileName.toString());
assertEquals(".doc", attachment.attachExtension.getValue());
assertEquals(null, attachment.attachMimeTag);
assertEquals(24064, attachment.attachData.getValue().length);
attachment = mapiMessage.getAttachmentFiles()[1];
attachment = twoSimpleAttachments.getAttachmentFiles()[1];
assertEquals("pj1.txt", attachment.attachFileName.toString());
assertEquals("pj1.txt", attachment.attachLongFileName.toString());
assertEquals(".txt", attachment.attachExtension.getValue());
assertEquals(null, attachment.attachMimeTag);
assertEquals(89, attachment.attachData.getValue().length);
}
/**
* Test that we can handle both PDF and MSG attachments
*/
public void testReadMsgAttachments() throws Exception {
AttachmentChunks[] attachments = pdfMsgAttachments.getAttachmentFiles();
assertEquals(2, attachments.length);
AttachmentChunks attachment;
// Second is a PDF
attachment = pdfMsgAttachments.getAttachmentFiles()[1];
assertEquals("smbprn~1.pdf", attachment.attachFileName.toString());
assertEquals("smbprn.00009008.KdcPjl.pdf", attachment.attachLongFileName.toString());
assertEquals(".pdf", attachment.attachExtension.getValue());
assertEquals(null, attachment.attachMimeTag);
assertEquals(null, attachment.attachmentDirectory);
assertEquals(13539, attachment.attachData.getValue().length);
// First in a nested message
attachment = pdfMsgAttachments.getAttachmentFiles()[0];
assertEquals("Test Attachment", attachment.attachFileName.toString());
assertEquals(null, attachment.attachLongFileName);
assertEquals(null, attachment.attachExtension);
assertEquals(null, attachment.attachMimeTag);
assertEquals(null, attachment.attachData);
assertNotNull(attachment.attachmentDirectory);
// Check we can see some bits of it
MAPIMessage nested = attachment.attachmentDirectory.getAsEmbededMessage();
assertEquals(1, nested.getRecipientNamesList().length);
assertEquals("Nick Booth", nested.getRecipientNames());
assertEquals("Test Attachment", nested.getConversationTopic());
}
}

Binary file not shown.