import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
+import java.util.Calendar;
import org.apache.poi.hsmf.datatypes.AttachmentChunks;
import org.apache.poi.hsmf.datatypes.ChunkGroup;
public String getMessageClass() throws ChunkNotFoundException {
return getStringFromChunk(mainChunks.messageClass);
}
+
+ /**
+ * Gets the date that the message was accepted by the
+ * server on.
+ */
+ public Calendar getMessageDate() throws ChunkNotFoundException {
+ if(mainChunks.submissionChunk != null) {
+ return mainChunks.submissionChunk.getAcceptedAtTime();
+ }
+ throw new ChunkNotFoundException();
+ }
/**
import org.apache.poi.util.IOUtils;
/**
- * A Chunk made up of a ByteArrayOutputStream.
+ * A Chunk that holds binary data, normally
+ * unparsed.
+ * Generally as we know how to make sense of the
+ * contents, we create a new Chunk class and add
+ * a special case in the parser for them.
*/
public class ByteChunk extends Chunk {
/* String parts of Outlook Messages that are currently known */
public static final int MESSAGE_CLASS = 0x001A;
public static final int SUBJECT = 0x0037;
- public static final int DATE = 0x0047;
+ // "PidTagMessageSubmissionId" as given by accepting server
+ public static final int SUBMISSION_ID_DATE = 0x0047;
+ // 0x0050 -> 0x006F seem to be routing info or similar
public static final int CONVERSATION_TOPIC = 0x0070;
public static final int SENT_BY_SERVER_TYPE = 0x0075;
// RECEIVEDEMAIL = 76
public static final int EMAIL_FROM = 0x0C1F;
public static final int DISPLAY_CC = 0x0E03;
public static final int DISPLAY_BCC = 0x0E02;
+ // 0x0E1D seems a duplicate of 0x0070 !
public static final int TEXT_BODY = 0x1000;
+ public static final int MESSAGE_ID = 0x1035;
/** Holds all the chunks that were found. */
private List<Chunk> allChunks = new ArrayList<Chunk>();
/** Type of server that the message originated from (SMTP, etc). */
public StringChunk sentByServerType;
/** TODO */
- public ByteChunk dateChunk;
+ public MessageSubmissionChunk submissionChunk;
/** TODO */
public StringChunk emailFromChunk;
+ /** The message ID */
+ public StringChunk messageId;
public Chunk[] getAll() {
return allChunks.toArray(new Chunk[allChunks.size()]);
case MESSAGE_CLASS:
messageClass = (StringChunk)chunk;
break;
+ case MESSAGE_ID:
+ messageId = (StringChunk)chunk;
+ break;
case SUBJECT:
subjectChunk = (StringChunk)chunk;
break;
- case DATE:
+ case SUBMISSION_ID_DATE:
// TODO - parse
- dateChunk = (ByteChunk)chunk;
+ submissionChunk = (MessageSubmissionChunk)chunk;
break;
case CONVERSATION_TOPIC:
conversationTopic = (StringChunk)chunk;
--- /dev/null
+/* ====================================================================
+ Licensed to the Apache Software Foundation (ASF) under one or more
+ contributor license agreements. See the NOTICE file distributed with
+ this work for additional information regarding copyright ownership.
+ The ASF licenses this file to You under the Apache License, Version 2.0
+ (the "License"); you may not use this file except in compliance with
+ the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+==================================================================== */
+package org.apache.poi.hsmf.datatypes;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.io.UnsupportedEncodingException;
+import java.util.Calendar;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+import org.apache.poi.util.IOUtils;
+
+/**
+ * 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 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 entryName) {
+ super(entryName);
+ }
+
+ /**
+ * Create a Byte Chunk, with the specified
+ * type.
+ */
+ public MessageSubmissionChunk(int chunkId, int type) {
+ super(chunkId, type);
+ }
+
+ public void readValue(InputStream value) throws IOException {
+ // Stored in the file as us-ascii
+ try {
+ byte[] data = IOUtils.toByteArray(value);
+ rawId = new String(data, "ASCII");
+ } catch(UnsupportedEncodingException e) {
+ throw new RuntimeException("Core encoding not found, JVM broken?", e);
+ }
+
+ // 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>
+ if(part.indexOf('-') != -1 &&
+ part.indexOf('-') != part.lastIndexOf('-')) {
+ String dateS = part.substring(part.indexOf('-')+1, part.lastIndexOf('-'));
+
+ // Should be yymmddhhmmssZ
+ Matcher m = datePatern.matcher(dateS);
+ if(m.matches()) {
+ date = Calendar.getInstance();
+ date.set(Calendar.YEAR, Integer.parseInt(m.group(1)) + 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.set(Calendar.MILLISECOND, 0);
+ } else {
+ System.err.println("Warning - unable to make sense of date " + dateS);
+ }
+ }
+ }
+ }
+ }
+
+ public void writeValue(OutputStream out) throws IOException {
+ try {
+ byte[] data = rawId.getBytes("ASCII");
+ out.write(data);
+ } catch(UnsupportedEncodingException e) {
+ throw new RuntimeException("Core encoding not found, JVM broken?", e);
+ }
+ }
+
+ /**
+ * Returns the date that the server accepted the
+ * message, as found from the message ID it generated.
+ * @return
+ */
+ public Calendar getAcceptedAtTime() {
+ return date;
+ }
+
+ /**
+ * Returns the full ID that the server generated when
+ * it accepted the message.
+ */
+ public String getSubmissionId() {
+ return rawId;
+ }
+}
import org.apache.poi.hsmf.datatypes.Chunk;
import org.apache.poi.hsmf.datatypes.ChunkGroup;
import org.apache.poi.hsmf.datatypes.Chunks;
+import org.apache.poi.hsmf.datatypes.MessageSubmissionChunk;
import org.apache.poi.hsmf.datatypes.NameIdChunks;
import org.apache.poi.hsmf.datatypes.RecipientChunks;
import org.apache.poi.hsmf.datatypes.StringChunk;
}
// See if we can get a type for it
- String ending = entry.getName().substring(entry.getName().length()-4);
+ String idType = entry.getName().substring(entry.getName().length()-8);
+ String idS = idType.substring(0, 4);
+ String typeS = idType.substring(4);
try {
- int type = Integer.parseInt(ending, 16);
+ int id = Integer.parseInt(idS, 16);
+ int type = Integer.parseInt(typeS, 16);
Chunk chunk = null;
- switch(type) {
- case Types.BINARY:
- chunk = new ByteChunk(entry.getName());
- break;
- case Types.ASCII_STRING:
- case Types.UNICODE_STRING:
- chunk = new StringChunk(entry.getName());
+ // Special cases based on the ID
+ switch(id) {
+ case Chunks.SUBMISSION_ID_DATE:
+ chunk = new MessageSubmissionChunk(entry.getName());
break;
+ default:
+ // Nothing special about this ID
+ // So, do the usual thing which is by type
+ switch(type) {
+ case Types.BINARY:
+ chunk = new ByteChunk(entry.getName());
+ break;
+ case Types.ASCII_STRING:
+ case Types.UNICODE_STRING:
+ chunk = new StringChunk(entry.getName());
+ break;
+ }
}
if(chunk != null) {
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
+import java.text.SimpleDateFormat;
+import java.util.Calendar;
import org.apache.poi.hsmf.MAPIMessage;
import org.apache.poi.hsmf.datatypes.AttachmentChunks;
} catch(ChunkNotFoundException e) {
fail();
}
+
+ // Check date too
+ try {
+ SimpleDateFormat f = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
+
+ Calendar c = msg.getMessageDate();
+ assertEquals( "2007-06-14 09:42:55", f.format(c.getTime()) );
+ } catch(ChunkNotFoundException e) {
+ fail();
+ }
}
public void testFindsRecips() throws IOException {