Start on the code to process properties, and wire it up. No properties reading code exists yet
git-svn-id: https://svn.apache.org/repos/asf/poi/trunk@1358813 13f79535-47bb-0310-9956-ffa450edef68
This commit is contained in:
parent
9a5378ff55
commit
eb368e205d
@ -724,6 +724,24 @@ public class LittleEndian implements LittleEndianConsts
|
||||
}
|
||||
return ( ch4 << 24 ) + ( ch3 << 16 ) + ( ch2 << 8 ) + ( ch1 << 0 );
|
||||
}
|
||||
|
||||
/**
|
||||
* get an unsigned int value from an InputStream
|
||||
*
|
||||
* @param stream
|
||||
* the InputStream from which the int is to be read
|
||||
* @return the unsigned int (32-bit) value
|
||||
* @exception IOException
|
||||
* will be propagated back to the caller
|
||||
* @exception BufferUnderrunException
|
||||
* if the stream cannot provide enough bytes
|
||||
*/
|
||||
public static long readUInt( InputStream stream ) throws IOException,
|
||||
BufferUnderrunException
|
||||
{
|
||||
long retNum = readInt(stream);
|
||||
return retNum & 0x00FFFFFFFFl;
|
||||
}
|
||||
|
||||
/**
|
||||
* get a long value from an InputStream
|
||||
|
@ -1026,6 +1026,8 @@ public class MAPIProperty {
|
||||
new MAPIProperty(-1, Types.UNKNOWN, "Unknown", null);
|
||||
|
||||
// 0x8??? ones are outlook specific, and not standard MAPI
|
||||
// TODO See http://msdn.microsoft.com/en-us/library/ee157150%28v=exchg.80%29 for some
|
||||
// info on how we might decode them properly in the future
|
||||
private static final int ID_FIRST_CUSTOM = 0x8000;
|
||||
private static final int ID_LAST_CUSTOM = 0xFFFE;
|
||||
|
||||
|
@ -0,0 +1,89 @@
|
||||
/* ====================================================================
|
||||
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.util.LittleEndian;
|
||||
|
||||
/**
|
||||
* A {@link PropertiesChunk} for a Message or Embedded-Message.
|
||||
* This has a 32 byte header
|
||||
*/
|
||||
public class MessagePropertiesChunk extends PropertiesChunk {
|
||||
private long nextRecipientId;
|
||||
private long nextAttachmentId;
|
||||
private long recipientCount;
|
||||
private long attachmentCount;
|
||||
|
||||
public MessagePropertiesChunk() {
|
||||
super();
|
||||
}
|
||||
|
||||
public long getNextRecipientId() {
|
||||
return nextRecipientId;
|
||||
}
|
||||
public long getNextAttachmentId() {
|
||||
return nextAttachmentId;
|
||||
}
|
||||
|
||||
public long getRecipientCount() {
|
||||
return recipientCount;
|
||||
}
|
||||
public long getAttachmentCount() {
|
||||
return attachmentCount;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void readValue(InputStream stream) throws IOException {
|
||||
// 8 bytes of reserved zeros
|
||||
LittleEndian.readLong(stream);
|
||||
|
||||
// Nexts and counts
|
||||
nextRecipientId = LittleEndian.readUInt(stream);
|
||||
nextAttachmentId = LittleEndian.readUInt(stream);
|
||||
recipientCount = LittleEndian.readUInt(stream);
|
||||
attachmentCount = LittleEndian.readUInt(stream);
|
||||
|
||||
// 8 bytes of reserved zeros
|
||||
LittleEndian.readLong(stream);
|
||||
|
||||
// Now properties
|
||||
readProperties(stream);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeValue(OutputStream out) throws IOException {
|
||||
// 8 bytes of reserved zeros
|
||||
out.write(new byte[8]);
|
||||
|
||||
// Nexts and counts
|
||||
LittleEndian.putUInt(nextRecipientId, out);
|
||||
LittleEndian.putUInt(nextAttachmentId, out);
|
||||
LittleEndian.putUInt(recipientCount, out);
|
||||
LittleEndian.putUInt(attachmentCount, out);
|
||||
|
||||
// 8 bytes of reserved zeros
|
||||
out.write(new byte[8]);
|
||||
|
||||
// Now properties
|
||||
writeProperties(out);
|
||||
}
|
||||
}
|
@ -26,7 +26,7 @@ import java.util.List;
|
||||
* NameID part of an outlook file
|
||||
*/
|
||||
public final class NameIdChunks implements ChunkGroup {
|
||||
public static final String PREFIX = "__nameid_version1.0";
|
||||
public static final String NAME = "__nameid_version1.0";
|
||||
|
||||
/** Holds all the chunks that were found. */
|
||||
private List<Chunk> allChunks = new ArrayList<Chunk>();
|
||||
|
@ -20,31 +20,68 @@ package org.apache.poi.hsmf.datatypes;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* A Chunk which holds fixed-length properties, and pointer
|
||||
* to the variable length ones (which get their own chunk)
|
||||
* to the variable length ones (which get their own chunk).
|
||||
* There are two kinds of PropertiesChunks, which differ only in
|
||||
* their headers.
|
||||
*/
|
||||
public class PropertiesChunk extends Chunk {
|
||||
public static final String PREFIX = "__properties_version1.0";
|
||||
public abstract class PropertiesChunk extends Chunk {
|
||||
public static final String NAME = "__properties_version1.0";
|
||||
|
||||
/**
|
||||
* Holds properties, indexed by type. Properties can be multi-valued
|
||||
*/
|
||||
private Map<MAPIProperty, List<PropertyValue>> properties =
|
||||
new HashMap<MAPIProperty, List<PropertyValue>>();
|
||||
|
||||
/**
|
||||
* Creates a Properties Chunk.
|
||||
*/
|
||||
public PropertiesChunk() {
|
||||
super(PREFIX, -1, Types.UNKNOWN);
|
||||
protected PropertiesChunk() {
|
||||
super(NAME, -1, Types.UNKNOWN);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getEntryName() {
|
||||
return PREFIX;
|
||||
return NAME;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns all the properties in the chunk
|
||||
*/
|
||||
public Map<MAPIProperty, List<PropertyValue>> getProperties() {
|
||||
return properties;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns all values for the given property, of null if none exist
|
||||
*/
|
||||
public List<PropertyValue> getValues(MAPIProperty property) {
|
||||
return properties.get(property);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the (first/only) value for the given property, or
|
||||
* null if none exist
|
||||
*/
|
||||
public PropertyValue getValue(MAPIProperty property) {
|
||||
List<PropertyValue> values = properties.get(property);
|
||||
if (values != null && values.size() > 0) {
|
||||
return values.get(0);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public void readValue(InputStream value) throws IOException {
|
||||
protected void readProperties(InputStream value) throws IOException {
|
||||
// TODO
|
||||
}
|
||||
|
||||
public void writeValue(OutputStream out) throws IOException {
|
||||
protected void writeProperties(OutputStream out) throws IOException {
|
||||
// TODO
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,53 @@
|
||||
/* ====================================================================
|
||||
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.util.LittleEndian;
|
||||
|
||||
/**
|
||||
* A {@link PropertiesChunk} for a Storage Properties, such as
|
||||
* Attachments and Recipients.
|
||||
* This only has a 8 byte header
|
||||
*/
|
||||
public class StoragePropertiesChunk extends PropertiesChunk {
|
||||
public StoragePropertiesChunk() {
|
||||
super();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void readValue(InputStream stream) throws IOException {
|
||||
// 8 bytes of reserved zeros
|
||||
LittleEndian.readLong(stream);
|
||||
|
||||
// Now properties
|
||||
readProperties(stream);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeValue(OutputStream out) throws IOException {
|
||||
// 8 bytes of reserved zeros
|
||||
out.write(new byte[8]);
|
||||
|
||||
// Now properties
|
||||
writeProperties(out);
|
||||
}
|
||||
}
|
@ -22,7 +22,6 @@ import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
import java.io.UnsupportedEncodingException;
|
||||
|
||||
import org.apache.poi.hsmf.datatypes.Types;
|
||||
import org.apache.poi.hsmf.datatypes.Types.MAPIType;
|
||||
import org.apache.poi.util.IOUtils;
|
||||
import org.apache.poi.util.StringUtil;
|
||||
|
@ -26,6 +26,7 @@ import java.util.Map;
|
||||
*/
|
||||
public final class Types {
|
||||
private static Map<Integer, MAPIType> builtInTypes = new HashMap<Integer, MAPIType>();
|
||||
private static Map<Integer, MAPIType> customTypes = new HashMap<Integer, Types.MAPIType>();
|
||||
|
||||
/** Unspecified */
|
||||
public static final MAPIType UNSPECIFIED = new MAPIType(0x0000, "Unspecified", -1);
|
||||
@ -95,6 +96,7 @@ public final class Types {
|
||||
this.id = id;
|
||||
this.name = asCustomName(id);
|
||||
this.length = length;
|
||||
customTypes.put(id, this);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -150,6 +152,24 @@ public final class Types {
|
||||
}
|
||||
|
||||
public static MAPIType createCustom(int typeId) {
|
||||
return new MAPIType(typeId, -1);
|
||||
// Check they're not being silly, and asking for a built-in one...
|
||||
if (getById(typeId) != null) {
|
||||
return getById(typeId);
|
||||
}
|
||||
|
||||
// Try to get an existing definition of this
|
||||
MAPIType type = customTypes.get(typeId);
|
||||
|
||||
// If none, do a thread-safe creation
|
||||
if (type == null) {
|
||||
synchronized (customTypes) {
|
||||
type = customTypes.get(typeId);
|
||||
if (type == null) {
|
||||
type = new MAPIType(typeId, -1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return type;
|
||||
}
|
||||
}
|
||||
|
@ -23,6 +23,8 @@ import java.io.IOException;
|
||||
import org.apache.poi.hsmf.datatypes.Chunk;
|
||||
import org.apache.poi.hsmf.datatypes.ChunkGroup;
|
||||
import org.apache.poi.hsmf.datatypes.MAPIProperty;
|
||||
import org.apache.poi.hsmf.datatypes.PropertiesChunk;
|
||||
import org.apache.poi.hsmf.datatypes.PropertyValue;
|
||||
import org.apache.poi.hsmf.parsers.POIFSChunkParser;
|
||||
import org.apache.poi.poifs.filesystem.POIFSFileSystem;
|
||||
|
||||
@ -42,17 +44,35 @@ public class HSMFDump {
|
||||
for(Chunk chunk : chunks.getChunks()) {
|
||||
MAPIProperty attr = MAPIProperty.get(chunk.getChunkId());
|
||||
|
||||
String idName = attr.id + " - " + attr.name;
|
||||
if(attr == MAPIProperty.UNKNOWN) {
|
||||
idName = chunk.getChunkId() + " - (unknown)";
|
||||
if (chunk instanceof PropertiesChunk) {
|
||||
PropertiesChunk props = (PropertiesChunk)chunk;
|
||||
System.out.println(
|
||||
" Properties - " + props.getProperties().size() + ":"
|
||||
);
|
||||
|
||||
for (MAPIProperty prop : props.getProperties().keySet()) {
|
||||
System.out.println(
|
||||
" * " + prop
|
||||
);
|
||||
for (PropertyValue v : props.getValues(prop)) {
|
||||
System.out.println(
|
||||
" = " + v.toString()
|
||||
);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
String idName = attr.id + " - " + attr.name;
|
||||
if(attr == MAPIProperty.UNKNOWN) {
|
||||
idName = chunk.getChunkId() + " - (unknown)";
|
||||
}
|
||||
|
||||
System.out.println(
|
||||
" " + idName + " - " + chunk.getType().getName()
|
||||
);
|
||||
System.out.println(
|
||||
" " + chunk.toString()
|
||||
);
|
||||
}
|
||||
|
||||
System.out.println(
|
||||
" " + idName + " - " + chunk.getType().getName()
|
||||
);
|
||||
System.out.println(
|
||||
" " + chunk.toString()
|
||||
);
|
||||
}
|
||||
System.out.println();
|
||||
}
|
||||
|
@ -27,9 +27,12 @@ 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.MAPIProperty;
|
||||
import org.apache.poi.hsmf.datatypes.MessagePropertiesChunk;
|
||||
import org.apache.poi.hsmf.datatypes.MessageSubmissionChunk;
|
||||
import org.apache.poi.hsmf.datatypes.NameIdChunks;
|
||||
import org.apache.poi.hsmf.datatypes.PropertiesChunk;
|
||||
import org.apache.poi.hsmf.datatypes.RecipientChunks;
|
||||
import org.apache.poi.hsmf.datatypes.StoragePropertiesChunk;
|
||||
import org.apache.poi.hsmf.datatypes.StringChunk;
|
||||
import org.apache.poi.hsmf.datatypes.Types;
|
||||
import org.apache.poi.hsmf.datatypes.Types.MAPIType;
|
||||
@ -66,7 +69,7 @@ public final class POIFSChunkParser {
|
||||
if(dir.getName().startsWith(AttachmentChunks.PREFIX)) {
|
||||
group = new AttachmentChunks(dir.getName());
|
||||
}
|
||||
if(dir.getName().startsWith(NameIdChunks.PREFIX)) {
|
||||
if(dir.getName().startsWith(NameIdChunks.NAME)) {
|
||||
group = new NameIdChunks();
|
||||
}
|
||||
if(dir.getName().startsWith(RecipientChunks.PREFIX)) {
|
||||
@ -110,87 +113,98 @@ public final class POIFSChunkParser {
|
||||
*/
|
||||
protected static void process(Entry entry, ChunkGroup grouping) {
|
||||
String entryName = entry.getName();
|
||||
Chunk chunk = null;
|
||||
|
||||
if(entryName.length() < 9) {
|
||||
// Name in the wrong format
|
||||
return;
|
||||
}
|
||||
if(entryName.indexOf('_') == -1) {
|
||||
// Name in the wrong format
|
||||
return;
|
||||
}
|
||||
|
||||
// Split it into its parts
|
||||
int splitAt = entryName.lastIndexOf('_');
|
||||
String namePrefix = entryName.substring(0, splitAt+1);
|
||||
String ids = entryName.substring(splitAt+1);
|
||||
|
||||
// Make sure we got what we expected, should be of
|
||||
// the form __<name>_<id><type>
|
||||
if(namePrefix.equals("Olk10SideProps") ||
|
||||
namePrefix.equals("Olk10SideProps_")) {
|
||||
// This is some odd Outlook 2002 thing, skip
|
||||
return;
|
||||
} else if(splitAt <= entryName.length()-8) {
|
||||
// In the right form for a normal chunk
|
||||
// We'll process this further in a little bit
|
||||
// Is it a properties chunk? (They have special names)
|
||||
if (entryName.equals(PropertiesChunk.NAME)) {
|
||||
if (grouping instanceof Chunks) {
|
||||
// These should be the properties for the message itself
|
||||
chunk = new MessagePropertiesChunk();
|
||||
} else {
|
||||
// Will be properties on an attachment or recipient
|
||||
chunk = new StoragePropertiesChunk();
|
||||
}
|
||||
} else {
|
||||
// Underscores not the right place, something's wrong
|
||||
throw new IllegalArgumentException("Invalid chunk name " + entryName);
|
||||
}
|
||||
|
||||
// Now try to turn it into id + type
|
||||
try {
|
||||
int chunkId = Integer.parseInt(ids.substring(0, 4), 16);
|
||||
int typeId = Integer.parseInt(ids.substring(4, 8), 16);
|
||||
|
||||
MAPIType type = Types.getById(typeId);
|
||||
if (type == null) {
|
||||
type = Types.createCustom(typeId);
|
||||
// Check it's a regular chunk
|
||||
if(entryName.length() < 9) {
|
||||
// Name in the wrong format
|
||||
return;
|
||||
}
|
||||
if(entryName.indexOf('_') == -1) {
|
||||
// Name in the wrong format
|
||||
return;
|
||||
}
|
||||
|
||||
Chunk chunk = null;
|
||||
// Split it into its parts
|
||||
int splitAt = entryName.lastIndexOf('_');
|
||||
String namePrefix = entryName.substring(0, splitAt+1);
|
||||
String ids = entryName.substring(splitAt+1);
|
||||
|
||||
// Special cases based on the ID
|
||||
if(chunkId == MAPIProperty.MESSAGE_SUBMISSION_ID.id) {
|
||||
chunk = new MessageSubmissionChunk(namePrefix, chunkId, type);
|
||||
}
|
||||
else {
|
||||
// Nothing special about this ID
|
||||
// So, do the usual thing which is by type
|
||||
if (type == Types.BINARY) {
|
||||
chunk = new ByteChunk(namePrefix, chunkId, type);
|
||||
// Make sure we got what we expected, should be of
|
||||
// the form __<name>_<id><type>
|
||||
if(namePrefix.equals("Olk10SideProps") ||
|
||||
namePrefix.equals("Olk10SideProps_")) {
|
||||
// This is some odd Outlook 2002 thing, skip
|
||||
return;
|
||||
} else if(splitAt <= entryName.length()-8) {
|
||||
// In the right form for a normal chunk
|
||||
// We'll process this further in a little bit
|
||||
} else {
|
||||
// Underscores not the right place, something's wrong
|
||||
throw new IllegalArgumentException("Invalid chunk name " + entryName);
|
||||
}
|
||||
|
||||
// Now try to turn it into id + type
|
||||
try {
|
||||
int chunkId = Integer.parseInt(ids.substring(0, 4), 16);
|
||||
int typeId = Integer.parseInt(ids.substring(4, 8), 16);
|
||||
|
||||
MAPIType type = Types.getById(typeId);
|
||||
if (type == null) {
|
||||
type = Types.createCustom(typeId);
|
||||
}
|
||||
else if (type == Types.DIRECTORY) {
|
||||
if(entry instanceof DirectoryNode) {
|
||||
chunk = new DirectoryChunk((DirectoryNode)entry, namePrefix, chunkId, type);
|
||||
}
|
||||
}
|
||||
else if (type == Types.ASCII_STRING ||
|
||||
type == Types.UNICODE_STRING) {
|
||||
chunk = new StringChunk(namePrefix, chunkId, type);
|
||||
|
||||
// Special cases based on the ID
|
||||
if(chunkId == MAPIProperty.MESSAGE_SUBMISSION_ID.id) {
|
||||
chunk = new MessageSubmissionChunk(namePrefix, chunkId, type);
|
||||
}
|
||||
else {
|
||||
// Type of an unsupported type! Skipping...
|
||||
// Nothing special about this ID
|
||||
// So, do the usual thing which is by type
|
||||
if (type == Types.BINARY) {
|
||||
chunk = new ByteChunk(namePrefix, chunkId, type);
|
||||
}
|
||||
else if (type == Types.DIRECTORY) {
|
||||
if(entry instanceof DirectoryNode) {
|
||||
chunk = new DirectoryChunk((DirectoryNode)entry, namePrefix, chunkId, type);
|
||||
}
|
||||
}
|
||||
else if (type == Types.ASCII_STRING ||
|
||||
type == Types.UNICODE_STRING) {
|
||||
chunk = new StringChunk(namePrefix, chunkId, type);
|
||||
}
|
||||
else {
|
||||
// Type of an unsupported type! Skipping...
|
||||
}
|
||||
}
|
||||
} catch(NumberFormatException e) {
|
||||
// Name in the wrong format
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if(chunk != null) {
|
||||
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 {
|
||||
if(chunk != null) {
|
||||
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());
|
||||
}
|
||||
}
|
||||
} catch(NumberFormatException e) {
|
||||
// Name in the wrong format
|
||||
return;
|
||||
} else {
|
||||
grouping.record(chunk);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user