Author: rdonkin
Date: Tue Feb 12 13:11:35 2008
New Revision: 627112

URL: http://svn.apache.org/viewvc?rev=627112&view=rev
Log:
Added new no recurse mode alongside recurse and raw. No recurse prevents the 
parser recursing into embedded RFC822 objects and instead treats them as 
unstructured.

Added:
    
james/mime4j/trunk/src/test/java/org/apache/james/mime4j/MimeTokenNoRecurseTest.java
Modified:
    
james/mime4j/trunk/src/main/java/org/apache/james/mime4j/MimeStreamParser.java
    
james/mime4j/trunk/src/main/java/org/apache/james/mime4j/MimeTokenStream.java

Modified: 
james/mime4j/trunk/src/main/java/org/apache/james/mime4j/MimeStreamParser.java
URL: 
http://svn.apache.org/viewvc/james/mime4j/trunk/src/main/java/org/apache/james/mime4j/MimeStreamParser.java?rev=627112&r1=627111&r2=627112&view=diff
==============================================================================
--- 
james/mime4j/trunk/src/main/java/org/apache/james/mime4j/MimeStreamParser.java 
(original)
+++ 
james/mime4j/trunk/src/main/java/org/apache/james/mime4j/MimeStreamParser.java 
Tue Feb 12 13:11:35 2008
@@ -128,7 +128,7 @@
      *        disables it.
      */
     public void setRaw(boolean raw) {
-        mimeTokenStream.setRaw(raw);
+        mimeTokenStream.setRecursionMode(MimeTokenStream.M_RAW);
     }
     
     /**

Modified: 
james/mime4j/trunk/src/main/java/org/apache/james/mime4j/MimeTokenStream.java
URL: 
http://svn.apache.org/viewvc/james/mime4j/trunk/src/main/java/org/apache/james/mime4j/MimeTokenStream.java?rev=627112&r1=627111&r2=627112&view=diff
==============================================================================
--- 
james/mime4j/trunk/src/main/java/org/apache/james/mime4j/MimeTokenStream.java 
(original)
+++ 
james/mime4j/trunk/src/main/java/org/apache/james/mime4j/MimeTokenStream.java 
Tue Feb 12 13:11:35 2008
@@ -168,6 +168,85 @@
             fieldChars.set(i);
         }
     }
+
+    /** 
+     * Recursively parse every <code>message/rfc822</code> part 
+     * @see #getRecursionMode() 
+     */
+    public static final int M_RECURSE = 0;
+    /**
+     * Do not recurse <code>message/rfc822</code> parts 
+     * @see #getRecursionMode()
+     */
+    public static final int M_NO_RECURSE = 1;
+    /** 
+     * Parse into raw entities
+     * @see #getRecursionMode() 
+     */
+    public static final int M_RAW = 2;
+    
+    /**
+     * Renders a state as a string suitable for logging.
+     * @param state 
+     * @return rendered as string, not null
+     */
+    public static final String stateToString(int state) {
+        final String result;
+        switch (state) {
+            case T_END_OF_STREAM:
+                result = "End of stream";
+                break;
+            case T_START_MESSAGE:
+                result = "Start message";
+                break;
+            case T_END_MESSAGE:
+                result = "End message";
+                break;
+            case T_RAW_ENTITY:
+                result = "Raw entity";
+                break;
+            case T_START_HEADER:
+                result = "Start header";
+                break;
+            case T_FIELD:
+                result = "Field";
+                break;
+            case T_END_HEADER:
+                result = "End header";
+                break;
+            case T_START_MULTIPART:
+                result = "Start multipart";
+                break;
+            case T_END_MULTIPART:
+                result = "End multipart";
+                break;
+            case T_PREAMBLE:
+                result = "Premable";
+                break;
+            case T_EPILOGUE:
+                result = "Epilogue";
+                break;
+            case T_START_BODYPART:
+                result = "Start bodypart";
+                break;
+            case T_END_BODYPART:
+                result = "End bodypart";
+                break;
+            case T_BODY:
+                result = "Body";
+                break;
+            case T_IN_BODYPART:
+                result = "Bodypart";
+                break;
+            case T_IN_MESSAGE:
+                result = "In message";
+                break;
+            default:
+                result = "Unknown";
+                break;
+        }
+        return result;
+    }
     
     /**
      * Creates a stream that strictly validates the input.
@@ -299,7 +378,7 @@
                     final String mimeType = body.getMimeType();
                     if (MimeUtil.isMultipart(mimeType)) {
                         state = T_START_MULTIPART;
-                    } else if (MimeUtil.isMessage(mimeType)) {
+                    } else if (recursionMode != M_NO_RECURSE && 
MimeUtil.isMessage(mimeType)) {
                         Cursor nextCursor = cursor;
                         final String transferEncoding = 
body.getTransferEncoding();
                         if (MimeUtil.isBase64Encoding(transferEncoding)) {
@@ -435,7 +514,8 @@
     private Cursor cursor;
     private StateMachine currentStateMachine;
     private final List entities = new ArrayList();
-    private boolean raw;
+    
+    private int recursionMode = M_RECURSE;
     
     /**
      * Constructs a standard (lax) stream.
@@ -462,10 +542,15 @@
     }
 
     private int parseMessage(Cursor cursor, BodyDescriptor parent) {
-        if (isRaw()) {
-            currentStateMachine = new RawEntity(cursor.getStream());
-        } else {
-            currentStateMachine = new Message(cursor, parent);
+        switch (recursionMode) {
+            case M_RAW:
+                currentStateMachine = new RawEntity(cursor.getStream());
+                break;
+            case M_NO_RECURSE:
+                // expected to be called only at start of paring
+            case M_RECURSE:
+                currentStateMachine = new Message(cursor, parent);
+                break;
         }
         entities.add(currentStateMachine);
         return currentStateMachine.state;
@@ -479,7 +564,7 @@
      * @see #setRaw(boolean)
      */
     public boolean isRaw() {
-        return raw;
+        return recursionMode == M_RAW;
     }
     
     /**
@@ -491,9 +576,40 @@
      * 
      * @param raw <code>true</code> enables raw mode, <code>false</code>
      *        disables it.
+     * @deprecated pass [EMAIL PROTECTED] #M_RAW} to [EMAIL PROTECTED] 
#setRecursionMode(int)} 
      */
     public void setRaw(boolean raw) {
-        this.raw = raw;
+        if (raw) {
+            recursionMode = M_RAW;
+        } else {
+            recursionMode = M_RECURSE;
+        }
+    }
+    
+    /**
+     * Gets the current recursion mode.
+     * The recursion mode specifies the approach taken to parsing parts.
+     * [EMAIL PROTECTED] #M_RAW}  mode does not parse the part at all.
+     * [EMAIL PROTECTED] #M_RECURSE} mode recursively parses each mail
+     * when an <code>message/rfc822</code> part is encounted;
+     * [EMAIL PROTECTED] #M_NO_RECURSE} does not.
+     * @return [EMAIL PROTECTED] #M_RECURSE}, [EMAIL PROTECTED] #M_RAW} or 
[EMAIL PROTECTED] #M_NO_RECURSE}
+     */
+    public int getRecursionMode() {
+        return recursionMode;
+    }
+    
+    /**
+     * Sets the current recursion.
+     * The recursion mode specifies the approach taken to parsing parts.
+     * [EMAIL PROTECTED] #M_RAW}  mode does not parse the part at all.
+     * [EMAIL PROTECTED] #M_RECURSE} mode recursively parses each mail
+     * when an <code>message/rfc822</code> part is encounted;
+     * [EMAIL PROTECTED] #M_NO_RECURSE} does not.
+     * @param mode [EMAIL PROTECTED] #M_RECURSE}, [EMAIL PROTECTED] #M_RAW} or 
[EMAIL PROTECTED] #M_NO_RECURSE}
+     */
+    public void setRecursionMode(int mode) {
+        this.recursionMode = mode;
     }
 
     /**

Added: 
james/mime4j/trunk/src/test/java/org/apache/james/mime4j/MimeTokenNoRecurseTest.java
URL: 
http://svn.apache.org/viewvc/james/mime4j/trunk/src/test/java/org/apache/james/mime4j/MimeTokenNoRecurseTest.java?rev=627112&view=auto
==============================================================================
--- 
james/mime4j/trunk/src/test/java/org/apache/james/mime4j/MimeTokenNoRecurseTest.java
 (added)
+++ 
james/mime4j/trunk/src/test/java/org/apache/james/mime4j/MimeTokenNoRecurseTest.java
 Tue Feb 12 13:11:35 2008
@@ -0,0 +1,137 @@
+package org.apache.james.mime4j;
+
+import java.io.ByteArrayInputStream;
+import java.io.InputStream;
+import java.nio.charset.Charset;
+
+import junit.framework.TestCase;
+
+public class MimeTokenNoRecurseTest extends TestCase {
+
+    private static final String INNER_MAIL = "From: Timothy Tayler <[EMAIL 
PROTECTED]>\r\n" +
+                "To: Joshua Tetley <[EMAIL PROTECTED]>\r\n" +
+                "Date: Tue, 12 Feb 2008 17:34:09 +0000 (GMT)\r\n" +
+                "Subject: Multipart Without RFC822 Part\r\n" +
+                "Content-Type: multipart/mixed;boundary=42\r\n\r\n" +
+                "--42\r\n" +
+                "Content-Type:text/plain; charset=US-ASCII\r\n\r\n" +
+                "First part of this mail\r\n" +
+                "--42\r\n" +
+                "Content-Type:text/plain; charset=US-ASCII\r\n\r\n" +
+                "Second part of this mail\r\n" +
+                "--42\r\n";
+
+    private static final String MAIL_WITH_RFC822_PART = "MIME-Version: 
1.0\r\n" +
+            "From: Timothy Tayler <[EMAIL PROTECTED]>\r\n" +
+            "To: Joshua Tetley <[EMAIL PROTECTED]>\r\n" +
+            "Date: Tue, 12 Feb 2008 17:34:09 +0000 (GMT)\r\n" +
+            "Subject: Multipart With RFC822 Part\r\n" +
+            "Content-Type: multipart/mixed;boundary=1729\r\n\r\n" +
+            "A short premable\r\n" +
+            "--1729\r\n\r\n" +
+            "First part has no headers\r\n" +
+            "--1729\r\n" +
+            "Content-Type: text/plain; charset=US-ASCII\r\n\r\n" +
+            "Second part is plain text\r\n" +
+            "--1729\r\n" +
+            "Content-Type: message/rfc822\r\n\r\n" +
+            INNER_MAIL +
+            "--1729\r\n" +
+            "Content-Type: text/plain; charset=US-ASCII\r\n\r\n" +
+            "Last part is plain text\r\n" +
+            "--1729--\r\n" +
+            "The End";
+    
+    MimeTokenStream stream;
+    
+    protected void setUp() throws Exception {
+        super.setUp();
+        stream = new MimeTokenStream();
+        byte[] bytes = 
Charset.forName("us-ascii").encode(MAIL_WITH_RFC822_PART).array();
+        InputStream in = new ByteArrayInputStream(bytes);
+        stream.parse(in);
+    }
+
+    protected void tearDown() throws Exception {
+        super.tearDown();
+    }
+
+    public void testWhenRecurseShouldRecurseInnerMail() throws Exception {
+        stream.setRecursionMode(MimeTokenStream.M_RECURSE);
+        nextIs(MimeTokenStream.T_START_HEADER);
+        nextIs(MimeTokenStream.T_FIELD);
+        nextIs(MimeTokenStream.T_FIELD);
+        nextIs(MimeTokenStream.T_FIELD);
+        nextIs(MimeTokenStream.T_FIELD);
+        nextIs(MimeTokenStream.T_FIELD);
+        nextIs(MimeTokenStream.T_FIELD);
+        nextIs(MimeTokenStream.T_END_HEADER);
+        
+        nextIs(MimeTokenStream.T_START_MULTIPART);
+        nextIs(MimeTokenStream.T_PREAMBLE);
+        nextShouldBeStandardPart(false);
+        
+        nextShouldBeStandardPart(true);
+        
+        nextIs(MimeTokenStream.T_START_BODYPART);
+        nextIs(MimeTokenStream.T_START_HEADER);
+        nextIs(MimeTokenStream.T_FIELD);
+        nextIs(MimeTokenStream.T_END_HEADER);
+        nextIs(MimeTokenStream.T_START_MESSAGE);
+        nextIs(MimeTokenStream.T_START_HEADER);
+        nextIs(MimeTokenStream.T_FIELD);
+        nextIs(MimeTokenStream.T_FIELD);
+        nextIs(MimeTokenStream.T_FIELD);
+        nextIs(MimeTokenStream.T_FIELD);
+        nextIs(MimeTokenStream.T_FIELD);
+        nextIs(MimeTokenStream.T_END_HEADER);
+        nextIs(MimeTokenStream.T_START_MULTIPART);
+        nextIs(MimeTokenStream.T_PREAMBLE);
+        nextShouldBeStandardPart(true);
+        nextShouldBeStandardPart(true);
+        nextIs(MimeTokenStream.T_EPILOGUE);
+        nextIs(MimeTokenStream.T_END_MULTIPART);
+        nextIs(MimeTokenStream.T_END_MESSAGE);
+        nextShouldBeStandardPart(true);
+        nextIs(MimeTokenStream.T_EPILOGUE);
+        nextIs(MimeTokenStream.T_END_MULTIPART);
+    }
+    
+
+    public void testWhenRecurseShouldTreatInnerMailAsAnyOtherPart() throws 
Exception {
+        stream.setRecursionMode(MimeTokenStream.M_NO_RECURSE);
+        nextIs(MimeTokenStream.T_START_HEADER);
+        nextIs(MimeTokenStream.T_FIELD);
+        nextIs(MimeTokenStream.T_FIELD);
+        nextIs(MimeTokenStream.T_FIELD);
+        nextIs(MimeTokenStream.T_FIELD);
+        nextIs(MimeTokenStream.T_FIELD);
+        nextIs(MimeTokenStream.T_FIELD);
+        nextIs(MimeTokenStream.T_END_HEADER);
+        
+        nextIs(MimeTokenStream.T_START_MULTIPART);
+        nextIs(MimeTokenStream.T_PREAMBLE);
+        nextShouldBeStandardPart(false);
+        
+        nextShouldBeStandardPart(true);
+        nextShouldBeStandardPart(true);
+        nextShouldBeStandardPart(true);
+        nextIs(MimeTokenStream.T_EPILOGUE);
+        nextIs(MimeTokenStream.T_END_MULTIPART);
+    }
+
+    private void nextShouldBeStandardPart(boolean withHeader) throws Exception 
{
+        nextIs(MimeTokenStream.T_START_BODYPART);
+        nextIs(MimeTokenStream.T_START_HEADER);
+        if (withHeader) {
+            nextIs(MimeTokenStream.T_FIELD);
+        }
+        nextIs(MimeTokenStream.T_END_HEADER);
+        nextIs(MimeTokenStream.T_BODY);
+        nextIs(MimeTokenStream.T_END_BODYPART);
+    }
+    
+    private void nextIs(int state) throws Exception {
+        assertEquals(MimeTokenStream.stateToString(state), 
MimeTokenStream.stateToString(stream.next()));
+    }
+}



---------------------------------------------------------------------
To unsubscribe, e-mail: [EMAIL PROTECTED]
For additional commands, e-mail: [EMAIL PROTECTED]

Reply via email to