You are viewing a plain text version of this content. The canonical link for it is here.
Posted to server-dev@james.apache.org by rd...@apache.org on 2007/07/24 23:01:36 UTC
svn commit: r559208 - in
/james/mime4j/trunk/src/main/java/org/apache/james/mime4j: Cursor.java
MimeTokenStream.java StreamCursor.java
Author: rdonkin
Date: Tue Jul 24 14:01:35 2007
New Revision: 559208
URL: http://svn.apache.org/viewvc?view=rev&rev=559208
Log:
Factored out cursor. This will allow an easy byte buffer implementation.
Added:
james/mime4j/trunk/src/main/java/org/apache/james/mime4j/Cursor.java
james/mime4j/trunk/src/main/java/org/apache/james/mime4j/StreamCursor.java
Modified:
james/mime4j/trunk/src/main/java/org/apache/james/mime4j/MimeTokenStream.java
Added: james/mime4j/trunk/src/main/java/org/apache/james/mime4j/Cursor.java
URL: http://svn.apache.org/viewvc/james/mime4j/trunk/src/main/java/org/apache/james/mime4j/Cursor.java?view=auto&rev=559208
==============================================================================
--- james/mime4j/trunk/src/main/java/org/apache/james/mime4j/Cursor.java (added)
+++ james/mime4j/trunk/src/main/java/org/apache/james/mime4j/Cursor.java Tue Jul 24 14:01:35 2007
@@ -0,0 +1,110 @@
+/****************************************************************
+ * 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.james.mime4j;
+
+import java.io.IOException;
+import java.io.InputStream;
+
+/**
+ * Manages parser input.
+ */
+public interface Cursor {
+
+ /**
+ * Stops processing.
+ */
+ public void stop();
+
+ /**
+ * Gets the number of CR LF sequences passed.
+ * @return number of lines passed
+ * @throws IOException
+ */
+ public int getLineNumber() throws IOException;
+
+ /**
+ * Decodes the next MIME part as BASE 64
+ * and returns a cursor for the contents.
+ * @return <code>Cursor</code>, not null
+ * @throws IOException TODO: subclass IOException with state exception
+ */
+ public Cursor decodeBase64() throws IOException;
+
+ /**
+ * Decodes the next MIME part as Quoted Printable
+ * and returns a cursor for the contents.
+ * @return <code>Cursor</code>, not null
+ * @throws IOException TODO: subclass IOException with state exception
+ */
+ public Cursor decodeQuotedPrintable() throws IOException;
+
+ /**
+ * Advances the cursor to the next boundary.
+ * @throws IOException TODO: subclass IOException with state exception
+ */
+ public void advanceToBoundary() throws IOException;
+
+ /**
+ * Is this cursor at the end of the input?
+ * @return true if the input is at the end,
+ * false otherwise
+ * @throws IOException
+ */
+ public boolean isEnded() throws IOException;
+
+ /**
+ * Are more parts available?
+ * @return true if this cursor is reading a MIME message and
+ * there are more parts available,
+ * false otherwise
+ * @throws IOException TODO: subclass IOException with state exception
+ */
+ public boolean moreMimeParts() throws IOException;
+
+ /**
+ * Sets the MIME boundary.
+ *
+ * @param boundary TODO: should this be a byte sequence?
+ * @throws IOException
+ */
+ public void boundary(String boundary) throws IOException;
+
+ /**
+ * Gets a cursor for the next mime part.
+ * @return <code>Cursor</code>, not null
+ * @throws IOException TODO: subclass IOException with state exception
+ */
+ public Cursor nextMimePartCursor() throws IOException;
+
+ /**
+ * Advances the cursor.
+ * @return the next byte
+ * @throws IOException
+ * @see #getStream()
+ */
+ public byte advance() throws IOException;
+
+ /**
+ * Gets a stream to read the contents of the next section of the message.
+ * @return <code>InputStream</code>, not null
+ */
+ public InputStream getStream();
+
+}
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?view=diff&rev=559208&r1=559207&r2=559208
==============================================================================
--- 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 Jul 24 14:01:35 2007
@@ -27,8 +27,6 @@
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
-import org.apache.james.mime4j.decoder.Base64InputStream;
-import org.apache.james.mime4j.decoder.QuotedPrintableInputStream;
/**
@@ -189,19 +187,18 @@
private abstract class Entity extends StateMachine {
private final BodyDescriptor parent;
- private final InputStream contents;
+ private final Cursor cursor;
private final StringBuffer sb = new StringBuffer();
private BodyDescriptor body;
private int pos, start;
private int lineNumber, startLineNumber;
private final int endState;
- private MimeBoundaryInputStream mbis;
- InputStream stream;
+
String field;
- Entity(InputStream contents, BodyDescriptor parent, int startState, int endState) {
+ Entity(Cursor cursor, BodyDescriptor parent, int startState, int endState) {
this.parent = parent;
- this.contents = contents;
+ this.cursor = cursor;
state = startState;
this.endState = endState;
}
@@ -211,21 +208,23 @@
}
private int setParseBodyPartState() throws IOException {
- mbis.consume();
- if (mbis.parentEOF()) {
+ cursor.advanceToBoundary();
+ if (cursor.isEnded()) {
if (log.isWarnEnabled()) {
- log.warn("Line " + rootInputStream.getLineNumber()
+ log.warn("Line " + cursor.getLineNumber()
+ ": Body part ended prematurely. "
+ "Higher level boundary detected or "
+ "EOF reached.");
}
} else {
- if (mbis.hasMoreParts()) {
- mbis = new MimeBoundaryInputStream(contents, body.getBoundary());
+ if (cursor.moreMimeParts()) {
+ final String boundary = body.getBoundary();
+ cursor.boundary(boundary);
+
if (isRaw()) {
- currentStateMachine = new RawEntity(mbis);
+ currentStateMachine = new RawEntity(cursor.getStream());
} else {
- currentStateMachine = new BodyPart(mbis, body);
+ currentStateMachine = new BodyPart(cursor.nextMimePartCursor(), body);
}
entities.add(currentStateMachine);
state = T_IN_BODYPART;
@@ -233,7 +232,6 @@
}
}
state = T_EPILOGUE;
- stream = new CloseShieldInputStream(contents);
return T_EPILOGUE;
}
@@ -254,25 +252,23 @@
if (body.isMultipart()) {
state = T_START_MULTIPART;
} else if (body.isMessage()) {
- InputStream is = contents;
+ Cursor nextCursor = cursor;
if (body.isBase64Encoded()) {
log.warn("base64 encoded message/rfc822 detected");
- is = new EOLConvertingInputStream(new Base64InputStream(contents));
+ nextCursor = cursor.decodeBase64();
} else if (body.isQuotedPrintableEncoded()) {
log.warn("quoted-printable encoded message/rfc822 detected");
- is = new EOLConvertingInputStream(new QuotedPrintableInputStream(contents));
+ nextCursor = cursor.decodeQuotedPrintable();
}
state = endState;
- return parseMessage(is, body);
+ return parseMessage(nextCursor, body);
} else {
- stream = new CloseShieldInputStream(contents);
state = T_BODY;
break;
}
break;
case T_START_MULTIPART:
- mbis = new MimeBoundaryInputStream(contents, body.getBoundary());
- stream = new CloseShieldInputStream(mbis);
+ cursor.boundary(body.getBoundary());
state = T_PREAMBLE;
break;
case T_PREAMBLE:
@@ -299,11 +295,11 @@
private void initHeaderParsing() throws IOException {
body = new BodyDescriptor(parent);
- startLineNumber = lineNumber = rootInputStream.getLineNumber();
+ startLineNumber = lineNumber = cursor.getLineNumber();
int curr = 0;
int prev = 0;
- while ((curr = contents.read()) != -1) {
+ while ((curr = cursor.advance()) != -1) {
if (curr == '\n' && (prev == '\n' || prev == 0)) {
/*
* [\r]\n[\r]\n or an immediate \r\n have been seen.
@@ -316,7 +312,7 @@
}
if (curr == -1 && log.isWarnEnabled()) {
- log.warn("Line " + rootInputStream.getLineNumber()
+ log.warn("Line " + cursor.getLineNumber()
+ ": Unexpected end of headers detected. "
+ "Boundary detected in header or EOF reached.");
}
@@ -375,19 +371,19 @@
}
private class Message extends Entity {
- Message(InputStream contents, BodyDescriptor parent) {
- super(contents, parent, T_START_MESSAGE, T_END_MESSAGE);
+ Message(Cursor cursor, BodyDescriptor parent) {
+ super(cursor, parent, T_START_MESSAGE, T_END_MESSAGE);
}
}
private class BodyPart extends Entity {
- BodyPart(InputStream contents, BodyDescriptor parent) {
- super(contents, parent, T_START_BODYPART, T_END_BODYPART);
+ BodyPart(Cursor cursor, BodyDescriptor parent) {
+ super(cursor, parent, T_START_BODYPART, T_END_BODYPART);
}
}
private int state = T_END_OF_STREAM;
- private RootInputStream rootInputStream;
+ private Cursor cursor;
private StateMachine currentStateMachine;
private final List entities = new ArrayList();
private boolean raw;
@@ -398,15 +394,15 @@
*/
public void parse(InputStream stream) {
entities.clear();
- rootInputStream = new RootInputStream(stream);
- state = parseMessage(rootInputStream, null);
+ cursor = new StreamCursor(stream);
+ state = parseMessage(cursor, null);
}
- private int parseMessage(InputStream pStream, BodyDescriptor parent) {
+ private int parseMessage(Cursor cursor, BodyDescriptor parent) {
if (isRaw()) {
- currentStateMachine = new RawEntity(pStream);
+ currentStateMachine = new RawEntity(cursor.getStream());
} else {
- currentStateMachine = new Message(pStream, parent);
+ currentStateMachine = new Message(cursor, parent);
}
entities.add(currentStateMachine);
return currentStateMachine.state;
@@ -450,7 +446,7 @@
* {@link ContentHandler#startMessage()}, etc.
*/
public void stop() {
- rootInputStream.truncate();
+ cursor.stop();
}
/**
@@ -490,7 +486,7 @@
case T_PREAMBLE:
case T_EPILOGUE:
case T_BODY:
- return ((Entity) currentStateMachine).stream;
+ return ((Entity) currentStateMachine).cursor.getStream();
default:
throw new IllegalStateException("Expected state to be either of T_RAW_ENTITY, T_PREAMBLE, or T_EPILOGUE.");
}
Added: james/mime4j/trunk/src/main/java/org/apache/james/mime4j/StreamCursor.java
URL: http://svn.apache.org/viewvc/james/mime4j/trunk/src/main/java/org/apache/james/mime4j/StreamCursor.java?view=auto&rev=559208
==============================================================================
--- james/mime4j/trunk/src/main/java/org/apache/james/mime4j/StreamCursor.java (added)
+++ james/mime4j/trunk/src/main/java/org/apache/james/mime4j/StreamCursor.java Tue Jul 24 14:01:35 2007
@@ -0,0 +1,152 @@
+/****************************************************************
+ * 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.james.mime4j;
+
+import java.io.IOException;
+import java.io.InputStream;
+
+import org.apache.james.mime4j.decoder.Base64InputStream;
+import org.apache.james.mime4j.decoder.QuotedPrintableInputStream;
+
+/**
+ * Stream based cursor.
+ */
+public class StreamCursor implements Cursor {
+
+ final RootInputStream rootInputStream;
+ final InputStream contents;
+ MimeBoundaryInputStream mbis;
+ InputStream stream;
+
+ /**
+ * Constructs a child cursor.
+ * @param parent <code>Cursor</code>, not null
+ * @param contents <code>InputStream</code> contents, not null
+ */
+ StreamCursor(StreamCursor parent, InputStream contents) {
+ this.rootInputStream = parent.rootInputStream;
+ this.contents = contents;
+ }
+
+ /**
+ * Constructs a new cursor from the given root contents.
+ * @param stream <code>InputStream</code>, not null
+ */
+ StreamCursor(InputStream stream) {
+ rootInputStream = new RootInputStream(stream);
+ contents = rootInputStream;
+ }
+
+ /**
+ * Constructs a cursor with the given root and contents.
+ * @param rootInputStream <code>RootInputStream</code>, not null
+ * @param contents <code>InputStream</code>, not null
+ */
+ StreamCursor(RootInputStream rootInputStream, InputStream contents) {
+ this.rootInputStream = rootInputStream;
+ this.contents = contents;
+ }
+
+ /**
+ * @see Cursor#stop()
+ */
+ public void stop() {
+ rootInputStream.truncate();
+ }
+
+ /**
+ * @see Cursor#getLineNumber()
+ */
+ public int getLineNumber() {
+ return rootInputStream.getLineNumber();
+ }
+
+ /**
+ * @see Cursor#decodeBase64()
+ */
+ public Cursor decodeBase64() throws IOException {
+ InputStream is = new EOLConvertingInputStream(new Base64InputStream(contents));
+ return new StreamCursor(rootInputStream, is);
+ }
+
+ /**
+ * @see Cursor#decodeQuotedPrintable()
+ */
+ public Cursor decodeQuotedPrintable() throws IOException {
+ InputStream is = new EOLConvertingInputStream(new QuotedPrintableInputStream(contents));
+ return new StreamCursor(rootInputStream, is);
+ }
+
+ /**
+ * @see Cursor#advanceToBoundary()
+ */
+ public void advanceToBoundary() throws IOException {
+ mbis.consume();
+ stream = null;
+ }
+
+ /**
+ * @see Cursor#isEnded()
+ */
+ public boolean isEnded() throws IOException {
+ return mbis.parentEOF();
+ }
+
+ /**
+ * @see Cursor#moreMimeParts()
+ */
+ public boolean moreMimeParts() throws IOException {
+ return mbis.hasMoreParts();
+ }
+
+ /**
+ * @see Cursor#boundary(String)
+ */
+ public void boundary(String boundary) throws IOException {
+ mbis = new MimeBoundaryInputStream(contents, boundary);
+ stream = new CloseShieldInputStream(mbis);
+ }
+
+ /**
+ * @see Cursor#nextMimePartCursor()
+ */
+ public Cursor nextMimePartCursor() {
+ return new StreamCursor(rootInputStream, mbis);
+ }
+
+ /**
+ * @see Cursor#getStream()
+ */
+ public InputStream getStream() {
+ InputStream result = stream;
+ if (result == null)
+ {
+ result = contents;
+ }
+ return result;
+ }
+
+ /**
+ * @see Cursor#advance()
+ */
+ public byte advance() throws IOException {
+ return (byte) getStream().read();
+ }
+}
---------------------------------------------------------------------
To unsubscribe, e-mail: server-dev-unsubscribe@james.apache.org
For additional commands, e-mail: server-dev-help@james.apache.org