]> source.dussan.org Git - poi.git/commitdiff
Support fetching the message date from the submission id
authorNick Burch <nick@apache.org>
Fri, 8 Jan 2010 13:42:53 +0000 (13:42 +0000)
committerNick Burch <nick@apache.org>
Fri, 8 Jan 2010 13:42:53 +0000 (13:42 +0000)
git-svn-id: https://svn.apache.org/repos/asf/poi/trunk@897201 13f79535-47bb-0310-9956-ffa450edef68

src/scratchpad/src/org/apache/poi/hsmf/MAPIMessage.java
src/scratchpad/src/org/apache/poi/hsmf/datatypes/ByteChunk.java
src/scratchpad/src/org/apache/poi/hsmf/datatypes/Chunks.java
src/scratchpad/src/org/apache/poi/hsmf/datatypes/MessageSubmissionChunk.java [new file with mode: 0644]
src/scratchpad/src/org/apache/poi/hsmf/parsers/POIFSChunkParser.java
src/scratchpad/testcases/org/apache/poi/hsmf/parsers/TestPOIFSChunkParser.java

index 936715f2b5ccb464501c5ee3af98033b780cfe25..d69ce00269bc023448c161a36e1555cedcf6cac0 100644 (file)
@@ -22,6 +22,7 @@ import java.io.FileInputStream;
 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;
@@ -192,6 +193,17 @@ public class MAPIMessage {
        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();
+       }
 
        
        /**
index e3c4bd08bdd1567127020be9cc6762ee0731017a..166a5feef40e4b22a1a795def3cb3e6ef74f9d9c 100644 (file)
@@ -23,7 +23,11 @@ import java.io.OutputStream;
 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 {
index d6b433c157c9769140ee11208483e134c21d26a1..36b52020991702d62770cd3c73a6a71e6e9af800 100644 (file)
@@ -30,7 +30,9 @@ public final class Chunks implements ChunkGroup {
    /* 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
@@ -39,7 +41,9 @@ public final class Chunks implements ChunkGroup {
    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>();
@@ -63,9 +67,11 @@ public final class Chunks implements ChunkGroup {
    /** 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()]);
@@ -82,12 +88,15 @@ public final class Chunks implements ChunkGroup {
       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;
diff --git a/src/scratchpad/src/org/apache/poi/hsmf/datatypes/MessageSubmissionChunk.java b/src/scratchpad/src/org/apache/poi/hsmf/datatypes/MessageSubmissionChunk.java
new file mode 100644 (file)
index 0000000..01793ab
--- /dev/null
@@ -0,0 +1,121 @@
+/* ====================================================================
+   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;
+   }
+}
index eb79ae9ad7f3a1079a8db1015442757bcef2f6a2..34a4d27d7e89cc94bc244e16a4ec334bef2385a4 100644 (file)
@@ -25,6 +25,7 @@ import org.apache.poi.hsmf.datatypes.ByteChunk;
 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;
@@ -111,19 +112,31 @@ public final class POIFSChunkParser {
       }
       
       // 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) {
index 5c9e55f96bf99de9deba11b7f724786e8dc0fe40..c86b586bec0b2333ae4c49170e725df00f9a65d9 100644 (file)
@@ -20,6 +20,8 @@ package org.apache.poi.hsmf.parsers;
 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;
@@ -67,6 +69,16 @@ public final class TestPOIFSChunkParser extends TestCase {
       } 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 {