Author: bago
Date: Tue Sep 4 14:56:58 2007
New Revision: 572819
URL: http://svn.apache.org/viewvc?rev=572819&view=rev
Log:
Let BodyDescriptor be an interface and move current behavior to
DefaultBodyDescriptor (MIME4J-22)
- [JW#2] Let BodyDescriptor provide full blown access to the headers
- Patch submitted by Jochen Wiedmann
Added:
james/mime4j/trunk/src/main/java/org/apache/james/mime4j/DefaultBodyDescriptor.java
(with props)
james/mime4j/trunk/src/main/java/org/apache/james/mime4j/util/MimeUtil.java
(with props)
Modified:
james/mime4j/trunk/src/main/java/org/apache/james/mime4j/BodyDescriptor.java
james/mime4j/trunk/src/main/java/org/apache/james/mime4j/MimeTokenStream.java
james/mime4j/trunk/src/main/java/org/apache/james/mime4j/SimpleContentHandler.java
james/mime4j/trunk/src/test/java/org/apache/james/mime4j/BodyDescriptorTest.java
Modified:
james/mime4j/trunk/src/main/java/org/apache/james/mime4j/BodyDescriptor.java
URL:
http://svn.apache.org/viewvc/james/mime4j/trunk/src/main/java/org/apache/james/mime4j/BodyDescriptor.java?rev=572819&r1=572818&r2=572819&view=diff
==============================================================================
---
james/mime4j/trunk/src/main/java/org/apache/james/mime4j/BodyDescriptor.java
(original)
+++
james/mime4j/trunk/src/main/java/org/apache/james/mime4j/BodyDescriptor.java
Tue Sep 4 14:56:58 2007
@@ -19,12 +19,8 @@
package org.apache.james.mime4j;
-import java.util.HashMap;
import java.util.Map;
-import org.apache.commons.logging.Log;
-import org.apache.commons.logging.LogFactory;
-
/**
* Encapsulates the values of the MIME-specific header fields
* (which starts with <code>Content-</code>).
@@ -32,379 +28,53 @@
*
* @version $Id: BodyDescriptor.java,v 1.4 2005/02/11 10:08:37 ntherning Exp $
*/
-public class BodyDescriptor {
- private static Log log = LogFactory.getLog(BodyDescriptor.class);
-
- private String mimeType = "text/plain";
- private String boundary = null;
- private String charset = "us-ascii";
- private String transferEncoding = "7bit";
- private Map parameters = new HashMap();
- private boolean contentTypeSet = false;
- private boolean contentTransferEncSet = false;
-
+public interface BodyDescriptor {
/**
- * Creates a new root <code>BodyDescriptor</code> instance.
+ * Returns the body descriptors boundary.
+ * @return Boundary string, if known, or null. The latter may be the
+ * case, in particular, if the body is no multipart entity.
*/
- public BodyDescriptor() {
- this(null);
- }
+ String getBoundary();
/**
- * Creates a new <code>BodyDescriptor</code> instance.
- *
- * @param parent the descriptor of the parent or <code>null</code> if this
- * is the root descriptor.
- */
- public BodyDescriptor(BodyDescriptor parent) {
- if (parent != null && parent.isMimeType("multipart/digest")) {
- mimeType = "message/rfc822";
- } else {
- mimeType = "text/plain";
- }
- }
-
- /**
- * Should be called for each <code>Content-</code> header field of
- * a MIME message or part.
- *
- * @param name the field name.
- * @param value the field value.
+ * Adds a field to the body descriptor.
+ * @param pFieldName The fields name.
+ * @param pFieldValue The unparsed fields value.
*/
- public void addField(String name, String value) {
-
- name = name.trim().toLowerCase();
-
- if (name.equals("content-transfer-encoding") &&
!contentTransferEncSet) {
- contentTransferEncSet = true;
-
- value = value.trim().toLowerCase();
- if (value.length() > 0) {
- transferEncoding = value;
- }
-
- } else if (name.equals("content-type") && !contentTypeSet) {
- contentTypeSet = true;
-
- value = value.trim();
-
- /*
- * Unfold Content-Type value
- */
- StringBuffer sb = new StringBuffer();
- for (int i = 0; i < value.length(); i++) {
- char c = value.charAt(i);
- if (c == '\r' || c == '\n') {
- continue;
- }
- sb.append(c);
- }
-
- Map params = getHeaderParams(sb.toString());
-
- String main = (String) params.get("");
- if (main != null) {
- main = main.toLowerCase().trim();
- int index = main.indexOf('/');
- boolean valid = false;
- if (index != -1) {
- String type = main.substring(0, index).trim();
- String subtype = main.substring(index + 1).trim();
- if (type.length() > 0 && subtype.length() > 0) {
- main = type + "/" + subtype;
- valid = true;
- }
- }
-
- if (!valid) {
- main = null;
- }
- }
- String b = (String) params.get("boundary");
-
- if (main != null
- && ((main.startsWith("multipart/") && b != null)
- || !main.startsWith("multipart/"))) {
-
- mimeType = main;
- }
-
- if (isMultipart()) {
- boundary = b;
- }
-
- String c = (String) params.get("charset");
- if (c != null) {
- c = c.trim();
- if (c.length() > 0) {
- charset = c.toLowerCase();
- }
- }
-
- /*
- * Add all other parameters to parameters.
- */
- parameters.putAll(params);
- parameters.remove("");
- parameters.remove("boundary");
- parameters.remove("charset");
- }
- }
-
- private Map getHeaderParams(String headerValue) {
- Map result = new HashMap();
-
- // split main value and parameters
- String main;
- String rest;
- if (headerValue.indexOf(";") == -1) {
- main = headerValue;
- rest = null;
- } else {
- main = headerValue.substring(0, headerValue.indexOf(";"));
- rest = headerValue.substring(main.length() + 1);
- }
-
- result.put("", main);
- if (rest != null) {
- char[] chars = rest.toCharArray();
- StringBuffer paramName = new StringBuffer();
- StringBuffer paramValue = new StringBuffer();
-
- final byte READY_FOR_NAME = 0;
- final byte IN_NAME = 1;
- final byte READY_FOR_VALUE = 2;
- final byte IN_VALUE = 3;
- final byte IN_QUOTED_VALUE = 4;
- final byte VALUE_DONE = 5;
- final byte ERROR = 99;
-
- byte state = READY_FOR_NAME;
- boolean escaped = false;
- for (int i = 0; i < chars.length; i++) {
- char c = chars[i];
-
- switch (state) {
- case ERROR:
- if (c == ';')
- state = READY_FOR_NAME;
- break;
-
- case READY_FOR_NAME:
- if (c == '=') {
- log.error("Expected header param name, got '='");
- state = ERROR;
- break;
- }
-
- paramName = new StringBuffer();
- paramValue = new StringBuffer();
-
- state = IN_NAME;
- // fall-through
-
- case IN_NAME:
- if (c == '=') {
- if (paramName.length() == 0)
- state = ERROR;
- else
- state = READY_FOR_VALUE;
- break;
- }
-
- // not '='... just add to name
- paramName.append(c);
- break;
-
- case READY_FOR_VALUE:
- boolean fallThrough = false;
- switch (c) {
- case ' ':
- case '\t':
- break; // ignore spaces, especially before '"'
-
- case '"':
- state = IN_QUOTED_VALUE;
- break;
-
- default:
- state = IN_VALUE;
- fallThrough = true;
- break;
- }
- if (!fallThrough)
- break;
-
- // fall-through
-
- case IN_VALUE:
- fallThrough = false;
- switch (c) {
- case ';':
- case ' ':
- case '\t':
- result.put(
- paramName.toString().trim().toLowerCase(),
- paramValue.toString().trim());
- state = VALUE_DONE;
- fallThrough = true;
- break;
- default:
- paramValue.append(c);
- break;
- }
- if (!fallThrough)
- break;
-
- case VALUE_DONE:
- switch (c) {
- case ';':
- state = READY_FOR_NAME;
- break;
+ void addField(String pFieldName, String pFieldValue);
- case ' ':
- case '\t':
- break;
-
- default:
- state = ERROR;
- break;
- }
- break;
-
- case IN_QUOTED_VALUE:
- switch (c) {
- case '"':
- if (!escaped) {
- // don't trim quoted strings; the spaces
could be intentional.
- result.put(
-
paramName.toString().trim().toLowerCase(),
- paramValue.toString());
- state = VALUE_DONE;
- } else {
- escaped = false;
- paramValue.append(c);
- }
- break;
-
- case '\\':
- if (escaped) {
- paramValue.append('\\');
- }
- escaped = !escaped;
- break;
-
- default:
- if (escaped) {
- paramValue.append('\\');
- }
- escaped = false;
- paramValue.append(c);
- break;
- }
- break;
-
- }
- }
-
- // done looping. check if anything is left over.
- if (state == IN_VALUE) {
- result.put(
- paramName.toString().trim().toLowerCase(),
- paramValue.toString().trim());
- }
- }
-
- return result;
- }
-
-
- public boolean isMimeType(String mimeType) {
- return this.mimeType.equals(mimeType.toLowerCase());
- }
-
- /**
- * Return true if the BodyDescriptor belongs to a message
- *
- * @return
- */
- public boolean isMessage() {
- return mimeType.equals("message/rfc822");
- }
-
- /**
- * Retrun true if the BodyDescripotro belogns to a multipart
- *
- * @return
- */
- public boolean isMultipart() {
- return mimeType.startsWith("multipart/");
- }
-
- /**
- * Return the MimeType
- *
- * @return mimeType
- */
- public String getMimeType() {
- return mimeType;
- }
-
- /**
- * Return the boundary
- *
- * @return boundary
- */
- public String getBoundary() {
- return boundary;
- }
-
/**
- * Return the charset
- *
- * @return charset
+ * Returns the body descriptors MIME type.
+ * @return The MIME type, which has been parsed from the
+ * content-type definition. Must not be null, but
+ * "text/plain", if no content-type was specified.
*/
- public String getCharset() {
- return charset;
- }
-
+ String getMimeType();
+
/**
- * Return all parameters for the BodyDescriptor
- *
- * @return parameters
+ * The body descriptors character set.
+ * @return Character set, which has been parsed from the
+ * content-type definition. Must not be null, but "US-ASCII",
+ * if no content-type was specified.
*/
- public Map getParameters() {
- return parameters;
- }
-
+ String getCharset();
+
/**
- * Return the TransferEncoding
- *
- * @return transferEncoding
+ * Returns the body descriptors transfer encoding.
+ * @return The transfer encoding. Must not be null, but "7bit",
+ * if no transfer-encoding was specified.
*/
- public String getTransferEncoding() {
- return transferEncoding;
- }
-
+ String getTransferEncoding();
+
/**
- * Return true if it's base64 encoded
- *
- * @return
- *
+ * Returns the map of parameters of the content-type header.
*/
- public boolean isBase64Encoded() {
- return "base64".equals(transferEncoding);
- }
-
+ Map getParameters();
+
/**
- * Return true if it's quoted-printable
- * @return
+ * Returns the body descriptors content-length.
+ * @return Content length, if known, or -1, to indicate the absence of a
+ * content-length header.
*/
- public boolean isQuotedPrintableEncoded() {
- return "quoted-printable".equals(transferEncoding);
- }
-
- public String toString() {
- return mimeType;
- }
+ long getContentLength();
}
Added:
james/mime4j/trunk/src/main/java/org/apache/james/mime4j/DefaultBodyDescriptor.java
URL:
http://svn.apache.org/viewvc/james/mime4j/trunk/src/main/java/org/apache/james/mime4j/DefaultBodyDescriptor.java?rev=572819&view=auto
==============================================================================
---
james/mime4j/trunk/src/main/java/org/apache/james/mime4j/DefaultBodyDescriptor.java
(added)
+++
james/mime4j/trunk/src/main/java/org/apache/james/mime4j/DefaultBodyDescriptor.java
Tue Sep 4 14:56:58 2007
@@ -0,0 +1,213 @@
+/*
+ * 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.util.HashMap;
+import java.util.Map;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.apache.james.mime4j.util.MimeUtil;
+
+/**
+ * Encapsulates the values of the MIME-specific header fields
+ * (which starts with <code>Content-</code>).
+ *
+ *
+ * @version $Id: BodyDescriptor.java,v 1.4 2005/02/11 10:08:37 ntherning Exp $
+ */
+public class DefaultBodyDescriptor implements BodyDescriptor {
+ private static Log log = LogFactory.getLog(DefaultBodyDescriptor.class);
+
+ private String mimeType = "text/plain";
+ private String boundary = null;
+ private String charset = "us-ascii";
+ private String transferEncoding = "7bit";
+ private Map parameters = new HashMap();
+ private boolean contentTypeSet;
+ private boolean contentTransferEncSet;
+ private long contentLength = -1;
+
+ /**
+ * Creates a new root <code>BodyDescriptor</code> instance.
+ */
+ public DefaultBodyDescriptor() {
+ this(null);
+ }
+
+ /**
+ * Creates a new <code>BodyDescriptor</code> instance.
+ *
+ * @param parent the descriptor of the parent or <code>null</code> if this
+ * is the root descriptor.
+ */
+ public DefaultBodyDescriptor(BodyDescriptor parent) {
+ if (parent != null && MimeUtil.isSameMimeType("multipart/digest",
parent.getMimeType())) {
+ mimeType = "message/rfc822";
+ } else {
+ mimeType = "text/plain";
+ }
+ }
+
+ /**
+ * Should be called for each <code>Content-</code> header field of
+ * a MIME message or part.
+ *
+ * @param name the field name.
+ * @param value the field value.
+ */
+ public void addField(String name, String value) {
+
+ name = name.trim().toLowerCase();
+
+ if (name.equals("content-transfer-encoding") &&
!contentTransferEncSet) {
+ contentTransferEncSet = true;
+
+ value = value.trim().toLowerCase();
+ if (value.length() > 0) {
+ transferEncoding = value;
+ }
+
+ } else if (name.equals("content-length") && contentLength != -1) {
+ try {
+ contentLength = Long.parseLong(value.trim());
+ } catch (NumberFormatException e) {
+ log.error("Invalid content-length: " + value);
+ }
+ } else if (name.equals("content-type") && !contentTypeSet) {
+ contentTypeSet = true;
+
+ value = value.trim();
+
+ /*
+ * Unfold Content-Type value
+ */
+ StringBuffer sb = new StringBuffer();
+ for (int i = 0; i < value.length(); i++) {
+ char c = value.charAt(i);
+ if (c == '\r' || c == '\n') {
+ continue;
+ }
+ sb.append(c);
+ }
+
+ Map params = MimeUtil.getHeaderParams(sb.toString());
+
+ String main = (String) params.get("");
+ if (main != null) {
+ main = main.toLowerCase().trim();
+ int index = main.indexOf('/');
+ boolean valid = false;
+ if (index != -1) {
+ String type = main.substring(0, index).trim();
+ String subtype = main.substring(index + 1).trim();
+ if (type.length() > 0 && subtype.length() > 0) {
+ main = type + "/" + subtype;
+ valid = true;
+ }
+ }
+
+ if (!valid) {
+ main = null;
+ }
+ }
+ String b = (String) params.get("boundary");
+
+ if (main != null
+ && ((main.startsWith("multipart/") && b != null)
+ || !main.startsWith("multipart/"))) {
+
+ mimeType = main;
+ }
+
+ if (MimeUtil.isMultipart(mimeType)) {
+ boundary = b;
+ }
+
+ String c = (String) params.get("charset");
+ if (c != null) {
+ c = c.trim();
+ if (c.length() > 0) {
+ charset = c.toLowerCase();
+ }
+ }
+
+ /*
+ * Add all other parameters to parameters.
+ */
+ parameters.putAll(params);
+ parameters.remove("");
+ parameters.remove("boundary");
+ parameters.remove("charset");
+ }
+ }
+
+ /**
+ * Return the MimeType
+ *
+ * @return mimeType
+ */
+ public String getMimeType() {
+ return mimeType;
+ }
+
+ /**
+ * Return the boundary
+ *
+ * @return boundary
+ */
+ public String getBoundary() {
+ return boundary;
+ }
+
+ /**
+ * Return the charset
+ *
+ * @return charset
+ */
+ public String getCharset() {
+ return charset;
+ }
+
+ /**
+ * Return all parameters for the BodyDescriptor
+ *
+ * @return parameters
+ */
+ public Map getParameters() {
+ return parameters;
+ }
+
+ /**
+ * Return the TransferEncoding
+ *
+ * @return transferEncoding
+ */
+ public String getTransferEncoding() {
+ return transferEncoding;
+ }
+
+ public String toString() {
+ return mimeType;
+ }
+
+ public long getContentLength() {
+ return contentLength;
+ }
+}
Propchange:
james/mime4j/trunk/src/main/java/org/apache/james/mime4j/DefaultBodyDescriptor.java
------------------------------------------------------------------------------
svn:mime-type = text/plain
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=572819&r1=572818&r2=572819&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 Sep 4 14:56:58 2007
@@ -27,6 +27,7 @@
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
+import org.apache.james.mime4j.util.MimeUtil;
/**
@@ -249,14 +250,16 @@
setParsingFieldState();
break;
case T_END_HEADER:
- if (body.isMultipart()) {
+ final String mimeType = body.getMimeType();
+ if (MimeUtil.isMultipart(mimeType)) {
state = T_START_MULTIPART;
- } else if (body.isMessage()) {
+ } else if (MimeUtil.isMessage(mimeType)) {
Cursor nextCursor = cursor;
- if (body.isBase64Encoded()) {
+ final String transferEncoding =
body.getTransferEncoding();
+ if (MimeUtil.isBase64Encoding(transferEncoding)) {
log.debug("base64 encoded message/rfc822
detected");
nextCursor = cursor.decodeBase64();
- } else if (body.isQuotedPrintableEncoded()) {
+ } else if
(MimeUtil.isQuotedPrintableEncoded(transferEncoding)) {
log.debug("quoted-printable encoded message/rfc822
detected");
nextCursor = cursor.decodeQuotedPrintable();
}
@@ -294,7 +297,7 @@
}
private void initHeaderParsing() throws IOException {
- body = new BodyDescriptor(parent);
+ body = newBodyDescriptor(parent);
startLineNumber = lineNumber = cursor.getLineNumber();
int curr = 0;
@@ -561,5 +564,14 @@
}
state = T_END_OF_STREAM;
return state;
+ }
+
+ /**
+ * Creates a new instance of [EMAIL PROTECTED] BodyDescriptor}. Subclasses
may override
+ * this in order to create body descriptors, that provide more specific
+ * information.
+ */
+ protected BodyDescriptor newBodyDescriptor(BodyDescriptor pParent) {
+ return new DefaultBodyDescriptor(pParent);
}
}
Modified:
james/mime4j/trunk/src/main/java/org/apache/james/mime4j/SimpleContentHandler.java
URL:
http://svn.apache.org/viewvc/james/mime4j/trunk/src/main/java/org/apache/james/mime4j/SimpleContentHandler.java?rev=572819&r1=572818&r2=572819&view=diff
==============================================================================
---
james/mime4j/trunk/src/main/java/org/apache/james/mime4j/SimpleContentHandler.java
(original)
+++
james/mime4j/trunk/src/main/java/org/apache/james/mime4j/SimpleContentHandler.java
Tue Sep 4 14:56:58 2007
@@ -23,6 +23,7 @@
import org.apache.james.mime4j.decoder.QuotedPrintableInputStream;
import org.apache.james.mime4j.field.Field;
import org.apache.james.mime4j.message.Header;
+import org.apache.james.mime4j.util.MimeUtil;
import java.io.InputStream;
import java.io.IOException;
@@ -87,10 +88,10 @@
* @see
org.apache.james.mime4j.AbstractContentHandler#body(org.apache.james.mime4j.BodyDescriptor,
java.io.InputStream)
*/
public final void body(BodyDescriptor bd, InputStream is) throws
IOException {
- if (bd.isBase64Encoded()) {
+ if (MimeUtil.isBase64Encoding(bd.getTransferEncoding())) {
bodyDecoded(bd, new Base64InputStream(is));
}
- else if (bd.isQuotedPrintableEncoded()) {
+ else if (MimeUtil.isQuotedPrintableEncoded(bd.getTransferEncoding())) {
bodyDecoded(bd, new QuotedPrintableInputStream(is));
}
else {
Added:
james/mime4j/trunk/src/main/java/org/apache/james/mime4j/util/MimeUtil.java
URL:
http://svn.apache.org/viewvc/james/mime4j/trunk/src/main/java/org/apache/james/mime4j/util/MimeUtil.java?rev=572819&view=auto
==============================================================================
--- james/mime4j/trunk/src/main/java/org/apache/james/mime4j/util/MimeUtil.java
(added)
+++ james/mime4j/trunk/src/main/java/org/apache/james/mime4j/util/MimeUtil.java
Tue Sep 4 14:56:58 2007
@@ -0,0 +1,229 @@
+package org.apache.james.mime4j.util;
+
+import java.util.HashMap;
+import java.util.Map;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+
+
+/**
+ * A utility class, which provides some MIME related application logic,
+ * depending on abstract objects like the [EMAIL PROTECTED] BodyDescriptor}.
+ */
+public class MimeUtil {
+ private static final Log log = LogFactory.getLog(MimeUtil.class);
+
+ /**
+ * Returns, whether the given two MIME types are identical.
+ */
+ public static boolean isSameMimeType(String pType1, String pType2) {
+ return pType1 != null && pType2 != null &&
pType1.equalsIgnoreCase(pType2);
+ }
+
+ /**
+ * Returns true, if the given MIME type is that of a message.
+ */
+ public static boolean isMessage(String pMimeType) {
+ return pMimeType != null &&
pMimeType.equalsIgnoreCase("message/rfc822");
+ }
+
+ /**
+ * Return true, if the given MIME type indicates a multipart entity.
+ */
+ public static boolean isMultipart(String pMimeType) {
+ return pMimeType != null &&
pMimeType.toLowerCase().startsWith("multipart/");
+ }
+
+ /**
+ * Returns, whether the given transfer-encoding is "base64".
+ */
+ public static boolean isBase64Encoding(String pTransferEncoding) {
+ return "base64".equalsIgnoreCase(pTransferEncoding);
+ }
+
+ /**
+ * Returns, whether the given transfer-encoding is "quoted-printable".
+ */
+ public static boolean isQuotedPrintableEncoded(String pTransferEncoding) {
+ return "quoted-printable".equals(pTransferEncoding);
+ }
+
+ /**
+ * Parses a complex field value into a map of key/value pairs. You may
+ * use this, for example, to parse a definition like
+ * <pre>
+ * text/plain; charset=UTF-8; boundary=foobar
+ * </pre>
+ * The above example would return a map with the keys "", "charset",
+ * and "boundary", and the values "text/plain", "UTF-8", and "foobar".
+ * @param pValue The field value to parse.
+ * @return The result map; use the key "" to retrieve the first value.
+ */
+ public static Map getHeaderParams(String pValue) {
+ Map result = new HashMap();
+
+ // split main value and parameters
+ String main;
+ String rest;
+ if (pValue.indexOf(";") == -1) {
+ main = pValue;
+ rest = null;
+ } else {
+ main = pValue.substring(0, pValue.indexOf(";"));
+ rest = pValue.substring(main.length() + 1);
+ }
+
+ result.put("", main);
+ if (rest != null) {
+ char[] chars = rest.toCharArray();
+ StringBuffer paramName = new StringBuffer();
+ StringBuffer paramValue = new StringBuffer();
+
+ final byte READY_FOR_NAME = 0;
+ final byte IN_NAME = 1;
+ final byte READY_FOR_VALUE = 2;
+ final byte IN_VALUE = 3;
+ final byte IN_QUOTED_VALUE = 4;
+ final byte VALUE_DONE = 5;
+ final byte ERROR = 99;
+
+ byte state = READY_FOR_NAME;
+ boolean escaped = false;
+ for (int i = 0; i < chars.length; i++) {
+ char c = chars[i];
+
+ switch (state) {
+ case ERROR:
+ if (c == ';')
+ state = READY_FOR_NAME;
+ break;
+
+ case READY_FOR_NAME:
+ if (c == '=') {
+ log.error("Expected header param name, got '='");
+ state = ERROR;
+ break;
+ }
+
+ paramName = new StringBuffer();
+ paramValue = new StringBuffer();
+
+ state = IN_NAME;
+ // fall-through
+
+ case IN_NAME:
+ if (c == '=') {
+ if (paramName.length() == 0)
+ state = ERROR;
+ else
+ state = READY_FOR_VALUE;
+ break;
+ }
+
+ // not '='... just add to name
+ paramName.append(c);
+ break;
+
+ case READY_FOR_VALUE:
+ boolean fallThrough = false;
+ switch (c) {
+ case ' ':
+ case '\t':
+ break; // ignore spaces, especially before '"'
+
+ case '"':
+ state = IN_QUOTED_VALUE;
+ break;
+
+ default:
+ state = IN_VALUE;
+ fallThrough = true;
+ break;
+ }
+ if (!fallThrough)
+ break;
+
+ // fall-through
+
+ case IN_VALUE:
+ fallThrough = false;
+ switch (c) {
+ case ';':
+ case ' ':
+ case '\t':
+ result.put(
+ paramName.toString().trim().toLowerCase(),
+ paramValue.toString().trim());
+ state = VALUE_DONE;
+ fallThrough = true;
+ break;
+ default:
+ paramValue.append(c);
+ break;
+ }
+ if (!fallThrough)
+ break;
+
+ case VALUE_DONE:
+ switch (c) {
+ case ';':
+ state = READY_FOR_NAME;
+ break;
+
+ case ' ':
+ case '\t':
+ break;
+
+ default:
+ state = ERROR;
+ break;
+ }
+ break;
+
+ case IN_QUOTED_VALUE:
+ switch (c) {
+ case '"':
+ if (!escaped) {
+ // don't trim quoted strings; the spaces
could be intentional.
+ result.put(
+
paramName.toString().trim().toLowerCase(),
+ paramValue.toString());
+ state = VALUE_DONE;
+ } else {
+ escaped = false;
+ paramValue.append(c);
+ }
+ break;
+
+ case '\\':
+ if (escaped) {
+ paramValue.append('\\');
+ }
+ escaped = !escaped;
+ break;
+
+ default:
+ if (escaped) {
+ paramValue.append('\\');
+ }
+ escaped = false;
+ paramValue.append(c);
+ break;
+ }
+ break;
+
+ }
+ }
+
+ // done looping. check if anything is left over.
+ if (state == IN_VALUE) {
+ result.put(
+ paramName.toString().trim().toLowerCase(),
+ paramValue.toString().trim());
+ }
+ }
+
+ return result;
+ }
+}
Propchange:
james/mime4j/trunk/src/main/java/org/apache/james/mime4j/util/MimeUtil.java
------------------------------------------------------------------------------
svn:mime-type = text/plain
Modified:
james/mime4j/trunk/src/test/java/org/apache/james/mime4j/BodyDescriptorTest.java
URL:
http://svn.apache.org/viewvc/james/mime4j/trunk/src/test/java/org/apache/james/mime4j/BodyDescriptorTest.java?rev=572819&r1=572818&r2=572819&view=diff
==============================================================================
---
james/mime4j/trunk/src/test/java/org/apache/james/mime4j/BodyDescriptorTest.java
(original)
+++
james/mime4j/trunk/src/test/java/org/apache/james/mime4j/BodyDescriptorTest.java
Tue Sep 4 14:56:58 2007
@@ -34,7 +34,7 @@
public void testGetParameters() {
BodyDescriptor bd = null;
- bd = new BodyDescriptor();
+ bd = new DefaultBodyDescriptor();
bd.addField("Content-Type ", "text/plain; charset=ISO-8859-1; "
+ "boundary=foo; param1=value1; param2=value2; param3=value3");
assertEquals(3, bd.getParameters().size());
@@ -42,7 +42,7 @@
assertEquals("value2", (String) bd.getParameters().get("param2"));
assertEquals("value3", (String) bd.getParameters().get("param3"));
- bd = new BodyDescriptor();
+ bd = new DefaultBodyDescriptor();
bd.addField("Content-Type ", "text/plain; param1=value1;
param2=value2;"
+ " param3=value3");
assertEquals(3, bd.getParameters().size());
@@ -50,7 +50,7 @@
assertEquals("value2", (String) bd.getParameters().get("param2"));
assertEquals("value3", (String) bd.getParameters().get("param3"));
- bd = new BodyDescriptor();
+ bd = new DefaultBodyDescriptor();
bd.addField("Content-Type ", "text/plain; "
+ "param1= \" value with\tspaces \" ; "
+ "param2=\"\\\"value4 with escaped \\\" \\\"\";");
@@ -62,7 +62,7 @@
* Make sure escaped characters (except ") are still escaped.
* The parameter value should be \n\"
*/
- bd = new BodyDescriptor();
+ bd = new DefaultBodyDescriptor();
bd.addField("Content-Type ", "text/plain; param=\"\\n\\\\\\\"\"");
assertEquals(1, bd.getParameters().size());
assertEquals("\\n\\\"", (String) bd.getParameters().get("param"));
@@ -74,7 +74,7 @@
/*
* Make sure that only the first Content-Type header added is used.
*/
- bd = new BodyDescriptor();
+ bd = new DefaultBodyDescriptor();
bd.addField("Content-Type ", "text/plain; charset=ISO-8859-1");
assertEquals("text/plain", bd.getMimeType());
assertEquals("iso-8859-1", bd.getCharset());
@@ -86,30 +86,30 @@
public void testGetMimeType() {
BodyDescriptor bd = null;
- bd = new BodyDescriptor();
+ bd = new DefaultBodyDescriptor();
bd.addField("Content-Type ", "text/PLAIN");
assertEquals("text/plain", bd.getMimeType());
- bd = new BodyDescriptor();
+ bd = new DefaultBodyDescriptor();
bd.addField("Content-Type ", "text/PLAIN;");
assertEquals("text/plain", bd.getMimeType());
- bd = new BodyDescriptor();
+ bd = new DefaultBodyDescriptor();
bd.addField("content-type", " TeXt / html ");
assertEquals("text/html", bd.getMimeType());
- bd = new BodyDescriptor();
+ bd = new DefaultBodyDescriptor();
bd.addField("CONTENT-TYPE", " x-app/yada ; param = yada");
assertEquals("x-app/yada", bd.getMimeType());
- bd = new BodyDescriptor();
+ bd = new DefaultBodyDescriptor();
bd.addField("CONTENT-TYPE", " yada");
assertEquals("text/plain", bd.getMimeType());
/*
* Make sure that only the first Content-Type header added is used.
*/
- bd = new BodyDescriptor();
+ bd = new DefaultBodyDescriptor();
bd.addField("Content-Type ", "text/plain");
assertEquals("text/plain", bd.getMimeType());
bd.addField("Content-Type ", "text/html");
@@ -121,18 +121,18 @@
BodyDescriptor child = null;
BodyDescriptor parent = null;
- parent = new BodyDescriptor();
+ parent = new DefaultBodyDescriptor();
parent.addField("Content-Type", "mutlipart/alternative; boundary=foo");
- child = new BodyDescriptor(parent);
+ child = new DefaultBodyDescriptor(parent);
assertEquals("text/plain", child.getMimeType());
child.addField("Content-Type", " child/type");
assertEquals("child/type", child.getMimeType());
- parent = new BodyDescriptor();
+ parent = new DefaultBodyDescriptor();
parent.addField("Content-Type", "multipart/digest; boundary=foo");
- child = new BodyDescriptor(parent);
+ child = new DefaultBodyDescriptor(parent);
assertEquals("message/rfc822", child.getMimeType());
child.addField("Content-Type", " child/type");
assertEquals("child/type", child.getMimeType());
@@ -145,12 +145,12 @@
/*
* Test charset.
*/
- bd = new BodyDescriptor();
+ bd = new DefaultBodyDescriptor();
assertEquals("us-ascii", bd.getCharset());
bd.addField("Content-Type ", "some/type; charset=ISO-8859-1");
assertEquals("iso-8859-1", bd.getCharset());
- bd = new BodyDescriptor();
+ bd = new DefaultBodyDescriptor();
assertEquals("us-ascii", bd.getCharset());
bd.addField("Content-Type ", "some/type");
assertEquals("us-ascii", bd.getCharset());
@@ -158,27 +158,27 @@
/*
* Test boundary.
*/
- bd = new BodyDescriptor();
+ bd = new DefaultBodyDescriptor();
bd.addField("Content-Type", "text/html; boundary=yada yada");
assertNull(bd.getBoundary());
- bd = new BodyDescriptor();
+ bd = new DefaultBodyDescriptor();
bd.addField("Content-Type", "multipart/yada; boundary=yada");
assertEquals("yada", bd.getBoundary());
/*
* Test some weird parameters.
*/
- bd = new BodyDescriptor();
+ bd = new DefaultBodyDescriptor();
bd.addField("Content-Type", "multipart/yada; boundary=yada yada");
assertEquals("yada", bd.getBoundary());
- bd = new BodyDescriptor();
+ bd = new DefaultBodyDescriptor();
bd.addField("Content-Type", "multipart/yada; boUNdarY= ya:*da;
\tcharset\t = big5");
assertEquals("ya:*da", bd.getBoundary());
assertEquals("big5", bd.getCharset());
- bd = new BodyDescriptor();
+ bd = new DefaultBodyDescriptor();
bd.addField("Content-Type", "multipart/yada; boUNdarY= \"ya
\\\"\\\"\tda \\\"\"; "
+ "\tcharset\t = \"\\\"hepp\\\" =us\t-ascii\"");
assertEquals("ya \"\"\tda \"", bd.getBoundary());
---------------------------------------------------------------------
To unsubscribe, e-mail: [EMAIL PROTECTED]
For additional commands, e-mail: [EMAIL PROTECTED]