Return-Path: X-Original-To: apmail-james-mime4j-dev-archive@minotaur.apache.org Delivered-To: apmail-james-mime4j-dev-archive@minotaur.apache.org Received: from mail.apache.org (hermes.apache.org [140.211.11.3]) by minotaur.apache.org (Postfix) with SMTP id E589849A2 for ; Thu, 23 Jun 2011 15:01:26 +0000 (UTC) Received: (qmail 96799 invoked by uid 500); 23 Jun 2011 15:01:26 -0000 Delivered-To: apmail-james-mime4j-dev-archive@james.apache.org Received: (qmail 96765 invoked by uid 500); 23 Jun 2011 15:01:26 -0000 Mailing-List: contact mime4j-dev-help@james.apache.org; run by ezmlm Precedence: bulk List-Help: List-Unsubscribe: List-Post: List-Id: Reply-To: mime4j-dev@james.apache.org Delivered-To: mailing list mime4j-dev@james.apache.org Received: (qmail 96757 invoked by uid 99); 23 Jun 2011 15:01:26 -0000 Received: from athena.apache.org (HELO athena.apache.org) (140.211.11.136) by apache.org (qpsmtpd/0.29) with ESMTP; Thu, 23 Jun 2011 15:01:26 +0000 X-ASF-Spam-Status: No, hits=-2000.0 required=5.0 tests=ALL_TRUSTED X-Spam-Check-By: apache.org Received: from [140.211.11.4] (HELO eris.apache.org) (140.211.11.4) by apache.org (qpsmtpd/0.29) with ESMTP; Thu, 23 Jun 2011 15:01:23 +0000 Received: from eris.apache.org (localhost [127.0.0.1]) by eris.apache.org (Postfix) with ESMTP id 607472388906; Thu, 23 Jun 2011 15:01:03 +0000 (UTC) Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit Subject: svn commit: r1138912 - in /james/mime4j/trunk/core/src/main/java/org/apache/james/mime4j: parser/MimeStreamParser.java stream/AbstractEntity.java stream/MimeEntity.java stream/MimeTokenStream.java Date: Thu, 23 Jun 2011 15:01:03 -0000 To: mime4j-dev@james.apache.org From: olegk@apache.org X-Mailer: svnmailer-1.0.8 Message-Id: <20110623150103.607472388906@eris.apache.org> Author: olegk Date: Thu Jun 23 15:01:02 2011 New Revision: 1138912 URL: http://svn.apache.org/viewvc?rev=1138912&view=rev Log: Merged AbstractEntity code into MimeEntity (a prerequisite for MutableBodyDescriptor refactoring) Removed: james/mime4j/trunk/core/src/main/java/org/apache/james/mime4j/stream/AbstractEntity.java Modified: james/mime4j/trunk/core/src/main/java/org/apache/james/mime4j/parser/MimeStreamParser.java james/mime4j/trunk/core/src/main/java/org/apache/james/mime4j/stream/MimeEntity.java james/mime4j/trunk/core/src/main/java/org/apache/james/mime4j/stream/MimeTokenStream.java Modified: james/mime4j/trunk/core/src/main/java/org/apache/james/mime4j/parser/MimeStreamParser.java URL: http://svn.apache.org/viewvc/james/mime4j/trunk/core/src/main/java/org/apache/james/mime4j/parser/MimeStreamParser.java?rev=1138912&r1=1138911&r2=1138912&view=diff ============================================================================== --- james/mime4j/trunk/core/src/main/java/org/apache/james/mime4j/parser/MimeStreamParser.java (original) +++ james/mime4j/trunk/core/src/main/java/org/apache/james/mime4j/parser/MimeStreamParser.java Thu Jun 23 15:01:02 2011 @@ -30,7 +30,6 @@ import org.apache.james.mime4j.stream.Fi import org.apache.james.mime4j.stream.MimeEntityConfig; import org.apache.james.mime4j.stream.MimeTokenStream; import org.apache.james.mime4j.stream.MutableBodyDescriptorFactory; -import org.apache.james.mime4j.stream.RawField; import org.apache.james.mime4j.stream.RecursionMode; /** Modified: james/mime4j/trunk/core/src/main/java/org/apache/james/mime4j/stream/MimeEntity.java URL: http://svn.apache.org/viewvc/james/mime4j/trunk/core/src/main/java/org/apache/james/mime4j/stream/MimeEntity.java?rev=1138912&r1=1138911&r2=1138912&view=diff ============================================================================== --- james/mime4j/trunk/core/src/main/java/org/apache/james/mime4j/stream/MimeEntity.java (original) +++ james/mime4j/trunk/core/src/main/java/org/apache/james/mime4j/stream/MimeEntity.java Thu Jun 23 15:01:02 2011 @@ -31,14 +31,31 @@ import org.apache.james.mime4j.io.Limite import org.apache.james.mime4j.io.LineNumberSource; import org.apache.james.mime4j.io.LineReaderInputStream; import org.apache.james.mime4j.io.LineReaderInputStreamAdaptor; +import org.apache.james.mime4j.io.MaxHeaderLimitException; +import org.apache.james.mime4j.io.MaxLineLimitException; import org.apache.james.mime4j.io.MimeBoundaryInputStream; +import org.apache.james.mime4j.util.ByteArrayBuffer; +import org.apache.james.mime4j.util.CharsetUtil; import org.apache.james.mime4j.util.MimeUtil; -class MimeEntity extends AbstractEntity { +class MimeEntity implements EntityStateMachine { + private final EntityState endState; + private final MimeEntityConfig config; + private final DecodeMonitor monitor; + private final FieldBuilder fieldBuilder; + private final MutableBodyDescriptor body; + + private final ByteArrayBuffer linebuf; private final LineNumberSource lineSource; private final BufferedLineReaderInputStream inbuffer; + private EntityState state; + private int lineCount; + private boolean endOfHeader; + private int headerCount; + private Field field; + private RecursionMode recursionMode; private MimeBoundaryInputStream currentMimePartStream; private LineReaderInputStreamAdaptor dataStream; @@ -54,7 +71,17 @@ class MimeEntity extends AbstractEntity DecodeMonitor monitor, FieldBuilder fieldBuilder, MutableBodyDescriptor body) { - super(config, startState, endState, monitor, fieldBuilder, body); + super(); + this.config = config; + this.state = startState; + this.endState = endState; + this.monitor = monitor; + this.fieldBuilder = fieldBuilder; + this.body = body; + this.linebuf = new ByteArrayBuffer(64); + this.lineCount = 0; + this.endOfHeader = false; + this.headerCount = 0; this.lineSource = lineSource; this.inbuffer = new BufferedLineReaderInputStream( instream, @@ -109,6 +136,10 @@ class MimeEntity extends AbstractEntity new DefaultFieldBuilder(-1), body); } + public EntityState getState() { + return state; + } + public RecursionMode getRecursionMode() { return recursionMode; } @@ -121,19 +152,132 @@ class MimeEntity extends AbstractEntity this.inbuffer.truncate(); } - @Override - protected int getLineNumber() { + private int getLineNumber() { if (lineSource == null) return -1; else return lineSource.getLineNumber(); } - @Override - protected LineReaderInputStream getDataStream() { + private LineReaderInputStream getDataStream() { return dataStream; } + /** + * Creates an indicative message suitable for display + * based on the given event and the current state of the system. + * @param event Event, not null + * @return message suitable for use as a message in an exception + * or for logging + */ + protected String message(Event event) { + final String message; + if (event == null) { + message = "Event is unexpectedly null."; + } else { + message = event.toString(); + } + + int lineNumber = getLineNumber(); + if (lineNumber <= 0) + return message; + else + return "Line " + lineNumber + ": " + message; + } + + protected void monitor(Event event) throws MimeException, IOException { + if (monitor.isListening()) { + String message = message(event); + if (monitor.warn(message, "ignoring")) { + throw new MimeParseEventException(event); + } + } + } + + private void readRawField() throws IOException, MimeException { + if (endOfHeader) + throw new IllegalStateException(); + LineReaderInputStream instream = getDataStream(); + try { + for (;;) { + // If there's still data stuck in the line buffer + // copy it to the field buffer + int len = linebuf.length(); + if (len > 0) { + fieldBuilder.append(linebuf); + } + linebuf.clear(); + if (instream.readLine(linebuf) == -1) { + monitor(Event.HEADERS_PREMATURE_END); + endOfHeader = true; + break; + } + len = linebuf.length(); + if (len > 0 && linebuf.byteAt(len - 1) == '\n') { + len--; + } + if (len > 0 && linebuf.byteAt(len - 1) == '\r') { + len--; + } + if (len == 0) { + // empty line detected + endOfHeader = true; + break; + } + lineCount++; + if (lineCount > 1) { + int ch = linebuf.byteAt(0); + if (ch != CharsetUtil.SP && ch != CharsetUtil.HT) { + // new header detected + break; + } + } + } + } catch (MaxLineLimitException e) { + throw new MimeException(e); + } + } + + protected boolean nextField() throws MimeException, IOException { + int maxHeaderCount = config.getMaxHeaderCount(); + // the loop is here to transparently skip invalid headers + for (;;) { + if (endOfHeader) { + return false; + } + if (maxHeaderCount > 0 && headerCount >= maxHeaderCount) { + throw new MaxHeaderLimitException("Maximum header limit exceeded"); + } + headerCount++; + fieldBuilder.reset(); + readRawField(); + try { + RawField rawfield = fieldBuilder.build(); + if (rawfield == null) { + continue; + } + if (rawfield.getDelimiterIdx() != rawfield.getName().length()) { + monitor(Event.OBSOLETE_HEADER); + } + Field newfield = body.addField(rawfield); + if (newfield != null) field = newfield; + else field = rawfield; + return true; + } catch (MimeException e) { + monitor(Event.INVALID_HEADER); + if (config.isMalformedHeaderStartsBody()) { + LineReaderInputStream instream = getDataStream(); + ByteArrayBuffer buf = fieldBuilder.getRaw(); + // Complain, if raw data is not available or cannot be 'unread' + if (buf == null || !instream.unread(buf)) { + throw new MimeParseEventException(Event.INVALID_HEADER); + } + return false; + } + } + } + } + public EntityStateMachine advance() throws IOException, MimeException { switch (state) { case T_START_MESSAGE: @@ -213,7 +357,7 @@ class MimeEntity extends AbstractEntity private void createMimePartStream() throws MimeException, IOException { String boundary = body.getBoundary(); try { - currentMimePartStream = new MimeBoundaryInputStream(inbuffer, boundary, + currentMimePartStream = new MimeBoundaryInputStream(inbuffer, boundary, config.isStrictParsing()); } catch (IllegalArgumentException e) { // thrown when boundary is too long @@ -293,6 +437,45 @@ class MimeEntity extends AbstractEntity } /** + *

Gets a descriptor for the current entity. + * This method is valid if {@link #getState()} returns:

+ *
    + *
  • {@link EntityState#T_BODY}
  • + *
  • {@link EntityState#T_START_MULTIPART}
  • + *
  • {@link EntityState#T_EPILOGUE}
  • + *
  • {@link EntityState#T_PREAMBLE}
  • + *
+ * @return BodyDescriptor, not nulls + */ + public BodyDescriptor getBodyDescriptor() { + switch (getState()) { + case T_BODY: + case T_START_MULTIPART: + case T_PREAMBLE: + case T_EPILOGUE: + case T_END_OF_STREAM: + return body; + default: + throw new IllegalStateException("Invalid state :" + stateToString(state)); + } + } + + /** + * This method is valid, if {@link #getState()} returns {@link EntityState#T_FIELD}. + * @return String with the fields raw contents. + * @throws IllegalStateException {@link #getState()} returns another + * value than {@link EntityState#T_FIELD}. + */ + public Field getField() { + switch (getState()) { + case T_FIELD: + return field; + default: + throw new IllegalStateException("Invalid state :" + stateToString(state)); + } + } + + /** * @see org.apache.james.mime4j.stream.EntityStateMachine#getContentStream() */ public InputStream getContentStream() { @@ -314,4 +497,67 @@ class MimeEntity extends AbstractEntity return decodedStream(getContentStream()); } + @Override + public String toString() { + return getClass().getName() + " [" + stateToString(state) + + "][" + body.getMimeType() + "][" + body.getBoundary() + "]"; + } + + /** + * Renders a state as a string suitable for logging. + * @param state + * @return rendered as string, not null + */ + public static final String stateToString(EntityState 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 = "Preamble"; + 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; + default: + result = "Unknown"; + break; + } + return result; + } + } Modified: james/mime4j/trunk/core/src/main/java/org/apache/james/mime4j/stream/MimeTokenStream.java URL: http://svn.apache.org/viewvc/james/mime4j/trunk/core/src/main/java/org/apache/james/mime4j/stream/MimeTokenStream.java?rev=1138912&r1=1138911&r2=1138912&view=diff ============================================================================== --- james/mime4j/trunk/core/src/main/java/org/apache/james/mime4j/stream/MimeTokenStream.java (original) +++ james/mime4j/trunk/core/src/main/java/org/apache/james/mime4j/stream/MimeTokenStream.java Thu Jun 23 15:01:02 2011 @@ -391,7 +391,7 @@ public class MimeTokenStream { * @return rendered as string, not null */ public static final String stateToString(EntityState state) { - return AbstractEntity.stateToString(state); + return MimeEntity.stateToString(state); }