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:
Nick Burch 2012-07-08 18:50:11 +00:00
parent 4088217930
commit 8789e74439
10 changed files with 342 additions and 90 deletions

View File

@ -725,6 +725,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
*

View File

@ -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;

View File

@ -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);
}
}

View File

@ -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>();

View File

@ -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;
}
public void readValue(InputStream value) throws IOException {
/**
* 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;
}
protected void readProperties(InputStream value) throws IOException {
// TODO
}
public void writeValue(OutputStream out) throws IOException {
protected void writeProperties(OutputStream out) throws IOException {
// TODO
}
}

View File

@ -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);
}
}

View File

@ -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;

View File

@ -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;
}
}

View File

@ -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,6 +44,23 @@ public class HSMFDump {
for(Chunk chunk : chunks.getChunks()) {
MAPIProperty attr = MAPIProperty.get(chunk.getChunkId());
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)";
@ -54,6 +73,7 @@ public class HSMFDump {
" " + chunk.toString()
);
}
}
System.out.println();
}
}

View File

@ -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,7 +113,19 @@ public final class POIFSChunkParser {
*/
protected static void process(Entry entry, ChunkGroup grouping) {
String entryName = entry.getName();
Chunk chunk = null;
// 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 {
// Check it's a regular chunk
if(entryName.length() < 9) {
// Name in the wrong format
return;
@ -149,8 +164,6 @@ public final class POIFSChunkParser {
type = Types.createCustom(typeId);
}
Chunk chunk = null;
// Special cases based on the ID
if(chunkId == MAPIProperty.MESSAGE_SUBMISSION_ID.id) {
chunk = new MessageSubmissionChunk(namePrefix, chunkId, type);
@ -174,6 +187,11 @@ public final class POIFSChunkParser {
// Type of an unsupported type! Skipping...
}
}
} catch(NumberFormatException e) {
// Name in the wrong format
return;
}
}
if(chunk != null) {
if(entry instanceof DocumentNode) {
@ -188,9 +206,5 @@ public final class POIFSChunkParser {
grouping.record(chunk);
}
}
} catch(NumberFormatException e) {
// Name in the wrong format
return;
}
}
}