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]