Modified: poi/trunk/src/scratchpad/src/org/apache/poi/hsmf/datatypes/MessagePropertiesChunk.java URL: http://svn.apache.org/viewvc/poi/trunk/src/scratchpad/src/org/apache/poi/hsmf/datatypes/MessagePropertiesChunk.java?rev=1773544&r1=1773543&r2=1773544&view=diff ============================================================================== --- poi/trunk/src/scratchpad/src/org/apache/poi/hsmf/datatypes/MessagePropertiesChunk.java (original) +++ poi/trunk/src/scratchpad/src/org/apache/poi/hsmf/datatypes/MessagePropertiesChunk.java Sat Dec 10 23:35:12 2016 @@ -24,66 +24,68 @@ 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 + * 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(ChunkGroup parentGroup) { - super(parentGroup); - } - - 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); - } + private long nextRecipientId; + private long nextAttachmentId; + private long recipientCount; + private long attachmentCount; + + public MessagePropertiesChunk(ChunkGroup parentGroup) { + super(parentGroup); + } + + 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); + } }
Modified: poi/trunk/src/scratchpad/src/org/apache/poi/hsmf/datatypes/MessageSubmissionChunk.java URL: http://svn.apache.org/viewvc/poi/trunk/src/scratchpad/src/org/apache/poi/hsmf/datatypes/MessageSubmissionChunk.java?rev=1773544&r1=1773543&r2=1773544&view=diff ============================================================================== --- poi/trunk/src/scratchpad/src/org/apache/poi/hsmf/datatypes/MessageSubmissionChunk.java (original) +++ poi/trunk/src/scratchpad/src/org/apache/poi/hsmf/datatypes/MessageSubmissionChunk.java Sat Dec 10 23:35:12 2016 @@ -31,100 +31,106 @@ import org.apache.poi.util.POILogFactory import org.apache.poi.util.POILogger; /** - * A Chunk that holds the details given back by the - * server at submission time. - * This includes the date the message was given to the - * server, and an ID that's used if you want to cancel - * a message or similar + * A Chunk that holds the details given back by the server at submission time. + * This includes the date the message was given to the server, and an ID that's + * used if you want to cancel a message or similar */ public class MessageSubmissionChunk extends Chunk { - private static POILogger logger = POILogFactory.getLogger(MessageSubmissionChunk.class); - private String rawId; - private Calendar date; - - private static final Pattern datePatern = - Pattern.compile("(\\d\\d)(\\d\\d)(\\d\\d)(\\d\\d)(\\d\\d)(\\d\\d)Z?"); - - /** - * Creates a Byte Chunk. - */ - public MessageSubmissionChunk(String namePrefix, int chunkId, MAPIType type) { - super(namePrefix, chunkId, type); - } - - /** - * Create a Byte Chunk, with the specified - * type. - */ - public MessageSubmissionChunk(int chunkId, MAPIType type) { - super(chunkId, type); - } - - public void readValue(InputStream value) throws IOException { - // Stored in the file as us-ascii - byte[] data = IOUtils.toByteArray(value); - rawId = new String(data, Charset.forName("ASCII")); - - // Now process the date - String[] parts = rawId.split(";"); - for(String part : parts) { - if(part.startsWith("l=")) { - // Format of this bit appears to be l=<id>-<time>-<number> - // ID may contain hyphens. - - String dateS = null; - final int numberPartBegin = part.lastIndexOf('-'); - if (numberPartBegin != -1) { - final int datePartBegin = part.lastIndexOf('-', numberPartBegin-1); - if (datePartBegin != -1 && - // cannot extract date if only one hyphen is in the string... - numberPartBegin > datePartBegin) { - dateS = part.substring(datePartBegin + 1, numberPartBegin); + private static POILogger logger = POILogFactory + .getLogger(MessageSubmissionChunk.class); + private String rawId; + private Calendar date; + + private static final Pattern datePatern = Pattern + .compile("(\\d\\d)(\\d\\d)(\\d\\d)(\\d\\d)(\\d\\d)(\\d\\d)Z?"); + + /** + * Creates a Byte Chunk. + */ + public MessageSubmissionChunk(String namePrefix, int chunkId, + MAPIType type) { + super(namePrefix, chunkId, type); + } + + /** + * Create a Byte Chunk, with the specified type. + */ + public MessageSubmissionChunk(int chunkId, MAPIType type) { + super(chunkId, type); + } + + public void readValue(InputStream value) throws IOException { + // Stored in the file as us-ascii + byte[] data = IOUtils.toByteArray(value); + rawId = new String(data, Charset.forName("ASCII")); + + // Now process the date + String[] parts = rawId.split(";"); + for (String part : parts) { + if (part.startsWith("l=")) { + // Format of this bit appears to be l=<id>-<time>-<number> + // ID may contain hyphens. + + String dateS = null; + final int numberPartBegin = part.lastIndexOf('-'); + if (numberPartBegin != -1) { + final int datePartBegin = part.lastIndexOf('-', + numberPartBegin - 1); + if (datePartBegin != -1 && + // cannot extract date if only one hyphen is in the + // string... + numberPartBegin > datePartBegin) { + dateS = part.substring(datePartBegin + 1, + numberPartBegin); + } + } + if (dateS != null) { + // Should be yymmddhhmmssZ + Matcher m = datePatern.matcher(dateS); + if (m.matches()) { + date = LocaleUtil.getLocaleCalendar(); + + // work around issues with dates like 1989, which appear as "89" here + int year = Integer.parseInt(m.group(1)); + date.set(Calendar.YEAR, year + (year > 80 ? 1900 : 2000)); + + // 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.HOUR_OF_DAY, + Integer.parseInt(m.group(4))); + date.set(Calendar.MINUTE, Integer.parseInt(m.group(5))); + date.set(Calendar.SECOND, Integer.parseInt(m.group(6))); + date.clear(Calendar.MILLISECOND); + } else { + logger.log(POILogger.WARN, + "Warning - unable to make sense of date " + + dateS); + } } } - if (dateS != null) { - // Should be yymmddhhmmssZ - Matcher m = datePatern.matcher(dateS); - if(m.matches()) { - date = LocaleUtil.getLocaleCalendar(); - - // work around issues with dates like 1989, which appear as "89" here - int year = Integer.parseInt(m.group(1)); - date.set(Calendar.YEAR, year + (year > 80 ? 1900 : 2000)); - - date.set(Calendar.MONTH, Integer.parseInt(m.group(2)) - 1); // Java is 0 based - date.set(Calendar.DATE, Integer.parseInt(m.group(3))); - date.set(Calendar.HOUR_OF_DAY, Integer.parseInt(m.group(4))); - date.set(Calendar.MINUTE, Integer.parseInt(m.group(5))); - date.set(Calendar.SECOND, Integer.parseInt(m.group(6))); - date.clear(Calendar.MILLISECOND); - } else { - logger.log(POILogger.WARN, "Warning - unable to make sense of date " + dateS); - } - } - } - } - } - - public void writeValue(OutputStream out) throws IOException { - byte[] data = rawId.getBytes(Charset.forName("ASCII")); - out.write(data); - } - - /** - * @return the date that the server accepted the - * message, as found from the message ID it generated. - * - */ - public Calendar getAcceptedAtTime() { - return date; - } - - /** - * @return the full ID that the server generated when - * it accepted the message. - */ - public String getSubmissionId() { - return rawId; - } + } + } + + public void writeValue(OutputStream out) throws IOException { + byte[] data = rawId.getBytes(Charset.forName("ASCII")); + out.write(data); + } + + /** + * @return the date that the server accepted the message, as found from the + * message ID it generated. + * + */ + public Calendar getAcceptedAtTime() { + return date; + } + + /** + * @return the full ID that the server generated when it accepted the + * message. + */ + public String getSubmissionId() { + return rawId; + } } Modified: poi/trunk/src/scratchpad/src/org/apache/poi/hsmf/datatypes/NameIdChunks.java URL: http://svn.apache.org/viewvc/poi/trunk/src/scratchpad/src/org/apache/poi/hsmf/datatypes/NameIdChunks.java?rev=1773544&r1=1773543&r2=1773544&view=diff ============================================================================== --- poi/trunk/src/scratchpad/src/org/apache/poi/hsmf/datatypes/NameIdChunks.java (original) +++ poi/trunk/src/scratchpad/src/org/apache/poi/hsmf/datatypes/NameIdChunks.java Sat Dec 10 23:35:12 2016 @@ -20,37 +20,35 @@ package org.apache.poi.hsmf.datatypes; import java.util.ArrayList; import java.util.List; - /** - * Collection of convenience chunks for the - * NameID part of an outlook file + * Collection of convenience chunks for the NameID part of an outlook file */ public final class NameIdChunks implements ChunkGroup { - public static final String NAME = "__nameid_version1.0"; - - /** Holds all the chunks that were found. */ - private List<Chunk> allChunks = new ArrayList<Chunk>(); - - public Chunk[] getAll() { - return allChunks.toArray(new Chunk[allChunks.size()]); - } - public Chunk[] getChunks() { - return getAll(); - } - - /** - * Called by the parser whenever a chunk is found. - */ - public void record(Chunk chunk) { - allChunks.add(chunk); - } - - /** - * Used to flag that all the chunks of the NameID - * have now been located. - */ - public void chunksComplete() { - // Currently, we don't need to do anything special once - // all the chunks have been located - } + public static final String NAME = "__nameid_version1.0"; + + /** Holds all the chunks that were found. */ + private List<Chunk> allChunks = new ArrayList<Chunk>(); + + public Chunk[] getAll() { + return allChunks.toArray(new Chunk[allChunks.size()]); + } + + public Chunk[] getChunks() { + return getAll(); + } + + /** + * Called by the parser whenever a chunk is found. + */ + public void record(Chunk chunk) { + allChunks.add(chunk); + } + + /** + * Used to flag that all the chunks of the NameID have now been located. + */ + public void chunksComplete() { + // Currently, we don't need to do anything special once + // all the chunks have been located + } } Modified: poi/trunk/src/scratchpad/src/org/apache/poi/hsmf/datatypes/PropertiesChunk.java URL: http://svn.apache.org/viewvc/poi/trunk/src/scratchpad/src/org/apache/poi/hsmf/datatypes/PropertiesChunk.java?rev=1773544&r1=1773543&r2=1773544&view=diff ============================================================================== --- poi/trunk/src/scratchpad/src/org/apache/poi/hsmf/datatypes/PropertiesChunk.java (original) +++ poi/trunk/src/scratchpad/src/org/apache/poi/hsmf/datatypes/PropertiesChunk.java Sat Dec 10 23:35:12 2016 @@ -42,240 +42,238 @@ import org.apache.poi.util.POILogFactory import org.apache.poi.util.POILogger; /** - * <p>A Chunk which holds (single) fixed-length properties, and pointer - * to the variable length ones / multi-valued ones (which get their - * own chunk). - * <p>There are two kinds of PropertiesChunks, which differ only in - * their headers. + * <p> + * A Chunk which holds (single) fixed-length properties, and pointer to the + * variable length ones / multi-valued ones (which get their own chunk). + * <p> + * There are two kinds of PropertiesChunks, which differ only in their headers. */ public abstract class PropertiesChunk extends Chunk { - public static final String NAME = "__properties_version1.0"; - - /** For logging problems we spot with the file */ - private POILogger logger = POILogFactory.getLogger(PropertiesChunk.class); - - /** - * Holds properties, indexed by type. If a property is multi-valued, - * or variable length, it will be held via a {@link ChunkBasedPropertyValue}. - */ - private Map<MAPIProperty, PropertyValue> properties = - new HashMap<MAPIProperty, PropertyValue>(); - - /** - * The ChunkGroup that these properties apply to. Used when - * matching chunks to variable sized and multi-valued properties - */ - private ChunkGroup parentGroup; - - /** - * Creates a Properties Chunk. - */ - protected PropertiesChunk(ChunkGroup parentGroup) { - super(NAME, -1, Types.UNKNOWN); - this.parentGroup = parentGroup; - } - - @Override - public String getEntryName() { - return NAME; - } - - /** - * Returns all the properties in the chunk, without - * looking up any chunk-based values - */ - public Map<MAPIProperty, PropertyValue> getRawProperties() { - return properties; - } - - /** - * <p>Returns all the properties in the chunk, along with their - * values. - * <p>Any chunk-based values will be looked up and returned as such - */ - public Map<MAPIProperty, List<PropertyValue>> getProperties() { - Map<MAPIProperty, List<PropertyValue>> props = - new HashMap<MAPIProperty, List<PropertyValue>>(properties.size()); - for (MAPIProperty prop : properties.keySet()) { - props.put(prop, getValues(prop)); - } - return props; - } - - /** - * Returns all values for the given property, looking up chunk based - * ones as required, of null if none exist - */ - public List<PropertyValue> getValues(MAPIProperty property) { - PropertyValue val = properties.get(property); - if (val == null) { - return null; - } - if (val instanceof ChunkBasedPropertyValue) { - // ChunkBasedPropertyValue cval = (ChunkBasedPropertyValue)val; - // TODO Lookup - return Collections.emptyList(); - } else { - return Collections.singletonList(val); - } - } - - /** - * Returns the value / pointer to the value chunk of - * the property, or null if none exists - */ - public PropertyValue getRawValue(MAPIProperty property) { - return properties.get(property); - } - - /** - * Called once the parent ChunkGroup has been populated, to match - * up the Chunks in it with our Variable Sized Properties. - */ - protected void matchVariableSizedPropertiesToChunks() { - // Index the Parent Group chunks for easy lookup - // TODO Is this the right way? - Map<Integer,Chunk> chunks = new HashMap<Integer, Chunk>(); - for (Chunk chunk : parentGroup.getChunks()) { - chunks.put(chunk.chunkId, chunk); - } - - // Loop over our values, looking for chunk based ones - for (PropertyValue val : properties.values()) { - if (val instanceof ChunkBasedPropertyValue) { - ChunkBasedPropertyValue cVal = (ChunkBasedPropertyValue)val; - Chunk chunk = chunks.get(cVal.getProperty().id); -//System.err.println(cVal.getProperty() + " = " + cVal + " -> " + HexDump.toHex(cVal.data)); - - // TODO Make sense of the raw offset value - - if (chunk != null) { - cVal.setValue(chunk); - } else { - logger.log(POILogger.WARN, "No chunk found matching Property " + cVal); - } - } - } - } - - protected void readProperties(InputStream value) throws IOException { - boolean going = true; - while (going) { - try { - // Read in the header - int typeID = LittleEndian.readUShort(value); - int id = LittleEndian.readUShort(value); - long flags = LittleEndian.readUInt(value); - - // Turn the Type and ID into helper objects - MAPIType type = Types.getById(typeID); - MAPIProperty prop = MAPIProperty.get(id); - - // Wrap properties we don't know about as custom ones - if (prop == MAPIProperty.UNKNOWN) { - prop = MAPIProperty.createCustom(id, type, "Unknown " + id); - } - if (type == null) { - logger.log(POILogger.WARN, "Invalid type found, expected ", prop.usualType, - " but got ", typeID, " for property ", prop); - going = false; - break; - } - - // Sanity check the property's type against the value's type - if (prop.usualType != type) { - // Is it an allowed substitution? - if (type == Types.ASCII_STRING && prop.usualType == Types.UNICODE_STRING || - 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) { - // We don't know what this property normally is, but it has come - // through with a valid type, so use that - logger.log(POILogger.INFO, "Property definition for ", prop, - " is missing a type definition, found a value with type ", type); + public static final String NAME = "__properties_version1.0"; + + /** For logging problems we spot with the file */ + private POILogger logger = POILogFactory.getLogger(PropertiesChunk.class); + + /** + * Holds properties, indexed by type. If a property is multi-valued, or + * variable length, it will be held via a {@link ChunkBasedPropertyValue}. + */ + private Map<MAPIProperty, PropertyValue> properties = new HashMap<MAPIProperty, PropertyValue>(); + + /** + * The ChunkGroup that these properties apply to. Used when matching chunks + * to variable sized and multi-valued properties + */ + private ChunkGroup parentGroup; + + /** + * Creates a Properties Chunk. + */ + protected PropertiesChunk(ChunkGroup parentGroup) { + super(NAME, -1, Types.UNKNOWN); + this.parentGroup = parentGroup; + } + + @Override + public String getEntryName() { + return NAME; + } + + /** + * Returns all the properties in the chunk, without looking up any + * chunk-based values + */ + public Map<MAPIProperty, PropertyValue> getRawProperties() { + return properties; + } + + /** + * <p> + * Returns all the properties in the chunk, along with their values. + * <p> + * Any chunk-based values will be looked up and returned as such + */ + public Map<MAPIProperty, List<PropertyValue>> getProperties() { + Map<MAPIProperty, List<PropertyValue>> props = + new HashMap<MAPIProperty, List<PropertyValue>>(properties.size()); + for (MAPIProperty prop : properties.keySet()) { + props.put(prop, getValues(prop)); + } + return props; + } + + /** + * Returns all values for the given property, looking up chunk based ones as + * required, of null if none exist + */ + public List<PropertyValue> getValues(MAPIProperty property) { + PropertyValue val = properties.get(property); + if (val == null) { + return null; + } + if (val instanceof ChunkBasedPropertyValue) { + // ChunkBasedPropertyValue cval = (ChunkBasedPropertyValue)val; + // TODO Lookup + return Collections.emptyList(); + } else { + return Collections.singletonList(val); + } + } + + /** + * Returns the value / pointer to the value chunk of the property, or null + * if none exists + */ + public PropertyValue getRawValue(MAPIProperty property) { + return properties.get(property); + } + + /** + * Called once the parent ChunkGroup has been populated, to match up the + * Chunks in it with our Variable Sized Properties. + */ + protected void matchVariableSizedPropertiesToChunks() { + // Index the Parent Group chunks for easy lookup + // TODO Is this the right way? + Map<Integer, Chunk> chunks = new HashMap<Integer, Chunk>(); + for (Chunk chunk : parentGroup.getChunks()) { + chunks.put(chunk.chunkId, chunk); + } + + // Loop over our values, looking for chunk based ones + for (PropertyValue val : properties.values()) { + if (val instanceof ChunkBasedPropertyValue) { + ChunkBasedPropertyValue cVal = (ChunkBasedPropertyValue) val; + Chunk chunk = chunks.get(cVal.getProperty().id); + // System.err.println(cVal.getProperty() + " = " + cVal + " -> " + // + HexDump.toHex(cVal.data)); + + // TODO Make sense of the raw offset value + + if (chunk != null) { + cVal.setValue(chunk); } else { - // Oh dear, something has gone wrong... - logger.log(POILogger.WARN, "Type mismatch, expected ", prop.usualType, - " but got ", type, " for property ", prop); - going = false; - break; + logger.log(POILogger.WARN, "No chunk found matching Property " + cVal); } } - - // TODO Detect if it is multi-valued, since if it is - // then even fixed-length strings store their multiple - // values in another chunk (much as variable length ones) - - // Work out how long the "data" is - // This might be the actual data, or just a pointer - // to another chunk which holds the data itself - boolean isPointer = false; - int length = type.getLength(); - if (! type.isFixedLength()) { - isPointer = true; - length = 8; - } - - // Grab the data block - byte[] data = new byte[length]; - IOUtils.readFully(value, data); - - // Skip over any padding - if (length < 8) { - byte[] padding = new byte[8-length]; - IOUtils.readFully(value, padding); - } - - // Wrap and store - PropertyValue propVal = null; - if (isPointer) { - // We'll match up the chunk later - propVal = new ChunkBasedPropertyValue(prop, flags, data); - } - else if (type == Types.NULL) { - propVal = new NullPropertyValue(prop, flags, data); - } - else if (type == Types.BOOLEAN) { - propVal = new BooleanPropertyValue(prop, flags, data); - } - else if (type == Types.SHORT) { - propVal = new ShortPropertyValue(prop, flags, data); - } - else if (type == Types.LONG) { - propVal = new LongPropertyValue(prop, flags, data); - } - else if (type == Types.LONG_LONG) { - propVal = new LongLongPropertyValue(prop, flags, data); - } - else if (type == Types.FLOAT) { - propVal = new FloatPropertyValue(prop, flags, data); - } - else if (type == Types.DOUBLE) { - propVal = new DoublePropertyValue(prop, flags, data); - } - else if (type == Types.CURRENCY) { - propVal = new CurrencyPropertyValue(prop, flags, data); - } - else if (type == Types.TIME) { - propVal = new TimePropertyValue(prop, flags, data); - } - // TODO Add in the rest of the types - else { - propVal = new PropertyValue(prop, flags, data); - } + } + } + + protected void readProperties(InputStream value) throws IOException { + boolean going = true; + while (going) { + try { + // Read in the header + int typeID = LittleEndian.readUShort(value); + int id = LittleEndian.readUShort(value); + long flags = LittleEndian.readUInt(value); + + // Turn the Type and ID into helper objects + MAPIType type = Types.getById(typeID); + MAPIProperty prop = MAPIProperty.get(id); + + // Wrap properties we don't know about as custom ones + if (prop == MAPIProperty.UNKNOWN) { + prop = MAPIProperty.createCustom(id, type, "Unknown " + id); + } + if (type == null) { + logger.log(POILogger.WARN, "Invalid type found, expected ", + prop.usualType, " but got ", typeID, + " for property ", prop); + going = false; + break; + } + + // Sanity check the property's type against the value's type + if (prop.usualType != type) { + // Is it an allowed substitution? + if (type == Types.ASCII_STRING + && prop.usualType == Types.UNICODE_STRING + || 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) { + // We don't know what this property normally is, but it + // has come + // through with a valid type, so use that + logger.log(POILogger.INFO, "Property definition for ", prop, + " is missing a type definition, found a value with type ", type); + } else { + // Oh dear, something has gone wrong... + logger.log(POILogger.WARN, "Type mismatch, expected ", + prop.usualType, " but got ", type, " for property ", prop); + going = false; + break; + } + } + + // TODO Detect if it is multi-valued, since if it is + // then even fixed-length strings store their multiple + // values in another chunk (much as variable length ones) + + // Work out how long the "data" is + // This might be the actual data, or just a pointer + // to another chunk which holds the data itself + boolean isPointer = false; + int length = type.getLength(); + if (!type.isFixedLength()) { + isPointer = true; + length = 8; + } + + // Grab the data block + byte[] data = new byte[length]; + IOUtils.readFully(value, data); + + // Skip over any padding + if (length < 8) { + byte[] padding = new byte[8 - length]; + IOUtils.readFully(value, padding); + } - if (properties.get(prop) != null) { - logger.log(POILogger.WARN, "Duplicate values found for " + prop); + // Wrap and store + PropertyValue propVal = null; + if (isPointer) { + // We'll match up the chunk later + propVal = new ChunkBasedPropertyValue(prop, flags, data); + } else if (type == Types.NULL) { + propVal = new NullPropertyValue(prop, flags, data); + } else if (type == Types.BOOLEAN) { + propVal = new BooleanPropertyValue(prop, flags, data); + } else if (type == Types.SHORT) { + propVal = new ShortPropertyValue(prop, flags, data); + } else if (type == Types.LONG) { + propVal = new LongPropertyValue(prop, flags, data); + } else if (type == Types.LONG_LONG) { + propVal = new LongLongPropertyValue(prop, flags, data); + } else if (type == Types.FLOAT) { + propVal = new FloatPropertyValue(prop, flags, data); + } else if (type == Types.DOUBLE) { + propVal = new DoublePropertyValue(prop, flags, data); + } else if (type == Types.CURRENCY) { + propVal = new CurrencyPropertyValue(prop, flags, data); + } else if (type == Types.TIME) { + propVal = new TimePropertyValue(prop, flags, data); + } + // TODO Add in the rest of the types + else { + propVal = new PropertyValue(prop, flags, data); + } + + if (properties.get(prop) != null) { + logger.log(POILogger.WARN, + "Duplicate values found for " + prop); + } + properties.put(prop, propVal); + } catch (BufferUnderrunException e) { + // Invalid property, ended short + going = false; } - properties.put(prop, propVal); - } catch (BufferUnderrunException e) { - // Invalid property, ended short - going = false; - } - } - } - - protected void writeProperties(OutputStream out) throws IOException { - // TODO - } + } + } + + protected void writeProperties(OutputStream out) throws IOException { + // TODO + } } Modified: poi/trunk/src/scratchpad/src/org/apache/poi/hsmf/datatypes/PropertyValue.java URL: http://svn.apache.org/viewvc/poi/trunk/src/scratchpad/src/org/apache/poi/hsmf/datatypes/PropertyValue.java?rev=1773544&r1=1773543&r2=1773544&view=diff ============================================================================== --- poi/trunk/src/scratchpad/src/org/apache/poi/hsmf/datatypes/PropertyValue.java (original) +++ poi/trunk/src/scratchpad/src/org/apache/poi/hsmf/datatypes/PropertyValue.java Sat Dec 10 23:35:12 2016 @@ -24,212 +24,228 @@ import org.apache.poi.util.LittleEndian; import org.apache.poi.util.LocaleUtil; /** - * An instance of a {@link MAPIProperty} inside a {@link PropertiesChunk}. - * Where the {@link Types} type is a fixed length one, this will contain the - * actual value. - * Where the {@link Types} type is a variable length one, this will contain - * the length of the property, and the value will be in the associated {@link Chunk}. + * An instance of a {@link MAPIProperty} inside a {@link PropertiesChunk}. Where + * the {@link Types} type is a fixed length one, this will contain the actual + * value. Where the {@link Types} type is a variable length one, this will + * contain the length of the property, and the value will be in the associated + * {@link Chunk}. */ public class PropertyValue { - private MAPIProperty property; - private long flags; - protected byte[] data; - - public PropertyValue(MAPIProperty property, long flags, byte[] data) { - this.property = property; - this.flags = flags; - this.data = data; - } - - public MAPIProperty getProperty() { - return property; - } - - /** - * Get the raw value flags. - * TODO Also provide getters for the flag meanings - */ - public long getFlags() { - return flags; - } - - public Object getValue() { - return data; - } - public void setRawValue(byte[] value) { - this.data = value; - } - - public String toString() { - Object v = getValue(); - if (v == null) - return "(No value available)"; - - if (v instanceof byte[]) { - return ByteChunk.toDebugFriendlyString((byte[])v); - } else { - // Just use the normal toString on the value - return v.toString(); - } - } - - public static class NullPropertyValue extends PropertyValue { - public NullPropertyValue(MAPIProperty property, long flags, byte[] data) { - super(property, flags, data); - } - - public Void getValue() { - return null; - } - } - - public static class BooleanPropertyValue extends PropertyValue { - public BooleanPropertyValue(MAPIProperty property, long flags, byte[] data) { - super(property, flags, data); - } - - public Boolean getValue() { - short val = LittleEndian.getShort(data); - return val > 0; - } - public void setValue(boolean value) { - if (data.length != 2) { - data = new byte[2]; - } - if (value) { - LittleEndian.putShort(data, 0, (short)1); - } - } - } - - public static class ShortPropertyValue extends PropertyValue { - public ShortPropertyValue(MAPIProperty property, long flags, byte[] data) { - super(property, flags, data); - } - - public Short getValue() { - return LittleEndian.getShort(data); - } - public void setValue(short value) { - if (data.length != 2) { - data = new byte[2]; - } - LittleEndian.putShort(data, 0, value); - } - } - - public static class LongPropertyValue extends PropertyValue { - public LongPropertyValue(MAPIProperty property, long flags, byte[] data) { - super(property, flags, data); - } - - public Integer getValue() { - return LittleEndian.getInt(data); - } - public void setValue(int value) { - if (data.length != 4) { - data = new byte[4]; - } - LittleEndian.putInt(data, 0, value); - } - } - - public static class LongLongPropertyValue extends PropertyValue { - public LongLongPropertyValue(MAPIProperty property, long flags, byte[] data) { - super(property, flags, data); - } - - public Long getValue() { - return LittleEndian.getLong(data); - } - public void setValue(long value) { - if (data.length != 8) { - data = new byte[8]; - } - LittleEndian.putLong(data, 0, value); - } - } - - public static class FloatPropertyValue extends PropertyValue { - public FloatPropertyValue(MAPIProperty property, long flags, byte[] data) { - super(property, flags, data); - } - - public Float getValue() { - return LittleEndian.getFloat(data); - } - public void setValue(float value) { - if (data.length != 4) { - data = new byte[4]; - } - LittleEndian.putFloat(data, 0, value); - } - } - - public static class DoublePropertyValue extends PropertyValue { - public DoublePropertyValue(MAPIProperty property, long flags, byte[] data) { - super(property, flags, data); - } - - public Double getValue() { - return LittleEndian.getDouble(data); - } - public void setValue(double value) { - if (data.length != 8) { - data = new byte[8]; - } - LittleEndian.putDouble(data, 0, value); - } - } - - /** - * signed 64-bit integer that represents a base ten decimal, - * with four digits to the right of the decimal point - */ - public static class CurrencyPropertyValue extends PropertyValue { - private static final BigInteger SHIFT = BigInteger.valueOf(10000); - public CurrencyPropertyValue(MAPIProperty property, long flags, byte[] data) { - super(property, flags, data); - } - - public BigInteger getValue() { - long unshifted = LittleEndian.getLong(data); - return BigInteger.valueOf(unshifted).divide(SHIFT); - } - public void setValue(BigInteger value) { - if (data.length != 8) { - data = new byte[8]; - } - long shifted = value.multiply(SHIFT).longValue(); - LittleEndian.putLong(data, 0, shifted); - } - } - - /** - * 64-bit integer specifying the number of 100ns periods since Jan 1, 1601 - */ - public static class TimePropertyValue extends PropertyValue { - private static final long OFFSET = 1000L * 60L * 60L * 24L * (365L * 369L + 89L); - public TimePropertyValue(MAPIProperty property, long flags, byte[] data) { - super(property, flags, data); - } - - public Calendar getValue() { - long time = LittleEndian.getLong(data); - time = (time / 10 / 1000) - OFFSET; - - Calendar timeC = LocaleUtil.getLocaleCalendar(); - timeC.setTimeInMillis(time); - - return timeC; - } - public void setValue(Calendar value) { - if (data.length != 8) { - data = new byte[8]; - } - long time = value.getTimeInMillis(); - time = (time + OFFSET) *10*1000; - LittleEndian.putLong(data, 0, time); - } - } + private MAPIProperty property; + private long flags; + protected byte[] data; + + public PropertyValue(MAPIProperty property, long flags, byte[] data) { + this.property = property; + this.flags = flags; + this.data = data; + } + + public MAPIProperty getProperty() { + return property; + } + + /** + * Get the raw value flags. TODO Also provide getters for the flag meanings + */ + public long getFlags() { + return flags; + } + + public Object getValue() { + return data; + } + + public void setRawValue(byte[] value) { + this.data = value; + } + + public String toString() { + Object v = getValue(); + if (v == null) + return "(No value available)"; + + if (v instanceof byte[]) { + return ByteChunk.toDebugFriendlyString((byte[]) v); + } else { + // Just use the normal toString on the value + return v.toString(); + } + } + + public static class NullPropertyValue extends PropertyValue { + public NullPropertyValue(MAPIProperty property, long flags, + byte[] data) { + super(property, flags, data); + } + + public Void getValue() { + return null; + } + } + + public static class BooleanPropertyValue extends PropertyValue { + public BooleanPropertyValue(MAPIProperty property, long flags, + byte[] data) { + super(property, flags, data); + } + + public Boolean getValue() { + short val = LittleEndian.getShort(data); + return val > 0; + } + + public void setValue(boolean value) { + if (data.length != 2) { + data = new byte[2]; + } + if (value) { + LittleEndian.putShort(data, 0, (short) 1); + } + } + } + + public static class ShortPropertyValue extends PropertyValue { + public ShortPropertyValue(MAPIProperty property, long flags, + byte[] data) { + super(property, flags, data); + } + + public Short getValue() { + return LittleEndian.getShort(data); + } + + public void setValue(short value) { + if (data.length != 2) { + data = new byte[2]; + } + LittleEndian.putShort(data, 0, value); + } + } + + public static class LongPropertyValue extends PropertyValue { + public LongPropertyValue(MAPIProperty property, long flags, byte[] data) { + super(property, flags, data); + } + + public Integer getValue() { + return LittleEndian.getInt(data); + } + + public void setValue(int value) { + if (data.length != 4) { + data = new byte[4]; + } + LittleEndian.putInt(data, 0, value); + } + } + + public static class LongLongPropertyValue extends PropertyValue { + public LongLongPropertyValue(MAPIProperty property, long flags, + byte[] data) { + super(property, flags, data); + } + + public Long getValue() { + return LittleEndian.getLong(data); + } + + public void setValue(long value) { + if (data.length != 8) { + data = new byte[8]; + } + LittleEndian.putLong(data, 0, value); + } + } + + public static class FloatPropertyValue extends PropertyValue { + public FloatPropertyValue(MAPIProperty property, long flags, + byte[] data) { + super(property, flags, data); + } + + public Float getValue() { + return LittleEndian.getFloat(data); + } + + public void setValue(float value) { + if (data.length != 4) { + data = new byte[4]; + } + LittleEndian.putFloat(data, 0, value); + } + } + + public static class DoublePropertyValue extends PropertyValue { + public DoublePropertyValue(MAPIProperty property, long flags, byte[] data) { + super(property, flags, data); + } + + public Double getValue() { + return LittleEndian.getDouble(data); + } + + public void setValue(double value) { + if (data.length != 8) { + data = new byte[8]; + } + LittleEndian.putDouble(data, 0, value); + } + } + + /** + * signed 64-bit integer that represents a base ten decimal, with four + * digits to the right of the decimal point + */ + public static class CurrencyPropertyValue extends PropertyValue { + private static final BigInteger SHIFT = BigInteger.valueOf(10000); + + public CurrencyPropertyValue(MAPIProperty property, long flags, byte[] data) { + super(property, flags, data); + } + + public BigInteger getValue() { + long unshifted = LittleEndian.getLong(data); + return BigInteger.valueOf(unshifted).divide(SHIFT); + } + + public void setValue(BigInteger value) { + if (data.length != 8) { + data = new byte[8]; + } + long shifted = value.multiply(SHIFT).longValue(); + LittleEndian.putLong(data, 0, shifted); + } + } + + /** + * 64-bit integer specifying the number of 100ns periods since Jan 1, 1601 + */ + public static class TimePropertyValue extends PropertyValue { + private static final long OFFSET = 1000L * 60L * 60L * 24L + * (365L * 369L + 89L); + + public TimePropertyValue(MAPIProperty property, long flags, byte[] data) { + super(property, flags, data); + } + + public Calendar getValue() { + long time = LittleEndian.getLong(data); + time = (time / 10 / 1000) - OFFSET; + + Calendar timeC = LocaleUtil.getLocaleCalendar(); + timeC.setTimeInMillis(time); + + return timeC; + } + + public void setValue(Calendar value) { + if (data.length != 8) { + data = new byte[8]; + } + long time = value.getTimeInMillis(); + time = (time + OFFSET) * 10 * 1000; + LittleEndian.putLong(data, 0, time); + } + } } Modified: poi/trunk/src/scratchpad/src/org/apache/poi/hsmf/datatypes/RecipientChunks.java URL: http://svn.apache.org/viewvc/poi/trunk/src/scratchpad/src/org/apache/poi/hsmf/datatypes/RecipientChunks.java?rev=1773544&r1=1773543&r2=1773544&view=diff ============================================================================== --- poi/trunk/src/scratchpad/src/org/apache/poi/hsmf/datatypes/RecipientChunks.java (original) +++ poi/trunk/src/scratchpad/src/org/apache/poi/hsmf/datatypes/RecipientChunks.java Sat Dec 10 23:35:12 2016 @@ -27,209 +27,199 @@ import java.util.Map; import org.apache.poi.util.POILogFactory; import org.apache.poi.util.POILogger; - /** - * Collection of convenience chunks for the - * Recip(ient) part of an outlook file. + * Collection of convenience chunks for the Recip(ient) part of an outlook file. * - * If a message has multiple recipients, there will be - * several of these. + * If a message has multiple recipients, there will be several of these. */ public final class RecipientChunks implements ChunkGroupWithProperties { - private static POILogger logger = POILogFactory.getLogger(RecipientChunks.class); + private static POILogger logger = POILogFactory.getLogger(RecipientChunks.class); + + public static final String PREFIX = "__recip_version1.0_#"; - public static final String PREFIX = "__recip_version1.0_#"; - - public static final MAPIProperty RECIPIENT_NAME = MAPIProperty.DISPLAY_NAME; - public static final MAPIProperty DELIVERY_TYPE = MAPIProperty.ADDRTYPE; - public static final MAPIProperty RECIPIENT_EMAIL_ADDRESS = MAPIProperty.EMAIL_ADDRESS; - public static final MAPIProperty RECIPIENT_SEARCH = MAPIProperty.SEARCH_KEY; - public static final MAPIProperty RECIPIENT_SMTP_ADDRESS = MAPIProperty.SMTP_ADDRESS; - public static final MAPIProperty RECIPIENT_DISPLAY_NAME = MAPIProperty.RECIPIENT_DISPLAY_NAME; - - /** Our 0 based position in the list of recipients */ - public int recipientNumber; - - /** TODO */ - public ByteChunk recipientSearchChunk; - /** - * The "name", which could be their name if an - * internal person, or their email address - * if an external person - */ - public StringChunk recipientNameChunk; - /** - * The email address of the recipient, which - * could be in SMTP or SEARCH format, but - * isn't always present... - */ - public StringChunk recipientEmailChunk; - /** - * The smtp destination email address of - * the recipient, but isn't always present... - */ - public StringChunk recipientSMTPChunk; - /** - * Normally EX or SMTP. Will generally affect - * where the email address ends up. - */ - public StringChunk deliveryTypeChunk; - /** - * The display name of the recipient. - * Normally seems to hold the same value - * as in recipientNameChunk - */ - public StringChunk recipientDisplayNameChunk; - /** - * Holds the fixed sized properties, and the - * pointers to the data of variable sized ones - */ - private PropertiesChunk recipientProperties; - - public RecipientChunks(String name) { - recipientNumber = -1; - int splitAt = name.lastIndexOf('#'); - if(splitAt > -1) { - String number = name.substring(splitAt+1); - try { - recipientNumber = Integer.parseInt(number, 16); - } catch(NumberFormatException e) { - logger.log(POILogger.ERROR, "Invalid recipient number in name " + name); - } - } - } - - /** - * Tries to find their name, - * in whichever chunk holds it. - */ - public String getRecipientName() { - if(recipientNameChunk != null) { - return recipientNameChunk.getValue(); - } - if(recipientDisplayNameChunk != null) { - return recipientDisplayNameChunk.getValue(); - } - - // Can't find it - return null; - } - - /** - * Tries to find their email address, in - * whichever chunk holds it given the - * delivery type. - */ - public String getRecipientEmailAddress() { - // If we have this, it really has the email - if(recipientSMTPChunk != null) { - return recipientSMTPChunk.getValue(); - } - - // This might be a real email, or might be - // in CN=... format - if(recipientEmailChunk != null) { - String email = recipientEmailChunk.getValue(); - int cne = email.indexOf("/CN="); - if(cne == -1) { - // Normal smtp address - return email; - } else { - // /O=..../CN=em@ail - return email.substring(cne+4); - } - } - - // Might be in the name field, check there - if(recipientNameChunk != null) { - String name = recipientNameChunk.getValue(); - if(name.indexOf('@') > -1) { - // Strip leading and trailing quotes if needed - if(name.startsWith("'") && name.endsWith("'")) { - return name.substring(1, name.length()-1); + public static final MAPIProperty RECIPIENT_NAME = MAPIProperty.DISPLAY_NAME; + public static final MAPIProperty DELIVERY_TYPE = MAPIProperty.ADDRTYPE; + public static final MAPIProperty RECIPIENT_EMAIL_ADDRESS = MAPIProperty.EMAIL_ADDRESS; + public static final MAPIProperty RECIPIENT_SEARCH = MAPIProperty.SEARCH_KEY; + public static final MAPIProperty RECIPIENT_SMTP_ADDRESS = MAPIProperty.SMTP_ADDRESS; + public static final MAPIProperty RECIPIENT_DISPLAY_NAME = MAPIProperty.RECIPIENT_DISPLAY_NAME; + + /** Our 0 based position in the list of recipients */ + public int recipientNumber; + + /** TODO */ + public ByteChunk recipientSearchChunk; + /** + * The "name", which could be their name if an internal person, or their + * email address if an external person + */ + public StringChunk recipientNameChunk; + /** + * The email address of the recipient, which could be in SMTP or SEARCH + * format, but isn't always present... + */ + public StringChunk recipientEmailChunk; + /** + * The smtp destination email address of the recipient, but isn't always + * present... + */ + public StringChunk recipientSMTPChunk; + /** + * Normally EX or SMTP. Will generally affect where the email address ends + * up. + */ + public StringChunk deliveryTypeChunk; + /** + * The display name of the recipient. Normally seems to hold the same value + * as in recipientNameChunk + */ + public StringChunk recipientDisplayNameChunk; + /** + * Holds the fixed sized properties, and the pointers to the data of + * variable sized ones + */ + private PropertiesChunk recipientProperties; + + public RecipientChunks(String name) { + recipientNumber = -1; + int splitAt = name.lastIndexOf('#'); + if (splitAt > -1) { + String number = name.substring(splitAt + 1); + try { + recipientNumber = Integer.parseInt(number, 16); + } catch (NumberFormatException e) { + logger.log(POILogger.ERROR, + "Invalid recipient number in name " + name); } - return name; - } - } - - // Check the search chunk, see if it's - // encoded as a SMTP destination in there. - if(recipientSearchChunk != null) { - String search = recipientSearchChunk.getAs7bitString(); - if(search.indexOf("SMTP:") != -1) { - return search.substring(search.indexOf("SMTP:") + 5); - } - } - - // Can't find it - return null; - } - - /** Holds all the chunks that were found. */ - private List<Chunk> allChunks = new ArrayList<Chunk>(); - - public Map<MAPIProperty,List<PropertyValue>> getProperties() { - if (recipientProperties != null) { - return recipientProperties.getProperties(); - } - else return Collections.emptyMap(); - } - public Chunk[] getAll() { - return allChunks.toArray(new Chunk[allChunks.size()]); - } - public Chunk[] getChunks() { - return getAll(); - } - - /** - * Called by the parser whenever a chunk is found. - */ - public void record(Chunk chunk) { - if(chunk.getChunkId() == RECIPIENT_SEARCH.id) { - // TODO - parse - recipientSearchChunk = (ByteChunk)chunk; - } - else if(chunk.getChunkId() == RECIPIENT_NAME.id) { - recipientDisplayNameChunk = (StringChunk)chunk; - } - else if(chunk.getChunkId() == RECIPIENT_DISPLAY_NAME.id) { - recipientNameChunk = (StringChunk)chunk; - } - else if(chunk.getChunkId() == RECIPIENT_EMAIL_ADDRESS.id) { - recipientEmailChunk = (StringChunk)chunk; - } - else if(chunk.getChunkId() == RECIPIENT_SMTP_ADDRESS.id) { - recipientSMTPChunk = (StringChunk)chunk; - } - else if(chunk.getChunkId() == DELIVERY_TYPE.id) { - deliveryTypeChunk = (StringChunk)chunk; - } - else if(chunk instanceof PropertiesChunk) { - recipientProperties = (PropertiesChunk) chunk; - } - - // And add to the main list - allChunks.add(chunk); - } - - public void chunksComplete() { - if (recipientProperties != null) { - recipientProperties.matchVariableSizedPropertiesToChunks(); - } else { - logger.log(POILogger.WARN, "Recipeints Chunk didn't contain a list of properties!"); - } - } - - /** - * Orders by the recipient number. - */ - public static class RecipientChunksSorter implements Comparator<RecipientChunks>, Serializable { - public int compare(RecipientChunks a, RecipientChunks b) { - if(a.recipientNumber < b.recipientNumber) - return -1; - if(a.recipientNumber > b.recipientNumber) - return +1; - return 0; - } - } + } + } + + /** + * Tries to find their name, in whichever chunk holds it. + */ + public String getRecipientName() { + if (recipientNameChunk != null) { + return recipientNameChunk.getValue(); + } + if (recipientDisplayNameChunk != null) { + return recipientDisplayNameChunk.getValue(); + } + + // Can't find it + return null; + } + + /** + * Tries to find their email address, in whichever chunk holds it given the + * delivery type. + */ + public String getRecipientEmailAddress() { + // If we have this, it really has the email + if (recipientSMTPChunk != null) { + return recipientSMTPChunk.getValue(); + } + + // This might be a real email, or might be + // in CN=... format + if (recipientEmailChunk != null) { + String email = recipientEmailChunk.getValue(); + int cne = email.indexOf("/CN="); + if (cne == -1) { + // Normal smtp address + return email; + } else { + // /O=..../CN=em@ail + return email.substring(cne + 4); + } + } + + // Might be in the name field, check there + if (recipientNameChunk != null) { + String name = recipientNameChunk.getValue(); + if (name.indexOf('@') > -1) { + // Strip leading and trailing quotes if needed + if (name.startsWith("'") && name.endsWith("'")) { + return name.substring(1, name.length() - 1); + } + return name; + } + } + + // Check the search chunk, see if it's + // encoded as a SMTP destination in there. + if (recipientSearchChunk != null) { + String search = recipientSearchChunk.getAs7bitString(); + if (search.indexOf("SMTP:") != -1) { + return search.substring(search.indexOf("SMTP:") + 5); + } + } + + // Can't find it + return null; + } + + /** Holds all the chunks that were found. */ + private List<Chunk> allChunks = new ArrayList<Chunk>(); + + public Map<MAPIProperty, List<PropertyValue>> getProperties() { + if (recipientProperties != null) { + return recipientProperties.getProperties(); + } else + return Collections.emptyMap(); + } + + public Chunk[] getAll() { + return allChunks.toArray(new Chunk[allChunks.size()]); + } + + public Chunk[] getChunks() { + return getAll(); + } + + /** + * Called by the parser whenever a chunk is found. + */ + public void record(Chunk chunk) { + if (chunk.getChunkId() == RECIPIENT_SEARCH.id) { + // TODO - parse + recipientSearchChunk = (ByteChunk) chunk; + } else if (chunk.getChunkId() == RECIPIENT_NAME.id) { + recipientDisplayNameChunk = (StringChunk) chunk; + } else if (chunk.getChunkId() == RECIPIENT_DISPLAY_NAME.id) { + recipientNameChunk = (StringChunk) chunk; + } else if (chunk.getChunkId() == RECIPIENT_EMAIL_ADDRESS.id) { + recipientEmailChunk = (StringChunk) chunk; + } else if (chunk.getChunkId() == RECIPIENT_SMTP_ADDRESS.id) { + recipientSMTPChunk = (StringChunk) chunk; + } else if (chunk.getChunkId() == DELIVERY_TYPE.id) { + deliveryTypeChunk = (StringChunk) chunk; + } else if (chunk instanceof PropertiesChunk) { + recipientProperties = (PropertiesChunk) chunk; + } + + // And add to the main list + allChunks.add(chunk); + } + + public void chunksComplete() { + if (recipientProperties != null) { + recipientProperties.matchVariableSizedPropertiesToChunks(); + } else { + logger.log(POILogger.WARN, "Recipeints Chunk didn't contain a list of properties!"); + } + } + + /** + * Orders by the recipient number. + */ + public static class RecipientChunksSorter + implements Comparator<RecipientChunks>, Serializable { + public int compare(RecipientChunks a, RecipientChunks b) { + if (a.recipientNumber < b.recipientNumber) + return -1; + if (a.recipientNumber > b.recipientNumber) + return +1; + return 0; + } + } } Modified: poi/trunk/src/scratchpad/src/org/apache/poi/hsmf/datatypes/StoragePropertiesChunk.java URL: http://svn.apache.org/viewvc/poi/trunk/src/scratchpad/src/org/apache/poi/hsmf/datatypes/StoragePropertiesChunk.java?rev=1773544&r1=1773543&r2=1773544&view=diff ============================================================================== --- poi/trunk/src/scratchpad/src/org/apache/poi/hsmf/datatypes/StoragePropertiesChunk.java (original) +++ poi/trunk/src/scratchpad/src/org/apache/poi/hsmf/datatypes/StoragePropertiesChunk.java Sat Dec 10 23:35:12 2016 @@ -24,30 +24,29 @@ 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 + * 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(ChunkGroup parentGroup) { - super(parentGroup); - } - - @Override - public void readValue(InputStream stream) throws IOException { - // 8 bytes of reserved zeros - LittleEndian.readLong(stream); - - // Now properties - readProperties(stream); - } + public StoragePropertiesChunk(ChunkGroup parentGroup) { + super(parentGroup); + } - @Override - public void writeValue(OutputStream out) throws IOException { - // 8 bytes of reserved zeros - out.write(new byte[8]); - - // Now properties - writeProperties(out); - } + @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); + } } \ No newline at end of file Modified: poi/trunk/src/scratchpad/src/org/apache/poi/hsmf/datatypes/StringChunk.java URL: http://svn.apache.org/viewvc/poi/trunk/src/scratchpad/src/org/apache/poi/hsmf/datatypes/StringChunk.java?rev=1773544&r1=1773543&r2=1773544&view=diff ============================================================================== --- poi/trunk/src/scratchpad/src/org/apache/poi/hsmf/datatypes/StringChunk.java (original) +++ poi/trunk/src/scratchpad/src/org/apache/poi/hsmf/datatypes/StringChunk.java Sat Dec 10 23:35:12 2016 @@ -30,120 +30,119 @@ import org.apache.poi.util.StringUtil; * A Chunk made up of a single string. */ public class StringChunk extends Chunk { - private static final String DEFAULT_ENCODING = "CP1252"; - private String encoding7Bit = DEFAULT_ENCODING; - private byte[] rawValue; - private String value; - - /** - * Creates a String Chunk. - */ - public StringChunk(String namePrefix, int chunkId, MAPIType type) { - super(namePrefix, chunkId, type); - } - - /** - * Create a String Chunk, with the specified - * type. - */ - public StringChunk(int chunkId, MAPIType type) { - super(chunkId, type); - } - - /** - * Returns the Encoding that will be used to - * decode any "7 bit" (non unicode) data. - * Most files default to CP1252 - */ - public String get7BitEncoding() { - return encoding7Bit; - } - - /** - * Sets the Encoding that will be used to - * decode any "7 bit" (non unicode) data. - * This doesn't appear to be stored anywhere - * specific in the file, so you may need - * to guess by looking at headers etc - */ - public void set7BitEncoding(String encoding) { - this.encoding7Bit = encoding; - - // Re-read the String if we're a 7 bit one - if(type == Types.ASCII_STRING) { - parseString(); - } - } - - public void readValue(InputStream value) throws IOException { - rawValue = IOUtils.toByteArray(value); - parseString(); - } - private void parseString() { - String tmpValue; - if (type == Types.ASCII_STRING) { - tmpValue = parseAs7BitData(rawValue, encoding7Bit); - } else if (type == Types.UNICODE_STRING) { - tmpValue = StringUtil.getFromUnicodeLE(rawValue); - } else { - throw new IllegalArgumentException("Invalid type " + type + " for String Chunk"); - } - - // Clean up - this.value = tmpValue.replace("\0", ""); - } - - public void writeValue(OutputStream out) throws IOException { - out.write(rawValue); - } - private void storeString() { - if (type == Types.ASCII_STRING) { - rawValue = value.getBytes(Charset.forName(encoding7Bit)); - } else if (type == Types.UNICODE_STRING) { - rawValue = StringUtil.getToUnicodeLE(value); - } else { - throw new IllegalArgumentException("Invalid type " + type + " for String Chunk"); - } - } - - /** - * Returns the Text value of the chunk - */ - public String getValue() { - return this.value; - } - - public byte[] getRawValue() { - return this.rawValue; - } - - public void setValue(String str) { - this.value = str; - storeString(); - } - - public String toString() { - return this.value; - } - - /** - * Parses as non-unicode, supposedly 7 bit CP1252 data - * and returns the string that that yields. - */ - protected static String parseAs7BitData(byte[] data) { - return parseAs7BitData(data, DEFAULT_ENCODING); - } - /** - * Parses as non-unicode, supposedly 7 bit data - * and returns the string that that yields. - */ - protected static String parseAs7BitData(byte[] data, String encoding) { - // Handle any encoding aliases, where outlook describes it differently - if ("ansi".equals(encoding)) { - encoding = DEFAULT_ENCODING; - } - - // Decode - return new String(data, Charset.forName(encoding)); - } + private static final String DEFAULT_ENCODING = "CP1252"; + private String encoding7Bit = DEFAULT_ENCODING; + private byte[] rawValue; + private String value; + + /** + * Creates a String Chunk. + */ + public StringChunk(String namePrefix, int chunkId, MAPIType type) { + super(namePrefix, chunkId, type); + } + + /** + * Create a String Chunk, with the specified type. + */ + public StringChunk(int chunkId, MAPIType type) { + super(chunkId, type); + } + + /** + * Returns the Encoding that will be used to decode any "7 bit" (non + * unicode) data. Most files default to CP1252 + */ + public String get7BitEncoding() { + return encoding7Bit; + } + + /** + * Sets the Encoding that will be used to decode any "7 bit" (non unicode) + * data. This doesn't appear to be stored anywhere specific in the file, so + * you may need to guess by looking at headers etc + */ + public void set7BitEncoding(String encoding) { + this.encoding7Bit = encoding; + + // Re-read the String if we're a 7 bit one + if (type == Types.ASCII_STRING) { + parseString(); + } + } + + public void readValue(InputStream value) throws IOException { + rawValue = IOUtils.toByteArray(value); + parseString(); + } + + private void parseString() { + String tmpValue; + if (type == Types.ASCII_STRING) { + tmpValue = parseAs7BitData(rawValue, encoding7Bit); + } else if (type == Types.UNICODE_STRING) { + tmpValue = StringUtil.getFromUnicodeLE(rawValue); + } else { + throw new IllegalArgumentException("Invalid type " + type + " for String Chunk"); + } + + // Clean up + this.value = tmpValue.replace("\0", ""); + } + + public void writeValue(OutputStream out) throws IOException { + out.write(rawValue); + } + + private void storeString() { + if (type == Types.ASCII_STRING) { + rawValue = value.getBytes(Charset.forName(encoding7Bit)); + } else if (type == Types.UNICODE_STRING) { + rawValue = StringUtil.getToUnicodeLE(value); + } else { + throw new IllegalArgumentException("Invalid type " + type + " for String Chunk"); + } + } + + /** + * Returns the Text value of the chunk + */ + public String getValue() { + return this.value; + } + + public byte[] getRawValue() { + return this.rawValue; + } + + public void setValue(String str) { + this.value = str; + storeString(); + } + + public String toString() { + return this.value; + } + + /** + * Parses as non-unicode, supposedly 7 bit CP1252 data and returns the + * string that that yields. + */ + protected static String parseAs7BitData(byte[] data) { + return parseAs7BitData(data, DEFAULT_ENCODING); + } + + /** + * Parses as non-unicode, supposedly 7 bit data and returns the string that + * that yields. + */ + protected static String parseAs7BitData(byte[] data, String encoding) { + // Handle any encoding aliases, where outlook describes it differently + if ("ansi".equals(encoding)) { + encoding = DEFAULT_ENCODING; + } + + // Decode + return new String(data, Charset.forName(encoding)); + } } Modified: poi/trunk/src/scratchpad/src/org/apache/poi/hsmf/datatypes/Types.java URL: http://svn.apache.org/viewvc/poi/trunk/src/scratchpad/src/org/apache/poi/hsmf/datatypes/Types.java?rev=1773544&r1=1773543&r2=1773544&view=diff ============================================================================== --- poi/trunk/src/scratchpad/src/org/apache/poi/hsmf/datatypes/Types.java (original) +++ poi/trunk/src/scratchpad/src/org/apache/poi/hsmf/datatypes/Types.java Sat Dec 10 23:35:12 2016 @@ -23,158 +23,169 @@ import java.util.Map; /** * The types list and details are available from - * http://msdn.microsoft.com/en-us/library/microsoft.exchange.data.contenttypes.tnef.tnefpropertytype%28v=EXCHG.140%29.aspx + * http://msdn.microsoft.com/en-us/library/microsoft.exchange.data.contenttypes.tnef.tnefpropertytype%28v=EXCHG.140%29.aspx */ 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>(); + 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); - /** Unknown */ - public static final MAPIType UNKNOWN = new MAPIType(-1, "Unknown", -1); - - /** Null - NULL property value */ - public static final MAPIType NULL = new MAPIType(0x0001, "Null", 0); - /** I2 - signed 16-bit value */ - public static final MAPIType SHORT = new MAPIType(0x0002, "Short", 2); - /** Long - signed 32-bit value */ - public static final MAPIType LONG = new MAPIType(0x0003, "Long", 4); - /** R4 - 4-byte floating point value */ - public static final MAPIType FLOAT = new MAPIType(0x0004, "Float", 4); - /** Double - floating point double */ - 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 */ - public static final MAPIType CURRENCY = new MAPIType(0x0006, "Currency", 8); - /** AppTime - application time value */ - public static final MAPIType APP_TIME = new MAPIType(0x0007, "Application Time", 8); - /** Error - 32-bit error value */ - public static final MAPIType ERROR = new MAPIType(0x000A, "Error", 4); - /** Boolean - 16-bit Boolean value. '0' is false. Non-zero is true */ - public static final MAPIType BOOLEAN = new MAPIType(0x000B, "Boolean", 2); - /** Object/Directory - embedded object in a property */ - public static final MAPIType DIRECTORY = new MAPIType(0x000D, "Directory", -1); - /** I8 - 8-byte signed integer */ - 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 */ - public static final MAPIType TIME = new MAPIType(0x0040, "Time", 8); - /** ClassId - OLE GUID */ - public static final MAPIType CLS_ID = new MAPIType(0x0048, "CLS ID GUID", 16); - - /** Binary - counted byte array */ - public static final MAPIType BINARY = new MAPIType(0x0102, "Binary", -1); - - /** - * An 8-bit string, probably in CP1252, but don't quote us... - * Normally used for everything before Outlook 3.0, and some - * fields in Outlook 3.0. - */ - public static final MAPIType ASCII_STRING = new MAPIType(0x001E, "ASCII String", -1); - /** A string, from Outlook 3.0 onwards. Normally unicode */ - public static final MAPIType UNICODE_STRING = new MAPIType(0x001F, "Unicode String", -1); - - /** MultiValued - Value part contains multiple values */ - public static final int MULTIVALUED_FLAG = 0x1000; - - public static final class MAPIType { - private final int id; - private final String name; - private final int length; - - /** - * Creates a standard, built-in type - */ - private MAPIType(int id, String name, int length) { - this.id = id; - this.name = name; - this.length = length; - builtInTypes.put(id, this); - } - /** - * Creates a custom type - */ - private MAPIType(int id, int length) { - this.id = id; - this.name = asCustomName(id); - this.length = length; - customTypes.put(id, this); - } - - /** - * Returns the length, in bytes, of values of this type, or - * -1 if it is a variable length type. - */ - public int getLength() { - return length; - } - /** - * Is this type a fixed-length type, or a variable-length one? - */ - public boolean isFixedLength() { - return (length != -1); - } - - public int getId() { - return id; - } - public String getName() { - return name; - } - - public String toString() { - return id + " / 0x" + asFileEnding() + " - " + name + " @ " + length; - } - - /** - * Return the 4 character hex encoded version, - * as used in file endings - */ - public String asFileEnding() { - return Types.asFileEnding(id); - } - } - - public static MAPIType getById(int typeId) { - return builtInTypes.get(typeId); - } - - public static String asFileEnding(int type) { - String str = Integer.toHexString(type).toUpperCase(Locale.ROOT); - while(str.length() < 4) { - str = "0" + str; - } - return str; - } - public static String asName(int typeId) { - MAPIType type = builtInTypes.get(typeId); - if (type != null) { - return type.name; - } - return asCustomName(typeId); - } - private static String asCustomName(int typeId) { - return "0x" + Integer.toHexString(typeId); - } - - public static MAPIType createCustom(int typeId) { - // 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); + /** Unspecified */ + public static final MAPIType UNSPECIFIED = new MAPIType(0x0000, + "Unspecified", -1); + /** Unknown */ + public static final MAPIType UNKNOWN = new MAPIType(-1, "Unknown", -1); + + /** Null - NULL property value */ + public static final MAPIType NULL = new MAPIType(0x0001, "Null", 0); + /** I2 - signed 16-bit value */ + public static final MAPIType SHORT = new MAPIType(0x0002, "Short", 2); + /** Long - signed 32-bit value */ + public static final MAPIType LONG = new MAPIType(0x0003, "Long", 4); + /** R4 - 4-byte floating point value */ + public static final MAPIType FLOAT = new MAPIType(0x0004, "Float", 4); + /** Double - floating point double */ + 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 + */ + public static final MAPIType CURRENCY = new MAPIType(0x0006, "Currency", 8); + /** AppTime - application time value */ + public static final MAPIType APP_TIME = new MAPIType(0x0007, "Application Time", 8); + /** Error - 32-bit error value */ + public static final MAPIType ERROR = new MAPIType(0x000A, "Error", 4); + /** Boolean - 16-bit Boolean value. '0' is false. Non-zero is true */ + public static final MAPIType BOOLEAN = new MAPIType(0x000B, "Boolean", 2); + /** Object/Directory - embedded object in a property */ + public static final MAPIType DIRECTORY = new MAPIType(0x000D, "Directory", -1); + /** I8 - 8-byte signed integer */ + 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 + */ + public static final MAPIType TIME = new MAPIType(0x0040, "Time", 8); + /** ClassId - OLE GUID */ + public static final MAPIType CLS_ID = new MAPIType(0x0048, "CLS ID GUID", 16); + + /** Binary - counted byte array */ + public static final MAPIType BINARY = new MAPIType(0x0102, "Binary", -1); + + /** + * An 8-bit string, probably in CP1252, but don't quote us... Normally used + * for everything before Outlook 3.0, and some fields in Outlook 3.0. + */ + public static final MAPIType ASCII_STRING = new MAPIType(0x001E, "ASCII String", -1); + /** A string, from Outlook 3.0 onwards. Normally unicode */ + public static final MAPIType UNICODE_STRING = new MAPIType(0x001F, "Unicode String", -1); + + /** MultiValued - Value part contains multiple values */ + public static final int MULTIVALUED_FLAG = 0x1000; + + public static final class MAPIType { + private final int id; + private final String name; + private final int length; + + /** + * Creates a standard, built-in type + */ + private MAPIType(int id, String name, int length) { + this.id = id; + this.name = name; + this.length = length; + builtInTypes.put(id, this); + } + + /** + * Creates a custom type + */ + private MAPIType(int id, int length) { + this.id = id; + this.name = asCustomName(id); + this.length = length; + customTypes.put(id, this); + } + + /** + * Returns the length, in bytes, of values of this type, or -1 if it is + * a variable length type. + */ + public int getLength() { + return length; + } + + /** + * Is this type a fixed-length type, or a variable-length one? + */ + public boolean isFixedLength() { + return (length != -1); + } + + public int getId() { + return id; + } + + public String getName() { + return name; + } + + public String toString() { + return id + " / 0x" + asFileEnding() + " - " + name + " @ " + + length; + } + + /** + * Return the 4 character hex encoded version, as used in file endings + */ + public String asFileEnding() { + return Types.asFileEnding(id); + } + } + + public static MAPIType getById(int typeId) { + return builtInTypes.get(typeId); + } + + public static String asFileEnding(int type) { + String str = Integer.toHexString(type).toUpperCase(Locale.ROOT); + while (str.length() < 4) { + str = "0" + str; + } + return str; + } + + public static String asName(int typeId) { + MAPIType type = builtInTypes.get(typeId); + if (type != null) { + return type.name; + } + return asCustomName(typeId); + } + + private static String asCustomName(int typeId) { + return "0x" + Integer.toHexString(typeId); + } + + public static MAPIType createCustom(int typeId) { + // 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; - } + } + + return type; + } } --------------------------------------------------------------------- To unsubscribe, e-mail: commits-unsubscr...@poi.apache.org For additional commands, e-mail: commits-h...@poi.apache.org