reindent code - prepare for cleanups

git-svn-id: https://svn.apache.org/repos/asf/poi/trunk@1773544 13f79535-47bb-0310-9956-ffa450edef68
This commit is contained in:
Andreas Beeker 2016-12-10 23:35:12 +00:00
parent f27507244c
commit 6a8cb7493c
18 changed files with 2574 additions and 2574 deletions

View File

@ -34,7 +34,8 @@ import org.apache.poi.util.POILogFactory;
import org.apache.poi.util.POILogger; import org.apache.poi.util.POILogger;
/** /**
* Collection of convenience chunks for standard parts of the MSG file attachment. * Collection of convenience chunks for standard parts of the MSG file
* attachment.
*/ */
public class AttachmentChunks implements ChunkGroup { public class AttachmentChunks implements ChunkGroup {
private static POILogger logger = POILogFactory.getLogger(AttachmentChunks.class); private static POILogger logger = POILogFactory.getLogger(AttachmentChunks.class);
@ -48,9 +49,8 @@ public class AttachmentChunks implements ChunkGroup {
public DirectoryChunk attachmentDirectory; public DirectoryChunk attachmentDirectory;
/** /**
* This is in WMF Format. You'll probably want to pass it * This is in WMF Format. You'll probably want to pass it to Apache Batik to
* to Apache Batik to turn it into a SVG that you can * turn it into a SVG that you can then display.
* then display.
*/ */
public ByteChunk attachRenderingWMF; public ByteChunk attachRenderingWMF;
@ -62,21 +62,20 @@ public class AttachmentChunks implements ChunkGroup {
/** Holds all the chunks that were found. */ /** Holds all the chunks that were found. */
private List<Chunk> allChunks = new ArrayList<Chunk>(); private List<Chunk> allChunks = new ArrayList<Chunk>();
public AttachmentChunks(String poifsName) { public AttachmentChunks(String poifsName) {
this.poifsName = poifsName; this.poifsName = poifsName;
} }
/** /**
* Is this Attachment an embedded MAPI message? * Is this Attachment an embedded MAPI message?
*/ */
public boolean isEmbeddedMessage() { public boolean isEmbeddedMessage() {
return (attachmentDirectory != null); return (attachmentDirectory != null);
} }
/** /**
* Returns the embedded MAPI message, if the attachment * Returns the embedded MAPI message, if the attachment is an embedded
* is an embedded message, or null otherwise * message, or null otherwise
*/ */
public MAPIMessage getEmbeddedMessage() throws IOException { public MAPIMessage getEmbeddedMessage() throws IOException {
if (attachmentDirectory != null) { if (attachmentDirectory != null) {
@ -86,9 +85,8 @@ public class AttachmentChunks implements ChunkGroup {
} }
/** /**
* Returns the embedded object, if the attachment is an * Returns the embedded object, if the attachment is an object based
* object based embedding (image, document etc), or null * embedding (image, document etc), or null if it's an embedded message
* if it's an embedded message
*/ */
public byte[] getEmbeddedAttachmentObject() { public byte[] getEmbeddedAttachmentObject() {
if (attachData != null) { if (attachData != null) {
@ -100,6 +98,7 @@ public class AttachmentChunks implements ChunkGroup {
public Chunk[] getAll() { public Chunk[] getAll() {
return allChunks.toArray(new Chunk[allChunks.size()]); return allChunks.toArray(new Chunk[allChunks.size()]);
} }
public Chunk[] getChunks() { public Chunk[] getChunks() {
return getAll(); return getAll();
} }
@ -123,23 +122,23 @@ public class AttachmentChunks implements ChunkGroup {
// - ATTACH_SIZE // - ATTACH_SIZE
final int chunkId = chunk.getChunkId(); final int chunkId = chunk.getChunkId();
if (chunkId == ATTACH_DATA.id) { if (chunkId == ATTACH_DATA.id) {
if(chunk instanceof ByteChunk) { if (chunk instanceof ByteChunk) {
attachData = (ByteChunk)chunk; attachData = (ByteChunk) chunk;
} else if(chunk instanceof DirectoryChunk) { } else if (chunk instanceof DirectoryChunk) {
attachmentDirectory = (DirectoryChunk)chunk; attachmentDirectory = (DirectoryChunk) chunk;
} else { } else {
logger.log(POILogger.ERROR, "Unexpected data chunk of type " + chunk); logger.log(POILogger.ERROR, "Unexpected data chunk of type " + chunk);
} }
} else if(chunkId == ATTACH_EXTENSION.id) { } else if (chunkId == ATTACH_EXTENSION.id) {
attachExtension = (StringChunk)chunk; attachExtension = (StringChunk) chunk;
} else if(chunkId == ATTACH_FILENAME.id) { } else if (chunkId == ATTACH_FILENAME.id) {
attachFileName = (StringChunk)chunk; attachFileName = (StringChunk) chunk;
} else if(chunkId == ATTACH_LONG_FILENAME.id) { } else if (chunkId == ATTACH_LONG_FILENAME.id) {
attachLongFileName = (StringChunk)chunk; attachLongFileName = (StringChunk) chunk;
} else if(chunkId == ATTACH_MIME_TAG.id) { } else if (chunkId == ATTACH_MIME_TAG.id) {
attachMimeTag = (StringChunk)chunk; attachMimeTag = (StringChunk) chunk;
} else if(chunkId == ATTACH_RENDERING.id) { } else if (chunkId == ATTACH_RENDERING.id) {
attachRenderingWMF = (ByteChunk)chunk; attachRenderingWMF = (ByteChunk) chunk;
} }
// And add to the main list // And add to the main list
@ -147,19 +146,18 @@ public class AttachmentChunks implements ChunkGroup {
} }
/** /**
* Used to flag that all the chunks of the attachment * Used to flag that all the chunks of the attachment have now been located.
* have now been located.
*/ */
public void chunksComplete() { public void chunksComplete() {
// Currently, we don't need to do anything special once // Currently, we don't need to do anything special once
// all the chunks have been located // all the chunks have been located
} }
/** /**
* Orders by the attachment number. * Orders by the attachment number.
*/ */
public static class AttachmentChunksSorter implements Comparator<AttachmentChunks>, Serializable { public static class AttachmentChunksSorter
implements Comparator<AttachmentChunks>, Serializable {
public int compare(AttachmentChunks a, AttachmentChunks b) { public int compare(AttachmentChunks a, AttachmentChunks b) {
return a.poifsName.compareTo(b.poifsName); return a.poifsName.compareTo(b.poifsName);
} }

View File

@ -24,10 +24,9 @@ import org.apache.poi.hsmf.datatypes.Types.MAPIType;
import org.apache.poi.util.IOUtils; import org.apache.poi.util.IOUtils;
/** /**
* A Chunk that holds binary data, normally unparsed. * A Chunk that holds binary data, normally unparsed. Generally as we know how
* Generally as we know how to make sense of the * to make sense of the contents, we create a new Chunk class and add a special
* contents, we create a new Chunk class and add * case in the parser for them.
* a special case in the parser for them.
*/ */
public class ByteChunk extends Chunk { public class ByteChunk extends Chunk {
@ -39,9 +38,9 @@ public class ByteChunk extends Chunk {
public ByteChunk(String namePrefix, int chunkId, MAPIType type) { public ByteChunk(String namePrefix, int chunkId, MAPIType type) {
super(namePrefix, chunkId, type); super(namePrefix, chunkId, type);
} }
/** /**
* Create a Byte Chunk, with the specified * Create a Byte Chunk, with the specified type.
* type.
*/ */
public ByteChunk(int chunkId, MAPIType type) { public ByteChunk(int chunkId, MAPIType type) {
super(chunkId, type); super(chunkId, type);
@ -58,6 +57,7 @@ public class ByteChunk extends Chunk {
public byte[] getValue() { public byte[] getValue() {
return value; return value;
} }
public void setValue(byte[] value) { public void setValue(byte[] value) {
this.value = value; this.value = value;
} }
@ -70,9 +70,8 @@ public class ByteChunk extends Chunk {
} }
/** /**
* Formats the byte array in a debug-friendly way, * Formats the byte array in a debug-friendly way, showing all of a short
* showing all of a short array, and the start of a * array, and the start of a longer one.
* longer one.
*/ */
protected static String toDebugFriendlyString(byte[] value) { protected static String toDebugFriendlyString(byte[] value) {
if (value == null) if (value == null)
@ -86,7 +85,7 @@ public class ByteChunk extends Chunk {
if (value.length > 16) { if (value.length > 16) {
limit = 12; limit = 12;
} }
for (int i=0; i<limit; i++) { for (int i = 0; i < limit; i++) {
if (i > 0) if (i > 0)
text.append(','); text.append(',');
text.append(value[i]); text.append(value[i]);
@ -99,11 +98,10 @@ public class ByteChunk extends Chunk {
} }
/** /**
* Returns the data, formatted as a string assuming it * Returns the data, formatted as a string assuming it was a non-unicode
* was a non-unicode string. * string. If your data isn't in fact stored as basically ASCII, don't
* If your data isn't in fact stored as basically * expect this to return much of any sense....
* ASCII, don't expect this to return much of any *
* sense....
* @return the data formatted as a string * @return the data formatted as a string
*/ */
public String getAs7bitString() { public String getAs7bitString() {

View File

@ -36,6 +36,7 @@ public abstract class Chunk {
this.chunkId = chunkId; this.chunkId = chunkId;
this.type = type; this.type = type;
} }
protected Chunk(int chunkId, MAPIType type) { protected Chunk(int chunkId, MAPIType type) {
this(DEFAULT_NAME_PREFIX, chunkId, type); this(DEFAULT_NAME_PREFIX, chunkId, type);
} }
@ -55,15 +56,18 @@ public abstract class Chunk {
} }
/** /**
* Creates a string to use to identify this chunk in the POI file system object. * Creates a string to use to identify this chunk in the POI file system
* object.
*/ */
public String getEntryName() { public String getEntryName() {
String type = this.type.asFileEnding(); String type = this.type.asFileEnding();
String chunkId = Integer.toHexString(this.chunkId); String chunkId = Integer.toHexString(this.chunkId);
while(chunkId.length() < 4) chunkId = "0" + chunkId; while (chunkId.length() < 4)
chunkId = "0" + chunkId;
return this.namePrefix + chunkId.toUpperCase(Locale.ROOT) return this.namePrefix
+ chunkId.toUpperCase(Locale.ROOT)
+ type.toUpperCase(Locale.ROOT); + type.toUpperCase(Locale.ROOT);
} }

View File

@ -17,10 +17,8 @@
package org.apache.poi.hsmf.datatypes; package org.apache.poi.hsmf.datatypes;
/** /**
* A variable length {@link PropertyValue} that is * A variable length {@link PropertyValue} that is backed by a {@link Chunk}
* backed by a {@link Chunk}
* TODO Provide a way to link these up with the chunks * TODO Provide a way to link these up with the chunks
*/ */
public class ChunkBasedPropertyValue extends PropertyValue { public class ChunkBasedPropertyValue extends PropertyValue {

View File

@ -18,14 +18,12 @@
package org.apache.poi.hsmf.datatypes; package org.apache.poi.hsmf.datatypes;
/** /**
* A group of chunks, that are at the same point in the * A group of chunks, that are at the same point in the file structure.
* file structure.
*/ */
public interface ChunkGroup { public interface ChunkGroup {
/** /**
* Returns the chunks that make up the group. * Returns the chunks that make up the group. Should certainly contain all
* Should certainly contain all the interesting Chunks, * the interesting Chunks, but needn't always contain all of the Chunks.
* but needn't always contain all of the Chunks.
*/ */
public Chunk[] getChunks(); public Chunk[] getChunks();

View File

@ -21,18 +21,15 @@ import java.util.List;
import java.util.Map; import java.util.Map;
/** /**
* A group of chunks which is indexable by {@link MAPIProperty} * A group of chunks which is indexable by {@link MAPIProperty} entries.
* entries.
*/ */
public interface ChunkGroupWithProperties extends ChunkGroup { public interface ChunkGroupWithProperties extends ChunkGroup {
/** /**
* Returns all the Properties contained in the Chunk, along * Returns all the Properties contained in the Chunk, along with their
* with their Values. * Values. Normally, each property will have one value, sometimes none, and
* Normally, each property will have one value, sometimes * rarely multiple (normally for Unknown etc). For fixed sized properties,
* none, and rarely multiple (normally for Unknown etc). * the value can be fetched straight from the {@link PropertyValue}. For
* For fixed sized properties, the value can be fetched * variable sized properties, you'll need to go via the chunk.
* straight from the {@link PropertyValue}. For variable
* sized properties, you'll need to go via the chunk.
*/ */
public Map<MAPIProperty,List<PropertyValue>> getProperties(); public Map<MAPIProperty, List<PropertyValue>> getProperties();
} }

View File

@ -26,7 +26,6 @@ import java.util.Map;
import org.apache.poi.util.POILogFactory; import org.apache.poi.util.POILogFactory;
import org.apache.poi.util.POILogger; import org.apache.poi.util.POILogger;
/** /**
* Collection of convenience chunks for standard parts of the MSG file. * Collection of convenience chunks for standard parts of the MSG file.
* *
@ -45,7 +44,7 @@ public final class Chunks implements ChunkGroupWithProperties {
* Normally a property will have zero chunks (fixed sized) or one chunk * Normally a property will have zero chunks (fixed sized) or one chunk
* (variable size), but in some cases (eg Unknown) you may get more. * (variable size), but in some cases (eg Unknown) you may get more.
*/ */
private Map<MAPIProperty,List<Chunk>> allChunks = new HashMap<MAPIProperty,List<Chunk>>(); private Map<MAPIProperty, List<Chunk>> allChunks = new HashMap<MAPIProperty, List<Chunk>>();
/** Type of message that the MSG represents (ie. IPM.Note) */ /** Type of message that the MSG represents (ie. IPM.Note) */
public StringChunk messageClass; public StringChunk messageClass;
@ -84,22 +83,24 @@ public final class Chunks implements ChunkGroupWithProperties {
/** The message properties */ /** The message properties */
private MessagePropertiesChunk messageProperties; private MessagePropertiesChunk messageProperties;
public Map<MAPIProperty,List<PropertyValue>> getProperties() { public Map<MAPIProperty, List<PropertyValue>> getProperties() {
if (messageProperties != null) { if (messageProperties != null) {
return messageProperties.getProperties(); return messageProperties.getProperties();
} else
return Collections.emptyMap();
} }
else return Collections.emptyMap();
}
public Map<MAPIProperty, PropertyValue> getRawProperties() { public Map<MAPIProperty, PropertyValue> getRawProperties() {
if (messageProperties != null) { if (messageProperties != null) {
return messageProperties.getRawProperties(); return messageProperties.getRawProperties();
} } else
else return Collections.emptyMap(); return Collections.emptyMap();
} }
public Map<MAPIProperty,List<Chunk>> getAll() { public Map<MAPIProperty, List<Chunk>> getAll() {
return allChunks; return allChunks;
} }
public Chunk[] getChunks() { public Chunk[] getChunks() {
ArrayList<Chunk> chunks = new ArrayList<Chunk>(allChunks.size()); ArrayList<Chunk> chunks = new ArrayList<Chunk>(allChunks.size());
for (List<Chunk> c : allChunks.values()) { for (List<Chunk> c : allChunks.values()) {
@ -116,64 +117,51 @@ public final class Chunks implements ChunkGroupWithProperties {
MAPIProperty prop = MAPIProperty.get(chunk.getChunkId()); MAPIProperty prop = MAPIProperty.get(chunk.getChunkId());
// Assign it for easy lookup, as best we can // Assign it for easy lookup, as best we can
if(prop == MAPIProperty.MESSAGE_CLASS) { if (prop == MAPIProperty.MESSAGE_CLASS) {
messageClass = (StringChunk)chunk; messageClass = (StringChunk) chunk;
} } else if (prop == MAPIProperty.INTERNET_MESSAGE_ID) {
else if(prop == MAPIProperty.INTERNET_MESSAGE_ID) { messageId = (StringChunk) chunk;
messageId = (StringChunk)chunk; } else if (prop == MAPIProperty.MESSAGE_SUBMISSION_ID) {
}
else if(prop == MAPIProperty.MESSAGE_SUBMISSION_ID) {
// TODO - parse // TODO - parse
submissionChunk = (MessageSubmissionChunk)chunk; submissionChunk = (MessageSubmissionChunk) chunk;
} } else if (prop == MAPIProperty.RECEIVED_BY_ADDRTYPE) {
else if(prop == MAPIProperty.RECEIVED_BY_ADDRTYPE) { sentByServerType = (StringChunk) chunk;
sentByServerType = (StringChunk)chunk; } else if (prop == MAPIProperty.TRANSPORT_MESSAGE_HEADERS) {
} messageHeaders = (StringChunk) chunk;
else if(prop == MAPIProperty.TRANSPORT_MESSAGE_HEADERS) {
messageHeaders = (StringChunk)chunk;
} }
else if(prop == MAPIProperty.CONVERSATION_TOPIC) { else if (prop == MAPIProperty.CONVERSATION_TOPIC) {
conversationTopic = (StringChunk)chunk; conversationTopic = (StringChunk) chunk;
} } else if (prop == MAPIProperty.SUBJECT) {
else if(prop == MAPIProperty.SUBJECT) { subjectChunk = (StringChunk) chunk;
subjectChunk = (StringChunk)chunk; } else if (prop == MAPIProperty.ORIGINAL_SUBJECT) {
}
else if(prop == MAPIProperty.ORIGINAL_SUBJECT) {
// TODO // TODO
} }
else if(prop == MAPIProperty.DISPLAY_TO) { else if (prop == MAPIProperty.DISPLAY_TO) {
displayToChunk = (StringChunk)chunk; displayToChunk = (StringChunk) chunk;
} } else if (prop == MAPIProperty.DISPLAY_CC) {
else if(prop == MAPIProperty.DISPLAY_CC) { displayCCChunk = (StringChunk) chunk;
displayCCChunk = (StringChunk)chunk; } else if (prop == MAPIProperty.DISPLAY_BCC) {
} displayBCCChunk = (StringChunk) chunk;
else if(prop == MAPIProperty.DISPLAY_BCC) {
displayBCCChunk = (StringChunk)chunk;
} }
else if(prop == MAPIProperty.SENDER_EMAIL_ADDRESS) { else if (prop == MAPIProperty.SENDER_EMAIL_ADDRESS) {
emailFromChunk = (StringChunk)chunk; emailFromChunk = (StringChunk) chunk;
} else if (prop == MAPIProperty.SENDER_NAME) {
displayFromChunk = (StringChunk) chunk;
} else if (prop == MAPIProperty.BODY) {
textBodyChunk = (StringChunk) chunk;
} else if (prop == MAPIProperty.BODY_HTML) {
if (chunk instanceof StringChunk) {
htmlBodyChunkString = (StringChunk) chunk;
} }
else if(prop == MAPIProperty.SENDER_NAME) { if (chunk instanceof ByteChunk) {
displayFromChunk = (StringChunk)chunk; htmlBodyChunkBinary = (ByteChunk) chunk;
} }
else if(prop == MAPIProperty.BODY) { } else if (prop == MAPIProperty.RTF_COMPRESSED) {
textBodyChunk = (StringChunk)chunk; rtfBodyChunk = (ByteChunk) chunk;
} } else if (chunk instanceof MessagePropertiesChunk) {
else if(prop == MAPIProperty.BODY_HTML) {
if(chunk instanceof StringChunk) {
htmlBodyChunkString = (StringChunk)chunk;
}
if(chunk instanceof ByteChunk) {
htmlBodyChunkBinary = (ByteChunk)chunk;
}
}
else if(prop == MAPIProperty.RTF_COMPRESSED) {
rtfBodyChunk = (ByteChunk)chunk;
}
else if(chunk instanceof MessagePropertiesChunk) {
messageProperties = (MessagePropertiesChunk) chunk; messageProperties = (MessagePropertiesChunk) chunk;
} }
@ -188,7 +176,8 @@ public final class Chunks implements ChunkGroupWithProperties {
if (messageProperties != null) { if (messageProperties != null) {
messageProperties.matchVariableSizedPropertiesToChunks(); messageProperties.matchVariableSizedPropertiesToChunks();
} else { } else {
logger.log(POILogger.WARN, "Message didn't contain a root list of properties!"); logger.log(POILogger.WARN,
"Message didn't contain a root list of properties!");
} }
} }
} }

View File

@ -25,11 +25,8 @@ import org.apache.poi.hsmf.datatypes.Types.MAPIType;
import org.apache.poi.poifs.filesystem.DirectoryNode; import org.apache.poi.poifs.filesystem.DirectoryNode;
/** /**
* A Chunk that is just a placeholder in the * A Chunk that is just a placeholder in the MAPIMessage directory structure,
* MAPIMessage directory structure, which * which contains children. This is most commonly used with nested MAPIMessages
* contains children.
* This is most commonly used with nested
* MAPIMessages
*/ */
public class DirectoryChunk extends Chunk { public class DirectoryChunk extends Chunk {
private DirectoryNode dir; private DirectoryNode dir;
@ -40,18 +37,16 @@ public class DirectoryChunk extends Chunk {
} }
/** /**
* Returns the directory entry for this chunk. * Returns the directory entry for this chunk. You can then use standard
* You can then use standard POIFS methods to * POIFS methods to enumerate the entries in it.
* enumerate the entries in it.
*/ */
public DirectoryNode getDirectory() { public DirectoryNode getDirectory() {
return dir; return dir;
} }
/** /**
* Treats the directory as an embeded MAPIMessage * Treats the directory as an embeded MAPIMessage (it normally is one), and
* (it normally is one), and returns a MAPIMessage * returns a MAPIMessage object to process it with.
* object to process it with.
*/ */
public MAPIMessage getAsEmbededMessage() throws IOException { public MAPIMessage getAsEmbededMessage() throws IOException {
return new MAPIMessage(dir); return new MAPIMessage(dir);

View File

@ -36,8 +36,8 @@ import java.util.Map;
import org.apache.poi.hsmf.datatypes.Types.MAPIType; import org.apache.poi.hsmf.datatypes.Types.MAPIType;
/** /**
* Holds the list of MAPI Attributes, and allows lookup * Holds the list of MAPI Attributes, and allows lookup by friendly name, ID and
* by friendly name, ID and MAPI Property Name. * MAPI Property Name.
* *
* These are taken from the following MSDN resources: * These are taken from the following MSDN resources:
* https://msdn.microsoft.com/en-us/library/microsoft.exchange.data.contenttypes.tnef.tnefpropertyid(v=exchg.150).aspx * https://msdn.microsoft.com/en-us/library/microsoft.exchange.data.contenttypes.tnef.tnefpropertyid(v=exchg.150).aspx
@ -1027,7 +1027,8 @@ public class MAPIProperty {
new MAPIProperty(-1, Types.UNKNOWN, "Unknown", null); new MAPIProperty(-1, Types.UNKNOWN, "Unknown", null);
// 0x8??? ones are outlook specific, and not standard MAPI // 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 // 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 // info on how we might decode them properly in the future
private static final int ID_FIRST_CUSTOM = 0x8000; private static final int ID_FIRST_CUSTOM = 0x8000;
private static final int ID_LAST_CUSTOM = 0xFFFE; private static final int ID_LAST_CUSTOM = 0xFFFE;
@ -1039,22 +1040,24 @@ public class MAPIProperty {
public final String name; public final String name;
public final String mapiProperty; public final String mapiProperty;
private MAPIProperty(int id, MAPIType usualType, String name, String mapiProperty) { private MAPIProperty(int id, MAPIType usualType, String name,
String mapiProperty) {
this.id = id; this.id = id;
this.usualType = usualType; this.usualType = usualType;
this.name = name; this.name = name;
this.mapiProperty = mapiProperty; this.mapiProperty = mapiProperty;
// If it isn't unknown or custom, store it for lookup // If it isn't unknown or custom, store it for lookup
if(id == -1 || (id >= ID_FIRST_CUSTOM && id <= ID_LAST_CUSTOM) if (id == -1
|| (id >= ID_FIRST_CUSTOM && id <= ID_LAST_CUSTOM)
|| (this instanceof CustomMAPIProperty)) { || (this instanceof CustomMAPIProperty)) {
// Custom/Unknown, skip // Custom/Unknown, skip
} else { } else {
if(attributes.containsKey(id)) { if (attributes.containsKey(id)) {
throw new IllegalArgumentException( throw new IllegalArgumentException(
"Duplicate MAPI Property with ID " + id + " : " + "Duplicate MAPI Property with ID " + id + " : "
toString() + " vs " + attributes.get(id).toString() + toString() + " vs "
); + attributes.get(id).toString());
} }
attributes.put(id, this); attributes.put(id, this);
} }
@ -1062,7 +1065,7 @@ public class MAPIProperty {
public String asFileName() { public String asFileName() {
String str = Integer.toHexString(id).toUpperCase(Locale.ROOT); String str = Integer.toHexString(id).toUpperCase(Locale.ROOT);
while(str.length() < 4) { while (str.length() < 4) {
str = "0" + str; str = "0" + str;
} }
return str + usualType.asFileEnding(); return str + usualType.asFileEnding();
@ -1074,7 +1077,7 @@ public class MAPIProperty {
str.append(" ["); str.append(" [");
str.append(id); str.append(id);
str.append("]"); str.append("]");
if(mapiProperty != null) { if (mapiProperty != null) {
str.append(" ("); str.append(" (");
str.append(mapiProperty); str.append(mapiProperty);
str.append(")"); str.append(")");
@ -1084,14 +1087,15 @@ public class MAPIProperty {
public static MAPIProperty get(int id) { public static MAPIProperty get(int id) {
MAPIProperty attr = attributes.get(id); MAPIProperty attr = attributes.get(id);
if(attr != null) { if (attr != null) {
return attr; return attr;
} else { } else {
return UNKNOWN; return UNKNOWN;
} }
} }
public static Collection<MAPIProperty> getAll() { public static Collection<MAPIProperty> getAll() {
return Collections.unmodifiableCollection( attributes.values() ); return Collections.unmodifiableCollection(attributes.values());
} }
public static MAPIProperty createCustom(int id, MAPIType type, String name) { public static MAPIProperty createCustom(int id, MAPIType type, String name) {

View File

@ -24,8 +24,8 @@ import java.io.OutputStream;
import org.apache.poi.util.LittleEndian; import org.apache.poi.util.LittleEndian;
/** /**
* A {@link PropertiesChunk} for a Message or Embedded-Message. * A {@link PropertiesChunk} for a Message or Embedded-Message. This has a 32
* This has a 32 byte header * byte header
*/ */
public class MessagePropertiesChunk extends PropertiesChunk { public class MessagePropertiesChunk extends PropertiesChunk {
private long nextRecipientId; private long nextRecipientId;
@ -40,6 +40,7 @@ public class MessagePropertiesChunk extends PropertiesChunk {
public long getNextRecipientId() { public long getNextRecipientId() {
return nextRecipientId; return nextRecipientId;
} }
public long getNextAttachmentId() { public long getNextAttachmentId() {
return nextAttachmentId; return nextAttachmentId;
} }
@ -47,6 +48,7 @@ public class MessagePropertiesChunk extends PropertiesChunk {
public long getRecipientCount() { public long getRecipientCount() {
return recipientCount; return recipientCount;
} }
public long getAttachmentCount() { public long getAttachmentCount() {
return attachmentCount; return attachmentCount;
} }

View File

@ -31,30 +31,29 @@ import org.apache.poi.util.POILogFactory;
import org.apache.poi.util.POILogger; import org.apache.poi.util.POILogger;
/** /**
* A Chunk that holds the details given back by the * A Chunk that holds the details given back by the server at submission time.
* server at submission time. * This includes the date the message was given to the server, and an ID that's
* This includes the date the message was given to the * used if you want to cancel a message or similar
* server, and an ID that's used if you want to cancel
* a message or similar
*/ */
public class MessageSubmissionChunk extends Chunk { public class MessageSubmissionChunk extends Chunk {
private static POILogger logger = POILogFactory.getLogger(MessageSubmissionChunk.class); private static POILogger logger = POILogFactory
.getLogger(MessageSubmissionChunk.class);
private String rawId; private String rawId;
private Calendar date; private Calendar date;
private static final Pattern datePatern = private static final Pattern datePatern = Pattern
Pattern.compile("(\\d\\d)(\\d\\d)(\\d\\d)(\\d\\d)(\\d\\d)(\\d\\d)Z?"); .compile("(\\d\\d)(\\d\\d)(\\d\\d)(\\d\\d)(\\d\\d)(\\d\\d)Z?");
/** /**
* Creates a Byte Chunk. * Creates a Byte Chunk.
*/ */
public MessageSubmissionChunk(String namePrefix, int chunkId, MAPIType type) { public MessageSubmissionChunk(String namePrefix, int chunkId,
MAPIType type) {
super(namePrefix, chunkId, type); super(namePrefix, chunkId, type);
} }
/** /**
* Create a Byte Chunk, with the specified * Create a Byte Chunk, with the specified type.
* type.
*/ */
public MessageSubmissionChunk(int chunkId, MAPIType type) { public MessageSubmissionChunk(int chunkId, MAPIType type) {
super(chunkId, type); super(chunkId, type);
@ -67,39 +66,46 @@ public class MessageSubmissionChunk extends Chunk {
// Now process the date // Now process the date
String[] parts = rawId.split(";"); String[] parts = rawId.split(";");
for(String part : parts) { for (String part : parts) {
if(part.startsWith("l=")) { if (part.startsWith("l=")) {
// Format of this bit appears to be l=<id>-<time>-<number> // Format of this bit appears to be l=<id>-<time>-<number>
// ID may contain hyphens. // ID may contain hyphens.
String dateS = null; String dateS = null;
final int numberPartBegin = part.lastIndexOf('-'); final int numberPartBegin = part.lastIndexOf('-');
if (numberPartBegin != -1) { if (numberPartBegin != -1) {
final int datePartBegin = part.lastIndexOf('-', numberPartBegin-1); final int datePartBegin = part.lastIndexOf('-',
numberPartBegin - 1);
if (datePartBegin != -1 && if (datePartBegin != -1 &&
// cannot extract date if only one hyphen is in the string... // cannot extract date if only one hyphen is in the
// string...
numberPartBegin > datePartBegin) { numberPartBegin > datePartBegin) {
dateS = part.substring(datePartBegin + 1, numberPartBegin); dateS = part.substring(datePartBegin + 1,
numberPartBegin);
} }
} }
if (dateS != null) { if (dateS != null) {
// Should be yymmddhhmmssZ // Should be yymmddhhmmssZ
Matcher m = datePatern.matcher(dateS); Matcher m = datePatern.matcher(dateS);
if(m.matches()) { if (m.matches()) {
date = LocaleUtil.getLocaleCalendar(); date = LocaleUtil.getLocaleCalendar();
// work around issues with dates like 1989, which appear as "89" here // work around issues with dates like 1989, which appear as "89" here
int year = Integer.parseInt(m.group(1)); int year = Integer.parseInt(m.group(1));
date.set(Calendar.YEAR, year + (year > 80 ? 1900 : 2000)); date.set(Calendar.YEAR, year + (year > 80 ? 1900 : 2000));
date.set(Calendar.MONTH, Integer.parseInt(m.group(2)) - 1); // Java is 0 based // Java is 0 based
date.set(Calendar.MONTH, Integer.parseInt(m.group(2)) - 1);
date.set(Calendar.DATE, Integer.parseInt(m.group(3))); date.set(Calendar.DATE, Integer.parseInt(m.group(3)));
date.set(Calendar.HOUR_OF_DAY, Integer.parseInt(m.group(4))); date.set(Calendar.HOUR_OF_DAY,
Integer.parseInt(m.group(4)));
date.set(Calendar.MINUTE, Integer.parseInt(m.group(5))); date.set(Calendar.MINUTE, Integer.parseInt(m.group(5)));
date.set(Calendar.SECOND, Integer.parseInt(m.group(6))); date.set(Calendar.SECOND, Integer.parseInt(m.group(6)));
date.clear(Calendar.MILLISECOND); date.clear(Calendar.MILLISECOND);
} else { } else {
logger.log(POILogger.WARN, "Warning - unable to make sense of date " + dateS); logger.log(POILogger.WARN,
"Warning - unable to make sense of date "
+ dateS);
} }
} }
} }
@ -112,8 +118,8 @@ public class MessageSubmissionChunk extends Chunk {
} }
/** /**
* @return the date that the server accepted the * @return the date that the server accepted the message, as found from the
* message, as found from the message ID it generated. * message ID it generated.
* *
*/ */
public Calendar getAcceptedAtTime() { public Calendar getAcceptedAtTime() {
@ -121,8 +127,8 @@ public class MessageSubmissionChunk extends Chunk {
} }
/** /**
* @return the full ID that the server generated when * @return the full ID that the server generated when it accepted the
* it accepted the message. * message.
*/ */
public String getSubmissionId() { public String getSubmissionId() {
return rawId; return rawId;

View File

@ -20,10 +20,8 @@ package org.apache.poi.hsmf.datatypes;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
/** /**
* Collection of convenience chunks for the * Collection of convenience chunks for the NameID part of an outlook file
* NameID part of an outlook file
*/ */
public final class NameIdChunks implements ChunkGroup { public final class NameIdChunks implements ChunkGroup {
public static final String NAME = "__nameid_version1.0"; public static final String NAME = "__nameid_version1.0";
@ -34,6 +32,7 @@ public final class NameIdChunks implements ChunkGroup {
public Chunk[] getAll() { public Chunk[] getAll() {
return allChunks.toArray(new Chunk[allChunks.size()]); return allChunks.toArray(new Chunk[allChunks.size()]);
} }
public Chunk[] getChunks() { public Chunk[] getChunks() {
return getAll(); return getAll();
} }
@ -46,8 +45,7 @@ public final class NameIdChunks implements ChunkGroup {
} }
/** /**
* Used to flag that all the chunks of the NameID * Used to flag that all the chunks of the NameID have now been located.
* have now been located.
*/ */
public void chunksComplete() { public void chunksComplete() {
// Currently, we don't need to do anything special once // Currently, we don't need to do anything special once

View File

@ -42,11 +42,11 @@ import org.apache.poi.util.POILogFactory;
import org.apache.poi.util.POILogger; import org.apache.poi.util.POILogger;
/** /**
* <p>A Chunk which holds (single) fixed-length properties, and pointer * <p>
* to the variable length ones / multi-valued ones (which get their * A Chunk which holds (single) fixed-length properties, and pointer to the
* own chunk). * variable length ones / multi-valued ones (which get their own chunk).
* <p>There are two kinds of PropertiesChunks, which differ only in * <p>
* their headers. * There are two kinds of PropertiesChunks, which differ only in their headers.
*/ */
public abstract class PropertiesChunk extends Chunk { public abstract class PropertiesChunk extends Chunk {
public static final String NAME = "__properties_version1.0"; public static final String NAME = "__properties_version1.0";
@ -55,15 +55,14 @@ public abstract class PropertiesChunk extends Chunk {
private POILogger logger = POILogFactory.getLogger(PropertiesChunk.class); private POILogger logger = POILogFactory.getLogger(PropertiesChunk.class);
/** /**
* Holds properties, indexed by type. If a property is multi-valued, * Holds properties, indexed by type. If a property is multi-valued, or
* or variable length, it will be held via a {@link ChunkBasedPropertyValue}. * variable length, it will be held via a {@link ChunkBasedPropertyValue}.
*/ */
private Map<MAPIProperty, PropertyValue> properties = private Map<MAPIProperty, PropertyValue> properties = new HashMap<MAPIProperty, PropertyValue>();
new HashMap<MAPIProperty, PropertyValue>();
/** /**
* The ChunkGroup that these properties apply to. Used when * The ChunkGroup that these properties apply to. Used when matching chunks
* matching chunks to variable sized and multi-valued properties * to variable sized and multi-valued properties
*/ */
private ChunkGroup parentGroup; private ChunkGroup parentGroup;
@ -81,17 +80,18 @@ public abstract class PropertiesChunk extends Chunk {
} }
/** /**
* Returns all the properties in the chunk, without * Returns all the properties in the chunk, without looking up any
* looking up any chunk-based values * chunk-based values
*/ */
public Map<MAPIProperty, PropertyValue> getRawProperties() { public Map<MAPIProperty, PropertyValue> getRawProperties() {
return properties; return properties;
} }
/** /**
* <p>Returns all the properties in the chunk, along with their * <p>
* values. * Returns all the properties in the chunk, along with their values.
* <p>Any chunk-based values will be looked up and returned as such * <p>
* Any chunk-based values will be looked up and returned as such
*/ */
public Map<MAPIProperty, List<PropertyValue>> getProperties() { public Map<MAPIProperty, List<PropertyValue>> getProperties() {
Map<MAPIProperty, List<PropertyValue>> props = Map<MAPIProperty, List<PropertyValue>> props =
@ -103,8 +103,8 @@ public abstract class PropertiesChunk extends Chunk {
} }
/** /**
* Returns all values for the given property, looking up chunk based * Returns all values for the given property, looking up chunk based ones as
* ones as required, of null if none exist * required, of null if none exist
*/ */
public List<PropertyValue> getValues(MAPIProperty property) { public List<PropertyValue> getValues(MAPIProperty property) {
PropertyValue val = properties.get(property); PropertyValue val = properties.get(property);
@ -121,21 +121,21 @@ public abstract class PropertiesChunk extends Chunk {
} }
/** /**
* Returns the value / pointer to the value chunk of * Returns the value / pointer to the value chunk of the property, or null
* the property, or null if none exists * if none exists
*/ */
public PropertyValue getRawValue(MAPIProperty property) { public PropertyValue getRawValue(MAPIProperty property) {
return properties.get(property); return properties.get(property);
} }
/** /**
* Called once the parent ChunkGroup has been populated, to match * Called once the parent ChunkGroup has been populated, to match up the
* up the Chunks in it with our Variable Sized Properties. * Chunks in it with our Variable Sized Properties.
*/ */
protected void matchVariableSizedPropertiesToChunks() { protected void matchVariableSizedPropertiesToChunks() {
// Index the Parent Group chunks for easy lookup // Index the Parent Group chunks for easy lookup
// TODO Is this the right way? // TODO Is this the right way?
Map<Integer,Chunk> chunks = new HashMap<Integer, Chunk>(); Map<Integer, Chunk> chunks = new HashMap<Integer, Chunk>();
for (Chunk chunk : parentGroup.getChunks()) { for (Chunk chunk : parentGroup.getChunks()) {
chunks.put(chunk.chunkId, chunk); chunks.put(chunk.chunkId, chunk);
} }
@ -143,9 +143,10 @@ public abstract class PropertiesChunk extends Chunk {
// Loop over our values, looking for chunk based ones // Loop over our values, looking for chunk based ones
for (PropertyValue val : properties.values()) { for (PropertyValue val : properties.values()) {
if (val instanceof ChunkBasedPropertyValue) { if (val instanceof ChunkBasedPropertyValue) {
ChunkBasedPropertyValue cVal = (ChunkBasedPropertyValue)val; ChunkBasedPropertyValue cVal = (ChunkBasedPropertyValue) val;
Chunk chunk = chunks.get(cVal.getProperty().id); Chunk chunk = chunks.get(cVal.getProperty().id);
//System.err.println(cVal.getProperty() + " = " + cVal + " -> " + HexDump.toHex(cVal.data)); // System.err.println(cVal.getProperty() + " = " + cVal + " -> "
// + HexDump.toHex(cVal.data));
// TODO Make sense of the raw offset value // TODO Make sense of the raw offset value
@ -176,8 +177,9 @@ public abstract class PropertiesChunk extends Chunk {
prop = MAPIProperty.createCustom(id, type, "Unknown " + id); prop = MAPIProperty.createCustom(id, type, "Unknown " + id);
} }
if (type == null) { if (type == null) {
logger.log(POILogger.WARN, "Invalid type found, expected ", prop.usualType, logger.log(POILogger.WARN, "Invalid type found, expected ",
" but got ", typeID, " for property ", prop); prop.usualType, " but got ", typeID,
" for property ", prop);
going = false; going = false;
break; break;
} }
@ -185,18 +187,22 @@ public abstract class PropertiesChunk extends Chunk {
// Sanity check the property's type against the value's type // Sanity check the property's type against the value's type
if (prop.usualType != type) { if (prop.usualType != type) {
// Is it an allowed substitution? // Is it an allowed substitution?
if (type == Types.ASCII_STRING && prop.usualType == Types.UNICODE_STRING || if (type == Types.ASCII_STRING
type == Types.UNICODE_STRING && prop.usualType == Types.ASCII_STRING) { && prop.usualType == Types.UNICODE_STRING
// It's fine to go with the specified instead of the normal || type == Types.UNICODE_STRING
&& prop.usualType == Types.ASCII_STRING) {
// It's fine to go with the specified instead of the
// normal
} else if (prop.usualType == Types.UNKNOWN) { } else if (prop.usualType == Types.UNKNOWN) {
// We don't know what this property normally is, but it has come // We don't know what this property normally is, but it
// has come
// through with a valid type, so use that // through with a valid type, so use that
logger.log(POILogger.INFO, "Property definition for ", prop, logger.log(POILogger.INFO, "Property definition for ", prop,
" is missing a type definition, found a value with type ", type); " is missing a type definition, found a value with type ", type);
} else { } else {
// Oh dear, something has gone wrong... // Oh dear, something has gone wrong...
logger.log(POILogger.WARN, "Type mismatch, expected ", prop.usualType, logger.log(POILogger.WARN, "Type mismatch, expected ",
" but got ", type, " for property ", prop); prop.usualType, " but got ", type, " for property ", prop);
going = false; going = false;
break; break;
} }
@ -211,7 +217,7 @@ public abstract class PropertiesChunk extends Chunk {
// to another chunk which holds the data itself // to another chunk which holds the data itself
boolean isPointer = false; boolean isPointer = false;
int length = type.getLength(); int length = type.getLength();
if (! type.isFixedLength()) { if (!type.isFixedLength()) {
isPointer = true; isPointer = true;
length = 8; length = 8;
} }
@ -222,7 +228,7 @@ public abstract class PropertiesChunk extends Chunk {
// Skip over any padding // Skip over any padding
if (length < 8) { if (length < 8) {
byte[] padding = new byte[8-length]; byte[] padding = new byte[8 - length];
IOUtils.readFully(value, padding); IOUtils.readFully(value, padding);
} }
@ -231,32 +237,23 @@ public abstract class PropertiesChunk extends Chunk {
if (isPointer) { if (isPointer) {
// We'll match up the chunk later // We'll match up the chunk later
propVal = new ChunkBasedPropertyValue(prop, flags, data); propVal = new ChunkBasedPropertyValue(prop, flags, data);
} } else if (type == Types.NULL) {
else if (type == Types.NULL) {
propVal = new NullPropertyValue(prop, flags, data); propVal = new NullPropertyValue(prop, flags, data);
} } else if (type == Types.BOOLEAN) {
else if (type == Types.BOOLEAN) {
propVal = new BooleanPropertyValue(prop, flags, data); propVal = new BooleanPropertyValue(prop, flags, data);
} } else if (type == Types.SHORT) {
else if (type == Types.SHORT) {
propVal = new ShortPropertyValue(prop, flags, data); propVal = new ShortPropertyValue(prop, flags, data);
} } else if (type == Types.LONG) {
else if (type == Types.LONG) {
propVal = new LongPropertyValue(prop, flags, data); propVal = new LongPropertyValue(prop, flags, data);
} } else if (type == Types.LONG_LONG) {
else if (type == Types.LONG_LONG) {
propVal = new LongLongPropertyValue(prop, flags, data); propVal = new LongLongPropertyValue(prop, flags, data);
} } else if (type == Types.FLOAT) {
else if (type == Types.FLOAT) {
propVal = new FloatPropertyValue(prop, flags, data); propVal = new FloatPropertyValue(prop, flags, data);
} } else if (type == Types.DOUBLE) {
else if (type == Types.DOUBLE) {
propVal = new DoublePropertyValue(prop, flags, data); propVal = new DoublePropertyValue(prop, flags, data);
} } else if (type == Types.CURRENCY) {
else if (type == Types.CURRENCY) {
propVal = new CurrencyPropertyValue(prop, flags, data); propVal = new CurrencyPropertyValue(prop, flags, data);
} } else if (type == Types.TIME) {
else if (type == Types.TIME) {
propVal = new TimePropertyValue(prop, flags, data); propVal = new TimePropertyValue(prop, flags, data);
} }
// TODO Add in the rest of the types // TODO Add in the rest of the types
@ -265,7 +262,8 @@ public abstract class PropertiesChunk extends Chunk {
} }
if (properties.get(prop) != null) { if (properties.get(prop) != null) {
logger.log(POILogger.WARN, "Duplicate values found for " + prop); logger.log(POILogger.WARN,
"Duplicate values found for " + prop);
} }
properties.put(prop, propVal); properties.put(prop, propVal);
} catch (BufferUnderrunException e) { } catch (BufferUnderrunException e) {

View File

@ -24,11 +24,11 @@ import org.apache.poi.util.LittleEndian;
import org.apache.poi.util.LocaleUtil; import org.apache.poi.util.LocaleUtil;
/** /**
* An instance of a {@link MAPIProperty} inside a {@link PropertiesChunk}. * An instance of a {@link MAPIProperty} inside a {@link PropertiesChunk}. Where
* Where the {@link Types} type is a fixed length one, this will contain the * the {@link Types} type is a fixed length one, this will contain the actual
* actual value. * value. Where the {@link Types} type is a variable length one, this will
* Where the {@link Types} type is a variable length one, this will contain * contain the length of the property, and the value will be in the associated
* the length of the property, and the value will be in the associated {@link Chunk}. * {@link Chunk}.
*/ */
public class PropertyValue { public class PropertyValue {
private MAPIProperty property; private MAPIProperty property;
@ -46,8 +46,7 @@ public class PropertyValue {
} }
/** /**
* Get the raw value flags. * Get the raw value flags. TODO Also provide getters for the flag meanings
* TODO Also provide getters for the flag meanings
*/ */
public long getFlags() { public long getFlags() {
return flags; return flags;
@ -56,6 +55,7 @@ public class PropertyValue {
public Object getValue() { public Object getValue() {
return data; return data;
} }
public void setRawValue(byte[] value) { public void setRawValue(byte[] value) {
this.data = value; this.data = value;
} }
@ -66,7 +66,7 @@ public class PropertyValue {
return "(No value available)"; return "(No value available)";
if (v instanceof byte[]) { if (v instanceof byte[]) {
return ByteChunk.toDebugFriendlyString((byte[])v); return ByteChunk.toDebugFriendlyString((byte[]) v);
} else { } else {
// Just use the normal toString on the value // Just use the normal toString on the value
return v.toString(); return v.toString();
@ -74,7 +74,8 @@ public class PropertyValue {
} }
public static class NullPropertyValue extends PropertyValue { public static class NullPropertyValue extends PropertyValue {
public NullPropertyValue(MAPIProperty property, long flags, byte[] data) { public NullPropertyValue(MAPIProperty property, long flags,
byte[] data) {
super(property, flags, data); super(property, flags, data);
} }
@ -84,7 +85,8 @@ public class PropertyValue {
} }
public static class BooleanPropertyValue extends PropertyValue { public static class BooleanPropertyValue extends PropertyValue {
public BooleanPropertyValue(MAPIProperty property, long flags, byte[] data) { public BooleanPropertyValue(MAPIProperty property, long flags,
byte[] data) {
super(property, flags, data); super(property, flags, data);
} }
@ -92,24 +94,27 @@ public class PropertyValue {
short val = LittleEndian.getShort(data); short val = LittleEndian.getShort(data);
return val > 0; return val > 0;
} }
public void setValue(boolean value) { public void setValue(boolean value) {
if (data.length != 2) { if (data.length != 2) {
data = new byte[2]; data = new byte[2];
} }
if (value) { if (value) {
LittleEndian.putShort(data, 0, (short)1); LittleEndian.putShort(data, 0, (short) 1);
} }
} }
} }
public static class ShortPropertyValue extends PropertyValue { public static class ShortPropertyValue extends PropertyValue {
public ShortPropertyValue(MAPIProperty property, long flags, byte[] data) { public ShortPropertyValue(MAPIProperty property, long flags,
byte[] data) {
super(property, flags, data); super(property, flags, data);
} }
public Short getValue() { public Short getValue() {
return LittleEndian.getShort(data); return LittleEndian.getShort(data);
} }
public void setValue(short value) { public void setValue(short value) {
if (data.length != 2) { if (data.length != 2) {
data = new byte[2]; data = new byte[2];
@ -126,6 +131,7 @@ public class PropertyValue {
public Integer getValue() { public Integer getValue() {
return LittleEndian.getInt(data); return LittleEndian.getInt(data);
} }
public void setValue(int value) { public void setValue(int value) {
if (data.length != 4) { if (data.length != 4) {
data = new byte[4]; data = new byte[4];
@ -135,13 +141,15 @@ public class PropertyValue {
} }
public static class LongLongPropertyValue extends PropertyValue { public static class LongLongPropertyValue extends PropertyValue {
public LongLongPropertyValue(MAPIProperty property, long flags, byte[] data) { public LongLongPropertyValue(MAPIProperty property, long flags,
byte[] data) {
super(property, flags, data); super(property, flags, data);
} }
public Long getValue() { public Long getValue() {
return LittleEndian.getLong(data); return LittleEndian.getLong(data);
} }
public void setValue(long value) { public void setValue(long value) {
if (data.length != 8) { if (data.length != 8) {
data = new byte[8]; data = new byte[8];
@ -151,13 +159,15 @@ public class PropertyValue {
} }
public static class FloatPropertyValue extends PropertyValue { public static class FloatPropertyValue extends PropertyValue {
public FloatPropertyValue(MAPIProperty property, long flags, byte[] data) { public FloatPropertyValue(MAPIProperty property, long flags,
byte[] data) {
super(property, flags, data); super(property, flags, data);
} }
public Float getValue() { public Float getValue() {
return LittleEndian.getFloat(data); return LittleEndian.getFloat(data);
} }
public void setValue(float value) { public void setValue(float value) {
if (data.length != 4) { if (data.length != 4) {
data = new byte[4]; data = new byte[4];
@ -174,6 +184,7 @@ public class PropertyValue {
public Double getValue() { public Double getValue() {
return LittleEndian.getDouble(data); return LittleEndian.getDouble(data);
} }
public void setValue(double value) { public void setValue(double value) {
if (data.length != 8) { if (data.length != 8) {
data = new byte[8]; data = new byte[8];
@ -183,11 +194,12 @@ public class PropertyValue {
} }
/** /**
* signed 64-bit integer that represents a base ten decimal, * signed 64-bit integer that represents a base ten decimal, with four
* with four digits to the right of the decimal point * digits to the right of the decimal point
*/ */
public static class CurrencyPropertyValue extends PropertyValue { public static class CurrencyPropertyValue extends PropertyValue {
private static final BigInteger SHIFT = BigInteger.valueOf(10000); private static final BigInteger SHIFT = BigInteger.valueOf(10000);
public CurrencyPropertyValue(MAPIProperty property, long flags, byte[] data) { public CurrencyPropertyValue(MAPIProperty property, long flags, byte[] data) {
super(property, flags, data); super(property, flags, data);
} }
@ -196,6 +208,7 @@ public class PropertyValue {
long unshifted = LittleEndian.getLong(data); long unshifted = LittleEndian.getLong(data);
return BigInteger.valueOf(unshifted).divide(SHIFT); return BigInteger.valueOf(unshifted).divide(SHIFT);
} }
public void setValue(BigInteger value) { public void setValue(BigInteger value) {
if (data.length != 8) { if (data.length != 8) {
data = new byte[8]; data = new byte[8];
@ -209,7 +222,9 @@ public class PropertyValue {
* 64-bit integer specifying the number of 100ns periods since Jan 1, 1601 * 64-bit integer specifying the number of 100ns periods since Jan 1, 1601
*/ */
public static class TimePropertyValue extends PropertyValue { public static class TimePropertyValue extends PropertyValue {
private static final long OFFSET = 1000L * 60L * 60L * 24L * (365L * 369L + 89L); private static final long OFFSET = 1000L * 60L * 60L * 24L
* (365L * 369L + 89L);
public TimePropertyValue(MAPIProperty property, long flags, byte[] data) { public TimePropertyValue(MAPIProperty property, long flags, byte[] data) {
super(property, flags, data); super(property, flags, data);
} }
@ -223,12 +238,13 @@ public class PropertyValue {
return timeC; return timeC;
} }
public void setValue(Calendar value) { public void setValue(Calendar value) {
if (data.length != 8) { if (data.length != 8) {
data = new byte[8]; data = new byte[8];
} }
long time = value.getTimeInMillis(); long time = value.getTimeInMillis();
time = (time + OFFSET) *10*1000; time = (time + OFFSET) * 10 * 1000;
LittleEndian.putLong(data, 0, time); LittleEndian.putLong(data, 0, time);
} }
} }

View File

@ -27,13 +27,10 @@ import java.util.Map;
import org.apache.poi.util.POILogFactory; import org.apache.poi.util.POILogFactory;
import org.apache.poi.util.POILogger; import org.apache.poi.util.POILogger;
/** /**
* Collection of convenience chunks for the * Collection of convenience chunks for the Recip(ient) part of an outlook file.
* Recip(ient) part of an outlook file.
* *
* If a message has multiple recipients, there will be * If a message has multiple recipients, there will be several of these.
* several of these.
*/ */
public final class RecipientChunks implements ChunkGroupWithProperties { public final class RecipientChunks implements ChunkGroupWithProperties {
private static POILogger logger = POILogFactory.getLogger(RecipientChunks.class); private static POILogger logger = POILogFactory.getLogger(RecipientChunks.class);
@ -53,61 +50,58 @@ public final class RecipientChunks implements ChunkGroupWithProperties {
/** TODO */ /** TODO */
public ByteChunk recipientSearchChunk; public ByteChunk recipientSearchChunk;
/** /**
* The "name", which could be their name if an * The "name", which could be their name if an internal person, or their
* internal person, or their email address * email address if an external person
* if an external person
*/ */
public StringChunk recipientNameChunk; public StringChunk recipientNameChunk;
/** /**
* The email address of the recipient, which * The email address of the recipient, which could be in SMTP or SEARCH
* could be in SMTP or SEARCH format, but * format, but isn't always present...
* isn't always present...
*/ */
public StringChunk recipientEmailChunk; public StringChunk recipientEmailChunk;
/** /**
* The smtp destination email address of * The smtp destination email address of the recipient, but isn't always
* the recipient, but isn't always present... * present...
*/ */
public StringChunk recipientSMTPChunk; public StringChunk recipientSMTPChunk;
/** /**
* Normally EX or SMTP. Will generally affect * Normally EX or SMTP. Will generally affect where the email address ends
* where the email address ends up. * up.
*/ */
public StringChunk deliveryTypeChunk; public StringChunk deliveryTypeChunk;
/** /**
* The display name of the recipient. * The display name of the recipient. Normally seems to hold the same value
* Normally seems to hold the same value
* as in recipientNameChunk * as in recipientNameChunk
*/ */
public StringChunk recipientDisplayNameChunk; public StringChunk recipientDisplayNameChunk;
/** /**
* Holds the fixed sized properties, and the * Holds the fixed sized properties, and the pointers to the data of
* pointers to the data of variable sized ones * variable sized ones
*/ */
private PropertiesChunk recipientProperties; private PropertiesChunk recipientProperties;
public RecipientChunks(String name) { public RecipientChunks(String name) {
recipientNumber = -1; recipientNumber = -1;
int splitAt = name.lastIndexOf('#'); int splitAt = name.lastIndexOf('#');
if(splitAt > -1) { if (splitAt > -1) {
String number = name.substring(splitAt+1); String number = name.substring(splitAt + 1);
try { try {
recipientNumber = Integer.parseInt(number, 16); recipientNumber = Integer.parseInt(number, 16);
} catch(NumberFormatException e) { } catch (NumberFormatException e) {
logger.log(POILogger.ERROR, "Invalid recipient number in name " + name); logger.log(POILogger.ERROR,
"Invalid recipient number in name " + name);
} }
} }
} }
/** /**
* Tries to find their name, * Tries to find their name, in whichever chunk holds it.
* in whichever chunk holds it.
*/ */
public String getRecipientName() { public String getRecipientName() {
if(recipientNameChunk != null) { if (recipientNameChunk != null) {
return recipientNameChunk.getValue(); return recipientNameChunk.getValue();
} }
if(recipientDisplayNameChunk != null) { if (recipientDisplayNameChunk != null) {
return recipientDisplayNameChunk.getValue(); return recipientDisplayNameChunk.getValue();
} }
@ -116,37 +110,36 @@ public final class RecipientChunks implements ChunkGroupWithProperties {
} }
/** /**
* Tries to find their email address, in * Tries to find their email address, in whichever chunk holds it given the
* whichever chunk holds it given the
* delivery type. * delivery type.
*/ */
public String getRecipientEmailAddress() { public String getRecipientEmailAddress() {
// If we have this, it really has the email // If we have this, it really has the email
if(recipientSMTPChunk != null) { if (recipientSMTPChunk != null) {
return recipientSMTPChunk.getValue(); return recipientSMTPChunk.getValue();
} }
// This might be a real email, or might be // This might be a real email, or might be
// in CN=... format // in CN=... format
if(recipientEmailChunk != null) { if (recipientEmailChunk != null) {
String email = recipientEmailChunk.getValue(); String email = recipientEmailChunk.getValue();
int cne = email.indexOf("/CN="); int cne = email.indexOf("/CN=");
if(cne == -1) { if (cne == -1) {
// Normal smtp address // Normal smtp address
return email; return email;
} else { } else {
// /O=..../CN=em@ail // /O=..../CN=em@ail
return email.substring(cne+4); return email.substring(cne + 4);
} }
} }
// Might be in the name field, check there // Might be in the name field, check there
if(recipientNameChunk != null) { if (recipientNameChunk != null) {
String name = recipientNameChunk.getValue(); String name = recipientNameChunk.getValue();
if(name.indexOf('@') > -1) { if (name.indexOf('@') > -1) {
// Strip leading and trailing quotes if needed // Strip leading and trailing quotes if needed
if(name.startsWith("'") && name.endsWith("'")) { if (name.startsWith("'") && name.endsWith("'")) {
return name.substring(1, name.length()-1); return name.substring(1, name.length() - 1);
} }
return name; return name;
} }
@ -154,9 +147,9 @@ public final class RecipientChunks implements ChunkGroupWithProperties {
// Check the search chunk, see if it's // Check the search chunk, see if it's
// encoded as a SMTP destination in there. // encoded as a SMTP destination in there.
if(recipientSearchChunk != null) { if (recipientSearchChunk != null) {
String search = recipientSearchChunk.getAs7bitString(); String search = recipientSearchChunk.getAs7bitString();
if(search.indexOf("SMTP:") != -1) { if (search.indexOf("SMTP:") != -1) {
return search.substring(search.indexOf("SMTP:") + 5); return search.substring(search.indexOf("SMTP:") + 5);
} }
} }
@ -168,15 +161,17 @@ public final class RecipientChunks implements ChunkGroupWithProperties {
/** Holds all the chunks that were found. */ /** Holds all the chunks that were found. */
private List<Chunk> allChunks = new ArrayList<Chunk>(); private List<Chunk> allChunks = new ArrayList<Chunk>();
public Map<MAPIProperty,List<PropertyValue>> getProperties() { public Map<MAPIProperty, List<PropertyValue>> getProperties() {
if (recipientProperties != null) { if (recipientProperties != null) {
return recipientProperties.getProperties(); return recipientProperties.getProperties();
} else
return Collections.emptyMap();
} }
else return Collections.emptyMap();
}
public Chunk[] getAll() { public Chunk[] getAll() {
return allChunks.toArray(new Chunk[allChunks.size()]); return allChunks.toArray(new Chunk[allChunks.size()]);
} }
public Chunk[] getChunks() { public Chunk[] getChunks() {
return getAll(); return getAll();
} }
@ -185,26 +180,20 @@ public final class RecipientChunks implements ChunkGroupWithProperties {
* Called by the parser whenever a chunk is found. * Called by the parser whenever a chunk is found.
*/ */
public void record(Chunk chunk) { public void record(Chunk chunk) {
if(chunk.getChunkId() == RECIPIENT_SEARCH.id) { if (chunk.getChunkId() == RECIPIENT_SEARCH.id) {
// TODO - parse // TODO - parse
recipientSearchChunk = (ByteChunk)chunk; recipientSearchChunk = (ByteChunk) chunk;
} } else if (chunk.getChunkId() == RECIPIENT_NAME.id) {
else if(chunk.getChunkId() == RECIPIENT_NAME.id) { recipientDisplayNameChunk = (StringChunk) chunk;
recipientDisplayNameChunk = (StringChunk)chunk; } else if (chunk.getChunkId() == RECIPIENT_DISPLAY_NAME.id) {
} recipientNameChunk = (StringChunk) chunk;
else if(chunk.getChunkId() == RECIPIENT_DISPLAY_NAME.id) { } else if (chunk.getChunkId() == RECIPIENT_EMAIL_ADDRESS.id) {
recipientNameChunk = (StringChunk)chunk; recipientEmailChunk = (StringChunk) chunk;
} } else if (chunk.getChunkId() == RECIPIENT_SMTP_ADDRESS.id) {
else if(chunk.getChunkId() == RECIPIENT_EMAIL_ADDRESS.id) { recipientSMTPChunk = (StringChunk) chunk;
recipientEmailChunk = (StringChunk)chunk; } else if (chunk.getChunkId() == DELIVERY_TYPE.id) {
} deliveryTypeChunk = (StringChunk) chunk;
else if(chunk.getChunkId() == RECIPIENT_SMTP_ADDRESS.id) { } else if (chunk instanceof PropertiesChunk) {
recipientSMTPChunk = (StringChunk)chunk;
}
else if(chunk.getChunkId() == DELIVERY_TYPE.id) {
deliveryTypeChunk = (StringChunk)chunk;
}
else if(chunk instanceof PropertiesChunk) {
recipientProperties = (PropertiesChunk) chunk; recipientProperties = (PropertiesChunk) chunk;
} }
@ -223,11 +212,12 @@ public final class RecipientChunks implements ChunkGroupWithProperties {
/** /**
* Orders by the recipient number. * Orders by the recipient number.
*/ */
public static class RecipientChunksSorter implements Comparator<RecipientChunks>, Serializable { public static class RecipientChunksSorter
implements Comparator<RecipientChunks>, Serializable {
public int compare(RecipientChunks a, RecipientChunks b) { public int compare(RecipientChunks a, RecipientChunks b) {
if(a.recipientNumber < b.recipientNumber) if (a.recipientNumber < b.recipientNumber)
return -1; return -1;
if(a.recipientNumber > b.recipientNumber) if (a.recipientNumber > b.recipientNumber)
return +1; return +1;
return 0; return 0;
} }

View File

@ -24,9 +24,8 @@ import java.io.OutputStream;
import org.apache.poi.util.LittleEndian; import org.apache.poi.util.LittleEndian;
/** /**
* A {@link PropertiesChunk} for a Storage Properties, such as * A {@link PropertiesChunk} for a Storage Properties, such as Attachments and
* Attachments and Recipients. * Recipients. This only has a 8 byte header
* This only has a 8 byte header
*/ */
public class StoragePropertiesChunk extends PropertiesChunk { public class StoragePropertiesChunk extends PropertiesChunk {
public StoragePropertiesChunk(ChunkGroup parentGroup) { public StoragePropertiesChunk(ChunkGroup parentGroup) {

View File

@ -43,34 +43,30 @@ public class StringChunk extends Chunk {
} }
/** /**
* Create a String Chunk, with the specified * Create a String Chunk, with the specified type.
* type.
*/ */
public StringChunk(int chunkId, MAPIType type) { public StringChunk(int chunkId, MAPIType type) {
super(chunkId, type); super(chunkId, type);
} }
/** /**
* Returns the Encoding that will be used to * Returns the Encoding that will be used to decode any "7 bit" (non
* decode any "7 bit" (non unicode) data. * unicode) data. Most files default to CP1252
* Most files default to CP1252
*/ */
public String get7BitEncoding() { public String get7BitEncoding() {
return encoding7Bit; return encoding7Bit;
} }
/** /**
* Sets the Encoding that will be used to * Sets the Encoding that will be used to decode any "7 bit" (non unicode)
* decode any "7 bit" (non unicode) data. * data. This doesn't appear to be stored anywhere specific in the file, so
* This doesn't appear to be stored anywhere * you may need to guess by looking at headers etc
* specific in the file, so you may need
* to guess by looking at headers etc
*/ */
public void set7BitEncoding(String encoding) { public void set7BitEncoding(String encoding) {
this.encoding7Bit = encoding; this.encoding7Bit = encoding;
// Re-read the String if we're a 7 bit one // Re-read the String if we're a 7 bit one
if(type == Types.ASCII_STRING) { if (type == Types.ASCII_STRING) {
parseString(); parseString();
} }
} }
@ -79,6 +75,7 @@ public class StringChunk extends Chunk {
rawValue = IOUtils.toByteArray(value); rawValue = IOUtils.toByteArray(value);
parseString(); parseString();
} }
private void parseString() { private void parseString() {
String tmpValue; String tmpValue;
if (type == Types.ASCII_STRING) { if (type == Types.ASCII_STRING) {
@ -96,6 +93,7 @@ public class StringChunk extends Chunk {
public void writeValue(OutputStream out) throws IOException { public void writeValue(OutputStream out) throws IOException {
out.write(rawValue); out.write(rawValue);
} }
private void storeString() { private void storeString() {
if (type == Types.ASCII_STRING) { if (type == Types.ASCII_STRING) {
rawValue = value.getBytes(Charset.forName(encoding7Bit)); rawValue = value.getBytes(Charset.forName(encoding7Bit));
@ -127,15 +125,16 @@ public class StringChunk extends Chunk {
} }
/** /**
* Parses as non-unicode, supposedly 7 bit CP1252 data * Parses as non-unicode, supposedly 7 bit CP1252 data and returns the
* and returns the string that that yields. * string that that yields.
*/ */
protected static String parseAs7BitData(byte[] data) { protected static String parseAs7BitData(byte[] data) {
return parseAs7BitData(data, DEFAULT_ENCODING); return parseAs7BitData(data, DEFAULT_ENCODING);
} }
/** /**
* Parses as non-unicode, supposedly 7 bit data * Parses as non-unicode, supposedly 7 bit data and returns the string that
* and returns the string that that yields. * that yields.
*/ */
protected static String parseAs7BitData(byte[] data, String encoding) { protected static String parseAs7BitData(byte[] data, String encoding) {
// Handle any encoding aliases, where outlook describes it differently // Handle any encoding aliases, where outlook describes it differently

View File

@ -30,7 +30,8 @@ public final class Types {
private static Map<Integer, MAPIType> customTypes = new HashMap<Integer, Types.MAPIType>(); private static Map<Integer, MAPIType> customTypes = new HashMap<Integer, Types.MAPIType>();
/** Unspecified */ /** Unspecified */
public static final MAPIType UNSPECIFIED = new MAPIType(0x0000, "Unspecified", -1); public static final MAPIType UNSPECIFIED = new MAPIType(0x0000,
"Unspecified", -1);
/** Unknown */ /** Unknown */
public static final MAPIType UNKNOWN = new MAPIType(-1, "Unknown", -1); public static final MAPIType UNKNOWN = new MAPIType(-1, "Unknown", -1);
@ -44,7 +45,10 @@ public final class Types {
public static final MAPIType FLOAT = new MAPIType(0x0004, "Float", 4); public static final MAPIType FLOAT = new MAPIType(0x0004, "Float", 4);
/** Double - floating point double */ /** Double - floating point double */
public static final MAPIType DOUBLE = new MAPIType(0x0005, "Double", 8); public static final MAPIType DOUBLE = new MAPIType(0x0005, "Double", 8);
/** Currency - signed 64-bit integer that represents a base ten decimal with four digits to the right of the decimal point */ /**
* Currency - signed 64-bit integer that represents a base ten decimal with
* four digits to the right of the decimal point
*/
public static final MAPIType CURRENCY = new MAPIType(0x0006, "Currency", 8); public static final MAPIType CURRENCY = new MAPIType(0x0006, "Currency", 8);
/** AppTime - application time value */ /** AppTime - application time value */
public static final MAPIType APP_TIME = new MAPIType(0x0007, "Application Time", 8); public static final MAPIType APP_TIME = new MAPIType(0x0007, "Application Time", 8);
@ -56,7 +60,10 @@ public final class Types {
public static final MAPIType DIRECTORY = new MAPIType(0x000D, "Directory", -1); public static final MAPIType DIRECTORY = new MAPIType(0x000D, "Directory", -1);
/** I8 - 8-byte signed integer */ /** I8 - 8-byte signed integer */
public static final MAPIType LONG_LONG = new MAPIType(0x0014, "Long Long", 8); public static final MAPIType LONG_LONG = new MAPIType(0x0014, "Long Long", 8);
/** SysTime - FILETIME 64-bit integer specifying the number of 100ns periods since Jan 1, 1601 */ /**
* SysTime - FILETIME 64-bit integer specifying the number of 100ns periods
* since Jan 1, 1601
*/
public static final MAPIType TIME = new MAPIType(0x0040, "Time", 8); public static final MAPIType TIME = new MAPIType(0x0040, "Time", 8);
/** ClassId - OLE GUID */ /** ClassId - OLE GUID */
public static final MAPIType CLS_ID = new MAPIType(0x0048, "CLS ID GUID", 16); public static final MAPIType CLS_ID = new MAPIType(0x0048, "CLS ID GUID", 16);
@ -65,9 +72,8 @@ public final class Types {
public static final MAPIType BINARY = new MAPIType(0x0102, "Binary", -1); public static final MAPIType BINARY = new MAPIType(0x0102, "Binary", -1);
/** /**
* An 8-bit string, probably in CP1252, but don't quote us... * An 8-bit string, probably in CP1252, but don't quote us... Normally used
* Normally used for everything before Outlook 3.0, and some * for everything before Outlook 3.0, and some fields in Outlook 3.0.
* fields in Outlook 3.0.
*/ */
public static final MAPIType ASCII_STRING = new MAPIType(0x001E, "ASCII String", -1); public static final MAPIType ASCII_STRING = new MAPIType(0x001E, "ASCII String", -1);
/** A string, from Outlook 3.0 onwards. Normally unicode */ /** A string, from Outlook 3.0 onwards. Normally unicode */
@ -90,6 +96,7 @@ public final class Types {
this.length = length; this.length = length;
builtInTypes.put(id, this); builtInTypes.put(id, this);
} }
/** /**
* Creates a custom type * Creates a custom type
*/ */
@ -101,12 +108,13 @@ public final class Types {
} }
/** /**
* Returns the length, in bytes, of values of this type, or * Returns the length, in bytes, of values of this type, or -1 if it is
* -1 if it is a variable length type. * a variable length type.
*/ */
public int getLength() { public int getLength() {
return length; return length;
} }
/** /**
* Is this type a fixed-length type, or a variable-length one? * Is this type a fixed-length type, or a variable-length one?
*/ */
@ -117,17 +125,18 @@ public final class Types {
public int getId() { public int getId() {
return id; return id;
} }
public String getName() { public String getName() {
return name; return name;
} }
public String toString() { public String toString() {
return id + " / 0x" + asFileEnding() + " - " + name + " @ " + length; return id + " / 0x" + asFileEnding() + " - " + name + " @ "
+ length;
} }
/** /**
* Return the 4 character hex encoded version, * Return the 4 character hex encoded version, as used in file endings
* as used in file endings
*/ */
public String asFileEnding() { public String asFileEnding() {
return Types.asFileEnding(id); return Types.asFileEnding(id);
@ -140,11 +149,12 @@ public final class Types {
public static String asFileEnding(int type) { public static String asFileEnding(int type) {
String str = Integer.toHexString(type).toUpperCase(Locale.ROOT); String str = Integer.toHexString(type).toUpperCase(Locale.ROOT);
while(str.length() < 4) { while (str.length() < 4) {
str = "0" + str; str = "0" + str;
} }
return str; return str;
} }
public static String asName(int typeId) { public static String asName(int typeId) {
MAPIType type = builtInTypes.get(typeId); MAPIType type = builtInTypes.get(typeId);
if (type != null) { if (type != null) {
@ -152,6 +162,7 @@ public final class Types {
} }
return asCustomName(typeId); return asCustomName(typeId);
} }
private static String asCustomName(int typeId) { private static String asCustomName(int typeId) {
return "0x" + Integer.toHexString(typeId); return "0x" + Integer.toHexString(typeId);
} }